NAME initpm

; Linker defined segment symbols.

          EXTRN _SEG_SYS_GDT_PADDR:ABS
          EXTRN _SEG_SYS_GDT_END:ABS

          EXTRN _SEG_SYS_IDT_PADDR:ABS
          EXTRN _SEG_SYS_IDT_END:ABS

          EXTRN _SEG_SYS_LDT_PADDR:ABS
          EXTRN _SEG_SYS_LDT_BEGIN:FAR
          EXTRN _SEG_SYS_LDT_END:ABS

          EXTRN _SEG_RAM_AREA_PADDR:ABS

          EXTRN _SEG_DUMMY_TSS_BEGIN:ABS

          EXTRN _SEG_MAIN_TSS_PADDR:ABS
          EXTRN _SEG_MAIN_TSS_BEGIN:FAR

          EXTRN _SEG_XCPT_TSS_PADDR:ABS
          EXTRN _SEG_XCPT_TSS_BEGIN:FAR

          EXTRN _SEG_INIT_PM_PADDR:FAR

          PUBLIC _mwINIT
          PUBLIC XCPT_HND


POWER_UP  SEGMENT ER

; The following instruction jumps to the code
; that initializes the 80286 processor and
; switches it into the protected mode.  This
; instruction must be placed at the reset
; address, 0FFFFF0H, using the -LOCATE SEGMENT
; POWER_UP 0FFFFF0H linker command.

POWERUP:  JMP    FAR PTR _SEG_INIT_PM_PADDR

; The segment value generated using this symbol
; is 0, and the offset is the least significant
; 16 bits of the INIT_PM segment's physical
; address.

POWER_UP  ENDS


RAM_AREA  SEGMENT PARA RW 'DATA'

GDT       DB 100H DUP(?)
IDT       DB 100H DUP(?)
LDT       DB 100H DUP(?)
GDT_LIMIT DW 1 DUP(?)
GDT_BASE  DW 2 DUP(?)
IDT_LIMIT DW 1 DUP(?)
IDT_BASE  DW 2 DUP(?)
LDT_LIMIT DW 1 DUP(?)
LDT_BASE  DW 2 DUP(?)
TSS_MAIN  DW 22 DUP(?)
TSS_XCPT  DW 22 DUP(?)

RAM_AREA  ENDS


STACK     SEGMENT BYTE STACK RW 'DATA'

          DB 3FFH DUP(?)
STK       DB 1 DUP(?)

STACK     ENDS


INIT_PM   SEGMENT ER 'CODE'

; Copy linker built descriptor tables and task
; state segments to RAM.  The descriptor tables
; and the RAM area are assumed to be in the
; first 64K bytes of memory (DS equals 0 after
; reset).  The first three instructions load
; ES with a segment value created from the
; physical address of the RAM_AREA segment.

_mwINIT:  MOV    AX,_SEG_RAM_AREA_PADDR
          SHR    AX,4
          MOV    ES,AX

; Put the number of words in the GDT into CX,
; and copy the GDT to the RAM area.

          MOV    AX,_SEG_SYS_GDT_END+1
          SHR    AX,1
          MOV    CX,AX
          MOV    SI,_SEG_SYS_GDT_PADDR
          MOV    DI,OFFSET GDT
      REP MOVSW

; Put the number of words in the IDT into CX,
; and copy the IDT to the RAM area.

          MOV    AX,_SEG_SYS_IDT_END+1
          SHR    AX,1
          MOV    CX,AX
          MOV    SI,_SEG_SYS_IDT_PADDR
          MOV    DI,OFFSET IDT
      REP MOVSW

; Put the number of words in the LDT into CX,
; and copy the LDT to the RAM area.

          MOV    AX,_SEG_SYS_LDT_END+1
          SHR    AX,1
          MOV    CX,AX
          MOV    SI,_SEG_SYS_LDT_PADDR
          MOV    DI,OFFSET LDT
      REP MOVSW

; Copy the MAIN_TSS to the RAM area.

          MOV    CX,16H
          MOV    SI,_SEG_MAIN_TSS_PADDR
          MOV    DI,OFFSET TSS_MAIN
      REP MOVSW

; Copy the XCPT_TSS to the RAM area.

          MOV    CX,16H
          MOV    SI,_SEG_XCPT_TSS_PADDR
          MOV    DI,OFFSET TSS_XCPT
      REP MOVSW

; Modify the base addresses in the LDT and
; TSS descriptors of the RAM GDT.

          MOV    DX,_SEG_RAM_AREA_PADDR
          MOV    BX,OFFSET GDT

          MOV    SI,SEG _SEG_SYS_LDT_BEGIN
          MOV    AX,OFFSET LDT
          ADD    AX,DX
          MOV    ES:[BX][SI+2],AX
          MOV    SI,SEG _SEG_MAIN_TSS_BEGIN
          MOV    AX,OFFSET TSS_MAIN
          ADD    AX,DX
          MOV    ES:[BX][SI+2],AX
          MOV    SI,SEG _SEG_XCPT_TSS_BEGIN
          MOV    AX,OFFSET TSS_XCPT
          ADD    AX,DX
          MOV    ES:[BX][SI+2],AX

; Put the GDT and IDT limit and base values
; into the locations that are used to load
; the GDTR and IDTR registers.

          MOV    AX,_SEG_SYS_GDT_END
          MOV    ES:GDT_LIMIT,AX
          MOV    AX,OFFSET GDT
          ADD    AX,DX
          MOV    ES:GDT_BASE,AX
          XOR    AX,AX
          MOV    ES:GDT_BASE+2,AX

          MOV    AX,_SEG_SYS_IDT_END
          MOV    ES:IDT_LIMIT,AX
          MOV    AX,OFFSET IDT
          ADD    AX,DX
          MOV    ES:IDT_BASE,AX
          XOR    AX,AX
          MOV    ES:IDT_BASE+2,AX

; The following instructions load the 80286's
; IDTR and GDTR registers.

          LIDT   ES:IDT_LIMIT
          LGDT   ES:GDT_LIMIT

; The following code switches the 80286
; processor into the protected mode (set
; PE and EM bits in MSW).

          MOV    AX,0FFF5H
          LMSW   AX

; Dummy jump to flush queue

          JMP    LD_TR

; Load the selector of a dummy TSS into the
; Task Register.  This is done to give the
; processor a place to "dump" its current
; information when the ; first task switch
; occurs.

LD_TR:    MOV    AX,SEG _SEG_DUMMY_TSS_BEGIN
          LTR    AX

; LDT is not needed.

          MOV    AX,0H
          LLDT   AX

; Set the stack pointer to a valid location
; in a valid stack segment.

          MOV    AX,SEG STK
          MOV    SS,AX
          MOV    SP,OFFSET STK

; The following instruction causes the first
; task switch.

          JMP    FAR PTR _SEG_MAIN_TSS_BEGIN

INIT_PM   ENDS


XCPT_HNDLR SEGMENT ER 'CODE'

XCPT_HND: HLT

XCPT_HNDLR ENDS


END ; POWERUP     ; Defines the start address.
