diff --git a/include/mtl/utility.hpp b/include/mtl/utility.hpp index e6aa5cd..6e5ac9c 100644 --- a/include/mtl/utility.hpp +++ b/include/mtl/utility.hpp @@ -17,10 +17,11 @@ void mtl_hybridmove(void* dst, const void* src, size_t num8); uint32_t mtl_udiv10(uint32_t x); uint32_t mtl_umod10(uint32_t x); -uint32_t mtl_utostr(uint32_t x, char* buf, size_t buf_size, char** buf_out); -uint32_t mtl_utostrx(uint32_t x, char* buf, size_t buf_size, char** buf_out); -uint32_t mtl_itostr(uint32_t x, char* buf, size_t buf_size, char** buf_out); -uint32_t mtl_itostrx(uint32_t x, char* buf, size_t buf_size, char** buf_out); + +uint32_t mtl_utostr(uint32_t x, char* buf); +uint32_t mtl_utostrx(uint32_t x, char* buf); +uint32_t mtl_itostr(int32_t x, char* buf); +uint32_t mtl_itostrx(int32_t x, char* buf); #endif } diff --git a/src/armv4t/to.s b/src/armv4t/to.s index 87f5c1a..1546a23 100644 --- a/src/armv4t/to.s +++ b/src/armv4t/to.s @@ -2,244 +2,350 @@ .include "mtl/armv4t/asm/math.s" -.section .rodata - -.align 1 -hex_digits_lo: -.string "0123456789abcdef" -hex_digits_up: -.string "0123456789ABCDEF" - .section .iwram, "ax", %progbits .arm .align 2 -/* - * Writes the ASCII representation of the given unsigned value X to the buffer, - * writes the beginning address of the string to an output ptr, and returns - * the number of characters written. The value is written in hexidecimal - * format. - * - * r0 = x - * r1 = buffer start - * r2 = buffer size - * r3 = string start out ptr - * ret= num of characters written - */ -.global mtl_utostrx -.type mtl_utostrx STT_FUNC -mtl_utostrx: - // Store variables - push {r4-r7} - // Move buffer to end - add r1, r2 - sub r1, $1 - // Add null terminator and decrement - mov r6, $0 - str r6, [r1], $-1 - // Load digit address - ldr r4, =hex_digits_lo - // Zero num of characters - mov r7, $0 - .Ldecode_digitx: - // Increment number of characters - add r7, $1 - // Get least significant octet - and r6, r0, 0xF - // Write the ascii digit to the string - ldrb r5, [r4, r6] - strb r5, [r1], $-1 - // Shift to remove the least significant octet - lsrs r0, $4 - // Repeat if there are more digits left - bne .Ldecode_digitx - // Move to the start of the string and account for "0x", - // currently one character before - sub r1, $1 - //add r1, $1 - // Write "0x" to the beginning of the string - mov r6, '0' - strb r6, [r1] - mov r6, 'x' - strb r6, [r1, $1] - // Write the start of the string to the pointer - str r1, [r3] - // Return the number of characters written, account for "0x" - add r0, r7, $2 - // Restore variables - pop {r4-r7} -bx lr - -/* - * Writes the ASCII representation of the given unsigned value X to the buffer, - * writes the beginning address of the string to an output ptr, and returns - * the number of characters written. The value is written in decimal format. - * - * r0 = x - * r1 = buffer start - * r2 = buffer size - * r3 = string start out ptr - * ret= num of characters written - */ -.global mtl_utostr -.type mtl_utostr STT_FUNC -mtl_utostr: - // Store variables - push {r4-r7} - // Move buffer to end - add r1, r2 - sub r1, $1 - // Add null terminator and decrement - mov r6, $0 - str r6, [r1], $-1 - // Zero num of characters - mov r7, $0 - .Ldecode_digit: - // Increment number of characters - add r7, $1 - // Use % 10 to get the least significant digit - umod10 r6, r0, r12 - // Write the ascii digit to the string - add r5, r6, '0' - strb r5, [r1], $-1 - // Divide by 10 to remove the least significant digit - udiv10 r6, r0, r12 - mov r0, r6 - // Repeat if there are more digits left - bne .Ldecode_digit - // Move to the start of the string, currently one char before - add r1, $1 - // Write the start of the string to the pointer - str r1, [r3] - // Return the number of characters written - mov r0, r7 - // Restore variables - pop {r4-r7} -bx lr - /* * Writes the ASCII representation of the given signed value X to the buffer, - * writes the beginning address of the string to an output ptr, and returns - * the number of characters written. The value is written in hexidecimal - * format. + * returns the number of characters written. The value is written in decimal + * format and is null terminated. The string will always begin at the start of + * the buffer. * * r0 = x - * r1 = buffer start - * r2 = buffer size - * r3 = string start out ptr - * ret= num of characters written - */ -.global mtl_itostrx -.type mtl_itostrx STT_FUNC -mtl_itostrx: - // Store variables - push {r4-r8} - // r8 = 1 if negative, 0 if positive. Negate if negative - lsrs r8, r0, $31 - mvnne r0, r0 - addne r0, $1 - // Move buffer to end - add r1, r2 - sub r1, $1 - // Add null terminator and decrement - mov r6, $0 - str r6, [r1], $-1 - // Load digit address - ldr r4, =hex_digits_lo - // Zero num of characters - mov r7, $0 - .Lsdecode_digitx: - // Increment number of characters - add r7, $1 - // Get least significant octet - and r6, r0, 0xF - // Write the ascii digit to the string - ldrb r5, [r4, r6] - strb r5, [r1], $-1 - // Shift to remove the least significant octet - lsrs r0, $4 - // Repeat if there are more digits left - bne .Lsdecode_digitx - // Move to the start of the string and account for "0x", - // currently one character before - sub r1, $1 - // Write "0x" to the beginning of the string - mov r6, '0' - strb r6, [r1] - mov r6, 'x' - strb r6, [r1, $1] - // Check if number is negative - cmp r8, $0 - // Add the negative sign to the string and increase number of - // characters written if needed - subne r1, $1 - movne r6, '-' - strbne r6, [r1] - addne r7, $1 - // Write the start of the string to the pointer - str r1, [r3] - // Return the number of characters written, account for "0x" - add r0, r7, $2 - // Restore variables - pop {r4-r8} -bx lr -/* - * Writes the ASCII representation of the given signed value X to the buffer, - * writes the beginning address of the string to an output ptr, and returns - * the number of characters written. The value is written in decimal format. - * - * r0 = x - * r1 = buffer start - * r2 = buffer size - * r3 = string start out ptr + * r1 = buffer * ret= num of characters written */ .global mtl_itostr .type mtl_itostr STT_FUNC mtl_itostr: - // Store variables - push {r4-r8} - // r8 = 1 if negative, 0 if positive. Negate if negative - lsrs r8, r0, $31 - mvnne r0, r0 - addne r0, $1 - // Move buffer to end - add r1, r2 - sub r1, $1 - // Add null terminator and decrement - mov r6, $0 - str r6, [r1], $-1 - // Zero num of characters - mov r7, $0 - .Lsdecode_digit: - // Increment number of characters - add r7, $1 - // Use % 10 to get the least significant digit - umod10 r6, r0, r12 - // Write the ascii digit to the string - add r5, r6, '0' - strb r5, [r1], $-1 - // Divide by 10 to remove the least significant digit - udiv10 r6, r0, r12 - mov r0, r6 - // Repeat if there are more digits left - bne .Lsdecode_digit - // Check if number is negative - cmp r8, $0 - // Add the negative sign to the string and increase number of - // characters written if needed - movne r6, '-' - strbne r6, [r1] - addne r7, $1 - // If the number was positive, currently positioned one char before str - // If the number was negative, currently at the correct position - // Move one character forward if needed - addeq r1, $1 - // Write the start of the string to the pointer - str r1, [r3] - // Return the number of characters written - mov r0, r7 - // Restore variables - pop {r4-r8} + // Branch immediately if the number is positive + lsrs r12, r0, $31 + beq mtl_utostr + // Write the negative sign to the string + mvn r0, r0 + add r0, $1 + // Get the absolute value of the number + mov r12, '-' + strb r12, [r1], $1 + // Write abs(x) to the string + push {fp, lr} + bl mtl_utostr + pop {fp, lr} + // One more character was written to the string ('-') + add r0, $1 +bx lr + +/* + * Writes the ASCII representation of the given signed value X to the buffer, + * returns the number of characters written. The value is written in hexadecimal + * format and is null terminated. The string will always begin at the start of + * the buffer. + * + * r0 = x + * r1 = buffer + * ret= num of characters written + */ +.global mtl_itostrx +.type mtl_itostrx STT_FUNC +mtl_itostrx: + // Branch immediately if the number is positive + lsrs r12, r0, $31 + beq mtl_utostrx + // Write the negative sign to the string + mov r12, '-' + strb r12, [r1], $1 + // Get the absolute value of the number + mvn r0, r0 + add r0, $1 + // Write abs(x) to the string + push {fp, lr} + bl mtl_utostrx + pop {fp, lr} + // One more character was written to the string ('-') + add r0, $1 +bx lr + +/* + * Writes the ASCII representation of the given unsigned value X to the buffer, + * returns the number of characters written. The value is written in decimal + * format and is null terminated. The string will always begin at the start of + * the buffer. + * + * Instead of using a loop like the previous implementation, this implementation + * uses a manually unrolled loop. This implementation also writes the digits + * from left to right instead of right to left. Using this method we can write + * the string to the beginning of the buffer and still avoid reversing the string. + * + * r0 = x + * r1 = buffer + * ret= num of characters written + */ +.global mtl_utostr +.type mtl_utostr STT_FUNC +mtl_utostr: +// Store variables +push {r4-r6} + // Temporary used to calculate 10^P + mov r5, $10 + // Minimum of 1 digit + mov r3, $1 + // If less than 10, one digit, otherwise more + mov r4, $10 + cmp r0, r4 + blo .Ldigits_1 + // Now 2 digits + add r3, $1 + // If less than 100, two digits, otherwise more + mul r4, r5 + cmp r0, r4 + blo .Ldigits_2 + // Repeat... + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_3 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_4 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_5 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_6 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_7 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_8 + add r3, $1 + mul r4, r5 + cmp r0, r4 + blo .Ldigits_9 + // 10 digits + add r3, $1 +.Ldigits_10: + // Divide by 10^9 to get the most significant digit + udiv1000000000 r5, r0, r12 + // Write the digit to the string + add r6, r5, '0' + strb r6, [r1], $1 + // Subtract the most significant digit + ldr r6, =1000000000 + mul r6, r5 + sub r0, r6 +.Ldigits_9: + // Repeat... + udiv100000000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + ldr r6, =100000000 + mul r6, r5 + sub r0, r6 +.Ldigits_8: + udiv10000000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + ldr r6, =10000000 + mul r6, r5 + sub r0, r6 +.Ldigits_7: + udiv1000000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + ldr r6, =1000000 + mul r6, r5 + sub r0, r6 +.Ldigits_6: + udiv100000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + ldr r6, =100000 + mul r6, r5 + sub r0, r6 +.Ldigits_5: + udiv10000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + ldr r6, =10000 + mul r6, r5 + sub r0, r6 +.Ldigits_4: + udiv1000 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + mov r6, $1000 + mul r6, r5 + sub r0, r6 +.Ldigits_3: + udiv100 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + mov r6, $100 + mul r6, r5 + sub r0, r6 +.Ldigits_2: + udiv10 r5, r0, r12 + add r6, r5, '0' + strb r6, [r1], $1 + mov r6, $10 + mul r6, r5 + sub r0, r6 +.Ldigits_1: + add r6, r0, '0' + strb r6, [r1], $1 + + // Write the null terminator + mov r6, $0 + strb r6, [r1] + // Return number of digits written + mov r0, r3 +// Restore variables +pop {r4-r6} +bx lr + +/* + * Writes the ASCII representation of the given unsigned value X to the buffer, + * returns the number of characters written. The value is written in hexadecimal + * format and is null terminated. The string will always begin at the start of + * the buffer. + * + * Instead of using a loop like the previous implementation, this implementation + * uses a manually unrolled loop. This implementation also writes the digits + * from left to right instead of right to left. Using this method we can write + * the string to the beginning of the buffer and still avoid reversing the string. + * + * r0 = x + * r1 = buffer + * ret= num of characters written + */ +.global mtl_utostrx +.type mtl_utostrx STT_FUNC +mtl_utostrx: +// Store variables +push {r4-r6} + // Write "0x" to the string + mov r6, '0' + strb r6, [r1], $1 + mov r6, 'x' + strb r6, [r1], $1 + + // Minimum of 3 characters (including "0x") + mov r3, $3 + // If less than 0x10, one digit, otherwise more + mov r4, $0x10 + cmp r0, r4 + blo .Lxdigits_1 + // Now four characters + add r3, $1 + // If less than 0x100, two digits, otherwise more + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_2 + // Repeat... + add r3, $1 + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_3 + add r3, $1 + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_4 + add r3, $1 + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_5 + add r3, $1 + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_6 + add r3, $1 + lsl r4, $4 + cmp r0, r4 + blo .Lxdigits_7 + // Eight digits + add r3, $1 +.Lxdigits_8: + // Right shift to get the most significant digit + lsr r5, r0, $28 + // Get digit character + add r6, r5, '0' + // If the digit > 9, change to a lowercase letter + cmp r6, '9' + addhi r6, $0x27 + // Write the digit + strb r6, [r1], $1 + // Subtract the digit + lsl r5, $28 + sub r0, r5 +.Lxdigits_7: + // Repeat... + lsr r5, r0, $24 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $24 + sub r0, r5 +.Lxdigits_6: + lsr r5, r0, $20 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $20 + sub r0, r5 +.Lxdigits_5: + lsr r5, r0, $16 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $16 + sub r0, r5 +.Lxdigits_4: + lsr r5, r0, $12 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $12 + sub r0, r5 +.Lxdigits_3: + lsr r5, r0, $8 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $8 + sub r0, r5 +.Lxdigits_2: + lsr r5, r0, $4 + add r6, r5, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + lsl r5, $4 + sub r0, r5 +.Lxdigits_1: + add r6, r0, '0' + cmp r6, '9' + addhi r6, $0x27 + strb r6, [r1], $1 + + // Write the null terminator + mov r6, $0 + strb r6, [r1] + // Return the number of digits written + mov r0, r3 +// Restore variables +pop {r4-r6} bx lr