z88dk / z88dk

The development kit for over a hundred z80 family machines - c compiler, assembler, linker, libraries.
https://www.z88dk.org
Other
913 stars 174 forks source link

passing char using __z88dk_callee - Question #303

Closed feilipu closed 7 years ago

feilipu commented 7 years ago

If I've a C function definition that passes one or multiple uint8_t to an asm function, how is it (are they) represented on the stack? Is a single byte pushed onto the stack for each char?

Is the best way (or only way) to retrieve a char from the stack something like this?

ld a, (sp)
inc sp
zx70 commented 7 years ago

IIRC with the parameter passing a single byte is still using a whole word space, so you should increment sp twice, this is quite a common approach with many recent and historical compilers. You may know the FASTCALL trick for the single parameter passing, where a simple assignment to hl substitutes the heavy sp load/unload, usually provided by the compiler itself. The CALLEE option is a clever, slightly more complex option used to reduce the stack dependency when multiple parameters are needed.. as said it is a medium complexity topic, you can look at the already implemented functions and at their correspondent header definitions (a little redundant, look for the redefinitions,too).

feilipu commented 7 years ago

Yes, I'm using __z88dk_fastcall where I can. But the issue is most painful for the pdrv bytes in disk_read and disk_write.

extern DSTATUS disk_initialize (BYTE pdrv) __z88dk_fastcall;
extern DSTATUS disk_status (BYTE pdrv) __z88dk_fastcall;
extern DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) __z88dk_callee;
extern DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) __z88dk_callee;
extern DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff) __z88dk_callee;

I'll have to change my disk_ioctl implementation, now I know that a char uses one whole 16 bit stack push. Glad to know that now, rather than later. I'll dig into some further reading. Thanks :+1:

aralbrec commented 7 years ago

It is different for zsdcc and sccz80. zsdcc pushes one byte but sccz80 pushes two.

aralbrec commented 7 years ago

For every function, the library provides two entry points :- one with standard linkage (push, call, pop) and the other with fastcall or callee. The standard linkage is required for calls through a function pointer so if you don't care about that, you can skip it.

For fastcall, of course, the parameters are in registers so you don't need to worry about the stack and one byte pushes.

For callee, you do :)

feilipu commented 7 years ago

Ok. So the short code segment in the question is the right way to do it then? I can't see a better way at the moment.

aralbrec commented 7 years ago

Ok. So the short code segment in the question is the right way to do it then? I can't see a better way at the moment.

ld a, (sp)
inc sp

The z180 doesn't have that instruction does it? I have to switch gears for the z180 but I think it's just mlt and the io instructions that have been added.

The best way to access single chars depends on the argument list.

If there are two chars in a row as in foo(unsigned char a, unsigned char b) for zsdcc you can just pop into a register pair:

_foo:

   pop hl   ;; return address
   ex (sp),hl   ;; l = a, h = b

   jp asm_foo

Sometimes it's less convenient as when a char is isolated amongst words. as in foo(unsigned char a, void *b):

_foo:

   pop hl   ;; return address
   dec sp
   pop bc   ;; b = a
   ex (sp),hl   ;; hl = void *b

   jp asm_foo

If the parameters are in reverse order foo(void *b, unsigned char a):

_foo:

   pop af   ;; return address
   pop hl   ;; hl = void *b
   dec sp
   pop bc  ;; b = a
   push af

   jp asm_foo

These are all callee linkage where the parameters are removed from the stack by the called function. I'm also careful to separate the c interface from the asm implementation, mainly because I don't want to supply c overhead to people calling the asm implementation only, and I'm prepared to accommodate both C compilers as well as standard linkage for function pointers.

When the argument list gets longer, it can get hairy. There are examples in the c lib.

feilipu commented 7 years ago
ld a, (sp)
inc sp

The z180 doesn't have that instruction does it? I have to switch gears for the z180 but I think it's just mlt and the io instructions that have been added.

🤦‍♂️ Of course, you're right. Which makes life substantially more complex, as you've described. Thanks for the code hints. Very useful.

I'm also careful to separate the c interface from the asm implementation,...

I'll follow that principal too. Generates a lot of files to manage, and lots of jump instructions, but sensible given the flexibility needed for multiple compilers and calling mechanisms.