Jump to content
  • 0

Using Sprites (in C with CC65)?


rje
 Share

Question

I'm trying to define and show a sprite, in C, using CC65.  It's a mess and I'm not sure what I'm doing wrong, so I'm going to write it afresh here and see if anyone can see what I'm doing wrong.

I am thinking that the following code should be sufficient to define and display a sprite, assuming of course that VRAM has been initialized with the sprite data at location $4000.

First, I have to set the port address in VERA.  I'll use port 0.

(Version 2)

#define     SPRITE_REGISTERS(spritenum)    ((spritenum << 3) + 0xfc00)
//
// set port 0 address and increment
// 
VERA.control = 0;   // port 0
VERA.address = SPRITE_REGISTERS(1);
VERA.address_hi = VERA_INC_1 + 1; // the "+1" is the VRAM high address bit. 

// So $1FC08 begins the registers for sprite 1.  Right?

Now I should be able to define sprites.

// sprite blocks are in 32 byte chunks.
#define	    SPRITE_BLOCK(addr)			(addr >> 5)
#define     SPRITE_MODE_8BPP     		128
#define	    SPRITE_32_BY_32		        (128 + 32)
#define	    SPRITE_64_BY_64		        (196 + 48)

#define	    SPRITE_DISABLED		        0
#define	    SPRITE_LAYER_BACKGROUND		(1 << 2)
#define	    SPRITE_LAYER_0		        (2 << 2)
#define	    SPRITE_LAYER_1		        (3 << 2)

int block = SPRITE_BLOCK(0x4000); 
int mode = SPRITE_MODE_8BPP;
int x = 100;
int y = 100;
int z = SPRITE_LAYER_1;
int dimensions = SPRITE_32_BY_32;
int palette_offset = 0;

vera_sprites_enable(1); // cx16.h

VERA.data0 = block & 0xff; // lower VRAM address bits
VERA.data0 = mode + ((block >> 8) & 0x1f);
VERA.data0 = x & 0xff;
VERA.data0 = x >> 8;
VERA.data0 = y & 0xff;
VERA.data0 = y >> 8;
VERA.data0 = z;                 // leave collision mask and flips alone for now.
VERA.data0 = dimensions + palette_offset;

 

Edited by rje
Link to comment
Share on other sites

Recommended Posts

  • 0
3 hours ago, rje said:

I'm trying to define and show a sprite, in C, using CC65.  It's a mess and I'm not sure what I'm doing wrong, so I'm going to write it afresh here and see if anyone can see what I'm doing wrong.

I am thinking that the following code should be sufficient to define and display a sprite, assuming of course that VRAM has been initialized with the sprite data at location $4000.

First, I have to set the port address in VERA.  I'll use port 0.


#define     SPRITE_REGISTERS(spritenum)    ((spritenum << 3) + 0xfc00)
	//
// set port 0 address and increment
// 
VERA.control = 0;   // port 0
VERA.address = SPRITE_REGISTERS(1);
VERA.address_hi = VERA_INC_1 + 1; // the "+1" is the VRAM high address bit.  It's supposed to be 1, right??

 

Now I should be able to define sprites.

 


#define     MODE_8BPP         128

int block = 0x4000; // sprite data address = 16384
int mode = MODE_8BPP;
int x = 100;
int y = 100;
int z = 1;
int height = 32;
int width = 32;

VERA.data0 = block & 0xff; // lower VRAM address bits
VERA.data0 = mode + (block << 8);
VERA.data0 = x & 0xff;
VERA.data0 = x >> 8;
VERA.data0 = y & 0xff;
VERA.data0 = y >> 8;
VERA.data0 = z << 2; // leave collision mask and flips alone for now.
VERA.data0 = (height << 6) + (width << 4);

 

 

 


 

 

 

If you're getting the sprite data from $04000 then the VERA  high address bit is 0.

 

