I am trying to convert an integer to a string and then displaying it using the MessageBoxA function. It is successful if the number is an xero, but any other positive integer is displayed as a blank pop-up message.
default rel
global WinMain
extern ExitProcess
extern MessageBoxA
section .data
title: db 'Melang64', 0
section .bss
buf: resb 80
section .text
WinMain:
sub rsp, 28h
mov rax, buf
mov rdi, 1
call uitoa
mov rcx, 0
lea rdx,[buf]
lea r8,[title]
mov r9d, 0
call MessageBoxA
mov ecx,eax
call ExitProcess
add rsp, 28h
hlt
uitoa:
mov rsi, rax
mov rax, rdi
cmp rax,0
jnz uitoa_convert_nrm
mov byte [rsi], 48
inc esi
mov byte [rsi], 0
jmp uitoa_end
uitoa_convert_nrm:
mov r10, 10
xor rcx, rcx
uitoa_loop:
xor rdx, rdx
div r10
inc ecx
cmp rax, 0
jnz uitoa_loop
inc ecx
add rsi, rcx
mov byte [rsi], 0
mov rax, rdi
dec ecx
uitoa_convert:
xor rdx, rdx
dec rsi
div r10
add rdx, 48
mov byte [rsi], dl
loopnz uitoa_convert
uitoa_end:
ret
I am using the following program to run this code, it is a c++ program that just sends commands to the command prompt to create the obj file, link it and then run the exe:
#include <cstdlib>
#include <windows.h>
#include <iostream>
#include <string>
int main() {
std::cout << "Hello\n";
system("nasm -f win64 Hi.asm");
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
const wchar_t* command = L"\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.38.33130\\bin\\Hostx64\\x64\\link.exe\" Hi.obj /subsystem:windows /entry:WinMain /libpath:\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.38.33130\\lib\\x64\" /nodefaultlib msvcrt.lib /libpath:\"C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.22621.0\\um\\x64\" /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no";
std::wstring writableCommand(command);
if (CreateProcess(NULL, &writableCommand[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else {
std::wcerr << L"Error: " << GetLastError() << std::endl;
}
system("Hi.exe");
return 0;
}
The c++ program is not the problem, however the asm part of converting the integer to the string is, I believe.
I am using nasm and I am using a windows machine. I am writing for 64 bit.
My assembly knowledge is not the best, but I tried to make sure that all the registers are correct, as well as the function calls. The program runs perfectly, it just doesn't display the number it should be, and is displaying a blank message, or buffer, in context to the code.
Your methode with 2 series of divisions is inefficient, but it is correct except that you put the terminating zero one byte too far. As a consequence Windows sees an empty string at the buffer that you provide.
What you currently obtain from converting the number 1 is:
0, '1', 0
^
buf
uitoa:
mov rsi, rax
mov rax, rdi
cmp rax,0
jnz uitoa_convert_nrm
mov byte [rsi], 48
inc esi
mov byte [rsi], 0
jmp uitoa_end
uitoa_convert_nrm:
mov r10, 10
xor rcx, rcx
uitoa_loop:
xor rdx, rdx
div r10
inc ecx
cmp rax, 0
jnz uitoa_loop
inc ecx <<<<<<< Remove this line!
add rsi, rcx
mov byte [rsi], 0
mov rax, rdi
dec ecx <<<<<<< Remove this line too!
uitoa_convert: RCX is at least 1 at this point
xor rdx, rdx
dec rsi
div r10
add rdx, 48
mov byte [rsi], dl
loopnz uitoa_convert <<<<<<< Just use LOOP (still inefficient)
uitoa_end:
ret
An alternative conversion routine for signed 64-bit integers follows. If you want it unsigned then simply remove the five lines that I've marked with ||
.
; IN (rcx,rdx) OUT (rax) MOD (rcx,r8,r9)
; RCX is the signed integer
; RDX is the buffer address
IntToStr:
push rdx ; (1)
mov r9, rdx
sub rsp, 24 ; At most 19 digits
mov rax, rsp
xchg rax, rcx ; -> RAX is 64-bit integer, RCX is temp buffer (stack)
mov r8d, 10 ; CONST R8 = 10
test rax, rax ||
jns .more ||
mov byte [rdx], '-' ||
inc r9 ||
neg rax ||
.more:
xor edx, edx
div r8 ; RDX:RAX / R8
add edx, '0'
mov [rcx], dl
inc rcx
test rax, rax
jnz .more
.copy:
dec rcx
movzx eax, byte [rcx]
mov [r9], al
inc r9
cmp rcx, rsp
ja .copy
mov byte [r9], 0 ; Zero-termination
add rsp, 24
mov rax, r9
pop rdx ; (1)
sub rax, rdx ; -> RAX is number of characters, but
ret ; not including the terminating zero
; Can be omitted