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
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
main        proc
    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 
    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
    cmp     bl,'0'
    jge     keep_checking
    jmp     not_a_number
    cmp     bl,'9'
    jle     while_loop
    jmp     not_a_number
    mov     bl,[si]  ;bl = value  
    ;check if end of string      
    cmp     bl,'$'
    je      end_while 
    ;check if not a number
    cmp     bl,'0'
    jge     keep_checking_
    jmp     not_a_number
    cmp     bl,'9'
    jle     add_to_number
    jmp     not_a_number
    and     bx,000Fh ;'0'-> 0, '1' -> 1,...
    mov     dx,base10 
    mul     dx       ;number = number*10
    ;catch (Exception ArithmeticOverflow)
    jo      overflow     
    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
    ;jo      overflow 
    cmp     ax,0
    jl      overflow
    add     si,1     ;next value   
    jmp     while_loop
;is negative?
    cmp     is_negative,1
    jne     begin_part2
    neg     ax
;;;part 2: print base-2 number
    mov     cx,16
    mov     bx,ax
    mov     ah,2
    cmp     cx,0
    je      after_print
    shl     bx,1 
;if bit == 1
    jnc     print_0 
;then print_1    
    mov     dl,'1'
    int     21h
    jmp     add_counter
;else print_0
    mov     dl,'0'
    int     21h
    dec     cx 
    jmp     print_

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

1 Answer 1


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


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

  • \$\begingroup\$ 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. \$\endgroup\$ Nov 25, 2017 at 5:08
  • \$\begingroup\$ 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.) \$\endgroup\$ Nov 25, 2017 at 5:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.