Jump to content
OldAtAtari

My Atari Lynx Programming Journal

Recommended Posts

23 minutes ago, OldAtAtari said:

Ok, I'm trying to assemble the Ottelo game that Karri made available for download, and I'm getting an "Unsolved labels" error. Anyone know where I'm going wrong?

 

image.thumb.png.9e610868e57b6b0f2143b08fe8a2914c.png

These should be defined in hardware.inc . Please check this file.

 

  • Like 1

Share this post


Link to post
Share on other sites
30 minutes ago, OldAtAtari said:

Ok, I'm trying to assemble the Ottelo game that Karri made available for download, and I'm getting an "Unsolved labels" error. Anyone know where I'm going wrong?

 

image.thumb.png.9e610868e57b6b0f2143b08fe8a2914c.png

These should be defined in hardware.inc . You should include it at the top , see mines.asm.

Previous versions of the macros used hardcoded addresses instead of symbols.

 

  • Like 1

Share this post


Link to post
Share on other sites
36 minutes ago, 42bs said:

These should be defined in hardware.inc . You should include it at the top , see mines.asm.

Previous versions of the macros used hardcoded addresses instead of symbols.

 

Yep, that did it. Thank you so much!

 

image.thumb.png.1669cc28a4b8b507715ced76c11fc1aa.png

 

image.thumb.png.b6b5c3753bf63e4dce314213ee172fbb.png

Share this post


Link to post
Share on other sites

I've been bouncing around from thing to thing, playing with the otello, mines, and game.c examples. I've been hacking away at game.c. I wanted to see if I could get the road to be stationary while the tree moves around the screen without resizing. Mission accomplished. A silly little goal, but then if I can figure out how to get either of the two sprite generators (sprpck and sp65) working, I'll be able to change that tree into a car, a robot, or whatever.

Little milestones.

image.thumb.png.ef6c859b102397c8fbc25da8f6bb105f.png

 

 

 

 

 

 

Edited by OldAtAtari
Removing excess screenshots
  • Like 2

Share this post


Link to post
Share on other sites
2 hours ago, OldAtAtari said:

if I can figure out how to get either of the two sprite generators (sprpck and sp65) working, I'll be able to change that tree into a car, a robot, or whatever.

 

You can just exchange the tree.pcx file to any pcx file you want, the makefile takes care of the conversion. You can see in the makefile (in the same folder) the parameters sent to sp65

tree.c : tree.pcx
	$(SP) -r $< -c lynx-sprite,mode=packed,ax=94,ay=168 -w $*.c,ident=$*,bytesperline=8

The palette might get messed up, but in that case you can change around the numbers (pens) in the sprite definition (i.e. SCB i.e Sprite Control Block) in the pen palette array to point to the correct colors. The last line here:

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef} //pen palette array, change here
};

Every number is a pen of its own. 0x is just for telling the compiler that it's a hex number, so 0 is one pen, 1 is one pen, 2 is one pen etc.

The palette and the sprite SCB's are good to read up on:
https://atarilynxdeveloper.wordpress.com/2012/10/10/programming-tutorial-part-7the-basics-of-sprites/
https://atarilynxdeveloper.wordpress.com/2012/10/14/programming-tutorial-part-8changing-appearances/

At least Aseprite and Gimp can handle pcx files.

This post and thread has a lot of info on sp65:


If you want to add more sprites into your game, in game.c you need to add:

extern unsigned char robot[];

//add a SCB
static SCB_REHV_PAL Srobot = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    robot,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
};

//add coordinates
Srobot.vpos = y;
Srobot.hpos = x

//draw it
tgi_sprite(&Srobot);


Also you need to add your new sprite/graphic into the object list in the make file:

objects= \
	game.o \
	robot.o \
	tree.o

 

Edited by Turbo Laser Lynx
  • Like 1

Share this post


Link to post
Share on other sites
6 minutes ago, Turbo Laser Lynx said:

 

You can just exchange the tree.pcx file to any pcx file you want, the makefile takes care of the conversion. You can see in the makefile (in the same folder) the parameters sent to sp65

