Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - devkitSMS - develop your homebrew in C

Reply to topic Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11  Next
Author Message
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Sun Jun 19, 2022 8:43 pm
sverx wrote
yeah you can use the sdcc4.2 branch of devkitSMS only with SDCC 4.2.0 or the master branch with SDCC 4.1.0 -> 4.1.11.

If you were using SDCC 4.2.0 and the sdcc4.2 branch of devkitSMS it should have worked, or it means I've got a bug somewhere, but I've not experienced it.


Could have been a mix up, looks like I installed 4.1 first at some point, then installed 4.2. Since the SDCC folder isn't removed on uninstall, it must have been using the wrong files from devkit sms. I made sure to delete the directory when I reinstalled 4.1.

In conclusion, this seems more my error than anything else.

For clearing VRAM, is there a recommended method?
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Sun Jun 19, 2022 8:48 pm
You usually don't need to clear VRAM because you load tiles and tilemap there, but in case you need to fill a tile with zeroes (to create an empty tile) or you want to set all the tilemap to use tile number zero you could use either of these:

void SMS_VRAMmemset (unsigned int dst, unsigned char value, unsigned int size);    /* memset (bytes) to VRAM */
void SMS_VRAMmemsetW (unsigned int dst, unsigned int value, unsigned int size);    /* memset (words) to VRAM */


note that the first fills the VRAM area with a byte value and the second with a word value (short int) - the specified size must always be bytes anyway, even in the case of SMS_VRAMmemsetW.

To calculate dst you can either use TILEtoADDR(x) that converts a VRAM tile number to a VRAM address or XYtoADDR(x,y) to convert a tilemap location to a VRAM address.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Sun Jun 19, 2022 9:03 pm
sverx wrote
You usually don't need to clear VRAM because you load tiles and tilemap there, but in case you need to fill a tile with zeroes (to create an empty tile) or you want to set all the tilemap to use tile number zero you could use either of these:

void SMS_VRAMmemset (unsigned int dst, unsigned char value, unsigned int size);    /* memset (bytes) to VRAM */
void SMS_VRAMmemsetW (unsigned int dst, unsigned int value, unsigned int size);    /* memset (words) to VRAM */


note that the first fills the VRAM area with a byte value and the second with a word value (short int) - the specified size must always be bytes anyway, even in the case of SMS_VRAMmemsetW.

To calculate dst you can either use TILEtoADDR(x) that converts a VRAM tile number to a VRAM address or XYtoADDR(x,y) to convert a tilemap location to a VRAM address.


Actually, I guess this is more of a tile map issue, as it carries over the previous screens tile map entries, to the next screen I'm piecing in portions of the tile map of using SMS_loadTileMapArea. Since that screen has multiple sub states. (Vertical scroll one portion, lock top, horizontal scroll another section, then pop in the final section)

Thank you for all your help. I'm very happy everything is working now.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Mon Jun 20, 2022 12:04 pm
you probably want to turn off the display instead of blanking the map, but of course it depends on the effect you want to achieve, if you want the player to see part of the background appear at different times, etc...
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Mon Jun 20, 2022 6:26 pm
sverx wrote
you probably want to turn off the display instead of blanking the map, but of course it depends on the effect you want to achieve, if you want the player to see part of the background appear at different times, etc...


Thank you, I will keep that in mind. I didn't get to play around much after I got the right versions together, but I did manage to get most of what I wanted done. I just got to feed the "Turtles" tiles to the left covered column, while horizontally scrolling it in.

The results (My apologies, the gif capture is not as smooth as the actual game play):

https://www.pyxosoft.com/projects/sms_tmnt/gifs/sms_tmnt_title.gif

I found by using your library, that I could put the vertically scrolling title in the hidden screen part after the 24th row, and just scroll in that way, from the top. Very cool. Each chunk of screen was utilizing SMS_loadTileMapArea, so thanks for the help.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Tue Jun 21, 2022 7:07 am
yes, the BG tilemap is 28 tiles tall on the SMS, so you can have up to 4 rows outside the screen and scroll them in vertically quite easily :)
  View user's profile Send private message Visit poster's website
  • Joined: 23 Jan 2010
  • Posts: 241
Reply with quote
Post Posted: Wed Jul 06, 2022 6:30 pm
Tutorial and examples in portuguese:
  View user's profile Send private message
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Text Color
Post Posted: Sat Jul 23, 2022 3:16 am
@segarule
Hi, I believe me question is a lot trivial, but i cannot know how to do ^^.
how change color from text on a list or make a line highlights. When i try change the color, all text on screen change, not only the line i want.

Thankyou for you attention and time.
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14274
  • Location: London
Reply with quote
Post Posted: Sat Jul 23, 2022 7:20 am
Option 1: use the sprite palette
Option 2: load a second font in new colours
Option 3: use line interrupts to change colours dynamically (difficult)
Option 4: find another way to show what’s selected, eg add a cursor
  View user's profile Send private message Visit poster's website
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Post Posted: Mon Jul 25, 2022 10:17 pm
Maxim wrote
Option 1: use the sprite palette
Option 2: load a second font in new colours
Option 3: use line interrupts to change colours dynamically (difficult)
Option 4: find another way to show what’s selected, eg add a cursor


Tried

SMS_load1bppTiles(devkitSMS_font__tiles__1bpp,0,sizeof(devkitSMS_font__tiles__1bpp),1,7);
SMS_setBGPaletteColor(1,RGB(1,1,1));
SMS_setBGPaletteColor(7,RGB(3,3,3));

without success.
if it's not too much to ask, can someone show a small example?

