Page 1 of 1

Newbie problem switching from protected mode to real mode

Posted: Mon Sep 24, 2012 8:51 am
by zxtonl
I have coded a small DOS assembly program to try the very old 386 protected mode thing and encountered a problem difficult to fix. Could some gurus provide some insight into the problem?

What it does is to set up GDT, switch to protected mode, write some string to screen, and switch back to real mode and exit to DOS. The program can show the string on screen, but it can not switch back to real mode and exit.

The source code is listed below, and also comes as an attachment. The program can be assembled by nasm, linked by tlink (turbo linker). The c16.mac file it includes is in nasm source distribution.

Thanks.

Code: Select all

%include "c16.mac"

section _mytext

; void get_xy(int x, int y);
proc    _goto_xy
%$x     arg
%$y     arg
        mov     ax,[bp+%$x]
        mov     [_cur_x],ax
        mov     ax,[bp+%$y]
        mov     [_cur_y],ax
endproc
        
; void set_attr(unsigned char attr);
proc    _set_attr
%$attr  arg
        mov     al,[bp+%$attr]
        mov     [_cur_attr],al
endproc

; void clear_scr(void);
global  _clear_scr
_clear_scr:
        mov      ah,[_cur_attr]
        mov      al,0
        mov      cx,2000
        mov      bx,0
clrchr:
        mov      [fs:bx],ax
        inc      bx
        inc      bx
        loop     clrchr
        ret     

; void write_str(char *str);
global  _write_str
_write_str:
        push    bp
        mov     bp,sp
        push    si
        cld
        
        mov      ax,[_cur_y]
        mov      cl,4
        shl      ax,cl
        mov      bx,ax  ; bx = _cur_y * 16
        shr      cl,1
        shl      ax,cl  ; ax = _cur_y * 64
        add      bx,ax  ; bx = _cur_y * 80
        add      bx,[_cur_x] ; bx = _cur_y * 80 + _cur_x
        shl      bx,1   ; bx = bx * 2
        mov      ah,[_cur_attr]
        
        mov      si,[bp+4]
loadchr:
        lodsb   
        cmp      al,0
        je      short finish
        mov      [fs:bx],ax
        add      bx,2
        jmp     short loadchr
finish:
        pop     si
        pop     bp
        ret     


..start:
        mov     ax,_mydata
        mov     ds,ax
        mov     es,ax

        ; set up stack  ; No need, DOS has done it
        ;mov    ax,_mystack
        ;mov    ss,ax
        ;mov    sp,stktop

        ;; save seg regs
        mov     ax,cs
        mov     [old_cs],ax
        mov     ax,ds
        mov     [old_ds],ax
        mov     ax,ss
        mov     [old_ss],ax
        ;mov    [old_sp],sp
        mov     ax,fs
        mov     [old_fs],ax
        
        ;; set up c16 descriptor
        xor     eax,eax
        mov     ax,cs
        shl     eax,4
        mov     [c16+2],ax
        shr     eax,16
        mov     [c16+4],al
        
        ;; set up d16 descriptor
        xor     eax,eax
        mov     ax,ds
        shl     eax,4
        mov     [d16+2],ax
        shr     eax,16
        mov     [d16+4],al

        ;; set up s16 descriptor
        xor     eax,eax
        mov     ax,ss
        shl     eax,4
        mov     [s16+2],ax
        shr     eax,16
        mov     [s16+4],al

        ;; disable interrupt
        cli

        ;; disable NMI
        in      al,70h
        or      al,80h
        out     70h,al

        ;; set up GDTR
        xor     eax,eax
        mov     ax,ds
        shl     eax,4
        add     eax,gdt
        mov     [gdtr+2],eax
        lgdt    [gdtr]

        ;; enter protected moe
        mov     eax,cr0
        or      eax,1
        mov     cr0,eax

        ;; clear ins cache & re-load CS
        jmp     0x08:.1
.1:
        ;; load DS
        mov     ax,10h
        mov     ds,ax

        ;; set up stack 
        mov     ax,18h
        mov     ss,ax
        ;mov    esp,pstop

        mov     ax,20h
        mov     fs,ax

        call    _clear_scr

        mov     ax,msg
        push    ax
        call    _write_str
        pop     cx

        mov     ax,10
        push    ax
        mov     ax,20
        push    ax
        call    _goto_xy
        pop     cx
        pop     cx

        mov     al,20
        push    ax
        call    _set_attr
        pop     cx

        mov     ax,msg
        push    ax
        call    _write_str
        pop     cx

        ;; change back to real mode
        mov     eax,cr0
        and     eax,0FFFFFFFEh
        mov     cr0,eax

        mov     ax,[old_cs]
        push    ax
        push    word .1
        retf
        ;jmp    _mytext:.2
.2:
        mov     ax,[old_ds]
        mov     ds,ax
        mov     es,ax

        mov     ax,[old_ss]
        mov     ss,ax
        ;mov    sp,[old_sp]
        mov     ax,[old_fs]
        mov     fs,ax
        
        ;; enable NMI
        in      al,70h
        and     al,7Fh
        out     70h,al

        ;; enable interrupt
        sti

        mov     ax,4C00h
        int     21h

section _mydata align=16
global old_sp
;old_sp dw      0
old_ss  dw      0
old_fs  dw      0
old_ds  dw      0
old_cs  dw      0

global gdt
gdt:
null    dq      0
c16     db      0FFh,0FFh,0,0,0,9Ah,0,0 ; nonconforming, readable, 16-bit
d16     db      0FFh,0FFh,0,0,0,92h,0,0 ; expand-up, writable, 16-bit
s16     db      0FFh,0FFh,0,0,0,92h,0,0
vga     db      0FFh,0FFh,0,80h,0Bh,92h,0,0     

gdtr    dw      $-gdt-1
        dd      gdt
global  _cur_x,_cur_y,_cur_attr,msg
_cur_x  dw      0
_cur_y  dw      0
_cur_attr db    0Fh

msg     db      'Hello World From Protected Mode!',0

section _mystack stack align=16
        resb    0x40
global stktop
stktop:

;section _pmstack aligh=16
;       resb    0x40
;pstop:

Re: Newbie problem switching from protected mode to real mod

Posted: Mon Sep 24, 2012 9:06 am
by Combuster
push ax
push word .1
retf
Wrong label?

Re: Newbie problem switching from protected mode to real mod

Posted: Mon Sep 24, 2012 10:04 am
by zxtonl
Yes. You've got acute eyes. And how dumb am I! Thanks. Now it works.

Actually, my previous non-working version has two different places from this version.

First difference is the stack GDT entry:
s16 db 03Fh,0,0,0,0,92h,0,0 ; the limit part was not 0FFh,0FFh but the actual stack length

Second difference is the long jump:
jmp _mytext:.2

I now know the trouble maker is the stack GDT entry, but I am not understand why a non 0xFFFF limit causes trouble. To my knowledge, DOS has its own stack, why does my stack segment definition interfere with DOS's internal working.

Another lesson I learned was NEVER modifying TWO OR MORE places when doing trial and error.