; -------------------------------------------------
; ----- A simple loader for Tran's PMode 3.0x -----
; -------------------------------------------------
; Programmed by Mikko Hyvarinen, 4.1.-26.2 '95
; This code is released to the public domain.
; You should read (and understand) this completely
; before use. I'm not responsible for anything this
; code might do to your computer, health, or
; anything. Use at your own risk.
; -------------------------------------------------
STACKLEN equ 0f00h
SELZERO equ 10h ; selector for all memory
PMODE_TEXT segment para public use16 'CODE'
PMODE_TEXT ends
extrn _pm_info:far, _pm_init:far
extrn _pm_selectors, _pm_pagetables, _pm_rmstacklen, _pm_rmstacks
extrn _pm_callbacks, _pm_pmstacklen, _pm_pmstacks
extrn pmstackbase, pmstacktop, rmstackbase, rmstacktop
.386p
; ----- Public items -----
public sel_code16, sel_data16, sel_stack16
public sel_psp, sel_code32, sel_data32, sel_display
; ----- 16-bit code & data -----
code16 segment para public use16
assume cs:code16, ds:data16, ss:stack16
; -- Loader program --
loader proc near
; -- Shrink loader stack segment --
mov ax, stack16
mov ds, ax
mov bx, 0fffh
mov ax, 4a00h
int 21h
jc @loader_shrink_err
mov ax, data16
mov ds, ax
; -- Get protected mode info
call _pm_info
jc @loader_pminfoerr
; -- Allocate stack space for PMode structures --
mov cx, ss
mov dx, STACKLEN
cmp dx, bx
jng @loader_nomem
mov es, cx
; -- Initialize protected mode --
call _pm_init
jc @loader_pminiterr
; -- Save selectors --
mov word ptr [_sel_code16], cs
mov word ptr [_sel_data16], ds
mov word ptr [_sel_stack16], ss
mov word ptr [_sel_psp], es
; -- Alllocate descriptor for data32 --
xor eax, eax
mov ecx, 1
int 31h
cmp ax, 8011h
jz @loader_selalloc_err
cmp ax, 8021h
jz @loader_selalloc_err
mov word ptr [_sel_data32], ax
; -- Set selector access rights --
mov bx, ax
movzx eax, [_sel_data16]
lar eax, eax
jnz @loader_end
shr eax, 8
mov cx, ax
or cx, 4000h
mov ax, 0009h
int 31h
cmp ax, 8021h
jz @loader_accright_err
cmp ax, 8022h
jz @loader_accright_err
cmp ax, 8025h
jz @loader_accright_err
; -- Set selector base address --
xor eax, eax
mov ax, data32
shl eax, 4
mov dx, ax
shr eax, 16
mov cx, ax
mov bx, word ptr [_sel_data32]
mov ax, 0007h
int 31h
cmp ax, 8022h
jz @loader_baseaddr_err
cmp ax, 8025h
jz @loader_baseaddr_err
; -- Set segment limit --
mov cx, 0ffffh
mov dx, cx
mov bx, word ptr [_sel_data32]
mov ax, 0008h
int 31h
cmp ax, 8021h
jz @loader_limit_err
cmp ax, 8022h
jz @loader_limit_err
cmp ax, 8025h
jz @loader_limit_err
; -- Test selector --
mov ax, word ptr [_sel_data32]
mov es, ax
verr ax
jnz @loader_selnotread
verw ax
jnz @loader_selnotwrite
; -- Alllocate descriptor for code32 --
xor eax, eax
mov ecx, 1
int 31h
cmp ax, 8011h
jz @loader_selalloc_err
cmp ax, 8021h
jz @loader_selalloc_err
mov word ptr [_sel_code32], ax
; -- Set selector access rights --
mov bx, ax
movzx eax, [_sel_code16]
lar eax, eax
jnz @loader_end
shr eax, 8
mov cx, ax
or cx, 4000h
mov ax, 0009h
int 31h
cmp ax, 8021h
jz @loader_accright_err
cmp ax, 8022h
jz @loader_accright_err
cmp ax, 8025h
jz @loader_accright_err
; -- Set selector base address --
xor eax, eax
mov ax, code32
shl eax, 4
mov dx, ax
shr eax, 16
mov cx, ax
mov bx, word ptr [_sel_code32]
mov ax, 0007h
int 31h
cmp ax, 8022h
jz @loader_baseaddr_err
cmp ax, 8025h
jz @loader_baseaddr_err
; -- Set segment limit --
mov cx, 0ffffh
mov dx, cx
mov bx, word ptr [_sel_code32]
mov ax, 0008h
int 31h
cmp ax, 8021h
jz @loader_limit_err
cmp ax, 8022h
jz @loader_limit_err
cmp ax, 8025h
jz @loader_limit_err
; -- Test selector --
mov ax, word ptr [_sel_code32]
mov es, ax
verr ax
jnz @loader_selnotread
; -- Alllocate descriptor for display segment --
xor eax, eax
mov ecx, 1
int 31h
cmp ax, 8011h
jz @loader_selalloc_err
cmp ax, 8021h
jz @loader_selalloc_err
mov word ptr [_sel_display], ax
; -- Set selector access rights --
mov bx, ax
movzx eax, [_sel_data16]
lar eax, eax
jnz @loader_end
shr eax, 8
mov cx, ax
or cx, 4000h
mov ax, 0009h
int 31h
cmp ax, 8021h
jz @loader_accright_err
cmp ax, 8022h
jz @loader_accright_err
cmp ax, 8025h
jz @loader_accright_err
; -- Set selector base address --
xor eax, eax
mov ax, 0a000h
shl eax, 4
mov dx, ax
shr eax, 16
mov cx, ax
mov bx, word ptr [_sel_display]
mov ax, 0007h
int 31h
cmp ax, 8022h
jz @loader_baseaddr_err
cmp ax, 8025h
jz @loader_baseaddr_err
; -- Set segment limit --
mov cx, 0ffffh
mov dx, cx
mov bx, word ptr [_sel_display]
mov ax, 0008h
int 31h
cmp ax, 8021h
jz @loader_limit_err
cmp ax, 8022h
jz @loader_limit_err
cmp ax, 8025h
jz @loader_limit_err
; -- Test selector --
mov ax, word ptr [_sel_display]
mov es, ax
verr ax
jnz @loader_selnotread
verw ax
jnz @loader_selnotwrite
; -- Copy selectors and jump to code32 --
mov ax, word ptr [_sel_data32]
mov es, ax
mov ax, word ptr ds:[_sel_code16]
mov bx, word ptr ds:[_sel_data16]
mov word ptr es:[sel_code16], ax
mov word ptr es:[sel_data16], bx
mov ax, word ptr ds:[_sel_stack16]
mov bx, word ptr ds:[_sel_code32]
mov word ptr es:[sel_stack16], ax
mov word ptr es:[sel_code32], bx
mov ax, word ptr ds:[_sel_data32]
mov bx, word ptr ds:[_sel_display]
mov word ptr es:[sel_data32], ax
mov word ptr es:[sel_display], bx
mov ax, word ptr ds:[_sel_psp]
mov word ptr es:[sel_psp], ax
mov ax, word ptr [_sel_code32]
push ax
mov ax, word ptr [_sel_data32]
mov es, ax
mov ds, ax
xor eax, eax ; Entry point to 32-bit code must be at offset 0
push eax
db 66h
retf
; The point of no return...
@loader_dpmi_err:
mov word ptr [regs_dx], offset msg_nodpmi
jmp @loader_rm_print
@loader_shrink_err:
mov word ptr [regs_dx], offset msg_shrinkerr
jmp @loader_rm_print
@loader_limit_err:
mov word ptr [regs_dx], offset msg_limiterr
jmp @loader_pm_print
@loader_selnotread:
mov word ptr [regs_dx], offset msg_notread
jmp @loader_pm_print
@loader_selnotwrite:
mov word ptr [regs_dx], offset msg_notwrite
jmp @loader_pm_print
@loader_baseaddr_err:
mov word ptr [regs_dx], offset msg_baseaddr
jmp @loader_pm_print
@loader_accright_err:
mov word ptr [regs_dx], offset msg_accright
jmp @loader_pm_print
@loader_selalloc_err:
mov word ptr [regs_dx], offset msg_selalloc
jmp @loader_pm_print
@loader_nomem:
mov dx, offset msg_nomem
jmp @loader_rm_print
@loader_pminfoerr:
shl ax, 1
mov si, offset pminfoerrtbl
add si, ax
mov dx, word ptr [si]
jmp @loader_rm_print
@loader_pminiterr:
shl ax, 1
mov si, offset pminiterrtbl
add si, ax
mov dx, word ptr [si]
jmp @loader_rm_print
@loader_pm_print:
push ds
pop es
mov ax, 0300h
mov bx, 0021h
xor cx, cx
mov edi, offset regs_begin
mov word ptr [regs_ax], 0900h
mov word ptr [regs_ds], data16
int 31h
jmp @loader_end
@loader_rm_print:
mov ax, 0900h
int 21h
@loader_end:
mov ax, 4c00h
int 21h
loader endp
code16 ends
; ----- 16-bit data -----
data16 segment para public use16
; Error messages for _pm_info
pminfoerrtbl dw pminiterr0, pminiterr1, pminiterr2, pminiterr3
; Error messages for _pm_init
pminiterrtbl dw pminiterr0, pminiterr1, pminiterr2, pminiterr3, pminiterr4, pminiterr5, pminiterr5
pminiterr0 db 'Not enough low memory!', 13, 10, '$'
pminiterr1 db 'No 80386+ detected!', 13, 10, '$'
pminiterr2 db 'System already in protected mode and no VCPI'
db ' or DPMI found!',13, 10, '$'
pminiterr3 db 'DPMI - host is not 32-bit!', 13, 10, '$'
pminiterr4 db 'Could not enable A20 gate!', 13, 10, '$'
pminiterr5 db 'DPMI - could not enter 32-bit protected mode!', 13, 10, '$'
pminiterr6 db 'DPMI - could not allocate needed selectors!', 13, 10, '$'
; Misc. error messages
msg_nomem db 'Not enough memory for protected mode data!', 13, 10, '$'
msg_selalloc db 'Error allocating selectors!', 13, 10, '$'
msg_accright db 'Error setting selector access rights!', 13, 10, '$'
msg_baseaddr db 'Error setting selector base address!', 13, 10, '$'
msg_notread db 'Error: selector not readable!', 13, 10, '$'
msg_notwrite db 'Error: selector not writable!', 13, 10, '$'
msg_limiterr db 'Error setting segment limit!', 13, 10, '$'
msg_nodpmi db 'Error loading DPMI!', 13, 10, '$'
msg_shrinkerr db 'Error shrinking code segment!', 13, 10, '$'
; Selectors
_sel_code16 dw 0
_sel_data16 dw 0
_sel_stack16 dw 0
_sel_psp dw 0
_sel_code32 dw 0
_sel_data32 dw 0
_sel_display dw 0
; Virtual register structure for 16-bit code
align 4
regs_begin label byte
regs_edi label dword
regs_di dw 0, 0
regs_esi label dword
regs_si dw 0, 0
regs_ebp label dword
regs_bp dw 0, 0
dd 0
regs_ebx label dword
regs_bx label word
regs_bl db 0
regs_bh db 0, 0, 0
regs_edx label dword
regs_dx label word
regs_dl db 0
regs_dh db 0, 0, 0
regs_ecx label dword
regs_cx label word
regs_cl db 0
regs_ch db 0, 0, 0
regs_eax label dword
regs_ax label word
regs_al db 0
regs_ah db 0, 0, 0
regs_flags dw 0
regs_es dw 0
regs_ds dw 0
regs_fs dw 0
regs_gs dw 0
regs_ip dw 0
regs_cs dw 0
regs_sp dw 0
regs_ss dw 0
regs_end label byte
data16 ends
; ----- 32-bit code -----
code32 segment page public use32
assume cs:code32, ds:data32
main proc far
; Here you are, running in a 32-bit segment.
; Note: You can't return to the 16-bit loader code,
; because I haven't figured out a way to do this (yet).
; You must do the cleanup and terminate from 32-bit code,
; but that shouldn't be a problem (RTFM).
mov eax, 4c00h
int 21h
main endp
code32 ends
; ----- 32-bit data -----
data32 segment page public use32
sel_code16 dw 0
sel_data16 dw 0
sel_stack16 dw 0
sel_psp dw 0
sel_code32 dw 0
sel_data32 dw 0
sel_display dw 0
; align 4 ; Not needed
regstruc_begin label byte
regstruc_edi label dword
regstruc_di dw 0, 0
regstruc_esi label dword
regstruc_si dw 0, 0
regstruc_ebp label dword
regstruc_bp dw 0, 0
dd 0
regstruc_ebx label dword
regstruc_bx label word
regstruc_bl db 0
regstruc_bh db 0, 0, 0
regstruc_edx label dword
regstruc_dx label word
regstruc_dl db 0
regstruc_dh db 0, 0, 0
regstruc_ecx label dword
regstruc_cx label word
regstruc_cl db 0
regstruc_ch db 0, 0, 0
regstruc_eax label dword
regstruc_ax label word
regstruc_al db 0
regstruc_ah db 0, 0, 0
regstruc_flags dw 0
regstruc_es dw 0
regstruc_ds dw 0
regstruc_fs dw 0
regstruc_gs dw 0
regstruc_ip dw 0
regstruc_cs dw 0
regstruc_sp dw 0
regstruc_ss dw 0
data32 ends
; ----- 16-bit stack -----
; Should be at the end of the file.
stack16 segment para public stack
db STACKLEN * 10h dup (0)
stack16 ends
end loader