问题
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:
- 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.
- 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
- 5+6=11 example
- two char + two char example
回答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.
来源:https://stackoverflow.com/questions/56324941/how-to-add-in-assembly