# Assembly 8086 program to input a 16-bit signed number (in string form) and output its binary equivalent

I'm working on an exercise using Assembly 8086 which inputs a number (in string form) then output the binary form. Here is what I did (tested on emu8086 by the way):

1. Convert the string to numeric form: I simply iterated through the string, and for each new value I simply multiply the old value by 10, then add by the new value. But there are some checks for overflow which was annoying: I have to check in both the multiplication and addition part, and since in this part I simply ignored the negative part (for simplicity) I had to check for a special case: a 16-bit signed number goes from $-2^{15} = -32768$ to $2^{15} - 1 = 32767$, so if the input is -32768 then there would be an error, so I had to check for that case specifically.

2. From numeric to binary: I simply left-shifted the number (which is now stored in AX) consecutively and printed the most significant bit.

Please give some feedback, because I'm not sure if the overflow part is ok.

;Written by Dang Manh Truong
.stack      100h
.data
base10_string       dw      "-32799$" biggest_16bits_signed equ 7FFFh special_16bits_signed equ 8000h special_16bits_signed_str dw "1000000000000000$"
error_overflow      dw      "Arithmetic overflow encountered. Abort$" error_not_a_number dw "Not a number. Abort$"
base10      equ     10
tmp         dw      0
is_negative dw      0
.code
main        proc
;Initialization
mov     ax,@data
mov     ds,ax

;;;part 1: convert string to value
mov     ax,0 ;number = 0;
lea     si,base10_string
;check if positive or negative
mov     bl,[si]  ;bl = value
cmp     bl,'+'
jne     check_if_negative
add     si,1    ;start from after the "+" sign
mov     ax,0
jmp     while_loop
check_if_negative:
cmp     bl,'-'
jne     check_if_not_a_number
mov     ax,1
mov     is_negative,ax
mov     ax,0
add     si,1    ;start from after the "-" sign
jmp     while_loop
check_if_not_a_number:
cmp     bl,'0'
jge     keep_checking
jmp     not_a_number
keep_checking:
cmp     bl,'9'
jle     while_loop
jmp     not_a_number
while_loop:
mov     bl,[si]  ;bl = value
;check if end of string
cmp     bl,'\$'
je      end_while
;check if not a number
check_:
cmp     bl,'0'
jge     keep_checking_
jmp     not_a_number
keep_checking_:
cmp     bl,'9'
jmp     not_a_number
and     bx,000Fh ;'0'-> 0, '1' -> 1,...
mov     dx,base10
;try
mul     dx       ;number = number*10
;catch (Exception ArithmeticOverflow)
jo      overflow
;try
add     ax,bx    ;number = number*10 + value
;catch <Exception ArithmeticOverflow)
;jo      overflow
cmp     is_negative,1
jne     _perform_check
cmp     ax,special_16bits_signed
jne     _perform_check
jmp     _aftercheck_overflow
_perform_check:
;jo      overflow
cmp     ax,0
jl      overflow
_aftercheck_overflow:
jmp     while_loop
end_while:
;is negative?
cmp     is_negative,1
jne     begin_part2
neg     ax
begin_part2:
;;;part 2: print base-2 number
mov     cx,16
mov     bx,ax
mov     ah,2
print_:
cmp     cx,0
je      after_print
shl     bx,1
;if bit == 1
jnc     print_0
;then print_1
mov     dl,'1'
int     21h
;else print_0
print_0:
mov     dl,'0'
int     21h
dec     cx
jmp     print_
after_print:

jmp     return_to_dos
overflow:
lea     dx,error_overflow
mov     ah,9
int     21h
jmp     return_to_dos
not_a_number:
lea     dx,error_not_a_number
mov     ah,9
int     21h
jmp     return_to_dos
return_to_dos:
mov     ah,4ch
int     21h
main        endp
end     main


One of the most problematic things to deal with in assembly and even other higher languages but to a lesser degree, is spaghetti code. The logic of the program becomes lost in scrolling back and forth through hundreds of lines, SO concise documentation of what is going in is essential.

Working from the inside out is a method that has worked very effectively for me.

; Read ASCII NULL terminated string and convert input to packed BCD.

GetNext:    lodsb                  ; Read next character into AL
or      al, al         ; Teminator ?
jz      Done

.......

jmp     GetNext

Done:


Sometimes you may even want to think of efficiency in terms of time. The most probable scenario is that you'd be encountering digits 0 to 9.

      GetNext:    lodsb                  ; Read next character into AL
cmp     al, '0'
jae     @F
cmp     al, '9'
jbe     @F
cmp     al, 0
jz      Done

; At this point we know our entry is invalid or alternately we might
; want to check for things like '+', '-' or in this case just ignore
; extraneous input.

jmp     GetNext

@@:  ; This is peculiar to MASM and is a local label.
; NASM it could be preceded with a period like .L0:

.......

jmp     GetNext

Done:

• Even better, range check with sub al, '0' / cmp al, 9 / ja non_digit(where you can also check for a terminating \0 by comparing against -'0'). Doing the whole range check with one compare is very nice, and it's a big win here because we actually want al - '0' to get the integer value of the digit. – Peter Cordes Nov 25 '17 at 5:08
• Also, if you care about performance, lodsb is slower than movzx eax, byte [si] / inc si. (lodsd / lodsq are efficient on Haswell and later, only 2 uops, but not on earlier Intel or on AMD.) – Peter Cordes Nov 25 '17 at 5:10