Newbie problem switching from protected mode to real mode
Posted: Mon Sep 24, 2012 8:51 am
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.
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: