I've been learning MASM64 over the last few days and written a simple demo, so I can get feedback on my understanding of x64 assembly programming.
It's really basic: it asks the user for their name, greets them, and then reverses the name string.
The point of this question is to get a confirmation I understand the basic stuff like calling convention, stack alignment, etc. correctly. It's surely not about performance.
The complete code:
extrn GetStdHandle : proc
extrn WriteConsoleA : proc
extrn ReadConsoleA : proc
.const
STDIN_HANDLE_ID equ -10
STDOUT_HANDLE_ID equ -11
INVALID_HANDLE_VALUE equ -1
NULL equ 0
.data
promptText db "Enter your name: ", 0
greetingText db "Hello, ", 0
backwardsText db "Your name backwards is: ", 0
.code
; Counts characters in a null-terminated string.
; Parameters:
; 1. Pointer to string.
; Return value:
; -> string length (without the terminator).
;
stringLength proc
; No need for shadow space.
mov rdx, rcx
mov rcx, -1
_nextChar:
inc rcx
mov al, byte ptr [rdx + rcx]
test al, al
jnz _nextChar
mov rax, rcx
ret
stringLength endp
; Displays a null-terminated string.
; Parameters:
; 1. Pointer to string to be displayed.
; 2. Number of characters to be displayed.
; Return value:
; -> On success: 0.
; -> On failure: -1.
;
stringPrint proc
; Reserve shadow space.
sub rsp, 48h
; Save string address for later use.
mov [rsp + 38h], rcx
call stringLength
; If (length == 0), just end the function.
test rax, rax
jz _stringPrintEnd
; Save string length for later use.
mov [rsp + 40h], rax
mov rcx, STDOUT_HANDLE_ID
call GetStdHandle
cmp rax, INVALID_HANDLE_VALUE
je _stringPrintEnd
mov qword ptr [rsp + 28h], NULL
mov r9, NULL
mov r8d, [rsp + 40h]
mov rdx, [rsp + 38h]
mov rcx, rax ; console handle
call WriteConsoleA
test rax, rax
jz _stringPrintError
xor rax, rax
_stringPrintEnd:
; Free shadow space.
add rsp, 48h
ret
_stringPrintError:
mov rax, -1
jmp _stringPrintEnd
stringPrint endp
; Reads count-1 characters from the standard input.
; Parameters:
; 1. Pointer to buffer to store the characters.
; 2. Character count.
; Return value:
; -> On success: 0.
; -> On error: -1.
;
stringRead proc
; Reserve shadow space.
sub rsp, 48h
; Save volatile registers for later use.
mov [rsp + 38h], rcx
mov [rsp + 40h], rdx
mov rcx, STDIN_HANDLE_ID
call GetStdHandle
cmp rax, INVALID_HANDLE_VALUE
je _stringReadError
mov qword ptr [rsp + 20h], NULL
lea r9, [rsp + 28h]
mov r8, [rsp + 40h]
mov rdx, [rsp + 38h]
mov rcx, rax
call ReadConsoleA
test rax, rax
jz _stringReadError
xor rax, rax
_stringReadError:
; Free shadow space.
add rsp, 48h
ret
stringRead endp
; Reverses a null-terminated string.
; Parameters:
; 1. Pointer to string to be reversed.
; 2. Pointer to buffer to store the output.
;
stringReverse proc
; Reserve shadow space.
sub rsp, 38h
; Save volatile registers.
mov [rsp + 28h], rcx ; src buffer
mov [rsp + 30h], rdx ; dst buffer
; RCX is already set.
call stringLength
test rax, rax
jz _stringReverseEnd
mov rcx, [rsp + 28h]
dec rcx
mov rdx, [rsp + 30h]
add rdx, rax
; Terminate the dst buffer.
mov byte ptr [rdx], 0
_reverseNextChar:
inc rcx
dec rdx
mov al, byte ptr [rcx]
; Check for terminator in src buffer.
test al, al
jz _stringReverseEnd
mov byte ptr [rdx], al
jmp _reverseNextChar
_stringReverseEnd:
xor rax, rax
; Free shadow space.
add rsp, 38h
ret
stringReverse endp
main proc
; Reserve shadow space: 20h for calls + 28h for buffers.
sub rsp, 48h
; Zero the buffers.
mov qword ptr [rsp + 20h], 0
mov qword ptr [rsp + 28h], 0
mov qword ptr [rsp + 30h], 0
mov qword ptr [rsp + 38h], 0
mov qword ptr [rsp + 40h], 0
lea rcx, promptText
call stringPrint
mov rdx, 14h
lea rcx, [rsp + 20h] ; reading buffer
call stringRead
lea rdx, [rsp + 34h] ; destination buffer
lea rcx, [rsp + 20h] ; source buffer
call stringReverse
lea rcx, greetingText
call stringPrint
lea rcx, [rsp + 20h]
call stringPrint
lea rcx, backwardsText
call stringPrint
lea rcx, [rsp + 34h]
call stringPrint
xor rax, rax
; Free shadow space.
add rsp, 48h
ret
main endp
end
Thanks for the help.