Reputation: 323
I have a problem with my assembly code. I want to print number stored in register cx, but when i tried to print it, it printed ascii character instead of ascii number, so I decided to write a procedure to convert ascii char to ascii value. Problem is, that when I try to call that procedure, the program freezes and I have to restart dosbox. Does anyone know whats wrong with this code? Thanks.
P4 PROC
MOV AX,CX ;CX = VALUE THAT I WANT TO CONVERT
MOV BX,10
ASC2:
DIV BX ;DIV AX/10
ADD DX,48 ;ADD 48 TO REMAINDER TO GET ASCII CHARACTER OF NUMBER
PUSH AX ;SAVE AX
MOV AH,2 ;PRINT REMAINDER STORED IN DX
INT 21H ;INTERRUP
POP AX ;POP AX BACK
CMP AX,0
JZ EXTT ;IF AX=0, END OF THE PROCEDURE
JMP ASC2 ;ELSE REPEAT
EXTT:
RET
P4 ENDP
Upvotes: 2
Views: 59012
Reputation: 5496
Here's the well tested code for printing number
.186
.model SMALL
.STACK 100h
.data
HelloMessage db 'Hello World', 13, 10, '$'
SuperNumber dw 13565
.code
; Writes word number from AX to output
; Usage
; .186
; ....
; mov ax, 13444
; call PRINT_NUMBER
PRINT_NUMBER PROC NEAR ; Outputs integer word number stored in AX registry. Requires CPU
; Save state of registers.
PUSHA ; Save all general purpose registers
PUSH BP ; We're going to change that.
; we need variables
; word number; 2 bytes
; byte digitsCount; 1 byte
; reserving space for variables on stack (3 bytes)
mov bp, sp; ; bp := address of the top of a stack
sub sp, 3*8 ; allocate 3 bytes on stack. Addresses of variables will be
; number: WORD PTR [rbp - 2*8]
; digitsCount: BYTE PTR [rbp - 3*8]
; Getting digits
; number = ax;
; digitsCount = 0;
; do
; {
; push number%10;
; digitsCount++;
; number = number / 10;
; }
; while (number > 0)
mov WORD PTR [bp - 2*8], ax ; number = ax;
mov BYTE PTR [bp - 3*8], 0 ; digitsCount = 0;
getDigits: ; do
mov ax, WORD PTR [bp - 2*8]; number/10: ax = number / 10, dx: number % 10
;cwd
mov dx, 0
mov bx, 10
div bx
push dx ; push number%10
mov WORD PTR[bp - 2*8], ax; number = number/10;
inc byte PTR[bp - 3*8] ; digitsCount++;
cmp WORD PTR[bp - 2*8], 0; compare number and 0
je getDigitsEnd ; if number == 0 goto getDigitsEnd
jmp getDigits ; goto getDigits;
getDigitsEnd:
mov ah, 9
mov dx, offset HelloMessage
int 21h
;while (digitsCount > 0)
;{
; pop digit into ax
; print digit
; digitsCount--;
;}
printDigits:
cmp BYTE PTR[bp - 3*8], 0; compare digits count and 0
je printDigitsEnd ; if digitsCount == 0 goto printDigitsEnd
pop ax ; pop digit into al
add al, 30h ; get character from digit into al
mov ah, 0eh ; wanna print digit!
int 10h ; BIOS, do it!
dec BYTE PTR[bp - 3*8] ; digitsCount--
jmp printDigits ; goto printDigits
printDigitsEnd:
; Deallocate local variables back.
mov sp, bp
; Restore state of registers in reverse order.
POP BP
POPA
; Exit from procedure.
RET
PRINT_NUMBER ENDP
start:
mov ax, @data
mov ds, ax
mov ah, 9
mov dx, offset HelloMessage
int 21h
mov ax, 64454
call Print_Number
mov ah, 4ch
int 21h
end start
Upvotes: 0
Reputation:
Thought I should post an update to this. This post really helped me out looking for a way to get key input for PHP CLI scripts. I couldn't find a Windows/DOS solution anywhere so I opted for an external program.
I'm using a really old version of a86 assembler so the code is pretty basic.
( and how do I turn off "Run Code Snippet"? )
;Key input for scripts that can use some "shell" function.
;Waits for a keypress then returns a string "key:scan"
;if "key" is 0 then you can check "scan" for arrow keys
;or other non-ASCII keys this way.
mov ah,0
int 16h
push ax
mov ah,0
mov cx,ax
call ASC
mov dx,':'
call COUT
pop ax
shr ax,8
mov ah,0
mov cx,ax
call ASC
jp EXIT
COUT:
mov ah,2
int 21h
ret
STROUT:
mov ah,9
mov dx,si
int 21h
ret
ASC:
mov byte [buffer+9],'$'
lea si,[buffer+9]
mov ax,cx
mov bx,10
ASC_LOOP:
mov dx,0
div bx
add dx,48
dec si
mov [si],dl
cmp ax,0
jz STROUT
jmp ASC_LOOP
EXIT:
mov ah,4ch
int 21h
ret
buffer: db " " ; 10 spaces. no "resb" in my assembler.
A sample PHP script to test it in:
<?php
function getKey() {
return shell_exec("getkey.com");
}
print "Press [ESC] to exit.\n\n";
$key = "";
while (ord($key) != 27) {
$getch = explode(":",getKey());
$key = chr($getch[0]);
$scan = $getch[1];
if (ord($key) != 0) {
print $key;
} else {
print "SCAN:$scan\n";
}
}
?>
Admittedly after all was said and done I realized I could have done the same thing in C++. But I learned a lot more about ASM this time around.
So thanks again folks!
Upvotes: 0
Reputation: 190
As Michael has written in his code, you need to clear DX , ie make it 0 before you divide.
But if you ask me, if you only need to display a number in the ASCII form (not get a smiley face when you want a number to be displayed). Converting the value to ASCII internally can be quite inconvenient.
Why don't you just use an array defined in the start of the program that has all the ASCII values of the numbers and pick the one that is corresponds to.
for eg. DB arr '0123456789'
and compare each number with the particular position and print that one. It's been a really long time since I've coded in 8086 but I remember using this logic for a program which required me to print an ASCII value of a hex number. So i used an array which had 0123456789ABCDEF
and it worked perfectly.
Just my two cents. Since you only wanted the result. doesn't really matter how you compute it.
Upvotes: 1
Reputation: 58447
Something like this would work better for printing a decimal value (the new code is in lowercase):
mov byte [buffer+9],'$'
lea si,[buffer+9]
MOV AX,CX ;CX = VALUE THAT I WANT TO CONVERT
MOV BX,10
ASC2:
mov dx,0 ; clear dx prior to dividing dx:ax by bx
DIV BX ;DIV AX/10
ADD DX,48 ;ADD 48 TO REMAINDER TO GET ASCII CHARACTER OF NUMBER
dec si ; store characters in reverse order
mov [si],dl
CMP AX,0
JZ EXTT ;IF AX=0, END OF THE PROCEDURE
JMP ASC2 ;ELSE REPEAT
EXTT:
mov ah,9 ; print string
mov dx,si
int 21h
RET
buffer: resb 10
Instead of printing each character directly it adds the characters to a buffer in reverse order. For the value 123 it would add '3' at buffer[8], '2' at buffer[7] and '1' at buffer[6] - so if you then print the string starting at buffer+6 you get "123".
I'm using NASM syntax but hopefully it should be clear enough.
Upvotes: 5