Convert 16-bit decimal number to other bases using NASM assembly
I am having some problems implementing the correct decimal conversion to binary, octal and hex using NASM assembly. I have most of the code as shown below. I have some problems that I am still working on. I have commented out the relevant information in the code.
%include "io.mac"
.STACK 100H
.DATA
msg1 db "Please enter a positive integer (max 16 bits): ",0
msg2 db "The binary (base-2) representation of the number is: ",0
msg3 db "The octal (base-8) representation of the number is: ",0
msg4 db "The hex (base-16) representation of the number is: ",0
msg5 db "A",0 ;These will be implemented when I get the
msg6 db "B",0 ;hexadecimal loop working to change 10, 11,
msg7 db "C",0 ;etc. to their hex equivalents.
msg8 db "D",0
msg9 db "E",0
msg10 db "F",0
.UDATA
num1 resw 1
quotient resw 1
remainder resw 1
.CODE
.STARTUP
PutStr msg1
nwln
GetInt [num1]
nwln
PutStr msg2
nwln
mov AX, [num1]
sub BH, BH
binary_loop:
push AX
mov BH, byte 2
div BH ;All loops output the remainders in reverse order at the moment.
mov [remainder], AH ;I was thinking of maybe moving the remainder value
PutInt [remainder] ;to a stack then calling it at the end of the loop,
mov [quotient], AL ;when the quotient value is zero. However,
mov AX, [quotient] ;I do not know enough to make that work. Any help
cmp [quotient], byte 0 ;would be greatly appreciated
jz binary_done ;Could also be je, as both do the same thing
loop binary_loop
binary_done:
nwln
PutStr msg3
nwln
mov AX, [num1]
sub BH, BH
octal_loop:
push AX
mov BH, byte 8
div BH
mov [remainder], AH
PutInt [remainder]
mov [quotient], AL
mov AX, [quotient]
cmp [quotient], byte 0
jz octal_done
loop octal_loop
octal_done:
nwln
PutStr msg4
nwln
mov AX, [num1]
sub BH, BH
hex_loop:
push AX
mov BH, byte 16
div BH
mov [remainder], AH
PutInt [remainder]
mov [quotient], AL
mov AX, [quotient]
cmp [quotient], byte 0
jz done
loop hex_loop
done:
.EXIT
Current output for test 16-bit number 155: Binary: 11011001 Eighth: 332 Hex: 119
+3
source to share
1 answer
There are 3 main problems in your code:
- It displays numbers in reverse order
- It displays hex digits as whole numbers (e.g. 0xB is displayed as 11)
- It is very badly commented (assembly is not high level language)
There are other problems in your code as well:
- It's inefficient to use registers (store things in slower variables instead)
- Duplicate code - the only procedure that takes "base" as input will halve the code size
- Violation of macros ("language within a language"); which makes the code difficult to understand (for example, I can't tell if a
PutInt
function call or inline code, or which logs its use / trashes), and makes it impossible to optimize the instructions around it correctly
For the first problem, it is best to construct the string in memory in reverse order and then print the resulting string. For the second problem, you need to stop using PutInt
- every digit needs to be converted to a character.
For example (32-bit 80x86, NASM, untested):
;Convert unsigned integer to string
;
;Input
; eax = integer
; ebx = base
;
;Output
; edi = address of string
;
;Trashed
; eax,edx
section .bss
tempString: resb 33 ;Worst case string size is 32 digits
; (for base2), plus string terminator
tempStringEnd:
section .text
convertIntegerToString:
mov byte [tempStringEnd-1],0 ;Store string terminator
mov edi,tempStringEnd-2 ;edi = address to store last character
.nextDigit:
xor edx,edx ;edx:eax = current value
div ebx ;eax = new current value, edx = next digit
add dl,'0' ;dl = character for next digit maybe
cmp dl,'9' ;Is the character correct?
jbe .gotChar ; yes
add dl,'A'-'0'-10 ; no, fix it up
.gotChar:
mov [edi],dl ;Store the character
dec edi ;edi = address for next character
test eax,eax ;Is the current value zero?
jne .nextDigit ; no, do the next digit
ret ;Done, EDI = address of completed string
+2
source to share