; -------------------------------------------------
; ----- 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
