Skip to content

Instantly share code, notes, and snippets.

@SplittyDev
Last active September 21, 2021 19:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SplittyDev/8e728627012e57ac0deac196660014fb to your computer and use it in GitHub Desktop.
Save SplittyDev/8e728627012e57ac0deac196660014fb to your computer and use it in GitHub Desktop.
x86 bare metal protected mode itoa implementation

Freestanding x86 itoa implementation

For use in OS development.
Designed to be assembled with NASM, porting it over to other assemblers should be easy.

License

You are free to use, modify, distribute and sell this code.
A small message referring to this gist would be nice, though not required.

Important

This implementation uses a custom calling convention.
It is meant to be used within assembly code and does not adhere to the cdecl calling convention.

Requirements

  • Protected mode (makes use of 32-bit registers)
  • A small stack (at least 512 bits are recommended)

FAQ

Is this implementation fast?
It is reasonably fast.

Will it work in long mode?
Probably, untested though.

Why are the file names prefixed with characters?
To keep them in order.

;
; Convenience wrapper for __itoa.
; Registers are preserved.
;
; Usage:
; kitoa <value> [, base]
;
; Produces a null-terminated string in __itoabuf32.
;
%macro kitoa 1-2 10
push eax
push ebx
push ecx
mov dword eax, %1
mov dword ecx, %2
mov dword ebx, __itoabuf32
call __itoa
pop ecx
pop ecx
pop eax
%endmacro
section .rodata
;
; Conversion table for __itoa.
; Works for bases [2 ... 36].
;
__itoacvt:
db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
section .text
;
; Routine to convert a 32-bit integer to a string.
; Registers are preserved.
;
; EAX: Source integer
; EBX: Target address
; ECX: Base
;
; Internal register layout:
; start:
; EAX: Source integer
; ECX: Target address
; EDX: Base
; checknegative:
; EAX: Source integer
; EBX: Target address (original)
; ECX: Target address (active)
; divrem:
; EAX: Source integer
; ECX: Target address (active)
; EDX: Base / Result
; reverse:
; EBX: Target address (original)
; ECX: Target address (active)
; EDX: Target address (temporary)
;
__itoa:
.start:
push eax
push ebx
push ecx
push edx
mov edx, ecx
mov ecx, ebx
.checknegative:
test eax, eax
jns .divrem
mov byte [ecx], 0x2D
inc ecx
mov ebx, ecx
neg eax
.divrem:
push edx
push ecx
mov ecx, edx
xor edx, edx
div ecx
mov byte dl, [__itoacvt + edx]
pop ecx
mov byte [ecx], dl
pop edx
inc ecx
cmp eax, 0x00
jne .divrem
mov byte [ecx], 0x00
dec ecx
.reverse:
cmp ebx, ecx
jge .end
mov byte dl, [ebx]
mov byte al, [ecx]
mov byte [ebx], al
mov byte [ecx], dl
inc ebx
dec ecx
jmp .reverse
.end:
pop edx
pop ecx
pop ebx
pop eax
ret
section .bss
;
; Buffer to store the result of __itoa in.
;
align 64
__itoabuf32:
resb 36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment