<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>http://wiki.specnext.dev/index.php?action=history&amp;feed=atom&amp;title=Calling_convention_notes</id>
	<title>Calling convention notes - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.specnext.dev/index.php?action=history&amp;feed=atom&amp;title=Calling_convention_notes"/>
	<link rel="alternate" type="text/html" href="http://wiki.specnext.dev/index.php?title=Calling_convention_notes&amp;action=history"/>
	<updated>2026-05-11T11:26:08Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>http://wiki.specnext.dev/index.php?title=Calling_convention_notes&amp;diff=11587&amp;oldid=prev</id>
		<title>Sol HSA: Created page with &quot;Note: these notes by AA were originally posted on the specnext discord  There are three calling conventions in z88dk:  standard C (stack based), fastcall (single parameter by...&quot;</title>
		<link rel="alternate" type="text/html" href="http://wiki.specnext.dev/index.php?title=Calling_convention_notes&amp;diff=11587&amp;oldid=prev"/>
		<updated>2020-10-12T10:34:45Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;Note: these notes by AA were originally posted on the specnext discord  There are three calling conventions in z88dk:  standard C (stack based), fastcall (single parameter by...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Note: these notes by AA were originally posted on the specnext discord&lt;br /&gt;
&lt;br /&gt;
There are three calling conventions in z88dk:  standard C (stack based), fastcall (single parameter by register) and callee.  Return values are by register unless it&amp;#039;s 64-bit (a different method is used) or struct (this will be suboptimal).  Values passed by register are via a subset of DEHL.  L if 8-bit, HL if 16-bit, DEHL if 32-bit.&lt;br /&gt;
&lt;br /&gt;
A. Standard C&lt;br /&gt;
&lt;br /&gt;
 int foo(int a, int b)&lt;br /&gt;
&lt;br /&gt;
CALLER&lt;br /&gt;
1. push values onto the stack in right to left order&lt;br /&gt;
2. call function&lt;br /&gt;
3. clean up stack&lt;br /&gt;
4. return value in subset of DEHL&lt;br /&gt;
&lt;br /&gt;
CALLEE&lt;br /&gt;
1. Stack above ret address must not be modified because the caller cleans up&lt;br /&gt;
2. Access via (ix+n) stack frame or pop/push sequence into registers&lt;br /&gt;
3. Place return value in subset of DEHL&lt;br /&gt;
&lt;br /&gt;
For asm subroutines, fastcall or callee is always better.  Standard stack based is required for variable number of arguments like printf and function pointers.&lt;br /&gt;
&lt;br /&gt;
B. Fastcall&lt;br /&gt;
&lt;br /&gt;
CALLER&lt;br /&gt;
1. Load subset of DEHL with single argument (L if 8-bit, HL if 16-bit, DEHL if 32-bit)&lt;br /&gt;
2. call function&lt;br /&gt;
3. return value is in a subset of DEHL&lt;br /&gt;
&lt;br /&gt;
CALLEE&lt;br /&gt;
1. Argument is in subset of DEHL&lt;br /&gt;
2. Place return value in a subset of DEHL&lt;br /&gt;
&lt;br /&gt;
C. Callee&lt;br /&gt;
&lt;br /&gt;
CALLER&lt;br /&gt;
1. push values onto stack in right to left order&lt;br /&gt;
2. call function&lt;br /&gt;
3. return value is in a subset of DEHL&lt;br /&gt;
&lt;br /&gt;
CALLEE&lt;br /&gt;
1. Pop values off stack into registers, restore return address, only return address remains on stack&lt;br /&gt;
2. Place return value in a subset of DEHL&lt;br /&gt;
&lt;br /&gt;
For asm functions specifically, it is far better not to inline code and rather just supply a prototype to tell the C compiler how to call the asm function and provide the implementation in a separate asm file.  Inlined asm affects a few things:  you lose control over section placement (stuff will be put in the c compiler&amp;#039;s destination sections), if c/asm is mixed it interferes with the optimizer and if c/asm is mixed you can&amp;#039;t always know if values are being temporarily held in registers or the stack.  z88dk provides special intrinsic functions in intrinsic.h to inline some common and simple asm like di/ei or endian conversion that don&amp;#039;t interfere with the optimizer.&lt;br /&gt;
&lt;br /&gt;
examples:&lt;br /&gt;
&lt;br /&gt;
 extern unsigned char foo(unsigned int a, unsigned int b);&lt;br /&gt;
&lt;br /&gt;
(standard c only use for c functions or asm calling convention for variable arguments / function pointer)&lt;br /&gt;
&lt;br /&gt;
 extern unsigned char foo(unsigned char a) preserves_regs(b,c,d,e,h,l) z88dk_fastcall;&lt;br /&gt;
&lt;br /&gt;
(fastcall function.  Parameter &amp;quot;a&amp;quot; is passed in register L, return value is expected in L.  Preserves spec tells the compiler it can store values in the listed registers across a call.  Remember if you return a value those registers are not preserved.  The c compiler can also generate z88dk_fastcall functions for c code without the preserves spec but it will only be good if the function is very short.  The implementation in a separate asm file would be:&lt;br /&gt;
&lt;br /&gt;
 SECTION code_user    ;; where in the memory map it goes, this will be in the main binary&lt;br /&gt;
              ;; other sections like PAGE_14 or BANK_7 can be specified too&lt;br /&gt;
 PUBLIC _foo    ;; c names have a leading underscore applied by the compiler when translating to asm&lt;br /&gt;
&lt;br /&gt;
 _foo:&lt;br /&gt;
  ; L = parameter a&lt;br /&gt;
&lt;br /&gt;
   ld l,0   ; return value&lt;br /&gt;
   ret&lt;br /&gt;
&lt;br /&gt;
 extern unsigned char foo(unsigned int a, unsigned int b) preserves_regs(b,c,d,e,h,l) z88dk_callee;&lt;br /&gt;
&lt;br /&gt;
(callee linkage, incoming parameters are on the stack, same sort of preserves spec)&lt;br /&gt;
&lt;br /&gt;
 SECTION code_user&lt;br /&gt;
 PUBLIC _foo&lt;br /&gt;
&lt;br /&gt;
 _foo:&lt;br /&gt;
   pop hl   ; hl = return address&lt;br /&gt;
   pop de  ; de  = &amp;#039;b&amp;#039;&lt;br /&gt;
   ex (sp),hl   ; hl = &amp;#039;a&amp;#039;&lt;br /&gt;
&lt;br /&gt;
   ld l,0   ;; return value&lt;br /&gt;
   ret&lt;br /&gt;
&lt;br /&gt;
A typical compile with files listed in the compile line:&lt;br /&gt;
&lt;br /&gt;
 zcc +zxn -v -clib=sdcc_iy -SO3 --max-allocs-per-node200000 main.c main.asm -o main -subtype=nex -Cz&amp;quot;--clean&amp;quot; -create-app&lt;br /&gt;
&lt;br /&gt;
The entire c library is written in asm in z88dk using fastcall and callee linkage.  The newlib implementation is rooted here:&lt;br /&gt;
&lt;br /&gt;
https://github.com/z88dk/z88dk/tree/master/libsrc/_DEVELOPMENT&lt;br /&gt;
&lt;br /&gt;
The implementation is normally split between an assembly implementation which uses registers as input and a c interface which does the fastcall / callee parameter collection and then jumps into the asm implementation.  Normally the fastcall c interface is an alias for the asm entry point.&lt;br /&gt;
&lt;br /&gt;
 long atol_fastcall(const char *buf)&lt;br /&gt;
&lt;br /&gt;
c fastcall interface: &lt;br /&gt;
https://github.com/z88dk/z88dk/blob/master/libsrc/_DEVELOPMENT/stdlib/c/sdcc_iy/atol_fastcall.asm&lt;br /&gt;
(it&amp;#039;s an alias for the asm entry point)&lt;br /&gt;
&lt;br /&gt;
c standard stack interface:&lt;br /&gt;
https://github.com/z88dk/z88dk/blob/master/libsrc/_DEVELOPMENT/stdlib/c/sdcc_iy/atol.asm&lt;br /&gt;
(used only for function pointers due to macro defined in header stdlib.h)&lt;br /&gt;
&lt;br /&gt;
asm implementation:&lt;br /&gt;
https://github.com/z88dk/z88dk/blob/master/libsrc/_DEVELOPMENT/stdlib/z80/asm_atol.asm&lt;br /&gt;
&lt;br /&gt;
The same pattern is followed for the entire library.  c/sdcc_iy/ holds the c interface.  z80/ holds the asm implementation.  The separation is made because the library is intended for asm programmers as well so calling directly there will avoid including c interface code.  For c code, the linker will normally place the c interface just before the asm implementation in memory so there is a &amp;quot;jp foo; foo:&amp;quot; bit of code.  The linker is meant to be modified later to eliminate these sorts of superfluous jumps.&lt;/div&gt;</summary>
		<author><name>Sol HSA</name></author>
	</entry>
</feed>