I have the following code, which takes a number in hexadecimal format and prints out its decimal format.
This program runs kinda fine in Turbo Debugger, but when I run it in DOS, I see some extra symbols in output after my number output:
.model small
.stack 100h
.486
.code
save proc
itm:
xor dx,dx
mov bx,10
div bx
add dl,30h
inc cx
pop bx
push dx
push bx
cmp ax,0
jne itm
ret
save endp
start:
mov ax, 0FFFFh
call save
mov ah, 02
print:
pop dx
int 21h
loop print
int 20h
end start
Output:
C:\TASM>lab31
65535 ò Φ■╤ 9°☻░╧♠UWSîÄPA÷0ó♥┴∞└εê$♦ó♥α♦▲ê$ó♦Σê←ë♦ó♥☻╨sÄ÷♣t╣ ╞ ÷┤ⁿ8sê¬ê²≤&mî│░ⁿ┘╗♥÷ t<sÿ☻╪╟♣I☼>♥b!├─4&Gê&_ëΩî∞[┴éΦ
z│Φ ☺\│Φ ♀fδ[♥3¡ïA1èG┴├═≥uè ç♦└┌é─Ω╕↕ëX╪♥♦♫↕Y^▼Z╖
←tÇ5▲♦▼δá♦├☻├ █
☻┬! C└(A∞1▬:↕ÿ├ƒ♥╞[%█☼C└≥░Φ
1357 46$♦♦
As you can see 65535 prints ok, but then garbage appears. When I run the program in Turbo Debugger, it hangs after writing out 65535.
There are a couple of problems with your code. You use CX
as your character counter, however in this code you increment without initialization:
save proc
itm:
xor dx,dx
mov bx,10
div bx
add dl,30h
inc cx ; increment CX, without initialization
pop bx
push dx
push bx
cmp ax,0
jne itm
ret
save endp
To fix this, you can set CX to zero by initializing it outside the main loop in your save
procedure:
save proc
xor cx,cx ; Explicitly clear CX counter.
itm:
xor dx,dx
To see information about the state of registers when an EXE loads see the bottom of this article in section register contents at program entry:
Register Contents AX If loading under DOS: AL contains the drive number for the first FCB in the PSP, and AH contains the drive number for the second FCB. BX Undefined. CX Undefined. DX Undefined. BP Undefined. SI Undefined. DI Undefined. IP Initial value copied from .EXE file header. SP Initial value copied from .EXE file header. CS Initial value (relocated) from .EXE file header. DS If loading under DOS: segment for start of PSP. ES If loading under DOS: segment for start of PSP. SS Initial value (relocated) from .EXE file header.
Register CX is considered undefined (so is BP,BX,DX,SI,DI) upon entry to your EXE program. AX likely will be non-zero since it is used to pass information to your EXE. Because CX is undefined it may or may not contain 0.
Sometimes when you run a debugger against an executable the value of CX might be different than running without the debugger. In the case of Turbo Debugger, CX appears to be zero upon entry. If you run your program outside the debugger it may not be zero, and would cause problems like you encountered. I recommend initializing your general purpose registers before using them.
As for the hang at the end of your program that is because you are using int 20h
. Your code suggests you are generating .EXE file (not .COM). The typical way to exit an .EXE program is to use int 21h
where AH=04ch and AL is your exit code. If you replace int 20h
with the following code it will exit with return value of 0:
mov ax, 4c00h
int 21h
With TASM you can also use the .exit n
directive (n = exit return value) as an alternative. This should generate the appropriate assembler code to exit back to DOS.
int 20h
is often used (retn
more common) in .COM programs. int 20h
relies on CS:0 being the address of the PSP block. This will be true by default in a .COM program, but in an .EXE program this isn't the case. More information on this can be found here and here:
Int 21h Function 4Ch
Notes: It is best to use INT 21h Function 4Ch to exit from '.exe' programs since Function 4Ch doesn't require that CS point to the PSP.
Int 20h
Notes: This function is an historical remnant offering no advantages over Function 4Ch. It's better to use function 4Ch, which returns an error code that other programs can access (via Function 4Dh or the ERRORLEVEL statement in batch files); also, CS need not be set first when using Function 4Ch. (INT 20h is equivalent to Function 0.)