tree.c : tree.pcx
	$(SP) -r $< -c lynx-sprite,mode=packed,ax=94,ay=168 -w $*.c,ident=$*,bytesperline=8

The palette might get messed up, but in that case you can change around the numbers (pens) in the sprite definition (i.e. SCB i.e Sprite Control Block) in the pen palette array to point to the correct colors. The last line here:

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef} //pen palette array, change here
};

Every number is a pen of its own. 0x is just for telling the compiler that it's a hex number, so 0 is one pen, 1 is one pen, 2 is one pen etc.

The palette and the sprite SCB's are good to read up on:
https://atarilynxdeveloper.wordpress.com/2012/10/10/programming-tutorial-part-7the-basics-of-sprites/
https://atarilynxdeveloper.wordpress.com/2012/10/14/programming-tutorial-part-8changing-appearances/

At least Aseprite and Gimp can handle pcx files.

This post and thread has a lot of info on sp65:

 

Thank you! So much to learn. And there are also shortcuts, so that can keep it fun while I'm learning.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Turbo Laser Lynx said:

 

You can just exchange the tree.pcx file to any pcx file you want, the makefile takes care of the conversion. You can see in the makefile (in the same folder) the parameters sent to sp65

tree.c : tree.pcx
	$(SP) -r $< -c lynx-sprite,mode=packed,ax=94,ay=168 -w $*.c,ident=$*,bytesperline=8

The palette might get messed up, but in that case you can change around the numbers (pens) in the sprite definition (i.e. SCB i.e Sprite Control Block) in the pen palette array to point to the correct colors. The last line here:

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef} //pen palette array, change here
};

Every number is a pen of its own. 0x is just for telling the compiler that it's a hex number, so 0 is one pen, 1 is one pen, 2 is one pen etc.

The palette and the sprite SCB's are good to read up on:
https://atarilynxdeveloper.wordpress.com/2012/10/10/programming-tutorial-part-7the-basics-of-sprites/
https://atarilynxdeveloper.wordpress.com/2012/10/14/programming-tutorial-part-8changing-appearances/

At least Aseprite and Gimp can handle pcx files.

This post and thread has a lot of info on sp65:


If you want to add more sprites into your game, in game.c you need to add:

extern unsigned char robot[];

//add a SCB
static SCB_REHV_PAL Srobot = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    robot,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
};

//add coordinates
Srobot.vpos = y;
Srobot.hpos = x

//draw it
tgi_sprite(&Srobot);


Also you need to add your new sprite/graphic into the object list in the make file:

objects= \
	game.o \
	robot.o \
	tree.o

 

My questions never end...

 

I used Gimp to turn the tree into a little guy. I kept the same tree.pcx name. I just opened it in Gimp, edited the image, and then exported as tree.pcx.

 

But "Make" complains now:

 

