0

I am trying to make a calculator in assembly. You introduce two variables and then you choose the operation. Now the operation is only addition. I am working only with BIOS interrupts, no DOS. Can someone explain to me how to make it? I have two problems and I am stuck with them:

  1. The first problem is when I add 5+6 for example it returns 59(48+11) in ASCII. I think a solution would be to print in decimal but I found nothing on the internet using only BIOS interrupts.
  2. The second problem is that when I add 49+59 it returns 9, so it adds the first characters only.

Code:

    firstVariableKeyboard: ;store in var1 first variable
        mov ah,0x0
        int 16h ;keyboard interrupt

        cmp al,0x0d
        je returnEnter

        mov ah,0eh ;tty mode
        int 10h

        mov byte [var1 + bx],al
        inc bx

        jmp firstVariableKeyboard

    secondVariableKeyboard: ;store in var2 second variable
        mov ah,0x0
        int 16h

        cmp al,0x0d
        je returnEnter

        mov ah,0eh
        int 10h

        mov byte [var2 + bx],al
        inc bx

        jmp secondVariableKeyboard

   equalMath:
        ;introduce first variable
        mov si,math_description0
        call printString

        mov bx,0
        call firstVariableKeyboard
        mov dl,[var1]
        ;conversion from ascii to decimal
        sub dl, '0'

        ;new line
        mov si,newLine
        call printString

        ;introduce second variable
        mov si,math_description1
        call printString

        mov bx,0
        call secondVariableKeyboard
        mov dh,[var2]
        ;conversion from ascii to decimal
        sub dh, '0'

        ;new line
        mov si,newLine
        call printString

        ;the result
        mov si,math_description2
        call printString

        ;adding
        add dl, dh
        ;conversion from decimal to ascii
        add dl, '0'

        mov byte [result], dl
        mov si,result
        call printString

        ;new line
        mov si,newLine
        call printString

        jmp mainLoop

printString:
    push ax                   ; save ax to stack

    mov ah, 0x0e              ; call tty function
    call printChar      

    pop ax                    ; take ax out of the stack
    ret                      
printChar:
    mov al, [si]              ; load symbol

    cmp al, 0                 ; if si empty then jump
    jz ifZero               

    int 0x10                  ; if not print al
    inc si                    ; increment si

    jmp printChar       ;again untill stack empty
ifZero:
    ret

    var1: times 64 db 0 
    var2: times 64 db 0 
    result:  times 64 db 0 
  1. 5+6=11 example
    5+6=11 example
  2. two char + two char example
    two char + two char example
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Vadim Doga
  • 11
  • 1
  • 3

1 Answers1

1

The two issues are almost the same. You are trying to represent a number containing 2 digits with 1 ASCII byte. You need to loop over your input AND over your output. Let me explain. I apologise in advance that the example is 64-bit ... you didn't post your complete code so I just ran with what I knew best to write this answer.

You're essentially doing this: (53 - 48) + (54 - 48). This gives us the value 11. So far so good. What is the ASCII value for representing 11 as a single character?

There isn't one. What you need, is to print the character '1', twice (to form the string "11").

What you need then, is a way to convert a number that has more than 1 digit into a string. Perhaps something like the itoa function in the C standard library would be sufficient.

Essentially you need to loop over the number and convert each digit into a character to form a string you can print. You can do this by dividing the number by 10 each iteration and checking the remainder. Here is an example running on IDEOne

The core of the itoa example is the division loop:

.divloop:
    xor rdx,rdx         ; Zero out rdx (where our remainder goes after idiv)
    idiv rcx            ; divide rax (the number) by 10 (the remainder is placed in rdx)
    add rdx,0x30        ; add 0x30 to the remainder so we get the correct ASCII value
    dec rdi             ; move the pointer backwards in the buffer
    mov byte [rdi],dl   ; move the character into the buffer
    inc dword [rbp-4]   ; increase the length

    cmp rax,0           ; was the quotient zero?
    jnz .divloop        ; no it wasn't, keep looping

Let's run the number 59 from your second example through this step by step:

Current buffer:

[0][0][0][0][0][0][0][0][0][0]

First iteration:

59 / 10 = 5 remainder 9
9 + 48 = 57 (the ASCII value of '9')

[0][0][0][0][0][0][0][0][0][57]
                            ^ place the ASCII value in the buffer, decrease the pointer and update the number to be the quotient from the idiv instruction

So if we print this now, we'd get the number 9. The quotient wasn't zero though so we loop around again:

5 / 10 = 0 remainder 5
5 + 48 = 53 (the ASCII value of '5')

[0][0][0][0][0][0][0][0][53][57]
                          ^ place the ASCII value in the buffer, decrease the pointer and update the number to be the quotient from the idiv instruction

The quotient is zero from this last iteration, and so we don't loop and exit the function.

These two bytes in the buffer are now 53 and 57 which are the ASCII values of the characters 5 and 9 respectively. If you were to print this buffer to your stdout, it would produce the string "59" (this example function also returns the length of the resulting string so that it can be printed correctly).

There is a similar issue with your input. You are passing in the strings "49" and "59" and only reading the very first character. You need to loop over this string and convert it to an integer. This should be fairly straight forward so I will leave that as an exercise for you.

The reason the second example "works" (it prints a number you somewhat expect) is because of the bug above. You're only adding 4 and 5 together which results in 9 - a number that has a single digit.

I hope that helps.

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • The question asked for 16-bit code, BTW; for some reason that's still popular as a hobby project, and for teaching at some universities(!). But more or less same difference, the asm is easy to port, just use `cwd` before `idiv r16` instead of `cqo` which you should have used before `idiv r64` instead of zero-extending. (Or use `div` if you want to zero-extend unsigned inputs.) – Peter Cordes Oct 13 '22 at 04:18
  • For other examples of `itoa` loops, [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) has 64-bit, [Add 2 numbers and print the result using Assembly x86](https://stackoverflow.com/a/28524951) has 32-bit, [Displaying numbers with DOS](https://stackoverflow.com/q/45904075) has 16-bit. – Peter Cordes Oct 13 '22 at 04:20