Height and width are both 32 pixels, so the bit values for the last byte in your attribute should be 10100000. But if you're starting out with 32 instead of 2 for both, 100000 shifted left 6 times shifts it right out of the byte. Set those Height and width values to 2 instead, or shift Height only twice and leave width alone.

  • Thanks 1
Link to comment
Share on other sites

  • 0

Now I'm doing something wrong.  I've loaded 64 x 64 sprite data into VERA at $8000 and up, like this

   // terrain at 0x8000
   loadVera("terrain/ocean.bin",             0x8000);
   loadVera("terrain/island-desert-x.bin",   0x8800);
   loadVera("terrain/island-grass-x.bin",    0x9000);
   loadVera("terrain/island-savannah-x.bin", 0x9800);
   loadVera("terrain/island-forest-x.bin",   0xa000);
   loadVera("terrain/island-hills-x.bin",    0xa800);
   loadVera("terrain/island-mountain-x.bin", 0xb000);

But when I define a 64 x 64 sprite and point it at 0x8000, it displays as noise.  A 64 x 64 bit sprite of noise.

When I move its block pointer down into the 32 x 32 sprite data, it shows the presence of that data -- it ain't pretty, but it proves that I know how to point sprites to their data.

It's like the load failed.  Why did the load fail, when the other 32 x 32 sprites loaded fine into $4000 through $7000?

They're all 8 BPP mode, so I can rule that out.

void initLand()
{
   sprdef.mode             = SPRITE_MODE_8BPP;
   sprdef.block            = 0x8800;
   sprdef.layer            = SPRITE_LAYER_1;
   sprdef.dimensions       = SPRITE_64_BY_64;
   sprdef.x                = 4000;             // this is ok; it's high on purpose.
   sprdef.y                = 4000;
   landpos.x               = sprdef.x;
   landpos.y               = sprdef.y;
   
   sprite_define(2, &sprdef);
}
Edited by rje
Link to comment
Share on other sites

  • 0
5 hours ago, Ed Minchau said:

A 64x64x8bpp sprite takes 64x64 bytes of data, or 16 pages of VRAM. I think you're only giving it 8 pages.

Thank you -- I'll check again, it's likely that that's done wrong.  Those files ARE 4K in size, so yeah, 16 pages are needed.

Even so, though, SOMETHING should be stored at those addresses.  

But yeah, I'm overlooking something in the sprite definition or the data load.

My BASIC code loads them two at a time from banked RAM like so:

rem ---------------------------------------
rem
rem  load ocean and land into VERA at $8000
rem
rem ---------------------------------------
vpoke %10000,$8000,0 :rem reset autoincrement
for bb=7 to 9 :rem banks 7 to 9
   poke $9f61,bb :rem point to the right bank
   for i=$a000 to $bfff
      poke $9f23,peek(i)
   next
next



So that's attempting to load VRAM at $8000, $9000, $a000, $b000, $c000, and $d000.

And it looks like it's still 8 BPP.  Actually I know it is.... huh do I though?

Yeah, yeah I do:

      VPOKE $F, S0+0, %00000000+ls    :REM LSB
      VPOKE $F, S0+1, %10000000+bk    :REM R...BBBB
      VPOKE $F, S0+2, X AND 255       :REM X
      VPOKE $F, S0+3, X  / 256        :REM X MSB
      VPOKE $F, S0+4, Y AND 255       :REM Y
      VPOKE $F, S0+5, Y  / 256        :REM Y MSB
      VPOKE $F, S0+6, %00000000+Z+FL  :REM coll,ZZ,FLIP
      VPOKE $F, S0+7, %11110000+PO    :REM HHWW..PO

The above code is in a land load loop.

S0 is the sprite register in question, so S0+1 includes the color depth, which is hard-coded to 8 BPP (%10000000).
I know for sure that land sprites are 64 x 64 because the last line has the height and width in the first nybble (%11110000).

So I'm missing something in the C code.

 

Edited by rje
Link to comment
Share on other sites

  • 0

Aha!  I found SOMETHING.  I just don't know what it is, yet.

I translated the load addresses for the land down to $4000 thru $9000.  And I can see land, although its color scheme is messed up.

