so I have this working procedure that is basically like func. 09h of int 21h except it terminates at 00h, and it uses 01h-04h as control characters that determine the style of the text.

I wrote it in the main procedure of a test program to get it working, and then I put it in its own procedure in a larger program. When I run it from the larger program, I get this error:
"title: 16 bit MS-DOS Subsystem

Command Prompt - cmp
NTVDM has encountered a System Error
Access is denied.
Choose 'Close' to terminate the application
Close | Ignore"

I can't really ignore it. Anyways, I narrowed the cause of this error down to a procedure without a "RET" statement. But it has a return statement at the end. If I just put RET in the procedure, it runs, if I put all of the code back into the test program, it runs. So I'm thinking maybe there's a maximum size for a procedure? It is quite long, I haven't tried to slim it down at all, I'm not sure if I really can. Anyway, here's the procedure:

outp proc

        push cx
        push bx
        xor cx,cx
        xor bx,bx
nexch:  mov si,dx
        push dx
        add si,cx
        push cx
        mov al,[si]
        cmp al,00h
        jnz nndpr
        jmp endpr
nndpr:  cmp al,0dh				;LINE FEED
        jnz not0d
        mov ah,03h
        int 10h
        cmp dh,24d
        jz scwin
        inc dh
        mov ah,02h
        int 10h
        jmp gnext
scwin:  mov ax,0601h
        xor cx,cx
        mov dh,24d
        mov dl,79d
        mov bl,attrWHT
        int 10h
        jmp gnext
not0d:  cmp al,0ah				;CARRIAGE RETURN
        jnz not0a
        mov ah,03h
        int 10h
        mov dl,00h
        mov ah,02h
        int 10h
        jmp gnext
not0a:  cmp al,01h				;WHITE
        jnz not01
        mov bl,attrWHT
        mov attrCUR,bl
        jmp gnext
not01:  cmp al,02h				;GREEN
        jnz not02
        mov bl,attrGRN
        mov attrCUR,bl
        jmp gnext
not02:  cmp al,03h				;YELLOW
        jnz not03
        mov bl,attrHLT
        mov attrCUR,bl
        jmp gnext
not03:  cmp al,04h				;EDIT/HEADER
        jnz not04
        mov bl,attrEDT
        mov attrCUR,bl
        jmp gnext
not04:  mov ah,09h
        mov bl,attrCUR
        mov cx,01h
        int 10h
        mov ah,03h
        int 10h
        inc dl
        mov ah,02h
        int 10h
gnext:  pop cx
        pop dx
        inc cx
        jmp nexch
endpr:  pop bx
        pop cx
        ret

outp endp

I'm using TASM, and the model of the program is small... I tried making it "Large" and "Huge" to see how that would affect it but it said there was a problem with my call statement in the main procedure if I did (Forward reference needs override). Has anyone run into something similar to this? How can I fix it and make it work?

haaaaallllpppp :(

---moar---
okay so, I SUPPOSE I could do something like,

outp proc

jmp stprc
fnprc: ret
stprc:

...

jmp fnprc

outp endp

but isn't there another way around this?
Also, this seems to stick it in an infinite loop of sorts

---moar---

so it seems that when I add the ret at the beginning and then call it from the end (or just circumvent that ret and add another one at the end of the procedure) the program won't return from the procedure. The cursor just sits there, blinking, ctrl+c won't work, nothing will. I just have to close the prompt. when I add mov ax,4c00h and int 21h at the bottom the program exits properly, so it's not the code that's looping, it just...fails to do anything after that procedure. This is really odd, I have no idea how to fix it.

so I figured out if I pop the stack into ax at the very beginning of the procedure (the return pointer), move that value into a variable I have saved, then push it back, then at the end of the procedure, move the variable into ax and push it again, then it will return properly, so I suppose I'm just not unwinding the stack correctly... Still I wonder why access to the procedure is denied if the RET statement is too far away? I'd like to just put it at the end and save all those unnecessary jumps

It sounds like you are burrying your return adress on the stack. One potential problem is that you make four pushes and one path is to jump to endpr, which only makes two pops. so if you enter the procedure and al is not equal to 0 the first time through you jump directly to endpr and make two pops. This leaves two other values on top of the return address in the stack.

thanks, it took me awhile to figure that one out, but it's fixed now. Any idea about the access denied error if the RET is at the bottom of the procedure?

Not sure about that one. It could be because the procedure is too long but that should be a long jump error. I have never encountered acess denied, of course I've never used tasm either. Actually, now that I think about it I think I have encountered that in masm but I can't remember what caused it. I want to say it is because I mispelled the name at either the the proc or endp statement but if your code is still as it is printed that is not the problem.

well it's not a problem with TASM, since TASM compiled it fine and I unassembled it in DOS and all looked fine. At first I thought it was just the original problem, with the stack not unwinding, and instead of the "return address" pointing to the actual return address it pointed to some place else in the memory and generated an access denied... I'm not sure that would even happen and of course now I know that's not the case anyways... Maybe my processor (or something) checks the next few instructions for a RET statement and if it's not there, abandons?

This article has been dead for over six months. Start a new discussion instead.