One of the irritations of writing x86-64 assembler code is the lack of standardisation between the Windows and Linux 64-bit ABIs. On Windows the first four (integer or pointer) parameters of a function get passed in
rcx,
rdx,
r8 and
r9 respectively whereas in Linux they get passed in
rdi,
rsi,
rdx and
rcx. Because they both use
rcx and
rdx, but for different parameters, there's no easy way of writing code that will run on both platforms (fortunately if there are no more than two parameters things are much more straightforward: you can simply load both the Windows and Linux registers).
Naively what you end up having to do is something like this:
Code: Select all
IF @platform% AND 7 THEN
[OPT pass%
mov rdi,parm1
mov rsi,parm2
mov rdx,parm3
mov rcx,pram4
]
ELSE
[OPT pass%
mov rcx,parm1
mov rdx,parm2
mov r8,parm3
mov r9,parm4
]
ENDIF
which is a pain, especially if your code includes many function calls. To make things a little easier I wrote this assembler 'macro' to do the work:
Code: Select all
DEF FNparm(pass%, p1$, p2$, p3$, p4$)
LOCAL G%, asm$
asm$ = "[opt " + STR$(pass% AND &FE)
IF @platform% AND 7 THEN
IF p4$<>"" asm$ += " : mov rcx," + p4$
IF p3$<>"" asm$ += " : mov rdx," + p3$
IF p2$<>"" asm$ += " : mov rsi," + p2$
IF p1$<>"" asm$ += " : mov rdi," + p1$
ELSE
IF p1$<>"" asm$ += " : mov rcx," + p1$
IF p2$<>"" asm$ += " : mov rdx," + p2$
IF p3$<>"" asm$ += " : mov r8," + p3$
IF p4$<>"" asm$ += " : mov r9," + p4$
ENDIF
asm$ += " : ]"
G% = OPENOUT (@tmp$ + "asmtmp.bba")
BPUT #G%, LEN(asm$) + 4
BPUT #G%, 0
BPUT #G%, 0
BPUT #G%, asm$;
BPUT #G%, &D
BPUT #G%, 0
BPUT #G%, &FF
BPUT #G%, &FF
CLOSE #G%
CALL @tmp$ + "asmtmp.bba"
= pass%
Now you can load the registers like this:
Code: Select all
OPT FNparm(pass%, "parm1", "parm2", "parm3", "parm4")
If one or more of the parameters happens itself to be a register (which is of course quite likely) you will need to be careful to ensure that it isn't modified by the macro before its contents are used. To avoid any risk of that, don't use
rcx,
rdx,
rsi or
r8 for parameters.