SP1 sp1_IterateSprChar() function needs to be __z88dk_fastcall

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
derekfountain
Member
Posts: 50
Joined: Mon Mar 26, 2018 1:49 pm

SP1 sp1_IterateSprChar() function needs to be __z88dk_fastcall

Post by derekfountain »

I'm trying to picture what's going on when sp1_IterateSprChar() calls the function it's given as a pointer. The "ex4a" example code has this function:

Code: Select all

void colourSpr(struct sp1_cs *c)
{
   c->attr_mask = amask;       // set the sprite character's attribute mask
   c->attr = attr;             // set the sprite character's attribute
}
which is called thus:

Code: Select all

sp1_IterateSprChar(s, colourSpr);    // colour the sprite
which seems to work in the given example, but not when I copy it over to my code.

Single stepping in the emulator I can see that the colourSpr() function's parameter, 'c', appears to be arriving as a null value. The code which sets the mask and attr values appears to be attempting to set them in bytes 0x0005 and 0x0006 - i.e. in ROM.

Much digging and guesswork later I discover that making it fastcall:

Code: Select all

void colourSpr(struct sp1_cs *c) __z88dk_fastcall
makes it work.

So:

* this is an issue fundamentally caused by the use of different compilers between the example code I copied and my code, right?
* __z88dk_fastcall is different from __fastcall__, which is what I keep finding in the wiki and which I couldn't get working?
* if the declaration of the function as __z88dk_fastcall is the correct solution, and is required, shouldn't that be documented somewhere nice and obvious?
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

* __z88dk_fastcall is different from __fastcall__, which is what I keep finding in the wiki and which I couldn't get working?
The __FASTCALL__ decoration between return type and function name is not compatible with zsdcc so we changed to __z88dk_fastcall after the function declaration is complete. Otherwise its meaning is identical.
* this is an issue fundamentally caused by the use of different compilers between the example code I copied and my code, right?
* if the declaration of the function as __z88dk_fastcall is the correct solution, and is required, shouldn't that be documented somewhere nice and obvious?
This is not correct anymore:

Code: Select all

void colourSpr(struct sp1_cs *c)
{
   c->attr_mask = amask;       // set the sprite character's attribute mask
   c->attr = attr;             // set the sprite character's attribute
}
The function is actually passed two parameters and should be declared like this:

Code: Select all

void colourSpr(unsigned int count, struct sp1_cs *c)
{
   c->attr_mask = amask;       // set the sprite character's attribute mask
   c->attr = attr;             // set the sprite character's attribute
}
The count starts at zero for the first sprite character and increase by one for each sprite character visited in row major order. This way you can know which sprite character in the sprite you are visiting.

Before zsdcc, there was only left->right parameter passing to worry about. Since for function pointers the calling convention is always standard, the caller repairs the stack, so the target function (colourSpr) is safe to ignore unused parameters. You can declare the function as "void colourSpr(struct sp1_cs *c)" and still be able to find the parameter "c" since it's the first one on the stack. However, zsdcc pushes right->left and "c" is not longer the first parameter so this declaration does no work. Anyway these functions should have been declared properly with the two parameters in the first place but this may have led to better code under the old sccz80.

A fastcall declaration will work because the asm that generates the call to "colourSpr()" has "struct sp1_cs *c" in HL when the call is made. This is actually not an accident but it's also not documented.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

A fastcall declaration will work because the asm that generates the call to "colourSpr()" has "struct sp1_cs *c" in HL when the call is made. This is actually not an accident but it's also not documented.
Just to add, the called function is also intended to be implementable in assembler so in addition to pushing the params on stack for C, the params are also passed in registers HL (c) and BC (count). This sort of thing will be done for all iterated functions and the registers used will be consistent across versions (I hope!) so it should be reliable.
derekfountain
Member
Posts: 50
Joined: Mon Mar 26, 2018 1:49 pm

Post by derekfountain »

Thanks for this. All working now. :)
Post Reply