Search code examples
assemblytasm

TASM program to take 3-digit input, subtract a number from the input, and output the result


Here's my program that I wrote using TASM:

dosseg
.model small
.stack 100h
.data
h1 db 0
t1 db 0
o1 db 0
n db 0
hc db 0
tc db 0
prompt db 'Input 3 digit number: $'
prompt2 db 'Result: $'
lf db 0Dh,0Ah,24h

.code
mov ax,@data
mov ds,ax

lea dx,prompt
mov ah,09h
int 21h

mov ah,01h
int 21h
sub al,30h
mov h1,al

int 21h
sub al,30h
mov t1,al

int 21h
sub al,30h
mov o1,al

cmp h1,00h
jg hn
add n,00h

o:
mov bl,o1
add n,bl
jmp t

hn:
add n,64h
dec h1,01h
cmp h1,00h
jne hn

t:
cmp t1,00h
jg tn
add n,00h
jmp d

tn:
add n,0Ah
dec t1,01h
cmp t1,00h
jg tn
jmp o

d:
sub n,14h
cmp n,64h
jge d2
mov hc,00h

da:
cmp n,0Ah
jge d3
mov tc,00h
jmp dsp

d2:
sub n,64h
add hc,01h
cmp n,64h
jge d2
jmp da

d3:
sub n,0Ah
add tc,01h
cmp n,0Ah
jge d3

dsp:
add hc,30h
add tc,30h
add n,30h

lea dx,lf
mov ah,09h
int 21h

lea dx,prompt2
mov ah,09h
int 21h




mov dl,hc
mov ah,02h
int 21h







mov dl,tc
mov ah,02h
int 21h


mov dl,n
mov ah,02h
int 21h

mov ah,4Ch
int 21h
end

The program displays the difference between the 3-digit input and 20. I had no problem with input which is less than 148. When I input 148 or higher, it doesn't display 128. What do I do?


Solution

  • You are using signed comparisons when converting back the result to digits. If the result is above 128, it will be seen as a negative number and your conversion will fail.

    Here is a de-obfuscated and slightly cleaned up version of your code that might work if you change the two buggy jge into jae.

    dosseg
    .model small
    .stack 100h
    .data
    
    ; input digits
    in_1        db 0
    in_10       db 0
    in_100      db 0
    
    ; output digits
    out_1       db 0
    out_10      db 0
    out_100     db 0
    
    ; display
    d_prompt    db 'Input 3 digit number: $'
    d_result    db 'Result: $'
    d_crlf      db 0Dh,10,'$'
    
    .code
    
        mov ax,@data    ; setup data segment
        mov ds,ax
    
        lea dx,d_prompt ; display prompt
        mov ah,09h
        int 21h
    
        mov ah,01h      ; read 3 decimal digits and turn them to numbers
        int 21h
        sub al,30h
        mov in_1,al
    
        int 21h
        sub al,30h
        mov in_10,al
    
        int 21h
        sub al,30h
        mov in_100,al
    
        cmp in_1,00h    ; convert them to a byte
        jg add_hundreds
    
    add_units:
        mov bl,in_100
        add out_1,bl
        jmp check_tens
    
    add_hundreds:
        add out_1, 100
        dec in_1
        cmp in_1,00h
        jne add_hundreds
    
    check_tens:
        cmp in_10,00h
        jg add_tens
        jmp compute
    
    add_tens:
        add out_1,10
        dec in_10
        cmp in_10,00h
        jg add_tens
        jmp add_units
    
    compute:
        sub out_1, 20       ; substract 20 to result
    
        cmp out_1, 100
        jge above_99        ; ***bug*** should use unsigned comparison (jae)
    
    convert_tens:
        cmp out_1,10
        jge between_10_and_99
        jmp display_result
    
    above_99:
        sub out_1,100       ; convert hundreds
        inc out_100 
        cmp out_1,100
        jge above_99        ; ***bug*** should use unsigned comparison (jae)
        jmp convert_tens
    
    between_10_and_99:
        sub out_1,10        ; convert tens
        inc out_10
        cmp out_1,10
        jge between_10_and_99
    
    display_result:
        add out_100,30h     ; convert digits to characters
        add out_10,30h
        add out_1,30h
    
        lea dx,d_crlf       ; display line break
        mov ah,09h
        int 21h
    
        lea dx,d_result     ; display result message
        mov ah,09h
        int 21h
    
        mov dl,out_100      ; display output
        mov ah,02h
        int 21h
    
        mov dl,out_10
        mov ah,02h
        int 21h
    
        mov dl,out_1
        mov ah,02h
        int 21h
    
        mov ah,4Ch      ; terminate program
        int 21h
    end
    

    My own go at the problem, to be able to handle numbers > 255
    This version will handle anything up to 65535, then roll over to 0.
    Negative results (i.e. inputs < 20) are not handled nicely.

    .model small
    .stack 100h
    .data
    
    ; value to substract
    SUB_VALUE   equ 20
    
    ; number of digits
    NUM_DIGITS  equ 5
    
    ; conversion steps
    convert label word
    num = 1
    rept NUM_DIGITS-1
    num = num * 10
    endm
    rept NUM_DIGITS
        dw  num
    num = num / 10
    endm
    
    ; display
    d_prompt    db 'Enter a ',NUM_DIGITS+'0',' digits number: $'
    d_result    db 0Dh, 0Ah, 'Result: $'
    
    .code
    
    main:
        mov ax,@data    ; setup data segment
        mov ds, ax
    
        lea dx,d_prompt ; display prompt
        mov ah, 09h
        int 21h
    
        mov ah,01h      ; read decimal number
        mov cx, NUM_DIGITS
        lea si, convert
        cld
    read:
        mov ah,01h  ; read a digit
        int 21h
        sub al, '0' ; convert to number
        mov dl, al
        xor dh, dh
        lodsw       ; multiply by power of 10
        mul dx
        add bx, ax  ; cumulate result
        loop    read
    
        sub bx, SUB_VALUE   ; do the substraction
    
        lea dx,d_result     ; display result message
        mov ah,09h
        int 21h
    
        mov cx, NUM_DIGITS
        lea si, convert
    write:
        lodsw       ; power of 10 divider
        xchg    ax,bx   
        xor dx, dx
        div bx  ; divide result by power of 10
        mov bx, dx  ; store remainder
        mov dl, '0' ; print current digit
        add dl, al
        mov ah,02h
        int 21h
        loop    write
    
        mov ah,4Ch      ; terminate program
        int 21h
    end main
    

    Took me a bit of time to get a DosBox with TASM running, but it reminded me of my days as a DOS hacker :)