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
Author Message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Thu Nov 30, 2017 4:52 pm
Last edited by sverx on Tue May 29, 2018 11:21 am; edited 1 time in total
Kagesan wrote
So, what does that mean for us oldschoolers who wish to keep using PSGlib from within our assembler code?


I suggest you keep the current version of PSGlib library, tools and assets with your existing projects (and current projects possibly) and switch to the new version with your next project - this is the "zero burden" approach.
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Tue Dec 19, 2017 10:25 am
I thought that I saw somewhere in this topic an estimate about how many cycles functions like SMS_setNextTileatXY, SMS_setTile take, and how it compares to SMS_loadTiles. But I've not found it anywhere.

I'll explain what I want to know with an example: what would be faster? Changing six non contiguous positions in the tilemap or loading three non contiguos tiles to VRAM? (because each tile I want to change, it's repeated twice)

In my first thought, I think It'll be faster to update the nametable, but just to be sure...
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Tue Dec 19, 2017 10:33 am
ah, nice question! I will run a few tests and tell you later :)

(I'd guess it's "changing six non contiguous positions in the tilemap" given they're at fixed locations, thus no math required to find the spot)
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Tue Dec 19, 2017 10:43 am
I'll clarify my need as:
+I need to update some tiles of a background, not always the same ones, but usually no more than 4-5 pairs per frame.
+I use the word pair because when I change a tile in the upper part of the screen, there's always another equal tile to change in the bottom part.
+Computing the tiles to update is not a big deal, but they're quite random.
+I can just update the nametable, or use a fixed nametable when the bottom part is the upper part flipped vertically, and just update a tile loading it to VRAM.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Tue Dec 19, 2017 10:55 am
SMS_setNextTileatXY(x,y)

which is in fact
SMS_setAddr(XYtoADDR((x),(y)))

which becomes an RST 08h, takes 39 cycles, according to Calindro's wonderful Emulicious profiler [this doesn't include the time taken to set HL to the desired value, the RST and RET opcode cycles]

41 cycles needed for
SMS_setTile(tile)

which becomes an RST 18h, again according to Emulicious profiler [once more this doesn't include the time taken to set HL to the desired value, the RST and RET opcode cycles]

Thus I would say it'll take you:
(39+41+(10+11+10)*2)*6=
(39+41+62)*6=
852 cycles

Loading 4 contiguous tiles to VRAM using
UNSAFE_SMS_load4Tiles()

takes 2249 cycles plus overhead, loading 3 noncontinuous tiles would take more than 3/4 of this surely, thus more than 1687 cycles.

Updating the tilemap seems to be the faster option by a tad :)
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Tue Dec 19, 2017 11:00 am
kusfo wrote
I'll clarify my need as:
+I need to update some tiles of a background, not always the same ones, but usually no more than 4-5 pairs per frame.
+I use the word pair because when I change a tile in the upper part of the screen, there's always another equal tile to change in the bottom part.
+Computing the tiles to update is not a big deal, but they're quite random.
+I can just update the nametable, or use a fixed nametable when the bottom part is the upper part flipped vertically, and just update a tile loading it to VRAM.


if your update of the tilemap may become too heavy in some situation, and cause a slowdown, then the other option may be preferable, as it would always cost the same number of cycles, making the whole thing somehow easier to manage, if you get what I mean.
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Tue Dec 19, 2017 11:01 am
wow! thanks for trying it so fast! :-D

I was expecting nametable updating to be faster, but that it's a clear win :-)

However, I'll test my particular case, to see which one is the best option!

Thank you sverx!
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 13435
  • Location: London
Reply with quote
Post Posted: Tue Dec 19, 2017 1:14 pm
Loading a tile is two address writes and 32 data writes. Setting a name table entry is two address writes and two data writes. The tile writing will always dominate, and the VDP access dominates the other logic.
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Tue Dec 19, 2017 1:32 pm
@Maxim: surely you push to VRAM much more data updating a tile compared to setting a few entries in the map, anyway I suspect that there's a point where the math needed to calculate the tilemap addresses, the call overheads and the set-address-then-set-data work itself could cost more than just pushing a bunch of bytes to VRAM during vblank. In short then I guess the only correct answer it's "it depends".
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 13435
  • Location: London
