To handle 64-bit stack alignment / parameter space issues we can align at the beginning of the routine, reserve necessary stack space before calling a Windows function, then clean up when it returns. For our own routines nothing special need be done, unless of course you want to use the Windows calling convention in your own code. This works fine in simple examples, but I was worried that something might go wrong in real code. So, before writing wrappers for all my necessary Windows calls, I decided to stress-test it a bit. The technique works fine, calling down as deep as 100 levels, and throwing in extra calls and pushes; why shouldn't it? The attached routine, TestStringAlignment.asm, uses recursion to stress the stack; it may be of interest to someone, so here it is. Admittedly, it's primitive; next step, I'll macro-ize the wrapper technique.
Please let me know if you have a better way, or see a mistake. Possibly no one will even look at it, since it's quite useless, so as a "teaser" I offer the following challenge: can you figure what the output will be without running it?
;; TestStackAlignment.asm, by rrr314159 1/5/15
; \bin\JWasm -win64 TestStackAlignment.asm
; \bin\link /subsystem:console TestStackAlignment.obj
; The purpose of this routine is to stress-test the stack alignment technique,
; making sure it works correctly at all levels of the calling stack, with
; random extra calls and pushes inserted. 4 and 5 Parameters are sent to Windows
; routines, demonstrating that it works when parameter space exceeds 20h.
sprintf proto :ptr SBYTE, :ptr SBYTE, :VARARG
printf proto :ptr SBYTE, :VARARG
ExitProcess proto :DWORD
includelib \lib\kernel32.lib
includelib \lib\msvcrt.lib
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
StringBuffer db 2560 dup (0)
teststring db "Hi, I'm a test string", 0
fmtstrpiiff db "RSP %x, level %i, %.4g, %.9g", 10, 0
fmtstrpiiS db "RSP %x, level %i, %s", 10,0
fmtstriiff db "rsp %x, level %i, %.4g, %.9g", 10, 0
fmtstriiS db "rsp %x, level %i, %s", 10,0
arealvalue REAL8 -130.3
arealvalpi REAL8 3.14159265
toggle1 dd 0
toggle2 dd 0
.code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; »»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
start:
and rsp, -10h
lea rcx, StringBuffer ; point rcx at begin of String Buffer
mov r15, 22 ; go down 21 levels
CALL RecursiveTest ; launch the test routine
lea rcx, StringBuffer ; print out the String Buffer
call printf
xor ecx, ecx
call ExitProcess
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
RecursiveTest: ; Recursive test of Stack Alignment technique
; call the 4 (s)print routines more-or-less randomly,
; with various calls and pushes between them, to
; demonstrate robustness of Stack Alignment technique
dec r15
je donerecurse
cmp r15, 2 ; these randomly selected levels will
je @F ; call piiS and spiiS, toggling between them
cmp r15, 10
je @F
cmp r15, 13
je @F
cmp r15, 20
je @F
cmp r15, 21
je @F
jmp continue
@@:
cmp toggle1, 0 ; toggle piiS and spiiS
je call1
call piiS
jmp done1
call1:
call spiiS
done1:
sub toggle1, 1
neg toggle1
jmp callRecursiveTest
continue:
cmp r15, 4 ; these randomly selected levels skip printing
je @F ; those left will call piiS and spiiS, toggling
cmp r15, 7
je @F
cmp r15, 14
je @F
cmp r15, 17
je @F
cmp r15, 18
je @F
cmp toggle2, 0 ; toggle piiff and spiif
je call2
call piiff
jmp done2
call2:
call spiiff
done2:
sub toggle2, 1
neg toggle2
@@:
callRecursiveTest:
CALL RecursiveTest ; call "this" routine recursively
donerecurse:
ret
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
piiS: ; Print rsp, level (r15) and a test string
push rcx
lea r9, teststring
mov r8, r15
mov rdx, rsp
lea rcx, fmtstrpiiS
sub rsp, 20h
call printf
add rsp, 20h
pop rcx
ret
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
spiiS: ; Use sprintf to put formatted string with rsp,
; level (r15) and a test string in buffer for later printing
lea rsi, teststring
push rsi
mov r9, r15
mov r8, rsp
lea rdx, fmtstriiS
sub rsp, 20h
call sprintf
add rsp, 28h
ret
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
piiff: ; Print rsp, level (r15) and two real numbers
push rcx
lea rsi, arealvalpi
push REAL8 PTR [rsi]
lea rsi, arealvalue
mov r9, REAL8 PTR [rsi]
mov r8, r15
mov rdx, rsp
lea rcx, fmtstrpiiff
sub rsp, 20h
call printf
add rsp, 28h
pop rcx
ret
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
spiiff: ; Use sprintf to put formatted string with rsp,
; level (r15) and two reals in buffer for later printing
lea rsi, arealvalpi
push REAL8 PTR [rsi]
lea rsi, arealvalue
push REAL8 PTR [rsi]
mov r9, r15
mov r8, rsp
lea rdx, fmtstriiff
sub rsp, 20h
call sprintf
add rsp, 30h
ret
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