Error: Error in PCX file `tree.pcx': 770 bytes at end of pixel data

 

Every seen anything like that before?

Share this post


Link to post
Share on other sites
1 hour ago, Turbo Laser Lynx said:

I forgot about that, the image mode has to be indexed (can be found under image/mode).:D

Hmmmm, "indexed" is turned on in the image/mode menu... Any other ideas? In the meantime, I'll randomly click buttons. 🙂

Share this post


Link to post
Share on other sites
2 hours ago, OldAtAtari said:

Hmmmm, "indexed" is turned on in the image/mode menu... Any other ideas? In the meantime, I'll randomly click buttons. 🙂

Ha! Ok, so this works in Windows Gimp, but not in Linux Gimp, for me anyway. Anyway, it's working. Thank you, Turbo Laser Lynx!

Edited by OldAtAtari
  • Like 1

Share this post


Link to post
Share on other sites

Ok, not my best work. But functional. The dude can walk around. Everything else is stationary. Onwards and upwards. Thanks again, Turbo Laser Lynx!

 

image.thumb.png.c8f1c159c2e89218a86692aa6d91a12f.png

Edited by OldAtAtari
  • Like 2

Share this post


Link to post
Share on other sites
7 hours ago, Turbo Laser Lynx said:

Woohoo! Glad to be of help! It's always the best feeling when you get a sprite-dude walking around the screen on a new retro system. Love that sprite! :D

🙂 Thanks! It was very exciting indeed! Next, I want to make him jump, and I want to make that tree move on its own horizontally across the screen, back and forth, or just scrolling endlessly. I want to see if the sprite dude can jump over, or on to, the tree. And that would pretty much be a game right there. 🙂 

Edited by OldAtAtari
  • Like 1

Share this post


Link to post
Share on other sites

Oh, yes, back to the sprite editing issue. My indexed .PCX wouldn't work in Gimp in Linux. But it works in Gimp in Windows 10.

 

Do any Linux users have a suggestion of how to get Linux Gimp to work, or what Linux drawing program to use instead?

 

Thanks.

Share this post


Link to post
Share on other sites

Getting there. Now the boy jumps as the tree moves from right to left. And there's a score that increments each time the boy jumps. There is no collision detection yet.

 

Code, messy with extra pieces and too few comments, but here for documentation. Feel free to pick it apart and criticize it:

#include <lynx.h>
#include <conio.h>
#include <joystick.h>
#include <tgi.h>
#include <stdlib.h>

unsigned char checkInput(void);
extern unsigned char reset; 

void lynx_snd_init ();
void lynx_snd_pause ();
void lynx_snd_continue ();
void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music);
void lynx_snd_stop ();

typedef struct {
    unsigned char *music0;
    unsigned char *music1;
    unsigned char *music2;
    unsigned char *music3;
} song_t;

extern song_t musicptr;

extern unsigned char tree[];

extern unsigned char odd_dude[];

static SCB_REHV_PAL Stree = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    tree,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
};


static SCB_REHV_PAL Sodd_dude = {
    BPP_4 | TYPE_NORMAL,
    0x10, 0x20,
    0,
    odd_dude,
    0, 0,
    0x0100, 0x100,
    {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
};

/* ----------------- Road -------------------- */
unsigned char pixel[] = { 3, 0x84, 0, 0 };

static SCB_REHVST_PAL Spixel = {
    BPP_1 | TYPE_BACKGROUND,
    0x30, 0x20,
    0,
    pixel,
    50, 50,
    0x2800, 0x800,
    0x00, 0x100,
    {0x68}
};

void polygon(int x1, int y1, int w1, int x2, int y2, int w2, unsigned char color)
{
    Spixel.hpos = x1;
    Spixel.vpos = y1;
    Spixel.hsize = w1 << 8;
    Spixel.vsize = (y2 - y1 + 1) << 8;
    Spixel.tilt = (x2 - x1) * 256 / (y2 - y1);
    Spixel.stretch = (w2 - w1) * 256 / (y2 - y1);
    Spixel.penpal[0] = color;
    tgi_sprite(&Spixel);
}

void
drawsegment(int lanes, int x1, int y1, int w1, int x2, int y2, int w2)
{
    int l1;
    int l2;
    int r1;
    int r2;
    int lanew1;
    int lanew2;
    int lanex1;
    int lanex2;
    int lane;
    l1 = w1 >> (lanes + 2);
    l2 = w2 >> (lanes + 2);
    r1 = w1 >> (lanes + 1);
    r2 = w2 >> (lanes + 1);
    // Draw left yellow rumble line
    polygon(x1 - w1 - r1 + 1, y1, r1, x2 - w2 - r2 + 1, y2, r2, COLOR_YELLOW);
    // Draw road
    polygon(x1 - w1, y1, w1, x2 - w2, y2, w2, COLOR_GREY);
    lanew1 = w1 * 2 / lanes;
    lanew2 = w2 * 2 / lanes;
    lanex1 = x1 - w1 + lanew1;
    lanex2 = x2 - w2 + lanew2;
    for (lane = 1; lane < lanes; lanex1 += lanew1, lanex2 += lanew2, lane++) {
        polygon(lanex1 - l1 / 2, y1, l1, lanex2 - l2 / 2, y2, l2, COLOR_WHITE);
    }
    polygon(x1 - 1 + l1 / 2, y1, w1, x2 - 1 + l2 / 2, y2, w2, COLOR_GREY);
    // Draw right yellow rumble line
    polygon(x1 + w1, y1, r1, x2 + w2, y2, r2, COLOR_YELLOW);
    // Draw right cleanup
    polygon(x1 + w1 + r1 - 1, y1, r1, x2 + w2 + r2 - 1, y2, r2, COLOR_DARKGREY);
}

void draw_tree (int x, int y, int scale)
{
    Stree.vpos = y;
    Stree.hpos = x + 0 * 256 / 256;
    Stree.vsize = 33;
    Stree.hsize = 33;
    tgi_sprite(&Stree);
//    Stree.hpos = x - 110 * 256 / 256;
//    tgi_sprite(&Stree);
}

void draw_dude (int dx, int dy, int scale)
{
    Sodd_dude.vpos = dy;
    Sodd_dude.hpos = dx + 0 * 256 / 256;
    Sodd_dude.vsize = 35;
    Sodd_dude.hsize = 35;
    tgi_sprite(&Sodd_dude);
//    Sodd_dude.hpos = dx - 110 * 256 / 256;
//    tgi_sprite(&Sodd_dude);
}

void
drawscreen(int x, int y, int dx, int dy, int turn, int score)
{
    int y2;
    int scale1, scale2;
    char textScore[20];
    itoa(score, textScore, 10);
    scale1 = 3 * y + 8;
    y2 = y + ((20 * scale1) >> 8);
    scale2 = 3 * y2 + 8;
    tgi_setcolor(COLOR_DARKGREY);
    tgi_bar(0, 0, 159, 101);
    tgi_setcolor(COLOR_YELLOW);
    tgi_outtextxy(120, 10, textScore);	
//    drawsegment(2, x, y, (80 * scale1) >> 8, x + turn, y2, (80 * scale2) >> 8);
    draw_tree(x, y, scale2);
    draw_dude(dx, dy, scale2);    
}

void game()
{
    int x = 160;
    int dx = 10;
    int y = 80;
    int dy = 80;
    int turn = 0;
	int jump = 0;
	int jumpUp = 25;
	int jumpDown = 25;
	int score = 0;

    while (!reset) { // never use while(1). Every loop needs to be halted by the reset event
        if (!tgi_busy()) {
            unsigned char joy;
            
// Instead of calling joy_read() directly, use checkInput() defined in resident.c . 
// It will return the joy state, but will handle pause and reset events too
            
//            joy = joy_read(JOY_1);
            joy = checkInput();
            if (JOY_BTN_UP(joy)) {
            }
            if (JOY_BTN_DOWN(joy)) {
            }
            if (JOY_BTN_RIGHT(joy)) {
                dx++;
                if (dx > 150) dx = 150;
            }
            if (JOY_BTN_LEFT(joy)) {
                dx--;
                if (dx < 10) dx = 10;
            }
            if (JOY_BTN_FIRE(joy) && jump == 0) {
                jump=1;
		lynx_snd_play(1, musicptr.music3);
		score++;
            }
	    if (jump == 1 && jumpUp != 0)
	    	{
	    	dy = dy - 2;
		jumpUp--;
		}
	    if (jump == 1 && jumpUp == 0 && jumpDown != 0)
	    	{
		dy = dy + 2;
		jumpDown--;
		}
	    if (jumpDown == 0)
	    	{
		jump = 0;
		jumpUp = 25;
		jumpDown = 25;
		}
            if (JOY_BTN_FIRE2(joy)) {
                turn--;
            }
	    if (x == 0)
	    	{
		x=160;
	    	}
	    x--;
            drawscreen(x, y, dx, dy, turn, score);
            tgi_updatedisplay();
        }
    }
}

 

image.thumb.png.eb8d7f51b9934cf5d4c325d21a3f1f46.png

Share this post


Link to post
Share on other sites

I suggest to use pre-increment/decrement instead of post.

Post action means to save the contents of the variable, then do the action and then pull the original contents back.

Modern C compilers do not care, but (new)cc65 might produce worse code.

Also, use char or unsigned char if the value fits into 8 bits.

  • Like 1

Share this post


Link to post
Share on other sites
34 minutes ago, 42bs said:

I suggest to use pre-increment/decrement instead of post.

Post action means to save the contents of the variable, then do the action and then pull the original contents back.

Modern C compilers do not care, but (new)cc65 might produce worse code.

Also, use char or unsigned char if the value fits into 8 bits.

Excellent thoughts, 42bs. Thank you!

Share this post


Link to post
Share on other sites
51 minutes ago, 42bs said:

I suggest to use pre-increment/decrement instead of post.

Post action means to save the contents of the variable, then do the action and then pull the original contents back.

Modern C compilers do not care, but (new)cc65 might produce worse code.

Also, use char or unsigned char if the value fits into 8 bits.

 

Is the following a proper pre-increment, as opposed to what I did before? Is it as simple as using ++x instead of x++? Thanks.
 

            if (JOY_BTN_FIRE(joy) && jump == 0) {
                jump=1;
		lynx_snd_play(1, musicptr.music3);
		++score;
            }

Share this post


Link to post
Share on other sites
Just now, OldAtAtari said:

 

Is the following a proper pre-increment, as opposed to what I did before? Is it as simple as using ++x instead of x++? Thanks.
 

            if (JOY_BTN_FIRE(joy) && jump == 0) {
                jump=1;
		lynx_snd_play(1, musicptr.music3);
		++score;
            }

Right. You can check in the assembly.

 

  • Like 1

Share this post


Link to post
Share on other sites
36 minutes ago, 42bs said:

Right. You can check in the assembly.

 

If you run my template just go to the directory where your C-code is. If your file is game.c you can type:

 

make game.s

 

This makes a human readable assembly listing that helps you see how C is turned into asm.

 

In @42bs example you would probably get:

 

;
; if (JOY_BTN_FIRE(joy) && jump == 0) {
;
    lda     _joy_masks+4
    and     _joy
    pha
    pla
    beq     L0287
    lda     _jump
    beq     L0288
L0287:  rts
;
; jump=1;
;
L0288:  lda     #$01
    sta     _jump
;
; lynx_snd_play(1, musicptr.music3);
;
    jsr     pusha
    lda     _musicptr+6
    ldx     _musicptr+6+1
    jsr     _lynx_snd_play
;
; ++score;
;
    inc     _score
;
; }
;
    rts

 

While post-incrementing would waste a few bytes more:

;
; score++;
;
    lda     _score
    ina
    sta     _score
;
; }
;
    rts
 

  • Like 1

Share this post


Link to post
Share on other sites
23 minutes ago, karri said:

If you run my template just go to the directory where your C-code is. If your file is game.c you can type:

 

make game.s

 

This makes a human readable assembly listing that helps you see how C is turned into asm.

 

In @42bs example you would probably get:

 

;
; if (JOY_BTN_FIRE(joy) && jump == 0) {
;
    lda     _joy_masks+4
    and     _joy
    pha
    pla
    beq     L0287
    lda     _jump
    beq     L0288
L0287:  rts
;
; jump=1;
;
L0288:  lda     #$01
    sta     _jump
;
; lynx_snd_play(1, musicptr.music3);
;
    jsr     pusha
    lda     _musicptr+6
    ldx     _musicptr+6+1
    jsr     _lynx_snd_play
;
; ++score;
;
    inc     _score
;
; }
;
    rts

 

While post-incrementing would waste a few bytes more:

;
; score++;
;
    lda     _score
    ina
    sta     _score
;
; }
;
    rts
 

I love that! I didn't know you could do that. I still want to learn assembly, so it will be great to see how it translates. Thank you!

Share this post


Link to post
Share on other sites
15 minutes ago, OldAtAtari said:

I love that! I didn't know you could do that. I still want to learn assembly, so it will be great to see how it translates. Thank you!

You can also edit the game.s and optimize it by hand. If it exists the compiler will use your edited version.

 

I often revert to the asm file when I am unhappy with performance of my game. So reading it is a greate tool for learning.

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, karri said:

You can also edit the game.s and optimize it by hand. If it exists the compiler will use your edited version.

 

I often revert to the asm file when I am unhappy with performance of my game. So reading it is a greate tool for learning.

Thanks for putting this template together, Karri. I'm learning a lot from it.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...