Reply with quote
Post Posted: Tue Dec 19, 2017 2:04 pm
It's true that for small numbers of tiles it may be the case that the logic gets more significant. I'd always kind of assumed that you start animating tiles after you run out of space to animate the references, but as you say it does depend on exactly what you're animating.
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Jan 03, 2018 10:43 am
Happy 2018! :)

I just pushed the updated BMP2Tile's STM plugin (it requires BMP2Tile 0.43) - the file format has changed (a 1-byte header holding the map's width has been added) and the official name of this format is now ShrunkTileMap - I know it's silly but Sverx's TileMap was even worse)

The asm decompressor (for those that are using WLA-DX) is now interrupt and VRAM safe. [HL = compressed tilemap binary (source), DE = VRAM address (destination)]

The update on the devkitSMS side (on the functions to load STMcompressed maps) will happen ASAP.
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Wed Jan 03, 2018 11:54 am
great news! :-D
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Jan 03, 2018 12:53 pm
devkitSMS updated - now SMS_loadSTMcompressedTileMap loads STM tilemaps of any width (and this info is now into STM file) - SMS_loadSTMcompressedTileMapArea so has been deprecated.

On a side note, calculation of the destination VRAM address turned into an
XYtoADDR(x,y)

macro call, thus if you use constants in your invocation, such as
SMS_loadSTMcompressedTileMap(0,0,bg__tilemap__stmcompr);

they'll turn into constant addresses such as
;main.c:243: SMS_loadSTMcompressedTileMap(0,0,bg__tilemap__stmcompr);
   ld   hl, #_bg__tilemap__stmcompr
   push   hl
   ld   hl, #0x7800
   push   hl
   call   _SMS_loadSTMcompressedTileMapatAddr
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 379
  • Location: Málaga, Spain
Reply with quote
Post Posted: Wed Jan 03, 2018 2:24 pm
Great!

Lets begin new project with the update!
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Jan 03, 2018 2:31 pm
SDCC 3.6.9 (#10195) suggested - released Jan 1 :)
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 379
  • Location: Málaga, Spain
Reply with quote
Post Posted: Fri Jan 05, 2018 6:37 am
Why? 31/12/2017 or 2/1/2018 versions dont work???? Is there a crítical change on sdcc?
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Fri Jan 05, 2018 9:19 am
no, I just mean to suggest the snapshot I'm using, as it seems to work correctly (they patched a lot of bugs I encountered in the past months)
  View user's profile Send private message Visit poster's website
  • Joined: 28 Jan 2017
  • Posts: 379
  • Location: Málaga, Spain
Reply with quote
Post Posted: Sat Jan 13, 2018 9:16 am
Hi sverx!!!

I dont forget you' simple no Rush!

... Can you explained Us the functions sms_addtwoadjoiningsprites (It draws two sprites horizontally? If so, What happens if i have usetallsprites enabled?) And setclippingwindow and addspriteclipping?

The reason is that i have a function to draw an array of sprites and discard those rows/cols which are outside screen' but maybe your functions Will be faster than my plain ANSI c one.

Regards.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Sat Jan 13, 2018 12:08 pm
As the name suggests, it places two sprites, the first at requested x,y and the second right at his side, at same y and at x+sprite_width.
It works with sprites of any size (normal, tall, even zoomed) and it's way faster than placing two sprites with two separate calls.
No window clipping is performed - but any sprite outside of screen won't be wasted thus for example if you place your two sprites at x=253, the 'left' sprite will be placed and will partially appear on screen while the 'right' sprite will just be skipped, as it x coordinate would overflow making it appear on the left of the screen
I hope this clears your doubts.
  View user's profile Send private message Visit poster's website
  • Joined: 28 May 2015
  • Posts: 118
Reply with quote
Post Posted: Thu Jan 18, 2018 3:53 am
I don't know how to use the clipping window. Is it suppose to hide the sprite when it's not inside the boundaries? I placed sprites[1] in multiple areas and it's not working for me. Here is my code.




sprites[1] = SMS_addSpriteClipping(sprPos[2], sprPos[3], SPR_TILES+2);

SMS_setClippingWindow (8, 8, 96, 96);

  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Thu Jan 18, 2018 9:14 am
clipping is performed by software - thus you should *first* set the window, then place the sprite(s): those falling outside the defined window won't be placed.
mind that it can't hide *part* of the sprite, just ensure that no sprite falling *completely* outside the window will appear.
  View user's profile Send private message Visit poster's website
  • Joined: 28 May 2015
  • Posts: 118
Reply with quote
Post Posted: Sun Feb 18, 2018 3:18 am
Might have found a bug. Your compression for tilemap is not working for maps larger then the screen. Using loadSTMcompressedTileMapArea, i can load a single screen, but a 3x3 screen is not working. Garbled or black screen.

Here is an example source attached. it loads my titlescreen to check if code is working. Go into the source and uncomment the other one to load the other map, and see the bug i'm talking about.
PotentialBug.zip (40.13 KB)

  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Sun Feb 18, 2018 12:02 pm
I don't think it's a bug but... yes, you can't have tilemaps larger than the screen (you wouldn't be able to scroll them anyway if you did...)

