vladfau
vladfau

Reputation: 1003

TASM program outputs garbage and may hang upon exit

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.

Upvotes: 3

Views: 1466

Answers (2)

Michael Petch
Michael Petch

Reputation: 47613

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 ndirective (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.)

Upvotes: 6

owacoder
owacoder

Reputation: 4873

It looks like the loop print continues past the end of the number because you do not initialize CX to zero beforehand. (Thanks to @MichaelPetch, I found this at the same time his comment appeared) I would change your save routine to look something like this:

save proc
    xor cx,cx
    itm:

Upvotes: 3

Related Questions