问题
What i am trying to do
I am trying to print a series of negative and positive numbers using array.
What is the problem
I am not able to print negative numbers.
Input:
2,-3,-4,5,2,9
Output:
2-,529
My 8086 Assembly Code:
.model small
.stack 100h
.data
elements db 2,-3,-4,5,2,9,'#'
.code
mov ax, @data
mov ds, ax
mov al, 03h
mov ah, 0
int 10h
mov si, 0
;lea si, elements
dis:
cmp elements[si], '#'
je exit
mov dl, elements[si]
add dl, 48
mov ah, 02h
int 21h
inc si
loop dis
exit:
mov ah, 04ch
int 21h
end
Note: I am a complete beginner in assembly.
回答1:
Your program has 2 major problems:
You're using the
loop
instruction without having initialized the counter registerCX
beforehand! Luckily your code has an extra terminating condition on top of theCX
register probably being zero to start with. And then to think that you don't even need this instruction in this program.You never test if the number that you read from the array is positive or negative.
Here's a complete solution with additional important comments:
xor si, si ;Same as "mov si,0"
dis:
mov bl, elements[si]
cmp bl, '#'
je exit
test bl, bl ;Same as "cmp bl, 0"
jns IsPositive
mov dl, "-" ;First display a minus sign
mov ah, 02h
int 21h
neg bl ;Turn the negative number into a positive number
IsPositive:
mov dl, "0"
add dl, bl ;Change the value [0,9] into character ["0","9"]
mov ah, 02h
int 21h
inc si
jmp dis ;Unconditionally jump back. Don't use "loop"
exit:
mov ax, 4C00h ;Always use the full AX, the API defines it that way!
int 21h
回答2:
As you are complete beginner, I will just "fix" your code to do the wrong thing correctly (not bothering to write new code doing the correct thing at all, as that would be too many changes any way).
.model small
.stack 100h
; array terminator changed to -128, because signed bytes values can be -128 to +127
; so this new terminator allows for array with values -127 to +127
ARRAY_TERMINATOR EQU -128
; the old '#' is value 35, which makes weird possible range: [-128, +34] U [+36, +127]
.data
elements db 2,-3,-4,5,2,9,ARRAY_TERMINATOR
.code
mov ax, @data
mov ds, ax
mov ax, 03h ; ax = 3 <=> ah = 0 (set mode), al = 3 (text mode)
int 10h ; set default 80x25 text mode (clears screen)
xor si,si ; si = 0 (common x86 assembly idiom how to set zero)
; but it also destroy flags, so in certain cases the "mov r?,0" is needed.
display_loop: ; use meaningful label names, when possible
; using short labels saves time while writing, but wastes time
; while reading + debugging, which you will do lot more often!
; load value first, so you can also compare it from register (faster/shorter)
mov dl, [elements+si] ; it doesn't hurt anything, when dl = -128 at end
; also I would rather put address label inside brackets, the "elements[]"
; way evokes false feeling, that it is array access from C. It is NOT.
; works like that for byte arrays, but already for WORD you need si*2!
; now it's possible to compare for terminator against register dl
cmp dl, ARRAY_TERMINATOR
je exit
add dl, '0' ; you can use ASCII '0' formatting of value 48 to better tell
; source reader, what you are trying to do, which is adding ASCII digit '0'
; try to write source to reflect your human intentions, you can write
; value 48 in many ways: 48, 30h, '0', 32+16, 3*16 ...
; All of them will compile into the same 48, but each tells different story
; to the reader of the source. +48 is arithmetic, +'0' is conversion to ASCII
mov ah, 02h
int 21h
; the code above will display correct digit only for values 0-9
; any other value will be mangled into some other ASCII character
; check ASCII table to get idea what happens for other values (10+48 = ':')
inc si
jmp display_loop ; don't use loop without initializing "cx"
; and don't use "loop" either, do rather "dec cx" "jnz loop"
; "loop" instruction is artifically slowed down to support some legacy code
; Any way, in this case you don't want to loop per cx count, but until
; array terminator is hit, so use "jmp" instead to jump every time
; to fix the output the inner part of loop would have to:
; display char '-' when value is negative (for example for value -123).
; convert absolute value into digit characters in base-10 (decimal) formatting
; which involves dividing the value by 10 until zero and remembering remainders
; For example |-123| = +123 => would produce remainders: 3, 2 and 1
; Then you add '0' to each remainder, and display them in reversed order.
; display ', ' characters (for start even after last number, when works, improve)
; (logic to avoid last comma requires often some thought and more code
; one usual way is to display first number without comma, and rest of array
; starts by displaying comma, then number)
exit:
mov ah, 04ch
int 21h
end
(But at least I added some "overview", what would have to be done to display the numbers in expected way... anyway, pay attention first to the tiny details, which I changed in your first version)
回答3:
This instruction
add dl, 48
adds 48 to the value stored in dl
(set from the elements
array), to map it onto the corresponding ASCII digit (ASCII "0" == 48).
When dl
is negative the result will be less than 48 and would map onto an ASCII character outside of the digit range. For example, when processing -3:
mov dl, -3
add dl, 48
will result in dl
containing 45. This maps onto ASCII -
(minus/dash), so that is what should be output.
Assuming that each value in elements
represents a single digit (0-9) you need to test whether the value in elements
is negative, take its absolute value, then add that to 48 to arrive at the correct ASCII value for the digit. Then you need to output a minus character (ASCII 45) followed by the newly calculated ASCII digit.
来源:https://stackoverflow.com/questions/42325964/unable-to-display-negative-numbers-using-array-in-the-following-8086-assembly-co