home *** CD-ROM | disk | FTP | other *** search
- ;****************************************************************************
- ;* this file contains a simple PM switcher to illustrate the examples shown *
- ;* in PMTUT.TXT. Please do not spread this file without PMTUT.TXT! *
- ;* USE AT YOUR OWN RISK! *
- ;* *
- ;* Written by Till Gerken *
- ;* (Internet: tig@ngo.ol.ni.schule.de) *
- ;****************************************************************************
-
- ideal
- P386
-
- ;----------------------------------------------------------------------------
-
- STACK16_SIZE = 100h ; stack size for Real Mode
- STACK32_SIZE = 100h ; stack size for Protected Mode
-
- struc segment_descriptor
- seg_length0_15 dw ? ; low word of the segment length
- base_addr0_15 dw ? ; low word of base address
- base_addr16_23 db ? ; low byte of high word of base addr.
- flags db ? ; segment type and misc. flags
- access db ? ; highest nibble of segment length
- ; and access flags
- base_addr24_31 db ? ; highest byte of base address
- ends segment_descriptor
-
- struc interrupt_descriptor
- offset0_15 dw ? ; low word of handler offset
- selector0_15 dw ? ; segment selector
- zero_byte db 0 ; unused in this descriptor format
- flags db ? ; flag-byte
- offset16_31 dw ? ; high-word of handler offset
- ends interrupt_descriptor
-
- ;****************************************************************************
-
- segment code16 para public use16 ; this segment contains all 16-bit
- assume cs:code16, ds:code16 ; code and data stuff
-
- ;----------------------------------------------------------------------------
-
- stack16 db STACK16_SIZE dup (?) ; 16-bit Real Mode stack
- label stack16_end word
-
- idt_real dw 3ffh,0,0 ; Real Mode IDT
-
- ;----------------------------------------------------------------------------
- ; quick and dirty exit
- ; In: DS:DX - pointer to '$' terminated exit message
- ; Out: Difficult to say, the function never returns :)
-
- proc err16exit
- mov ah,9 ; select DOS' print string function
- int 21h ; print the msg
- mov ax,4cffh ; exit with exit-code 0ffh
- int 21h ; good bye...
- endp err16exit
-
- ;----------------------------------------------------------------------------
- ; checks if the processor is at least a 80386
-
- no386e db 'Sorry, at least a 80386 is needed!',13,10,'$'
-
- proc check_processor
- pushf ; save flags for later
- xor ah,ah ; clear high byte
- push ax ; push AX on the stack
- popf ; pop this value into the flag register
- pushf ; push flags on the stack
- pop ax ; ...and get flags into AX
- and ah,0f0h ; try to set the high nibble
- cmp ah,0f0h ; on a 80386, the high nibble can never be 0f0h
- je no386
- mov ah,70h ; now try to set NT and IOPL
- push ax
- popf
- pushf
- pop ax
- and ah,70h ; if they couldn't be modified, no 386 is installed
- jz no386
- popf ; restore flags
- ret ; and return
- no386:
- mov dx,offset no386e; if there is no 386, exit with error msg
- jmp err16exit
- endp check_processor
-
- ;----------------------------------------------------------------------------
- ; checks if we are running in Real Mode
-
- nrme db 'You are currently running in V86 mode!',13,10,'$'
-
- proc check_mode
- mov eax,cr0 ; get CR0 into EAX
- and al,1 ; check if PM bit is set
- jnz not_real_mode ; it is set, so exit
- ret ; nope, it isn't, Real Mode is good!
- not_real_mode:
- mov dx,offset nrme ; exit with msg
- jmp err16exit
- endp check_mode
-
- ;----------------------------------------------------------------------------
- ; this procedure just writes a zero-terminated message to the screen
- ; format: word x, word y, attribute byte, string, 0
- ; In: DS:SI - pointer to format string
-
- proc write_msg
- push ax si di es
- mov ax,0b800h ; segment of text screen
- mov es,ax ; get it to ES
- mov ax,[si+2] ; get Y position
- mov di,160
- mul di
- add ax,[si]
- mov di,ax
- mov ah,[si+4] ; get attribute byte
- add si,5
- write_loop_pm:
- mov al,[si]
- or al,al ; end of string?
- jz loop_end_pm
- inc si
- mov [es:di],ax
- inc di
- inc di
- jmp write_loop_pm
- loop_end_pm:
- pop es di si ax
- ret
- endp write_msg
-
- ;----------------------------------------------------------------------------
- ; main procedure, this is the entry point
-
- rm_msg db 0,0,0,0,1fh,'Now in Real Mode - press a key to switch '
- db 'to Protected Mode!',0
- rm2_msg db 0,0,3,0,1fh,'Back in Real Mode - press a key to return '
- db 'to DOS!',0
-
- start16:
- mov ax,cs ; load code-segment into DS and ES
- mov ds,ax
- mov es,ax
- cli ; better disable interrupts while setting
- mov ss,ax ; SS and SP
- mov sp,offset stack16_end
- sti ; now interrupts don't disturb any more
-
- call check_processor ; check if we are running on at least a 80386
- call check_mode ; check if we are running in Real Mode
-
- mov ax,code16 ; get code segment into AX
- movzx eax,ax ; clear high word
- shl eax,4 ; make a physical address
- mov [ds:code16_descriptor.base_addr0_15],ax ; store it in the dscr
- mov [ds:data16_descriptor.base_addr0_15],ax
- shr eax,8
- mov [ds:code16_descriptor.base_addr16_23],ah
- mov [ds:data16_descriptor.base_addr16_23],ah
-
- mov ax,code32 ; get 32-bit code segment into AX
- movzx eax,ax ; clear high word
- shl eax,4 ; make a physical address
- mov [ds:code32_descriptor.base_addr0_15],ax ; store it in the dscr
- mov [ds:data32_descriptor.base_addr0_15],ax
- shr eax,8
- mov [ds:code32_descriptor.base_addr16_23],ah
- mov [ds:data32_descriptor.base_addr16_23],ah
-
- mov ax,code32 ; get 32-bit code segment into AX
- movzx eax,ax ; clear high word
- shl eax,4 ; make a physical address
- add eax,offset dummy_descriptor ; calculate physical address of GDT
- mov [dword ds:gdt_start+2],eax
-
- mov ax,code32 ; get 32-bit code segment into AX
- movzx eax,ax ; clear high word
- shl eax,4 ; make a physical address
- add eax,offset interrupt_0 ; calculate physical address of IDT
- mov [dword ds:idt_start+2],eax
-
- mov ax,3 ; set text mode 3, just used to clear screen
- int 10h ; do it
-
- mov si,offset rm_msg; write real mode message to screen
- call write_msg
-
- xor ah,ah
- int 16h
-
- cli ; disable interrupts
- lgdt [fword ds:global_descriptor_table] ; load GDT register
- lidt [fword ds:interrupt_descriptor_table] ; load IDT register
- mov eax,cr0 ; get CR0 into EAX
- or al,1 ; set Protected Mode bit
- mov cr0,eax ; after this we are in Protected Mode!
- db 0eah ; opcode for far jump (to set CS correctly)
- dw offset start32,code32_idx
-
- exit16: ; the protected mode code returns here
- mov eax,cr0 ; get CR0 into EAX
- and al,not 1 ; clear Protected Mode bit
- mov cr0,eax ; after this we are back in Real Mode!
- db 0eah
- dw offset flush_ipq,code16
- flush_ipq:
- mov ax,cs ; restore important registers
- mov ss,ax
- mov sp,offset stack16_end
- mov ds,ax
- mov es,ax
- lidt [fword idt_real]
- sti ; enable interrupts
-
- mov si,offset rm2_msg ; write second message
- call write_msg
-
- xor ah,ah ; wait for a key
- int 16h
-
- mov ax,3 ; clear screen once again
- int 10h
-
- mov ax,4c00h ; everything is okay, we exit with exit-code 0
- int 21h ; bye...
-
- ;----------------------------------------------------------------------------
-
- ends code16
-
- segment code32 para public use32 ; this segment contains all 32-bit
- assume cs:code32, ds:code32 ; code and data stuff
-
- stack32 db STACK32_SIZE dup (?) ; 32-bit stack
- label stack32_end dword
-
- ;----------------------------------------------------------------------------
-
- label global_descriptor_table fword ; here begins the GDT
-
- gdt_start dw gdt_size,0,0 ; val for GDT reg
- dummy_descriptor segment_descriptor <0,0,0,0,0,0>
- code32_descriptor segment_descriptor <0ffffh,0,0,9ah,0cfh,0> ; 4GB 32-bit code
- data32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit data
- core32_descriptor segment_descriptor <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit core
- code16_descriptor segment_descriptor <0ffffh,0,0,9ah,0,0> ; 64k 16-bit code
- data16_descriptor segment_descriptor <0ffffh,0,0,92h,0,0> ; 64k 16-bit data
-
- gdt_size=$-(offset dummy_descriptor)
-
- code32_idx = 08h ; offset of 32-bit code segment in GDT
- data32_idx = 10h ; offset of 32-bit data segment in GDT
- core32_idx = 18h ; offset of 32-bit core segment in GDT
- code16_idx = 20h ; offset of 16-bit code segment in GDT
- data16_idx = 28h ; offset of 16-bit data segment in GDT
-
- label interrupt_descriptor_table fword ; here begins the IDT
-
- idt_start dw idt_size,0,0
- interrupt_0 interrupt_descriptor <demo_int,code32_idx,0,8eh,0>
-
- idt_size=$-(offset interrupt_0)
-
- ;----------------------------------------------------------------------------
-
- start32: ; here we start in Protected Mode
- mov ax,data32_idx ; load needed registers with the appr.
- mov ss,ax ; selectors
- mov esp,offset stack32_end ; stack size
- mov ds,ax
- mov es,ax
- mov fs,ax
- mov gs,ax
-
- call main ; now, everything is set up: call main!
-
- db 0eah ; far jump opcode ; when main returns, get back
- dw offset exit16,0,code16_idx ; to the Real Mode code
-
- ;----------------------------------------------------------------------------
- ; protected mode translation of write_msg
- ; In: DS:ESI - pointer to format string
-
- proc write_msg_pm
- push ax esi edi es
- mov ax,core32_idx ; in protected mode, we have to use
- ; core memory to address the screen
- mov es,ax
- movzx di,[esi+2] ; get Y position
- imul edi,160
- add di,[esi] ; add X position
- add di,[esi]
- add edi,0b8000h ; physical address of text screen
- mov ah,[esi+4] ; get attribute byte
- add esi,5
- write_loop:
- mov al,[esi]
- or al,al ; end of string?
- jz loop_end
- inc esi
- mov [es:edi],ax
- inc edi
- inc edi
- jmp write_loop
- loop_end:
- pop es edi esi ax
- ret
- endp write_msg_pm
-
- ;----------------------------------------------------------------------------
- ; sample interrupt handler
-
- int_msg db 0,0,2,0,1fh,'I''m the Interrupt Handler - returning '
- db 'now!',0
-
- proc demo_int
- mov esi,offset int_msg
- call write_msg_pm
- iretd
- endp demo_int
-
- ;----------------------------------------------------------------------------
- ; main procedure for protected mode
-
- pm_msg db 0,0,1,0,1fh,'Now in Protected Mode - calling Interrupt '
- db 'Handler!',0
-
- main:
- mov esi,offset pm_msg ; just put the message...
- call write_msg_pm
- int 0 ; ...call a sample interrupt...
- ret ; ...and return
-
- ;----------------------------------------------------------------------------
-
- ends code32
-
- ;****************************************************************************
-
- end start16
-