I alread implemented the cursor using sprite, but want see changing color(from text, not for cursor ^^)
  View user's profile Send private message
  • Joined: 23 Jan 2010
  • Posts: 241
Reply with quote
Post Posted: Tue Jul 26, 2022 10:50 am
squall926 wrote
Maxim wrote
Option 1: use the sprite palette
Option 2: load a second font in new colours
Option 3: use line interrupts to change colours dynamically (difficult)
Option 4: find another way to show what’s selected, eg add a cursor


Tried

SMS_load1bppTiles(devkitSMS_font__tiles__1bpp,0,sizeof(devkitSMS_font__tiles__1bpp),1,7);
SMS_setBGPaletteColor(1,RGB(1,1,1));
SMS_setBGPaletteColor(7,RGB(3,3,3));

without success.
if it's not too much to ask, can someone show a small example?

I alread implemented the cursor using sprite, but want see changing color(from text, not for cursor ^^)


Quote
@segarule
Hi, I believe me question is a lot trivial, but i cannot know how to do ^^.
how change color from text on a list or make a line highlights. When i try change the color, all text on screen change, not only the line i want.

Thankyou for you attention and time.

I know nothing about C. I posted the video for help any interested and mainly portuguese speakers.
Maxim is an expert in programming. And here you can ask to @sverx that created these tools. My advice? Wait and you will have in time the necessary help.
  View user's profile Send private message
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Post Posted: Tue Jul 26, 2022 4:18 pm
squall926 wrote
Maxim wrote
Option 1: use the sprite palette
Option 2: load a second font in new colours
Option 3: use line interrupts to change colours dynamically (difficult)
Option 4: find another way to show what’s selected, eg add a cursor


Tried

SMS_load1bppTiles(devkitSMS_font__tiles__1bpp,0,sizeof(devkitSMS_font__tiles__1bpp),1,7);
SMS_setBGPaletteColor(1,RGB(1,1,1));
SMS_setBGPaletteColor(7,RGB(3,3,3));

without success.
if it's not too much to ask, can someone show a small example?

I alread implemented the cursor using sprite, but want see changing color(from text, not for cursor ^^)



I realized why when i tried, that not worked. rsrs, Without the
SMS_autoSetUpTextRenderer();
i need call
SMS_displayOn();

Only after open the source and look inside i undertood.
Missing know what the arg tileForm on SMS_load1bppTiles is.

Now, mosaic and fade_in/out time to learn;
  View user's profile Send private message
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Post Posted: Tue Aug 09, 2022 3:20 pm
Hi!!!

This new branch works on real hardware? Tested at master system 2 from tectoy but just got " software error" https://github.com/sverx/devkitSMS/tree/sdcc4.2

Yes, Installed the sdcc4.2.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Tue Aug 09, 2022 3:58 pm
make sure you use
SMS_EMBED_SEGA_ROM_HEADER_16KB(productCode,revision);
when using banked code
  View user's profile Send private message Visit poster's website
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Post Posted: Wed Aug 17, 2022 8:34 pm
sverx wrote
make sure you use
SMS_EMBED_SEGA_ROM_HEADER_16KB(productCode,revision);
when using banked code


Thankyou, this solves.
But i have another,.. maybe problem ^^.

I used the released devkit from site together sdcc 4.1, that's works nicelly.
Now i jumped to new "test devkit" with sdcc 4.2, but some functions broke. simple functions like:


u8 SD_readSingleBlock(u8 *inputbuffer, u32 startBlock) {
  u8 response;
  u16 retry = 0, bc = 0x200;

  response = SD_sendCommand(READ_SINGLE_BLOCK, startBlock); //read a Block command

  if (response != 0x00)
  {
    return response; //check for SD status: 0x00 - OK (No flags set)
  }

  SPI_EnableChipSelect();

  retry = 0;
  while (SPI_Read() != 0xfe) //wait for start block token 0xfe (0x11111110)
  {
    if (retry++ > 0xfffe)
    {
      SPI_DisableChipSelect();
      return 1; //return if time-out
    }
  }
 
  // Replace SPI_Read_Buffer, with code below.
  while(bc--){
    SPI_WAIT_READY
      *inputbuffer++ = SPI_DATA;      
  }
  //SPI_Read_Buffer(inputbuffer, 512);

  SPI_Read(); //receive incoming CRC (16-bit), CRC is ignored here
  SPI_Read();

  SPI_Read(); //extra 8 clock pulses
  SPI_DisableChipSelect();

  return 0;
}


When call the code below inside code above, the loops interact only 256 instead from 512.

void SPI_Read_Buffer(u8 *buff, u16 bc) {
   do{
      SPI_WAIT_READY
      *buff++ = SPI_DATA;      
   }while(--bc);
}


You have some ideia from what can causing this?
  View user's profile Send private message
  • Joined: 03 Apr 2022
  • Posts: 8
Reply with quote
Post Posted: Wed Aug 17, 2022 8:47 pm
Other question.
Wich is the correct way to increase the heap?
On old release, i tried

#ifndef HEAP_SIZE
#define HEAP_SIZE 2048
#endif

__xdata char __sdcc_heap[HEAP_SIZE];
const unsigned int __sdcc_heap_size = HEAP_SIZE;

but this code no compile.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Aug 18, 2022 7:11 am
squall926 wrote
You have some idea from what can causing this?


Honestly, I have really no idea. If your code was working with SDCC 4.1 and the stable devkitSMS using that, I would simply keep on using those, unless you really need to switch to the new calling convention for any reason.

As for the heap, did you check the SDCC manual? I never used the heap on SDCC so I can't help.
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 435
  • Location: Málaga, Spain
Reply with quote
Post Posted: Fri Aug 26, 2022 4:56 pm
Heap? what is a heap? xD

Now seriously, i never needed to think about the heap, simply, I suppose I don't use so much ram in defined variables to get to the (in ram) heap space...
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Fri Aug 26, 2022 7:43 pm
squall926 wrote
Now i jumped to new "test devkit" with sdcc 4.2, but some functions broke.


I recently switched the 'official' devkitSMS to require SDCC 4.2 and even more recently lots of bugs were found and removed (mostly thanks to Raphnet!) so you might want to try again. Please do use the latest SDCC release available and in case some of your code isn't working as expected, try to isolate it in a short source and submit it to SDCC bug tracker so they could fix it.

Let me know if you need help anyway.
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 435
  • Location: Málaga, Spain
Reply with quote
Post Posted: Sat Aug 27, 2022 9:21 pm
In fact, I have changed today my old sdcc 4.1 for the latest 4.2 and downloaded the github smsdevkit zip, without a single error compiling and running the same code of the new game i am doing.

INow the compilation (with the same config) is a bit slower.... But the rom size for the same code has decreased fron 80% of the available space on banks 0+1 to 78%.. have not checked the profiler but i guess the performance should be a bit better too.

So.... Well done PKK!!!! (Edit: and you too, sverx)
  View user's profile Send private message
  • Joined: 06 Mar 2022
  • Posts: 255
  • Location: London, UK
Reply with quote
Post Posted: Sun Nov 27, 2022 1:44 pm
sverx wrote
yeah you can use the sdcc4.2 branch of devkitSMS only with SDCC 4.2.0 or the master branch with SDCC 4.1.0 -> 4.1.11.

If you were using SDCC 4.2.0 and the sdcc4.2 branch of devkitSMS it should have worked, or it means I've got a bug somewhere, but I've not experienced it.

Hey @sverx, I was just sitting down for a proper play with devkitSMS and ran into this confusion over the (new?) calling conventions in SDCC 4.2, saw this forum post.

I think you imply there is a branch of devkitSMS called "sdcc4.2" somewhere but I can only see master, plus two tags (v1.0 and v1.2).

Is that intentional - i.e. with SDCC 4.2+ we should be using master only at this point? If so, do you plan to make any new tags at some stage to point to certain known stable versions against SDCC 4.2+?
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Mon Nov 28, 2022 8:35 am
yes, current devkitSMS requires SDCC 4.2.0 or newer as written in the README

There are two releases tagged v1.0 and v1.2 for previous SDCC versions in case someone needs to fix legacy code using legacy tools, but I don't plan to create new tags unless strictly necessary.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Fri Dec 02, 2022 5:43 pm
I have a question, are there some more management functions for the SAT? SMS_initSprites() seems to be the call if you want to clear the table, and then start adding sprites, followed by a SMS_copySpritestoSAT().

Ideally, I would like to add, remove, and insert sprites dynamically, without clearing the table every time. Am I missing something? It would be useful for z-ordering, as well as sprite optimization between frames of animations. In situations where calling SMS_initSprites() and adding all entity sprites every vblank is too intensive. I'm currently staggering tile loads, and having the ability to add sprites during those staggered loads would be ideal.

I see UNSAFE_SMS_copySpritestoSAT(), but I'm not exactly sure on how it works.

Currently, I'm just loading the sprites once, and manipulating them using SMS_updateSpritePosition. Which has been great. Unfortunately, this doesn't solve z-ordering, or if one frame of animation could use less sprites.
  View user's profile Send private message
  • Joined: 04 Jul 2010
  • Posts: 450
  • Location: Angers, France
Reply with quote
Post Posted: Sat Dec 03, 2022 9:29 am
you'll have to write your own functions metasprites handling & yorder (and many others)

metasprites handling is directly bound to your needs/choices of structure

generally I make meta struct/meta handling in ASM
(easier/faster -for me- than in C)

my_character_meta_left1:
.db Y, X, T, terminator
.db Y, X, T, terminator
.db Y, X, T, terminator
...

Y, X are offset values, +0, -12, etc.
T is tile number
terminator, can be: continue, new bloc/new line, end of meta.
(I tend to use $FD, $FE, $FF values for this)

---

in the active frame time,
- sort Y sprites
(personnaly i'm pushing/ordering entity adr (every entity have its "y" base at the same adr on its struct to be faster/easier)

- make all metasprites (pushed into a large RAM array(s))

in the vblank
, call your copySpritestoSAT() (it's fast)
  View user's profile Send private message
  • Joined: 06 Mar 2022
  • Posts: 255
  • Location: London, UK
Reply with quote
Post Posted: Sat Dec 03, 2022 11:34 am
xfixium wrote
I see UNSAFE_SMS_copySpritestoSAT(), but I'm not exactly sure on how it works.

I think it works exactly the same as the the safe version, except that it's too fast to run during the active display and can only safely be run during blanking or when the display is off. The unsafe version uses a tight outi block which is super fast but takes less than the ~29 cycles needed for safe operation in active display.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Sat Dec 03, 2022 4:25 pm
ichigobankai wrote
you'll have to write your own functions metasprites handling & yorder (and many others)

metasprites handling is directly bound to your needs/choices of structure

generally I make meta struct/meta handling in ASM
(easier/faster -for me- than in C)

my_character_meta_left1:
.db Y, X, T, terminator
.db Y, X, T, terminator
.db Y, X, T, terminator
...

Y, X are offset values, +0, -12, etc.
T is tile number
terminator, can be: continue, new bloc/new line, end of meta.
(I tend to use $FD, $FE, $FF values for this)

---

in the active frame time,
- sort Y sprites
(personnaly i'm pushing/ordering entity adr (every entity have its "y" base at the same adr on its struct to be faster/easier)

- make all metasprites (pushed into a large RAM array(s))

in the vblank
, call your copySpritestoSAT() (it's fast)


While I get the implementation of handling the meta sprites will vary, the SAT is always the same. It's the same chunk of memory, from what I understand anyways. I just want to manipulate that chunk of memory a bit more. Devkit SMS has some functions already. Like adding sprites. Changing existing sprites (x,y,tileid). However, there is no method (That I can see anyway) to remove existing sprites, or reorder existing sprites (Insert in a specific point in the SAT) without wiping the SAT and adding again from scratch.

That being said, I think it's probably just the way I'm adding the sprites. I have a tool that optimizes the meta sprite output. It outputs a .bin file of sprite data. In code, I condition on the type of entity, and the entity's state, I load the binary data using SMS_mapROMBank(), and then I read all the sprites in (x,y,t) for the meta sprite via the compiled bin resource (Something like entity_idle_right_bin for instance), in a for loop. It may be too slow. I could just output asm to a clipboard via my tool, or the such. If that gets better results.

Thank you for sharing your method, it offers some better insight on the process.

willbritton wrote

I think it works exactly the same as the the safe version, except that it's too fast to run during the active display and can only safely be run during blanking or when the display is off. The unsafe version uses a tight outi block which is super fast but takes less than the ~29 cycles needed for safe operation in active display.


Oh wow, very interesting, thank you for the info. I'll have to play around with it.
tmnt_0001.png (36.81 KB)
context 01
tmnt_0001.png
tmnt_0002.png (45.65 KB)
context 02
tmnt_0002.png

  View user's profile Send private message
  • Joined: 04 Jul 2010
  • Posts: 450
  • Location: Angers, France
Reply with quote
Post Posted: Sat Dec 03, 2022 5:58 pm
the SAT adr will always the same, not DATAS in.
Updating the SAT is fairly quick (even with the maximum ; 64 sprites)
It should be updated as soon as values are changers (± always/each frame, as with many actors there ±always something moving)

to be faster you should not call for each meta line the putSprite() function (or whatever its called), but something better that will process all your actor's metas in a row.

You will never have thousands of complex actors to process just few (4 to 6 for a BTU as more will ends in a flickering tempest ^^)

Test under emulicious (debugger > profiler) to check how many cycles your function eats.
Or you can make a little things more "visual", with changing the color of the backdrop before each function (so you can see in real time the cpu charge)
In Options > Emulation > Master System > Borders must be visible (will make a larger window)

void set_border_color(unsigned char color_index) __z88dk_fastcall __naked{
__asm
ld a, l
or #0xF0
out (#0xBF), a
ld a, #0x87
out (#0xBF), a
ret
__endasm;
}
//color_index is 16 to 31 (sprite color)

in code it will be like this (see capture for in game):
set_border_color(18);
update_player();
set_border_color(20);
vbl();
set_border_color(28);

  View user's profile Send private message
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Sat Dec 03, 2022 7:28 pm
ichigobankai wrote
the SAT adr will always the same, not DATAS in.
Updating the SAT is fairly quick (even with the maximum ; 64 sprites)
It should be updated as soon as values are changers (± always/each frame, as with many actors there ±always something moving)

to be faster you should not call for each meta line the putSprite() function (or whatever its called), but something better that will process all your actor's metas in a row.

You will never have thousands of complex actors to process just few (4 to 6 for a BTU as more will ends in a flickering tempest ^^)

Test under emulicious (debugger > profiler) to check how many cycles your function eats.
Or you can make a little things more "visual", with changing the color of the backdrop before each function (so you can see in real time the cpu charge)
In Options > Emulation > Master System > Borders must be visible (will make a larger window)

void set_border_color(unsigned char color_index) __z88dk_fastcall __naked{
__asm
ld a, l
or #0xF0
out (#0xBF), a
ld a, #0x87
out (#0xBF), a
ret
__endasm;
}
//color_index is 16 to 31 (sprite color)

in code it will be like this (see capture for in game):
set_border_color(18);
update_player();
set_border_color(20);
vbl();
set_border_color(28);


Thanks for the tips! I will try that out.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Mon Dec 05, 2022 8:08 am
xfixium wrote
I have a question, are there some more management functions for the SAT? SMS_initSprites() seems to be the call if you want to clear the table, and then start adding sprites, followed by a SMS_copySpritestoSAT().

Ideally, I would like to add, remove, and insert sprites dynamically, without clearing the table every time. Am I missing something? It would be useful for z-ordering, as well as sprite optimization between frames of animations. In situations where calling SMS_initSprites() and adding all entity sprites every vblank is too intensive. I'm currently staggering tile loads, and having the ability to add sprites during those staggered loads would be ideal.

I see UNSAFE_SMS_copySpritestoSAT(), but I'm not exactly sure on how it works.

Currently, I'm just loading the sprites once, and manipulating them using SMS_updateSpritePosition. Which has been great. Unfortunately, this doesn't solve z-ordering, or if one frame of animation could use less sprites.


There are functions to manipulate data in the buffer copy of the SAT, instead of clearing the table and starting over. These are:
void SMS_updateSpritePosition (signed char sprite, unsigned char x, unsigned char y)
void SMS_updateSpriteImage (signed char sprite, unsigned char tile)
void SMS_hideSprite (signed char sprite)
which might be enough for your needs. There are no functions to rearrange the order of the existing sprites in the buffer, though.

When you have done manipulating the buffer copy of the SAT, you can copy that to VRAM using
UNSAFE_SMS_copySpritestoSAT()
safely - when in vblank.

also, if you need to change the backdrop/border color at runtime, you can use
void SMS_setBackdropColor (unsigned char entry)
note that the parameter 'entry' is not a color but the index of the entry in the sprite palette you want to use.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Mon Dec 05, 2022 11:59 am
sverx wrote

There are functions to manipulate data in the buffer copy of the SAT, instead of clearing the table and starting over. These are:
void SMS_updateSpritePosition (signed char sprite, unsigned char x, unsigned char y)
void SMS_updateSpriteImage (signed char sprite, unsigned char tile)
void SMS_hideSprite (signed char sprite)
which might be enough for your needs. There are no functions to rearrange the order of the existing sprites in the buffer, though.

When you have done manipulating the buffer copy of the SAT, you can copy that to VRAM using
UNSAFE_SMS_copySpritestoSAT()
safely - when in vblank.

also, if you need to change the backdrop/border color at runtime, you can use
void SMS_setBackdropColor (unsigned char entry)
note that the parameter 'entry' is not a color but the index of the entry in the sprite palette you want to use.


Aye, I'm using those functions to update the sprites. I think I have work arounds. They should work, assuming SMS_updateSpriteImage() isn't too intensive. I tried using UNSAFE_SMS_copySpritestoSAT(). It seems to just corrupt everything no matter where I use it. XD I think it's because I'm using an interrupt to split the screen. Things get dicey using one of those. Thanks for the info.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Mon Dec 05, 2022 12:16 pm
xfixium wrote
Aye, I'm using those functions to update the sprites. I think I have work arounds. They should work, assuming SMS_updateSpriteImage() isn't too intensive. I tried using UNSAFE_SMS_copySpritestoSAT(). It seems to just corrupt everything no matter where I use it. XD I think it's because I'm using an interrupt to split the screen. Things get dicey using one of those. Thanks for the info.


SMS_updateSpriteImage()
updates just one byte in the buffer, it shouldn't be very intensive.

regarding
UNSAFE_SMS_copySpritestoSAT()
this should work fine if you use it right after
SMS_waitForVBlank()
even if you're using line interrupts.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Thu Dec 08, 2022 12:53 am
sverx wrote

SMS_updateSpriteImage()
updates just one byte in the buffer, it shouldn't be very intensive.

regarding
UNSAFE_SMS_copySpritestoSAT()
this should work fine if you use it right after
SMS_waitForVBlank()
even if you're using line interrupts.


What type of performance should I be expecting? I plan on using SMS_updateSpriteImage() and SMS_updateSpritePosition() together to update loaded sprites. However, those functions seem pretty heavy handed for the amount of sprites I'm going to manipulate.

I created a new DevKit SMS project, just so I can stress test against 63 sprites, updating their positions and tileids in the main game loop. When I check the CPU usage, it's roughly 60ish percent on Emulicious. The lion's share of the processing is calling the update functions. UNSAFE_SMS_copySpritestoSAT is indeed very fast, no gripes there.

Is this typical? I'm not entirely sure. Just updating sprites seems to take a lot of processing power. Also, is there just a plain ole SMS_UpdateSprite(sprite_index,x,y,tileid), or is that not doable?

I have attached my results and the test project. Just wondering if I'm doing all this correctly. Thanks for your help.
performance_0001.png (100.91 KB)
Emulicious debugger
performance_0001.png
performance_test.zip (3.08 MB)
Bare bones test

  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 9:03 am
Surely updating every sprite's position and image would be pretty slow, for a few reasons, but I would say mainly because those functions were not meant to be used extensively, thus they're not written using heavily optimized Z80 asm.

If you want to get so many sprites on screen I suggest you use SMS_addSprite() and whenever possible also SMS_addTwoAdjoiningSprites() and SMS_addThreeAdjoiningSprites() because they shave off additional cycles and reduce overhead by adding two or three sprites in a single function call.

As a last resort, you can skip these functions and write directly to the SAT buffer in RAM. There are two arrays and one counter to update to make sure everything works:
unsigned char SpriteTableY[64];
unsigned char SpriteTableXN[64*2];
unsigned char SpriteNextFree;

The first array contains the "Y-1" (yes, you need to decrement Y) coordinates for the sprites and the second contains the X coordinate and tile number for each sprite. SpriteNextFree needs to be set to the number of sprites you had used to make sure (UNSAFE_)SMS_copySpritestoSAT() will work correctly.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Thu Dec 08, 2022 10:04 am
sverx wrote
Surely updating every sprite's position and image would be pretty slow, for a few reasons, but I would say mainly because those functions were not meant to be used extensively, thus they're not written using heavily optimized Z80 asm.

If you want to get so many sprites on screen I suggest you use SMS_addSprite() and whenever possible also SMS_addTwoAdjoiningSprites() and SMS_addThreeAdjoiningSprites() because they shave off additional cycles and reduce overhead by adding two or three sprites in a single function call.

As a last resort, you can skip these functions and write directly to the SAT buffer in RAM. There are two arrays and one counter to update to make sure everything works:
unsigned char SpriteTableY[64];
unsigned char SpriteTableXN[64*2];
unsigned char SpriteNextFree;

The first array contains the "Y-1" (yes, you need to decrement Y) coordinates for the sprites and the second contains the X coordinate and tile number for each sprite. SpriteNextFree needs to be set to the number of sprites you had used to make sure (UNSAFE_)SMS_copySpritestoSAT() will work correctly.


When I run it using SMS_addSprite() within the game loop, I do see a performance boost. Now only 45% of the CPU is being used for 63 sprites.

I was under the impression SMS_addSprite() was slower than using the updating calls. This was based on what I had seen in my actual game engine. However, I understand now that was a wrong assumption, and why I got the corruption I did when using the line interrupt.

I called the sprite code in vblank, but it doesn't finish executing until after the line interrupt, thus causing it to corrupt. It also got corrupted if I ran it in the line interrupt because it was too long before the vblank code. During the line interrupt call, I stagger streaming in tiles. Having both SMS_addSprite() and that code at the same time during line interrupt was too much. Thus why it didn't seem to work where ever I put SMS_addSprite().

Chalk that up to my inexperience. I believe I can work around these issues based on the information you have given me. I can write meta data to take advantage of the adjoining sprites functionality. Last resort,
accessing using SpriteNextFree and the arrays you described.

What ichigobankai described sounded like sprite batching. I would assume that would be ideal. So I may be looking to do that as well, or something similar.

Thank you again.
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14274
  • Location: London
Reply with quote
Post Posted: Thu Dec 08, 2022 10:12 am
In assembly you’d likely walk through some game structures and populate the sprite table in RAM, then blast it to VRAM during vblank. I think that’s equivalent to what’s happening here. For speed you’d probably walk a pointer through memory and then use quick maths (or ix/iy offsets) to write x, n, y using that pointer. I suspect that’s equivalent to what the devkitsms function is doing, but it doesn’t seem like it should use 45% of the CPU.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Thu Dec 08, 2022 10:35 am
Maxim wrote
In assembly you’d likely walk through some game structures and populate the sprite table in RAM, then blast it to VRAM during vblank. I think that’s equivalent to what’s happening here. For speed you’d probably walk a pointer through memory and then use quick maths (or ix/iy offsets) to write x, n, y using that pointer. I suspect that’s equivalent to what the devkitsms function is doing, but it doesn’t seem like it should use 45% of the CPU.


I was hoping it wasn't going to use 45% of the CPU lol. I did a final test using the adjoining code. It faired way better. Running the CPU at about 25% instead. This is by far more acceptable. I've attached some screen caps, and the C testing source. Could be that I setup the code incorrectly, and there's more optimizations to be had. But I tried to be as minimalist as possible.

Overall, as long as it doesn't hit the line interrupt, It should be fine. Plenty of cycles left to do the actual game logic.

Testing Source:
https://www.pyxosoft.com/projects/sms_tmnt/performance_test_final.zip
performance_add_sprites_cpu.png (114.97 KB)
Add Sprites Method CPU Usage
performance_add_sprites_cpu.png
performance_add_sprites_events.png (44.89 KB)
Add Sprites Method Events View
performance_add_sprites_events.png
performance_adjoining_sprites_cpu.png (100.71 KB)
Adjoining Sprites Method CPU Usage
performance_adjoining_sprites_cpu.png
performance_adjoining_sprites_events.png (40.99 KB)
Adjoining Sprites Method Events View
performance_adjoining_sprites_events.png

  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 10:59 am
All those functions that add sprite(s) do perform checks to ensure that you're not adding too many of them (64 at most are allowed) or that you're not placing a sprite at a Y coordinate that would result in a 'sprite terminator marker', which would make all the next sprites disappear.

If you want to deactivate these checks, you can recompile the library with
#define NO_SPRITE_CHECKS
and have that also before your
#include SMSlib.h
to use a faster version of SMS_addSprite() and SMS_addTwoAdjoiningSprites() (but not on SMS_addThreeAdjoiningSprites() as that is still missing)

Of course then you have to make sure that you don't get into troubles.
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 255
  • Location: London, UK
Reply with quote
Post Posted: Thu Dec 08, 2022 10:59 am
xfixium wrote
During the line interrupt call, I stagger streaming in tiles.

Out of interest, why are you using line interrupts to stream tile data to the VDP? You have very little time to make transfers in active display, and the overhead of dealing with interrupts reduces that even more. Wouldn't it be more efficient to simply stream the tile data continuously either during vblank (at the 16 t-state rate), or during active display (at the 29 t-state rate)?
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 11:05 am
Last edited by sverx on Thu Dec 08, 2022 11:10 am; edited 1 time in total
Maxim wrote
In assembly you’d likely walk through some game structures and populate the sprite table in RAM, then blast it to VRAM during vblank. I think that’s equivalent to what’s happening here. For speed you’d probably walk a pointer through memory and then use quick maths (or ix/iy offsets) to write x, n, y using that pointer. I suspect that’s equivalent to what the devkitsms function is doing, but it doesn’t seem like it should use 45% of the CPU.


If you could walk though your structures and populate the Y and XN sprites arrays in a tight loop it would be much faster of course, but that's not what devkitSMS does as it has no direct 'link' to the game itself. On the contrary, it has to 'queue' sprites upon request, which is surely slower.

That's why I suggested to skip these add_sprite functions entirely if performance becomes a serious issue.
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 11:08 am
willbritton wrote
xfixium wrote
During the line interrupt call, I stagger streaming in tiles.

Out of interest, why are you using line interrupts to stream tile data to the VDP? You have very little time to make transfers in active display, and the overhead of dealing with interrupts reduces that even more. Wouldn't it be more efficient to simply stream the tile data continuously either during vblank (at the 16 t-state rate), or during active display (at the 29 t-state rate)?


I had missed this!

Indeed, vblank is the best moment to stream tiles.
Use:
UNSAFE_SMS_load1Tile(src,theTile)
UNSAFE_SMS_load2Tiles(src,tilefrom)
UNSAFE_SMS_load4Tiles(src,tilefrom)
for quick writes to VRAM.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Thu Dec 08, 2022 11:42 am
willbritton wrote

Out of interest, why are you using line interrupts to stream tile data to the VDP? You have very little time to make transfers in active display, and the overhead of dealing with interrupts reduces that even more. Wouldn't it be more efficient to simply stream the tile data continuously either during vblank (at the 16 t-state rate), or during active display (at the 29 t-state rate)?


I load sprite animation tiles dynamically at that time. The only reason I placed it in the line interrupt, is because of the time it took to do addsprite() in the vblank (Ref: test results I just shared). Originally, my optimistic and doe eyed former self had placed streaming in the vblank. It just kept corrupting by the time the line interrupt was called.

And to be a lil more detailed, I stagger those stream calls. Since I have a finite amount of entities, I stagger the stream calls based on a resetting vblank counter for each major entity (Player1, Player2, Enemy1 etc.. etc..). Upside, the streams are very fast, downside, the quickest animation time is now slower. Not a big deal in this instance, still very fast. I was originally worried it wasn't going to be responsive enough vs the input as well, but that's not the case.

With this new information (I am aware of the disappearing sprite y position thing, as I thought I would have to condition for that) I can remold the graphics code again. Hopefully a little more optimized ;)

EDIT: I should also note that I'm not using the line interrupt for streaming tiles, the only reason I have it at all is to split the screen scrolling, after the 4th row.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 2:19 pm
I'm not sure if I got that wrong, but you shouldn't wait for vblank to add sprites. The main point of having a sprite buffer in RAM is to prepare the update of the SAT beforehand and have it actually updated in VRAM only during vblank.

So usually during vblank you just do two things: you update the SAT (quickly) and you stream (some) tiles quickly. You can probably update up to 20 tiles in a single vblank (at 60Hz - even more at 50Hz) if the logic to pick the correct tiles is simple and you do 4 tiles copy with a single call.

If you want to be sure that your UNSAFE vblank operations don't spill on the active phase, where the VRAM would corrupt because of the 'wrong' write speed, you can turn off the screen before that and turn it on again when done. Of course you might see a few lines disappearing from the top of your screen...

Another option would be to limit the amount of UNSAFE streaming to an absolutely safe value and then follow with some 'slow' tile loading, which won't cause any issue if it gets to run on the active phase.
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 435
  • Location: Málaga, Spain
Reply with quote
Post Posted: Thu Dec 08, 2022 2:25 pm
ummm....

my experience says that in the vblank time you can update the sat and about 8-10 tiles using unsafe functions without fear.

It says, also, that the non unsafe functions to update tiles in screen time are not too bad, I am updating four tiles by frame this way, and does not consume too much.

Found more problematic the moment in which you scroll the screen, and update a new row/column, if needed, if you don't want to see garbage in the borders of the screen (the process of updating a full 32 tiles row or 24 (or 28) tiles column is time expensive, more if you are using a wxh 16x16 tile based map) ...
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Thu Dec 08, 2022 3:37 pm
eruiz00 wrote
my experience says that in the vblank time you can update the sat and about 8-10 tiles using unsafe functions without fear.


of course it always depends, but copying a full SAT (64 sprites) is roughly equivalent to copy data worth 3 tiles, and even in this worst case I can call UNSAFE_SMS_load4Tiles() 5 times without having Emulicious complain of violating VDP constraints, keeping the offset index in a variable, as an example:

SMS_mapROMBank(BGAnimation__tiles__bin_bank);
UNSAFE_SMS_load4Tiles (&BGAnimation__tiles__bin[tiles_index],ANIMATED_BG_TILES);
UNSAFE_SMS_load4Tiles (&BGAnimation__tiles__bin[tiles_index+128],ANIMATED_BG_TILES+4);
UNSAFE_SMS_load4Tiles (&BGAnimation__tiles__bin[tiles_index+128*2],ANIMATED_BG_TILES+4*2);
UNSAFE_SMS_load4Tiles (&BGAnimation__tiles__bin[tiles_index+128*3],ANIMATED_BG_TILES+4*3);
UNSAFE_SMS_load4Tiles (&BGAnimation__tiles__bin[tiles_index+128*4],ANIMATED_BG_TILES+4*4);

tiles_index+=128*6;
if (tiles_index==(128*6*8))
  tiles_index=0;

(the whole vblank duration would be enough to copy 997 bytes in a purely theoretical situation, and here we're just copying 736 bytes max...)
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Thu Dec 08, 2022 4:48 pm
Hmm, from the basic code I have tried, I can keep it from spilling into the active area if all SAT calls are using SMS_addThreeAdjoiningSprites, but the moment I try to use UNSAFE_SMS_load4Tiles, I can see it overflow into the active area just a lil bit. So, updating any tiles seems risky? I'm jealous of your memory write speeds XD.

This is the code I used:

#include "lib\SMSlib.h"
#include <stdbool.h>
#include "bank2.h"

// Header
SMS_EMBED_SEGA_ROM_HEADER(9999, 0);
SMS_EMBED_SDSC_HEADER(1, 0, 2022, 12, 7, "Test", "Test", "Test");

void AddAdjoiningSprites() {
   // Entity 1
   SMS_addThreeAdjoiningSprites(0, 0, 0);
   SMS_addThreeAdjoiningSprites(0, 16, 6);
   SMS_addThreeAdjoiningSprites(0, 32, 12);

   // Entity 2
   SMS_addThreeAdjoiningSprites(0, 48, 0);
   SMS_addThreeAdjoiningSprites(0, 64, 6);
   SMS_addThreeAdjoiningSprites(0, 80, 12);

   // Entity 3
   SMS_addThreeAdjoiningSprites(0, 96, 0);
   SMS_addThreeAdjoiningSprites(0, 112, 6);
   SMS_addThreeAdjoiningSprites(0, 128, 12);

   // Entity 4
   SMS_addThreeAdjoiningSprites(0, 144, 0);
   SMS_addThreeAdjoiningSprites(0, 160, 6);
   SMS_addThreeAdjoiningSprites(0, 176, 12);

   // Entity 5
   SMS_addThreeAdjoiningSprites(32, 0, 0);
   SMS_addThreeAdjoiningSprites(32, 16, 6);
   SMS_addThreeAdjoiningSprites(32, 32, 12);

   // Entity 6
   SMS_addThreeAdjoiningSprites(32, 48, 0);
   SMS_addThreeAdjoiningSprites(32, 64, 6);
   SMS_addThreeAdjoiningSprites(32, 80, 12);

   // Entity 7
   SMS_addThreeAdjoiningSprites(32, 96, 0);
   SMS_addThreeAdjoiningSprites(32, 112, 6);
   SMS_addThreeAdjoiningSprites(32, 128, 12);
}

// Program entry
void main()
{
   // Initialization
   SMS_init();
   SMS_setSpriteMode (SPRITEMODE_TALL); // 8 x 16 sized sprites
   SMS_useFirstHalfTilesforSprites(false);
   SMS_displayOn();

   // Load crap into memory
   SMS_mapROMBank(blank_pal_bin_bank);
   SMS_loadBGPalette(blank_pal_bin);
   SMS_mapROMBank(blank_tiles_bin_bank);
    SMS_loadTiles(blank_tiles_bin, 0, blank_tiles_bin_size);
   SMS_mapROMBank(blank_map_bin_bank);
    SMS_loadTileMap(0, 0, blank_map_bin, blank_map_bin_size);
   SMS_mapROMBank(leo_pal_bin_bank);
   SMS_loadSpritePalette(leo_pal_bin);
   SMS_mapROMBank(leo_idle_right_tiles_bin_bank);
    SMS_loadTiles(leo_idle_right_tiles_bin, 256, leo_idle_right_tiles_bin_size);

   // Game loop
    for (;;) {
      SMS_initSprites();
      AddAdjoiningSprites();
      SMS_waitForVBlank();
      UNSAFE_SMS_copySpritestoSAT();
      SMS_mapROMBank(leo_idle_right_tiles_bin_bank);
      UNSAFE_SMS_load4Tiles(&leo_idle_right_tiles_bin[0], 1);
   }
}
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3318
  • Location: Torino, then London, now Stockholm
Reply with quote
Post Posted: Fri Dec 09, 2022 8:41 am
I don't see any problems with your code. During vblank you're just updating SAT and copying 4 tiles and I doubt there's any chance of spilling into active phase doing just that.

Also, but on a unrelated note, you don't need to call
SMS_init()
as that's already called by devkitSMS' crt0 before main() gets started.
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Fri Dec 09, 2022 12:06 pm
sverx wrote
I don't see any problems with your code. During vblank you're just updating SAT and copying 4 tiles and I doubt there's any chance of spilling into active phase doing just that.

Also, but on a unrelated note, you don't need to call
SMS_init()
as that's already called by devkitSMS' crt0 before main() gets started.


Yeah, I keep forgetting to exclude it. XD

I think this is probably just my unfamiliarity with the data that Emulicious displays on it's events viewer. If we're just talking about VRAM writes, it seems to not go into the active display. The RAM access seems to bleed into the active display, however. If I'm reading it right.

All in all I'll just play around with it a bit. I'll try to compile a DNO_SPRITE_CHECKS version of SMSlib, and see how that fairs.

If the VRAM is what I should be looking at on the test code, it looks like I could actually stream 24 tiles in vblank. Which would be perfect, as long as the RAM writes from those operations don't interfere with where I'm planning on putting the interrupt request. Which I will test next. Thanks for your help.
performance_0003.png (19.1 KB)
20 tiles streamed results event viewer
performance_0003.png

  View user's profile Send private message
  • Joined: 06 Mar 2022
  • Posts: 255
  • Location: London, UK
Reply with quote
Post Posted: Fri Dec 09, 2022 4:02 pm
xfixium wrote
If the VRAM is what I should be looking at on the test code, it looks like I could actually stream 24 tiles in vblank. Which would be perfect, as long as the RAM writes from those operations don't interfere with where I'm planning on putting the interrupt request.

Oh yeah, you're only bothered about VRAM accesses for sure. The RAM accesses you are seeing are presumably the SAT buffer being safely manipulated in RAM after you've copied it to the VDP, which is exactly what it's designed to do.

24 tiles does sound much more like it - I'd say that's a pretty good result all things considered, close to the theoretical limit.
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 255
  • Location: London, UK
Reply with quote
Post Posted: Fri Dec 09, 2022 4:04 pm
sverx wrote
...but copying a full SAT (64 sprites) is roughly equivalent to copy data worth 3 tiles

Just checking for the record, shouldn't this be 6 tiles rather than 3 (192/32)?
  View user's profile Send private message Visit poster's website
  • Joined: 18 Jul 2020
  • Posts: 315
Reply with quote
Post Posted: Fri Dec 09, 2022 5:17 pm
willbritton wrote
Oh yeah, you're only bothered about VRAM accesses for sure. The RAM accesses you are seeing are presumably the SAT buffer being safely manipulated in RAM after you've copied it to the VDP, which is exactly what it's designed to do.

24 tiles does sound much more like it - I'd say that's a pretty good result all things considered, close to the theoretical limit.


Thank you for the confirmation and added information. This should do nicely.
  View user's profile Send private message
Reply to topic Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11  Next



Back to the top of this page

Back to SMS Power!