So one or more loads are messing up VRAM somehow.

But, there's no data at $8000 and $9000, even though I attempt to load to VRAM at those addresses.

The palette is really screwy.  I wonder what I'm missing.  The palelette offset is 0.

Maybe these land sprites are 4 BPP.

 

AH!  I think they are!   That would explain a lot.

 

Edited by rje
Link to comment
Share on other sites

  • 0
2 hours ago, Ender said:

Have you tried actually using the emulator debugger to look at what's in VRAM? Sounds like that could be helpful to you.

What a great idea.  You know, I've never actually done that before, and I think that is an excellent idea.  Thank you.

 

@Ed - yes, as far as I can tell, it's the default palette.

 

Edited by rje
Link to comment
Share on other sites

  • 0
3 hours ago, Ender said:

Have you tried actually using the emulator debugger to look at what's in VRAM? Sounds like that could be helpful to you.

OK, so I have the debugger up.  I can see memory locations by using the m xxxx command -- for example, to see RAM bank 4 I can do this:

b ram 4   
m a000     (actually, this command appears to ignore the bank set and always displays bank 0)

Now VERA's memory is.... in VERA.  What's the debugger convention for displaying VERA?  The docs don't say (https://github.com/commanderx16/x16-emulator), and trying something like "m 14000" defaults to 0x4000.

Edited by rje
Link to comment
Share on other sites

  • 0
1 hour ago, rje said:

OK, so I have the debugger up.  I can see memory locations by using the m xxxx command -- for example, to see RAM bank 4 I can do this:


b ram 4   
m a000     (actually, this command appears to ignore the bank set and always displays bank 0)

Now VERA's memory is.... in VERA.  What's the debugger convention for displaying VERA?  The docs don't say (https://github.com/commanderx16/x16-emulator), and trying something like "m 14000" defaults to 0x4000.

You can see VRAM with the "v" command.  Like "v 10000".  It hasn't been added to the documentation yet, probably because it originally came from a pull request.

  • Thanks 1
Link to comment
Share on other sites

  • 0
8 hours ago, rje said:

b ram 4

m a000 (actually, this command appears to ignore the bank set and always displays bank 0)

To switch bank, you just insert the bank number before the memory address, so to see address A000 in bank 4 you do

Quote

m 4a000

Same thing works for the code view, which is helpful if you want to step through kernal calls

Quote

d 4ffe4

 

Link to comment
Share on other sites

  • 0

Wow, ok.  While I’m here asking questions, what else isn’t in the debugger’s current documentation, so I can update the README and do a pull request?

* PR created with Vera, bank notation added.

Edited by rje
  • Thanks 1
Link to comment
Share on other sites

  • 0

Hmmmm how about this:

BASIC's "LOAD" command works differently than cbm_k_load()?  I wonder...

load "island-mountain-x.bin", 8, 9,$b000 :rem 9b

The above fragment loads a file into one of the RAM banks, which I later transfer into VERA.

This method requires those initial two null bytes in the file.

 

Meanwhile, my C code uses this:

void loadVera(char *fname, unsigned int address)
{
   cbm_k_setnam(fname);
   cbm_k_setlfs(0,8,0);
   cbm_k_load(TO_VERA, address);    <--- THIS
}

Now, does THIS method throw out the first two bytes of a file?  I assume it does.  But if it doesn't...

 

"This function LOADs data bytes from any input device directly into the memory."

 

Edited by rje
Link to comment
Share on other sites

  • 0

From what I can tell, it just uses the kernel LOAD function to do the loading.  Are you on R38 and using an SD card image per chance?  That has a bug where it ignores the address passed in and always uses the header for the address.

  • Thanks 1
Link to comment
Share on other sites

  • 0
1 hour ago, Ender said:

From what I can tell, it just uses the kernel LOAD function to do the loading.  Are you on R38 and using an SD card image per chance?  That has a bug where it ignores the address passed in and always uses the header for the address.

