From 70ab8b19c37cb8f76f5133bda90c0264b6aa6838 Mon Sep 17 00:00:00 2001 From: Kwarde Date: Tue, 15 Jul 2025 16:31:52 +0200 Subject: [PATCH] Allow printing prefixes for %x,%X,%b and %o (pass ~base to udec) --- src/console.asm | 26 ++++++++++++--- src/convert.asm | 87 ++++++++++++++++++++++++++++++++++++++++++------- src/tests.asm | 13 ++++++++ 3 files changed, 111 insertions(+), 15 deletions(-) diff --git a/src/console.asm b/src/console.asm index fe41669..701243e 100644 --- a/src/console.asm +++ b/src/console.asm @@ -70,8 +70,9 @@ puts: ; %b Unsigned integer, printed as binary number ; %o Unsigned integer, printed as octal number ; %s String -; %N* Pad left, N chars (maximum 64). Supported specifiers: d, i, u, x, X, b, o -; *0N* Pad left with zeroes, N chars (maximum 64). Supported specifiers: d, i, u, x, X, b, o +; %#{x} Use prefix for printed number (non-decimal). Supported specifiers: x, X, b, o. Works in combination with width specifier (see below) +; %[#]n* Pad left, n chars (maximum 64). Supported specifiers: d, i, u, x, X, b, o +; *[#]0n* Pad left with zeroes, n chars (maximum 64). Supported specifiers: d, i, u, x, X, b, o ; Unsupported specifiers are printed as-is ; For all specifiers (except %%) an argument is expected. Mismatch between arguments given and specifiers provided will lead to issues ; @@ -91,6 +92,8 @@ puts: ; r12 Padding for numbers (0=nopadding, >0=padding w/ spaces, NOT >0=padding w/ zeroes) ; r13 Padding: zeroes (=1) or spaces (=0) ; r14 Keeps track of amount of processed format specifiers +; r15 Include prefix for specifiers (1=yes, 0=no) +; TODO: Keep r13 and r15 in one register (use bitmasking to determine whether to pad with zeroes or spaces and whether to include prefix) printf: %macro load_arg 1 cmp r14, 4 @@ -124,7 +127,13 @@ printf: load_arg rsi push_regs mov rdi, rsi + test r15, r15 + jz %%baseNormal + mov rsi, ~%1 + jmp %%args + %%baseNormal: mov rsi, %1 + %%args: mov rdx, r12 mov rcx, r13 mov r8, %2 @@ -140,7 +149,7 @@ printf: push r12 push r13 push r14 - sub rsp, SIZE_QWORD + push r15 cmp byte [rdi], EOS je .emptyStr @@ -156,6 +165,7 @@ printf: xor r10, r10 xor r11, r11 xor r14, r14 + xor r15, r15 .process: cmp byte [rdi], EOS @@ -206,6 +216,14 @@ printf: je .wrapup cmp byte [rdi + 1], '%' je .rep_pct + ; Include specifier (r15=1) or not (r15=0) + xor r15, r15 + cmp byte [rdi + 1], '#' + jne .argGetPadZeroes + inc rdi + mov r15, 1 + ; Padding: zeroes (r13=1) or spaces (r13=0) + .argGetPadZeroes: xor r13, r13 mov r12, 1 cmp byte [rdi + 1], '0' @@ -384,7 +402,7 @@ printf: xor rax, rax .quit: - add rsp, SIZE_QWORD + pop r15 pop r14 pop r13 pop r12 diff --git a/src/convert.asm b/src/convert.asm index bda7dc7..83d6fbd 100644 --- a/src/convert.asm +++ b/src/convert.asm @@ -30,8 +30,13 @@ itoa: ; Before doing anything, check if base is valid cmp rsi, 10 je .ok + + cmp rsi, ~10 + je .negBase xor rax, rax ret + .negBase: + not rsi .ok: push r12 push r13 @@ -122,24 +127,54 @@ itoa: ;----- utoa(int, base, padLen, bool padZeroes, bool upperCase) -----; ; Converts an unsigned integer to a string -; Return value: Pointer to converted string or 0(EOS) if entered base is invalid +; Return value: Pointer to converted string or 0(EOS) if entered base is invalid. +; To use prefix (ie 0x, 0b or 0o), pass ~base instead of base ; Supported bases: 2 (binary), 8, (octal), 10 (decimal), 16 (hexadecimal) ; Used registers: ; rax* num to divide (DIV) >> Copy bytes when reversing string >> (ret) Pointer to converted string ; rbx Stores (upperCase) -; rdi* (arg) Integer to convert -; rsi* (arg) Base to convert to -; rdx* (arg) Padding length >> modulo (DIV) -; rcx* (arg) 0=pad spaces, otherwise pad zeroes >> Loop counter -; r8* (arg) 0=lowercase letters (for bases using letters, ie 16), uppercase otherwise >> Stores character to copy to when determining number or uppercase/lowercase letter +; rdi* (arg) Integer to convert >> base (positive) >> pointer to cnvtBuff[] +; *rsi (arg) Base to convert to +; *rdx (arg) Padding length >> modulo (DIV) +; *rcx (arg) 0=pad spaces, otherwise pad zeroes >> Loop counter +; *r8 (arg) 0=lowercase letters (for bases using letters, ie 16), uppercase otherwise >> Stores padding character >> used for inserting prefix ; r9* Counts string length ; r10* Points to cnvtBuff(Rev)[] -; r11* Remembers if number is negative (r11b) +; *r11 Stores padZeroes ; r12 Stores (int) ; r13 Stores (base) ; r14 Stores (padLen) ; r15 Stores (padZeroes) utoa: + %macro insert_prefix 0 + xor r8, r8 + xor rsi, rsi + + mov r8, 'x0' + cmp rdi, 16 + cmove rsi, r8 + je %%insert + + mov r8, 'o0' + cmp rdi, 8 + cmove rsi, r8 + je %%insert + + cmp rdi, 2 + jne %%continue + mov rsi, 'b0' + + %%insert: + mov word [r10], si + add r10, 2 + add r9, 2 + %%continue: + ;nop + %endmacro + + ;; + ;; ENTRY + ;; ; Before doing anything, check if base is valid cmp rsi, 2 je .ok @@ -149,8 +184,19 @@ utoa: je .ok cmp rsi, 16 je .ok + + cmp rsi, ~16 + je .ok + cmp rsi, ~10 + je .negBase + cmp rsi, ~8 + je .ok + cmp rsi, ~2 + je .ok xor rax, rax ret + .negBase: + not rsi .ok: push rbx push r12 @@ -174,8 +220,13 @@ utoa: mov rdx, '0' test r15, r15 cmovnz rdi, rdx - mov r15, rdi + mov r11, rdi ; Prepare for conversion + mov rdi, r13 + test r13, r13 + jns .continuePrepare + not rdi + .continuePrepare: mov rax, r12 xor r9, r9 lea r10, [rel cnvtBuffRev] @@ -186,7 +237,7 @@ utoa: mov r8, 'A'-10 .convert: xor rdx, rdx - div r13 + div rdi cmp dl, 9 jle .noLetter add dl, r8b @@ -199,17 +250,31 @@ utoa: inc r9 test rax, rax jnz .convert + ; Insert prefix if needed + test r13, r13 ;prefix needed? (MSB not set) + jns .checkPadding + test r15, r15 ;prefix now? (padding with spaces; r15 is zero) + jnz .checkPadding + insert_prefix + .checkPadding: ; Substract length of converted number from padLen sub r14, r9 cmp r14, 0 - jle .makeString + jle .checkPrefix mov rcx, r14 .addPadding: - mov byte [r10], r15b + mov byte [r10], r11b inc r10 inc r9 loop .addPadding + ; Insert prefix if needed + .checkPrefix: + test r13, r13 ;prefix needed? (MSB not set) + jns .makeString + test r15, r15 ;prefix now? (padding with zeroes; r15 is not zero) + jz .makeString + insert_prefix .makeString: dec r10 ;don't point to past last character lea rdi, [rel cnvtBuff] diff --git a/src/tests.asm b/src/tests.asm index e32c26d..daf35a6 100644 --- a/src/tests.asm +++ b/src/tests.asm @@ -99,6 +99,8 @@ section .rodata printf12Str db "\nRAX\t%064b\nEAX\t\t\t\t\t%032b\n AX\t\t\t\t\t\t\t%016b\n AH\t\t\t\t\t\t\t%08b\t\n AL\t\t\t\t\t\t\t\t%08b\n",EOS printf13 db TAB,"printf(",DQUO,"%o | %8o | %08o\n",DQUO,", 1500, 1500, 1500): ",NL,TAB,TAB,EOS printf13Str db "%o | %8o | %08o\n",EOS + printf14 db TAB,"printf(",DQUO,"%#b | %#08b | %#x | %#8X\n | %#o",DQUO,", 8, 8, 0xABCDEF, 0x12345678, 15): ",NL,TAB,TAB,EOS + printf14Str db "%#b | %#08b | %#x | %#8X | %#o\n",EOS ; strlen() msgStrlen db NL,"TEST strlen()",NL,EOS @@ -383,6 +385,17 @@ _start: mov rdx, 1500 mov rcx, 1500 call printf + + ; TEST 14 + lea rdi, [rel printf14] + call print + lea rdi, [rel printf14Str] + mov rsi, 8 + mov rdx, 8 + mov rcx, 0xABCDEF + mov r8, 0x12345678 + mov r9, 15 + call printf %endif ;---