If you need to handle big maps, there's Psidum wonderful GSLib, which includes a C wrapper so that you can use it from your project.
  View user's profile Send private message Visit poster's website
  • Joined: 28 May 2015
  • Posts: 118
Reply with quote
Post Posted: Sun Feb 18, 2018 5:52 pm
sverx wrote
I don't think it's a bug but... yes, you can't have tilemaps larger than the screen (you wouldn't be able to scroll them anyway if you did...)

If you need to handle big maps, there's Psidum wonderful GSLib, which includes a C wrapper so that you can use it from your project.



Ah okay, i was making sure. Yeah, i'm now using it.
  View user's profile Send private message
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Mon Mar 05, 2018 12:57 pm
I'm writing here on behalf of one fellow member in a spanish forum, he's having problems trying to make a road effect using scanline interruptions.

The problem seems like the effect is happening sometimes in every scanline (the intended effect), sometimes every 2, sometimes every 3, and any possible combination of those. I supposed that maybe he was trying to do too much process in HBLANK, but he even has implemented stripped down versions of background scroll functions, to no avail.

He just posted the code and a screenshot, first screenshot has a very noticeable defect, while second is using the stripped down version of the background calls, and the defect is not so visible.

Maybe sverx would be able to see what's the problem.

Original post in:

https://www.elotrolado.net/hilo_tutorial-muy-basico-de-c-para-master-system_2208028_s370#p1745438338



inline void SS_write_VDPRegister (unsigned char VDPReg, unsigned char value)
{
   VDPControlPort=value;
   VDPControlPort=VDPReg|0x80;
}

void SS_setLineCounter (unsigned char count)
{
    SS_write_VDPRegister(0x0A,count);
}
void lineInterruptHandler(void)
{
    SS_write_VDPRegister(0x08,offsetVector[offsCounter++]);
}
void main()
{
   init_console();
   load_graphics2vram();
   SMS_enableLineInterrupt();
   SMS_setLineInterruptHandler(&lineInterruptHandler);
   SS_setLineCounter (0);

   while (1)
  {
     // Leo input del player.   
    SMS_waitForVBlank();
    SMS_disableLineInterrupt();
    // Calculo nuevos valores para el array offsetVector   
    LoadLevelPalette ();   
    SMS_enableLineInterrupt();
    SS_setLineCounter(0);
 }
}




  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Mon Mar 05, 2018 1:37 pm
the code is basically correct - the point is to have the line handler as fast as possible if you want to scroll at each and every scanline.

for setting the line/interval you can use
SMS_setLineCounter (unsigned char count);

for the scroll, you can use
SMS_setBGScrollX (unsigned char scrollX);


your handler would be something like
void lineInterruptHandler(void)
{
    SMS_setBGScrollX(*offsetPointer++);
}


in your main, you can set the handler and the line/interval value and enable the lineIRQ outside the loop and start the loop when ready

SMS_setLineInterruptHandler(&lineInterruptHandler);
SMS_setLineCounter(0);
SMS_enableLineInterrupt();
while (1)
{
    SMS_waitForVBlank();
    // calculate new values for the offset Array here, reset offsetPointer to &offArray[1]
    do_your_math();
    offsetPointer=&offArray[1];
    //  set uppermost line value
    SMS_setBGScrollX(offArray[0]);
}


edit: you should disable the leftmost column too! Add:
SMS_VDPturnOnFeature (VDPFEATURE_LEFTCOLBLANK);

right at the beginning :)

edit 2:sorry, there was a * missing in the handler

edit 3:to be completely honest, devkitSMS/SMSlib isn't optimized for such a heavy use of line interrupts. If I had to do something like that, I'd probably tweak the ISR, in this part:

1$:                                         ; line interrupt
    push bc
    push de
    push iy
    ld hl,(_SMS_theLineInterruptHandler)
    call ___sdcc_call_hl
    pop iy
    pop de
    pop bc
2$:

putting there some Z80 ASM code that does the H-scrolling as fast as possible, maybe even just saving (push/pop) only the registers really used. After all, you've got only 228 CPU cycles per scanline, and the line interrupt overhead isn't negligible...
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Tue Feb 04, 2020 3:13 pm
Resurrecting quite old, but official devkitSMS topic just to ask about the best way to deal with something I'm fighting with:

What would be the best way to include LUT tables on a bank? (together with Graphics) I can compile the LUT to get a .rel, but how I can have as a .bin and then pack it with other assets using assets2banks?
PD: Maybe this is quite easy, but I'm not getting how to compile to pure bin.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Tue Feb 04, 2020 4:08 pm
if you put all the LUTs together you can compile a .rel and have them all together in the same bank, by themselves - this is the easy approach and I mainly suggest this.

otherwise, if you want them handled by assets2banks, an approach could be either:

- have each single LUT in form of a binary file (but AFAIK you can't easily create a LUT in a C source file and compile that to some bin file that contains only the LUT) - which means you're probably going to edit some file with hex editor :(

edit: try googling txt2bin, there are a few interesting results!

- if LUT(s) is(are) small, you can abuse assets2banks' :append attribute and create an empty file in the assets folder, one for each LUT, name it the way you want the LUT to be called and then in assets2banks.cfg for each empty file you append the data for example as in:

test_char_LUT.bin
:append  0 1 2 3 4 5 6 7 8


or in

test_int_LUT.bin
:format unsigned int
:append  0x1234 0x5678
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Wed Feb 05, 2020 8:48 am
My problem is that I'm using also complex structures on some files I would like to put in a bank. For instance, an example of content I'll like to put ina bank together with its graphics is:
https://github.com/kusfo/mastersystembrawler/blob/master/playercharacter.h

This is not really easy to convert to bin (I know I'll also need to get rid of pointers and so, but it's an example).

I think I saw some people doing similar things and using assembly data to fill the structs...
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Feb 05, 2020 11:36 am
yeah, those structures need a compiler (and also need to be output as RELs since you've got pointers that the linker should initialize)

so if you need to keep that data in the same bank as some graphics, you could move those graphics out of the general 'asset' folder to a specific folder, then use folder2c to turn the data in there into C sources

so you then merge (juxtapose) the generated C file with your source that contains those structures - and you finally compile this resulting file into a REL, that you will link with all the other code/data in the end.
  View user's profile Send private message Visit poster's website
  • Joined: 29 Mar 2012
  • Posts: 640
  • Location: Spain
Reply with quote
Post Posted: Wed Feb 05, 2020 1:18 pm
Mmm, the merge solution is not a bad solution at all!

Thanks, I'll think a bit about it!
  View user's profile Send private message
  • Joined: 11 Dec 2019
  • Posts: 28
  • Location: Finland
Reply with quote
Post Posted: Wed Oct 28, 2020 6:59 am
I'm experimenting with SDCC snapshot (v4.0.3) and devkitSMS.
I modified SGlib to suit the SC-3000 but now when I recompile SGlib it places an EI at the beginning of SG_isr screwing up everything during execution, eg. pushing stuff on stack forever.
The only thing I modified is crt0_sg.s stackpointer (pointing to c7f0) and VDP values in SGlib.c. What is causing this?


0038:  jp 08A1h
...
08A1: ei
08A2: push af
08A3: push bc
08A4: push de
...
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Oct 28, 2020 10:26 am
According to the updated SDCC manual, with version 4.0.x they slightly changed how the interrupts are declared (it's on page 46).

So you might want to try switching from
void SG_isr (void) __interrupt {

to
void SG_isr (void)  __critical __interrupt(0) {

to signal that you don't want your interrupt routine to be interrupted (!)

Let me know if that works and generate correct code.
  View user's profile Send private message Visit poster's website
  • Joined: 11 Dec 2019
  • Posts: 28
  • Location: Finland
Reply with quote
Post Posted: Wed Oct 28, 2020 2:54 pm
sverx wrote
According to the updated SDCC manual, with version 4.0.x they slightly changed how the interrupts are declared (it's on page 46).

So you might want to try switching from
void SG_isr (void) __interrupt {

to
void SG_isr (void)  __critical __interrupt(0) {

to signal that you don't want your interrupt routine to be interrupted (!)

Let me know if that works and generate correct code.

Okay, didn't even imagine someone wanting the interrupt routine to be interrupted. But that solution indeed does work and generates correct code. Thanks sverx!
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Oct 28, 2020 3:16 pm
helmha wrote
that solution indeed does work and generates correct code


I'm curious now... is there a single EI or two of them before the RETI in the generated code?
  View user's profile Send private message Visit poster's website
  • Joined: 11 Dec 2019
  • Posts: 28
  • Location: Finland
Reply with quote
Post Posted: Wed Oct 28, 2020 3:54 pm
sverx wrote

I'm curious now... is there a single EI or two of them before the RETI in the generated code?

This is how it looks before correcting SGlib.c:

;SGlib.c:384: void SG_isr (void) __interrupt {
;   ---------------------------------
; Function SG_isr
; ---------------------------------
_SG_isr::
   ei
   push   af
   push   bc
   push   de
   push   hl
   push   iy
   push   ix
   ld   ix,#0
   add   ix,sp
   dec   sp
;SGlib.c:385: volatile unsigned char VDPStatus=VDPStatusPort;  /* this also aknowledge interrupt at VDP */
   in   a, (_VDPStatusPort)
;SGlib.c:390: if (VDPStatus & 0x80) {
   ld   -1 (ix), a
   rlca
   jr   NC, 00102$
;SGlib.c:391: VDPBlank=true;             /* frame interrupt */
   ld   iy, #_VDPBlank
   ld   0 (iy), #0x01
;SGlib.c:393: PreviousKeysStatus=KeysStatus;
   ld   hl, (_KeysStatus)
   ld   (_PreviousKeysStatus), hl
;SGlib.c:394: KeysStatus=~(((IOPortH)<<8)|IOPortL);
   in   a, (_IOPortH)
   ld   b, a
   xor   a, a
   ld   c, #0x00
   in   a, (_IOPortL)
   ld   e, a
   ld   d, #0x00
   ld   a, c
   or   a, e
   ld   c, a
   ld   a, b
   or   a, d
   cpl
   ld   (_KeysStatus+1), a
   ld   a, c
   cpl
   ld   (_KeysStatus+0), a
00102$:
;SGlib.c:397: ENABLE_INTERRUPTS;
   ei   
;SGlib.c:398: }
   inc   sp
   pop   ix
   pop   iy
   pop   hl
   pop   de
   pop   bc
   pop   af
   reti


With the correction in place, there's only one EI at the end of routine.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 2790
Reply with quote
Post Posted: Wed Oct 28, 2020 4:02 pm
so it looks like they add EI at the beginning of the routine if they want to allow for an ISR that can be interrupted but they don't care to add EI before the RETI if one wants an ISR that can't be interrupted.

I'll ask them.
  View user's profile Send private message Visit poster's website
Reply to topic Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9



Back to the top of this page

Back to SMS Power!