问题
I'm writing a simple program that takes in two numbers between 1-99, adds them and prints out the result. I'm done with the printing part, I can print up too three digits, 99+99=198 so it's enough for this program. I have the program working for two one digit numbers.
Example: 1 1 = 2 , 4 4 = 8
So the digits is separated by spaces. Now I need the following; 11 1 = 12, 1 45 = 46
what I got so far, when I first read a valid number i store it on the stack while I check what the next char is, if the next is space then this number is only one-digit. If I have another character I have to multiply the first by 10 and then add the latest character.
22 would be read like this: 2 2*10 = 20+2 = 22
Note that the rest of my program needs the result from this to be in register dl. Got one tiny question about registers and assembly, can a number stored in dl be referenced with dx? I'm wondering because mov and arithmetic operators seems to demand that the registers(operands) are of the same size.
My code so far
ReadNumber: ;; Reads one number 1-99
push ax
push bx
push cx
Lokke:
;; reads from keyboard
mov ah,01h
int 21h
cmp al, " "
je Lokke
cmp al,"0"
jb PrintError
cmp al,"9"
ja PrintError
;; First character is a valid number
push ax
;;storing the first on the stack while checking the next character
mov ah,01h
int 21h
cmp al," "
je OneNumber
;;This part runs if the next char is a valid digit.
mov cl, al ;cl next digit
pop ax ; getting back the first from the stack
sub ax,"0" THIS LINE IS ADDED THANKS!!!!
mov bl, 10
mul bl
add ax, cx
mov dx,ax
mov dh,0 ;success variable set to true
mov dl,al
sub dl,"0"
pop ax
pop bx
pop cx
ret
OneNumber:
pop ax ;; Don't need it.
mov dh,0 ;success variable set too true
mov dl,al
sub dl,"0"
pop ax
pop bx
pop cx
ret
This does not work properly, not even for one-digit numbers, I can't understand why :( It looks just fine! Thanks for input :)
回答1:
One obvious mistake (there may be others), is that you you are storing the ASCII value of the digit, yet you are multiplying it as if it were an integer.
In other words, the value of the first digit, stored temporarily on the stack is between 48 and 57 (decimal), for respectively "0" through "9".
I'm checking for other issues...
Edit: Second pass...
I went ahead and provided an evolutionary version from your snippet, changing a few things which I found wrong/missing, but also laying the ground for handling more than 2 digits.
The main changes are:
- Added test for digit 0-9 for 2nd (and subsequent) characters read from input
- Systematically converted to integer value from ASCII, as soon as digit validity is asserted. (by being consistent we avoid errors)
- Used the word at top of stack to hold the cumulative value (this way of doing is a bit funky as it causes you to ensure to pop this out, whenever you may branch/exit. More on this late
- ensured that the high part of the registers were zero-ed out (this may have been causing some of the bugs)
- We now have only one exit point (for the normal case, i.e. not counting the "PrintError"
Note: this code is untested, indeed not even compiled (don't have MASM here/now...)
;; ReadNumber ;; Reads a number from the input and returns its value in dx register ;; For now 0-99 values only ;; dh contains 0 = success code ;; dl contains the value ;; (this API should change so that bigger # can be returned) ReadNumber: push ax push bx push cx Lokke: ;; First digit is handled separately, as to to allow for preceding spaces mov ah,01h int 21h cmp al, " " je Lokke ;; skip spaces till first digit cmp al,"0" jb PrintError cmp al,"9" ja PrintError ;; First character is a valid number mov dh, 0 mov dl, al sub dl, "0" push dx ;; *** Cumulative value kept on top of stack *** MoreDigits: mov ah,01h int 21h pop cx ;; in case of premature exit want the stack as expected... cmp al," " je DoneWithDigits cmp al,"0" jb PrintError cmp al,"9" ja PrintError mov dh, 0 ;; good: character is a digit mov dl, al sub dl, "0" ;; down from ASCII mov ax, cx mov bl, 10 mul bl add ax, dx ;; fixed syntax error... push ax ;; *** keep cumulative value on top of stack jmp DoneWithDigits ;; For now only 2 digit max (remove this ;; jmp to try with more) ;; @@@ here to test for overflow / too many char input jmp MoreDigits ;; Almost done DoneWithDigits: pop dx mov dh,0 ;success variable set too true pop ax pop bx pop cx ret
And now, a few more general considerations
- Noted that you do not use any in-memory variables (i.e. memory locations that you'd define with "MyLabel dw ?" type syntax, to create "global" variables), or even local variables (found at relative offset of the frame pointer). This is maybe just to ease into assembly programming etc. and/or a requirement of your (?) assignment. No rush to get into addressing such variables, but you'll find these handy when you do.
- Calling convention: The ReadNumber() methods seems to be in charge of safeguarding the ax, bx and cx registers. (and to reestablish these before exiting). This is a bit of an odd convention, typically the caller to a method pushes his own context onto the stack (and then pushes any parameter to the function, if any). Anyway you'll see these conventions in action, along with the way the frame pointer register (BP) is used etc. Maybe check this wikipedia article as a preview (BTW, depending on the assembler you use in particular if this is a macro assembler, you can get much of this mechanics taken care of.) No rush to do all this, first get familiar with basics assembly.
- the layout of the code is a bit strange, with assembly we typically like the 4 column layout with the labels in the first column, nice and visible, then the Instructions, followed by the operands, and last the line comments.
Hope this helps,
now, have fun!
来源:https://stackoverflow.com/questions/1640994/dos-assembly-read-two-succeeding-characters-and-convert-to-number