Yeah, I'm using 38, but I always specify the address so the contents of those two bytes don't matter to me.  I always write two zero bytes when I create these images.

Link to comment
Share on other sites

  • 0
3 hours ago, rje said:

Yeah, I'm using 38, but I always specify the address so the contents of those two bytes don't matter to me.  I always write two zero bytes when I create these images.

Right, but the bug is that it will always use the first two bytes of the file for the address, so if it's 0, it will try to load it to 0.  Only if you're using an SD card image though.

Link to comment
Share on other sites

  • 0

Plainly I'm doing something wrong with my 64 x 64 sprites -- which are 8BPP after all.  So I'm going to approach this the same way I

approached the 32 x 32 sprites: slow and steady.

#define    TO_RAM             0
#define    TO_VERA            2
#define     SPRITE_REGISTERS(spritenum)    ((spritenum << 3) + 0xfc00)

// sprite blocks are in 32 byte chunks.
#define     SPRITE_BLOCK(addr)                  (addr >> 5)
#define     SPRITE_MODE_8BPP                    128
#define     SPRITE_32_BY_32                     (128 + 32)
#define     SPRITE_64_BY_64                     (196 + 48)
#define     SPRITE_LAYER_BACKGROUND             (1 << 2)
#define     SPRITE_LAYER_0                      (2 << 2)
#define     SPRITE_LAYER_1                      (3 << 2)

int block = SPRITE_BLOCK(0x4000);
int mode = SPRITE_MODE_8BPP;
int x = 100;
int y = 100;
int z = SPRITE_LAYER_1;
int dimensions = SPRITE_64_BY_64;
int palette_offset = 0;

//
//. load the sprite into VRAM
//
cbm_k_setnam("terrain/island-mountain-x.bin"); //
cbm_k_setlfs(0,8,0);                           //
cbm_k_load(TO_VERA,  0x4000);                  // this all works

//
// set port 0 address and increment
//
VERA.control = 0;                        // port 0
VERA.address = SPRITE_REGISTERS(1);      // sprite 1
VERA.address_hi = VERA_INC_1 + 1;        // the "+1" is the VRAM high address bit.
	
vera_sprites_enable(1);         // in cx16.h
VERA.data0 = block & 0xff;  // lower VRAM address bits
VERA.data0 = mode + ((block >> 8 ) & 0x1f);
VERA.data0 = x & 0xff;
VERA.data0 = x >> 8;
VERA.data0 = y & 0xff;
VERA.data0 = y >> 8;
VERA.data0 = z;                 // leave collision mask and flips alone for now.
VERA.data0 = dimensions + palette_offset;

837301064_ScreenShot2021-09-22at10_15_36AM.png.17171278768632bba16ec7462e075ae1.png

 

Edited by rje
Link to comment
Share on other sites

  • 0

LOL.  Here's the correct image, from the BASIC program, next to the image loaded from the C code:

779061612_ScreenShot2021-09-22at11_30_40AM.png.7898ff11001968fc4931970c39f4fa39.png591434023_ScreenShot2021-09-22at11_30_25AM.png.c05f9e98f01144c55f1c2918b7568b02.png

Obviously a palette shift of some kind.

The transparency layer looks like it's dark blue.
The "grass" is cyan-ish.
The mountain shadow is a darker cyan-like thing.
The mountain proper is brown, with a lot of chaos where there are shades of white-gray in the correct image.

Edited by rje
Link to comment
Share on other sites

  • 0

It gets even funner.  I just did a fresh copy of all these images back into the /basic folder, and re-ran the BASIC program... just to confirm it's loading the same files... and the mountain is CORRECT there.

Hilarious.

So now I look at what BASIC is doing, versus what C is doing.

BASIC is loading those images into RAM banks, and then the data in those banks is being copied into VRAM.

 

There's a VLOAD command in BASIC, isn't there?  Can I vload straight to VERA $4000 in BASIC?

==> YES, yes I can, and it works great (when loading from the host file system).

Edited by rje
Link to comment
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
Answer this question...

×   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.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use