;******************************************************************************
; Emulation monitor for HP64789 Intel 80386 EX emulator. 
;
; A Hewlett-Packard Software Product
; Copyright Hewlett-Packard Co. 1994
;******************************************************************************
; Search for the **USER_MODIFICATION** comments for places where it is safe
; for the emulator user to make changes to this code.  At these places you
; can insert code to call a routine of yours, add an INT N, or insert in-line
; code, but you must accept the following restrictions:
;    If you change any register other than EFLAGS (and EIP), you must restore
;       them before the monitor code resumes (like a well-written interrupt
;       routine would work)
;    CS points to the monitor's segment; all other registers have the same
;       values as just prior to monitor entry.  You can load monitor data
;       relative to CS, but not store data.  If you want to store data in the
;       montor's address space, you should save DS, load a new value into DS,
;       do your work, then restore DS.
; To trace this code (and any code called by it), you must use the "tck -ub"
; command, since all states between monitor entry & exit are considered
; monitor states.
;******************************************************************************
; On monitor entry (ICEMODE), the processor state has been saved in the
; LOADALL image and the processor is running in real-address mode, in
; background memory relative from address 0.
;
; During background monitor operation, bus cycles are not shown to the target
; system and target interrupts are blocked.  If the user has selected the
; foreground monitor, the background monitor will exit into the foreground
; monitor running in real- or protected-address mode, in foreground memory
; relative from the monitor address specified in configuration.  During the
; foreground monitor wait loops, the monitor runs in the exact same context
; as the user's program and can be interrupted by the target system to
; service critical interrupts.
;
; Prior to running the foreground monitor, the background monitor verifies
; that the user has configured the foreground monitor correctly to run in the
; processor state saved on monitor entry.  To run in real mode, the monitor
; address specified in configuration cannot exceed 0xFC000.  If it does, the
; foreground monitor will not be used (no warning issued).  To run
; in protected mode, the user must reserve a GDT entry for defining the
; monitor's code segment and specify the GDT offset in configuration.  If the
; offset exceeds the user's GDT limit, or the descriptor contains an incorrect
; value and the background monitor cannot change it, the foreground monitor
; will not be used (no warning issued).
;******************************************************************************

		NAME MON386

;******************************************************************************
;***************   M O N I T O R   D A T A   S E G M E N T   ******************
;******************************************************************************

DATASEG		SEGMENT RW USE16

;------------------------------------------------------------------------------
; Structure Definitions
;------------------------------------------------------------------------------
DESC		STRUC
AR		DD ?
BASE		DD ?
LIMIT		DD ?
DESC		ENDS

TPTR		STRUC
TLIMIT		DW ?
TBASE		DD ?
TPTR		ENDS
	
;------------------------------------------------------------------------------
; LOADALL Image (296 bytes) - initialized prior to monitor entry
;------------------------------------------------------------------------------
LR_CR0		DD ?
LR_EFLAGS	DD ?
LR_EIP		DD ?
LR_EDI		DD ?
LR_ESI		DD ?
LR_EBP		DD ?
LR_ESP		DD ?
LR_EBX		DD ?
LR_EDX		DD ?
LR_ECX		DD ?
LR_EAX		DD ?
LR_DR6		DD ?
LR_DR7		DD ?
LR_TSSR		DD ?
LR_LDTR		DD ?
LR_GS		DD ?
LR_FS		DD ?
LR_DS		DD ?
LR_SS		DD ?
LR_CS		DD ?
LR_ES		DD ?
LD_TSS		DESC <>
LD_IDT		DESC <>
LD_GDT		DESC <>
LD_LDT		DESC <>
LD_GS		DESC <>
LD_FS		DESC <>
LD_DS		DESC <>
LD_SS		DESC <>
LD_CS		DESC <>
LD_ES		DESC <>

LA_NUMDWORDS	EQU (($-LR_CR0)/4)

LD_RSVD		DD 23 DUP(?)

;------------------------------------------------------------------------------
; User Register Area
;------------------------------------------------------------------------------
UR_CR2		DD ?
UR_CR3		DD ?
UR_DR0		DD ?
UR_DR1		DD ?
UR_DR2		DD ?
UR_DR3		DD ?
UR_TR6          DD ?
UR_TR7          DD ?

UR_CR0		DD ?
UR_EFLAGS	DD ?
UR_EIP		DD ?
UR_EDI		DD ?
UR_ESI		DD ?
UR_EBP		DD ?
UR_ESP		DD ?
UR_EBX		DD ?
UR_EDX		DD ?
UR_ECX		DD ?
UR_EAX		DD ?
UR_DR6		DD ?
UR_DR7		DD ?
UR_TSSR		DD ?
UR_LDTR		DD ?
UR_GS		DD ?
UR_FS		DD ?
UR_DS		DD ?
UR_SS		DD ?
UR_CS		DD ?
UR_ES		DD ?
UD_TSS		DESC <>
UD_IDT		DESC <>
UD_GDT		DESC <>
UD_LDT		DESC <>
UD_GS		DESC <>
UD_FS		DESC <>
UD_DS		DESC <>
UD_SS		DESC <>
UD_CS		DESC <>
UD_ES		DESC <>

;------------------------------------------------------------------------------
; Monitor Control Variables
;------------------------------------------------------------------------------

; MON_VERSION variable indicates the version of the monitor and must start with
; the product number.  Prior the executing a monitor command, the emulation
; drivers check this value and will automatically reload the monitor, prior to
; generating a break, if it is invalid.

MON_VERSION     DD  64789000H

; MON_GDTR and MON_IDTR variables are used to load the monitor's own GDTR/IDTR
; values during command execution in protected mode.  These variables describe
; the limit and linear address of each table.  If the monitor is relocated, the
; emulation drivers will adjust the linear address both variables.  In addition,
; linear addresses defined in the GDT itself are also adjusted.

		DW ?
MON_GDTR	TPTR <GDTLIMIT,offset GDTBASE>

		DW ?
MON_IDTR	TPTR <IDTLIMIT,offset IDTBASE>

; MON_ADDRESS and MON_SELECTOR variables determine the code segment (CS value) to
; use when the foreground monitor runs in the idle loop in real or protected mode
; based on the user's context.  During command execution, the monitor always
; runs in protected mode using its own tables.  These variables are derived
; from configuration and initialized when the monitor is loaded.

MON_ADDRESS	DD  ?
MON_SELECTOR	DD  ?

; MON_FLAGS variable contains a set of bit flags used to communicate various
; information between the emulation firmware and the monitor.  Equates MF_*
; indicate bit positions.

MON_FLAGS	DD  0            ;misc. flags

MF_FGMONUSE     EQU 0        ; use foreground monitor, set by drivers and used
                             ;  by mon.asm 
MF_FGMON        EQU 1        ; foreground monitor is running, set by mon.asm
                             ;  when transitioning from background monitor to
                             ;  foreground monitor. Cleared by mon.asm at
                             ;  CHECK_REENTRY.
MF_RSTMON       EQU 2        ; set/clear when processor enters monitor
                             ; based on HW_RSTMON(2008)
MF_BRKREQ       EQU 3        ; drivers requested break
MF_MONENTRY     EQU 4        ; set by monitor when background
                             ; entry is complete
MF_EMULDREGS    EQU 5        ; emulator using debug registers
MF_RESETBRK     EQU 6        ; controlled by cf reset
                             ;  set cf reset=tomon
                             ;  clr cf reset=touser
MF_CORERESETBRK EQU 7        ; controlled by cf port92
                             ;  set cf port92=tomon
                             ;  clr cf port92=touser
MF_CORERSTMON   EQU 8        ; set/clear when processor enters monitor
                             ;  based on HW_CORERSTMON(2009)
MF_EMULRESET    EQU 9        ; driver requested reset (just released from
                             ;  emulator reset)
MF_TMPBKGSMM    EQU 10       ; temporary use of background for SMM access

MF_SMMMODE      EQU 11       ; set by drivers is last state was SMM
;------------------------------------------------------------------------------
; Monitor Command Variables
;------------------------------------------------------------------------------

; CMD_REQUEST and CMD_STATUS variables are used for executing monitor commands.
; During the idle loop, the monitor continuously polls CMD_REQUEST waiting for
; emulation firmware to set it to a non-zero value.  This value indicates the
; command to be executed and is used as an index into the CMDTABLE jump table.
; Upon completion, the monitor stores the result in CMD_STATUS and clears
; CMD_REQUEST to indicating to the emulation firmware that the command is done.

CMD_REQUEST     DW  0            ;command to execute

CR_MONPOLL     	EQU 1            ;poll the monitor
CR_MONEXIT     	EQU 2            ;exit the monitor
CR_MEMREAD     	EQU 3            ;read memory
CR_MEMWRITE    	EQU 4            ;write memory 
CR_IOREAD      	EQU 5            ;read I/O space
CR_IOWRITE     	EQU 6            ;write I/O space
CR_FPUREAD     	EQU 7            ;read fpu register
CR_FPUWRITE    	EQU 8            ;write fpu register
CR_SMMREAD     	EQU 9            ;SMM read memory
CR_SMMWRITE    	EQU 10           ;SMM write memory 

CMD_STATUS      DW  ?            ;command result

CS_SUCCESS      EQU 0            ;success
CS_NOCOPROC     EQU 7            ;coprocessor not available
CS_GPFAULT      EQU 13           ;general protection fault
CS_PGFAULT      EQU 14           ;page fault
CS_BADCMD       EQU 64           ;unimplemented command
CS_ACCVIO       EQU 65           ;grd/wrrom access violation 

; ADDRESS, COUNT and ACCESS variables are parameters used for commands which
; access memory and I/O.  These parameters are initialized by emulation
; firmware prior to requesting the command.

ADDRESS         DD  ?            ;memory/SMM memory/IO address
ACCSIZE         DW  ?            ;memory/SMM memory/IO access size

ACCSIZE8        EQU 1            ;access bytes
ACCSIZE16	EQU 2            ;access words
ACCSIZE32	EQU 4            ;access double words

COUNT           DW  ?            ;number of accesses requested

; DATABUF array is used for transferring memory and I/O data to or from the
; target system

DATABUF		DB 400h DUP(?)

;------------------------------------------------------------------------------
; Local Variables
;------------------------------------------------------------------------------

; MON_STACK variable provides a 128-byte stack for the monitor during protected
; mode command execution to make subroutine calls and for exception processing.

 	DB 80h DUP(?)
MON_STACK	LABEL BYTE

; The monitor's GDT is used to run in protected-address mode during command
; execution.  The GDT contains a null selector, overlapping 64K code and data
; segments, and a 4-Gbyte alias segment for accessing target memory.  The
; linear base address of the two 64K segments are set to the base address of
; the monitor when the monitor is loaded.   NOTE:  although the monitor runs
; entirely within a 16K block, 64K limits are used to simplify switching back
; to real-address mode.

		align 4
GDTBASE		dq 0000000000000000h        ;null selector
CSDESC          dq 00009b000000ffffh        ;reloc 64K code segment
		dq 000093000000ffffh        ;reloc 64K data segment
		dq 008f93000000ffffh        ;flat 4G data segment
GDTLIMIT	EQU ($-GDTBASE)

FLATSEG         EQU 18h                     ;flat 4G data segment

; The monitor's IDT is used to trap exceptions generated by the monitor during
; protected-address mode command execution (ie: page fault on memory access).

		align 4
IDTBASE		dd EXCEPTION_0,00008F00h
		dd EXCEPTION_1,00008F00h
		dd EXCEPTION_2,00008F00h
		dd EXCEPTION_3,00008F00h
		dd EXCEPTION_4,00008F00h
		dd EXCEPTION_5,00008F00h
		dd EXCEPTION_6,00008F00h
		dd EXCEPTION_7,00008F00h
		dd EXCEPTION_8,00008F00h
		dd EXCEPTION_9,00008F00h
		dd EXCEPTION_10,00008F00h
		dd EXCEPTION_11,00008F00h
		dd EXCEPTION_12,00008F00h
		dd EXCEPTION_13,00008F00h
		dd EXCEPTION_14,00008F00h
		dd EXCEPTION_15,00008F00h
		dd EXCEPTION_16,00008F00h
IDTLIMIT	equ ($-IDTBASE)

; UR_GDTR and UR_IDTR variables are used to restore the user's GDTR and IDTR
; prior to entering interruptable idle loop.  These variables are only used
; during foreground mode operation. 

UR_GDTR		TPTR <>
UR_IDTR		TPTR <>

; CMDTABLE is used as a jump table containing the offset to each command and
; indexed by the command requested.

CMDTABLE	DW  offset BADCMD           ;called on invalid command
		DW  offset CMD_MONPOLL      ;command 1  - poll the monitor
		DW  offset CMD_MONEXIT      ;command 2  - exit the monitor
		DW  offset CMD_MEMREAD      ;command 3  - read memory
		DW  offset CMD_MEMWRITE     ;command 4  - write memory 
		DW  offset CMD_IOREAD       ;command 5  - read I/O space
		DW  offset CMD_IOWRITE      ;command 6  - write I/O space
		DW  offset CMD_FPUREAD      ;command 7  - read fpu register
		DW  offset CMD_FPUWRITE     ;command 8  - write fpu register
		DW  offset CMD_SMMREAD      ;command 9  - SMM read memory
		DW  offset CMD_SMMWRITE     ;command 10 - SMM write memory 
CMDLIMIT	EQU ($-CMDTABLE)

;------------------------------------------------------------------------------
;                           **USER_MODIFICATION**
; add any variables here and only here.  Make sure this segment does not exceed
; 800h bytes.  There are about 50 bytes left for you, but check the assembler
; listing to be sure. Note that you can read them CS-relative, but you must
; modify DS in order to write to them.
;------------------------------------------------------------------------------


;------------------------------------------------------------------------------
; Address offset 800..1dff reserved for monitor code
;------------------------------------------------------------------------------

            ORG 1E00H
;------------------------------------------------------------------------------
; Temporary Register Area
;------------------------------------------------------------------------------
TR_CR2      DD ?
TR_CR3      DD ?
TR_DR0      DD ?
TR_DR1      DD ?
TR_DR2      DD ?
TR_DR3      DD ?
TR_TR6          DD ?
TR_TR7          DD ?

TR_CR0      DD ?
TR_EFLAGS   DD ?
TR_EIP      DD ?
TR_EDI      DD ?
TR_ESI      DD ?
TR_EBP      DD ?
TR_ESP      DD ?
TR_EBX      DD ?
TR_EDX      DD ?
TR_ECX      DD ?
TR_EAX      DD ?
TR_DR6      DD ?
TR_DR7      DD ?
TR_TSSR     DD ?
TR_LDTR     DD ?
TR_GS    DD ?
TR_FS    DD ?
TR_DS    DD ?
TR_SS    DD ?
TR_CS    DD ?
TR_ES    DD ?
TD_TSS      DESC <>
TD_IDT      DESC <>
TD_GDT      DESC <>
TD_LDT      DESC <>
TD_GS    DESC <>
TD_FS    DESC <>
TD_DS    DESC <>
TD_SS    DESC <>
TD_CS    DESC <>
TD_ES    DESC <>

;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; Memory Mapped I/O Ports to Emulator
;------------------------------------------------------------------------------
                ORG 2000h

HW_FGMON        db ?    ;0
                        db ?            ; no registers on odd addresses
HW_FGINTR       db ?    ;2
                        db ?            ; no registers on odd addresses
HW_ERYTRANS     db ?    ;4
                        db ?            ; no registers on odd addresses
HW_MON2XLX      db ?    ;6
                        db ?            ; no registers on odd addresses

HW_CMBGO_L      db ?    ;8
                        db ?            ; no registers on odd addresses
HW_CMBBRK_L     db ?    ;A
                        db ?            ; no registers on odd addresses
HW_ACCVIO       db ?    ;C
                        db ?            ; no registers on odd addresses
                db ?
                        db ?            ; no registers on odd addresses

HW_RSTMON       db ?    ;10
                        db ?            ; no registers on odd addresses

HW_CORERSTMON  db ?     ;12
                        db ?            ; no registers on odd addresses
DATASEG         ENDS


;------------------------------------------------------------------------------
; EQUATES for commonly used bits
;------------------------------------------------------------------------------
CR0_PE		EQU 0
CR0_PG		EQU 31
DR6_BK		EQU 12        ; allow icebp...
DR7_UM      EQU 10        ; allow access to SMM memory 
EFLAGS_TF	EQU 8
EFLAGS_VM	EQU 17


;******************************************************************************
;****************   M O N I T O R    C O D E   S E G M E N T   ****************
;******************************************************************************

CODESEG		SEGMENT ER USE16
		ORG 800h
		ASSUME DS:DATASEG,SS:DATASEG

;------------------------------------------------------------------------------
; CODEMACROS for ICEMODE instructions
;
; NOTE: The umov codemacros use 32-bit effective address [ESI]/[EDI] with
; a flat data segment based at 0.  The background monitor must be running in
; protected-address mode to access addresses at or above 64K.
;------------------------------------------------------------------------------
CODEMACRO	umov dst:r,src:m      ;umov r16,r/m16 or umov r32,r/m32
		PREFIX67 src
		PREFIX66 dst
		SEGFIX src
		db 0fh,13h
		MODRM dst,src
		ENDM

CODEMACRO	umov dst:rb,src:mb    ;umov r8,r/m8
		PREFIX67 src
		SEGFIX src
		db 0fh,12h
		MODRM dst,src
		ENDM

CODEMACRO	umov dst:m,src:r      ;umov r/m16,r16 or umov r/m32,r32
		PREFIX67 dst
		PREFIX66 src
		SEGFIX dst
		db 0fh,11h
		MODRM src,dst
		ENDM

CODEMACRO	umov dst:mb,src:rb    ;umov r/m8,r8
		PREFIX67 dst
		SEGFIX dst
		db 0fh,10h
		MODRM src,dst
		ENDM

CODEMACRO	loadall
		db 0fh,07h
		ENDM

CODEMACRO	icebp
		db 0f1h
		ENDM

CODEMACRO	lgdtd src:m
		SEGFIX src
		db 66h,0fh,01h
		MODRM 2,src
		ENDM

CODEMACRO	lidtd src:m
		SEGFIX src
		db 66h,0fh,01h
		MODRM 3,src
		ENDM

;------------------------------------------------------------------------------
; Background Monitor Entry
;
; This is the entry point for the monitor in background mode (ICEMODE entry).
; The processor state has already been saved in the LOADALL image and the
; processor is running in background memory, in real-address mode, relative
; from address 0.  The target system will not see any #ADS or #RDY signals.
;
; Determine the cause of monitor entry.  Recursive monitor entry can only
; occur in foreground mode and is normal during monitor exit.  In the case
; of monitor exit (which must be completed in background), the monitor
; executes a breakpoint instruction to transition from foreground mode to
; background mode.  Execution will continue in background mode with the
; instruction immediately following the breakpoint.  If target system
; interrupts the monitor and executes a breakpoint instruction, then monitor
; reentry is allowed; however, subsequent exit will be denied until processor
; reset because the original user registers values are overwritten.
;------------------------------------------------------------------------------
CHECK_REENTRY:	btr  MON_FLAGS,MF_FGMON
		jnb  CHECK_RSTMON           ;not reentry - wasn't fgmon
		mov  eax,LD_CS.BASE
		cmp  eax,MON_ADDRESS
		jnz  CHECK_RSTMON           ;not reentry - wasn't fgmon address
		mov  eax,LR_EIP
		jmp  (ax)                   ;monitor reentry

CHECK_RSTMON:   mov  CMD_REQUEST,0   ;abort previous command (if any)
      MOV  AL,HW_RSTMON           ;set/clear mf_rstmon in mon_flags
		bt   AX,0                   ; as per HW_RSTMON
      jb   GOT_HWRST              
;     btr  MON_FLAGS,MF_RSTMON
      jmp  CHECK_CORERST
GOT_HWRST:
      bts  MON_FLAGS,MF_RSTMON

CHECK_CORERST:
      MOV  AL,HW_CORERSTMON       ;set/clear mf_corerstmon in mon_flags
      bt   AX,0                   ; as per HW_CORERSTMON
      jb   GOT_HWCORERST
;     btr  MON_FLAGS,MF_CORERSTMON
      jmp  CHECK_BRKREQ
GOT_HWCORERST:
      bts  MON_FLAGS,MF_CORERSTMON

CHECK_BRKREQ:
      bt   MON_FLAGS,MF_BRKREQ    ;check for emulator break request
      jb   GOT_BRKREQ
      btr  MON_FLAGS,MF_EMULRESET ;no - check if just released from reset
      jnb  CHECK_TOMON            ;no - check any cf tomon
    
;-----------------------------------------------------------------------------
; At this point we have release from emulator reset and no break request
; so clear mf_rstmon and mf_corerstmon in mon_flags and exit monitor
;-----------------------------------------------------------------------------
      btr  MON_FLAGS,MF_RSTMON      ; ignore any HW_RSTMON and 
      btr  MON_FLAGS,MF_CORERSTMON  ;  and HW_CORERSTMON
      jmp  DBG_MONEXIT              ; and exit monitor

;-----------------------------------------------------------------------------
; At this point we have an emulator break request
;-----------------------------------------------------------------------------
GOT_BRKREQ:                         ;got emulator break request so
      btr  MON_FLAGS,MF_RSTMON      ; ignore any HW_RSTMON and 
      btr  MON_FLAGS,MF_CORERSTMON  ;  and HW_CORERSTMON
      btr  MON_FLAGS,MF_EMULRESET   ;  and MF_EMULRESET
      jmp  SAVE_UREGS               ; and stay in monitor

;-----------------------------------------------------------------------------
; At this point we have no break from emulator and we have no
; release from reset from emulator. We may have a reset or core reset
; from the target. If we have target reset and the cf reset=tomon
; then stay in monitor. OR if we have core reset and cf port92=tomon
; then stay in monitor. Otherwise exit monitor.
;-----------------------------------------------------------------------------
CHECK_TOMON:
      bt   MON_FLAGS,MF_CORERSTMON ;check for hardware target core reset
      jb   HAVE_CORERST        
      jmp  CHECK_HWRESET
HAVE_CORERST:
      btr  MON_FLAGS,MF_RSTMON
      bt   MON_FLAGS,MF_CORERESETBRK ;have target core reset, check 
      jb   SAVE_UREGS                ; cf port92=tomon
      bt   UR_EFLAGS,EFLAGS_TF     ;first check if we had a step operation
      jb   SAVE_UREGS
      jmp  DBG_MONEXIT

CHECK_HWRESET:
      bt   MON_FLAGS,MF_RSTMON    ;check for hardware reset to monitor
      jb   HAVE_RSTMON           
      jmp  SAVE_UREGS             ;no  
HAVE_RSTMON:
      bt   MON_FLAGS,MF_RESETBRK  ;have target reset, check cf reset=tomon
      jb   SAVE_UREGS             ;yes - stay in monitor
      bt   UR_EFLAGS,EFLAGS_TF     ;first check if we had a step operation
      jb   SAVE_UREGS
      jmp  DBG_MONEXIT            ;no - exit monitor

;------------------------------------------------------------------------------
; Copy all registers contained in the loadall image plus additional debug and
; control registers not contained in loadall image to the user register area. 
; If breakpoints are enabled, DR0-DR3 and DR7 in the user register area are
; predefined by the emulator and are not to be modified on monitor entry.
;------------------------------------------------------------------------------
SAVE_UREGS:	bt   MON_FLAGS,MF_EMULDREGS
		jnb  SAVE_DREGS
		mov  eax,UR_DR7             ;monitor's DR7 --> loadall image
		mov  LR_DR7,eax
		jmp  SAVE_CREGS

SAVE_DREGS:	mov  eax,DR0                ;DR? regs --> user regs
		mov  UR_DR0,eax
		mov  eax,DR1
		mov  UR_DR1,eax
		mov  eax,DR2
		mov  UR_DR2,eax
		mov  eax,DR3
		mov  UR_DR3,eax

SAVE_CREGS:	mov  eax,CR2                ;CR?/TR? regs --> user regs
		mov  UR_CR2,eax
		mov  eax,CR3
		mov  UR_CR3,eax
		mov  eax,TR6
		mov  UR_TR6,eax
		mov  eax,TR7
		mov  UR_TR7,eax

		mov  CX,LA_NUMDWORDS        ;loadall image --> user regs
		mov  SI,offset LR_CR0
		mov  DI,offset UR_CR0
		cld
		rep  movsd

;------------------------------------------------------------------------------
; Background monitor entry complete.  If the user has selected the background
; mode monitor, jump directly to the monitor's idle loop.  Otherwise, attempt
; to transition out of background into foreground, running in the idle loop
; with the same processor state that was in effect prior to monitor entry.
;
; This code is a bit tricky.  Before leaving background mode, the monitor 
; verifies that the foreground mode monitor will be able to run in the user's
; context without generating exception(s).  If not, the monitor remains in
; background and a warning message is issued.  In order to run in real-address
; mode, the configured monitor address must be less than 1 megabyte.  In order
; to run in protected-address mode, the configured monitor selector must be
; less than the GDT limit and point to a code segment descriptor containing 
; the base address of the monitor and limit no less than 16K.  If paging is
; enabled, identity translations must exist for the entire address range of
; the foreground monitor.
;------------------------------------------------------------------------------
		bts  MON_FLAGS,MF_MONENTRY  ;flag monitor entry complete

		bt   MON_FLAGS,MF_FGMONUSE
		jnb  IDLE_ENTRY             ;background monitor configured

      btr  MON_FLAGS,MF_SMMMODE 
      jb   IDLE_ENTRY             ;break while in SMM mode, stay in background 

RET_TOFGMON:                      ;used by SMM read/write commands

		bt   LR_CR0,CR0_PE
		jb   FG_PROT_MODE           ;protected-address mode

;------------------------------------------------------------------------------
; Running in real address mode, verify that the configured monitor address is
; in the first 1 Mbyte address space.  If not, stay in background.
;------------------------------------------------------------------------------
FG_REAL_MODE:	mov  eax,MON_ADDRESS
		shr  eax,4                  ;eax = paragraph address (segment)
		cmp  eax,010000h
		jb   FG_REAL_TRANS
		jmp  IDLE_ENTRY             ;monitor address exceeds maximum

;-------------------------------------------------------------------------------
; Running in protected-address mode, switch to protected-address mode using the
; monitor's GDT and verify that the user's GDT contains the correct code segment
; descriptor for the monitor.  The segment base must be equal to the configured
; monitor address and the segment limit cannot be less than 16K.  If the
; configured selector exceeds the GDT limit, or the descriptor contains an
; incorrect value and cannot be modified, stay in background.
;-------------------------------------------------------------------------------
FG_PROT_MODE:	lgdtd cs:MON_GDTR           ;transition into protected mode,
		mov  eax,cr0                ;no paging using monitor's GDT
		bts  eax,0                 
		mov  cr0,eax
		jmp  far ptr CHECK_SELECTOR

CHECK_SELECTOR:	mov  ax,FLATSEG             ;now in bgmon protected mode
		mov  ds,ax
		mov  es,ax
		mov  ss,ax

		mov  eax,MON_SELECTOR
		cmp  eax,LD_GDT.LIMIT
		jae  IDLE_ENTRY             ;selector exceeds GDT limit

		add  eax,LD_GDT.BASE
		mov  bx,offset CSDESC
CHECK_DESC:	umov ecx,[eax]              ;read and verify 8-byte descriptor
		mov  edx,dword ptr [bx]
		cmp  ecx,edx
		jz   DWORD_OKAY
		umov [eax],edx              ;modify, read and compare
		umov ecx,[eax]        
		cmp  ecx,edx
		jnz  IDLE_ENTRY             ;modify failed - use bgmon
DWORD_OKAY:	cmp  bx,offset CSDESC
		jnz  SELECTOR_OKAY
		add  eax,4
		add  bx,4
		jmp  CHECK_DESC
SELECTOR_OKAY:

;-------------------------------------------------------------------------------
; If paging is enabled, verify that the user has provided identity translations
; for the monitor.  The appropriate page table must be present and contain the
; correct 4 entries (16K) for the monitor.  If the present bit is not set or
; the page frame address is not equal to the linear address, the monitor will
; attempt to modify the page table entries to the correct value.  The U/S and
; R/W bits have no effect since the monitor runs at protection level 0.  If
; identity translations cannot be created, the monitor remains in background.
;-------------------------------------------------------------------------------
CHECK_PAGING:	bt   LR_CR0,CR0_PG
		jnb  PAGING_OKAY            ;paging is disabled

		mov  ebp,UR_CR3             ;read page directory entry which
		and  ebp,0fffff000h         ;points to page table
		mov  eax,MON_ADDRESS
		shr  eax,20
		and  eax,0ffch
		umov ebp,[ebp][eax]
		bt   ebp,0
		jnb  IDLE_ENTRY             ;page table not present

		and  ebp,0fffff000h         ;read page table entries
		mov  eax,MON_ADDRESS
		shr  eax,10
		and  eax,0ffch
		or   ebp,eax
		mov  ecx,3

CHECK_PTE:	umov eax,[ebp][ecx*4]
		mov  ebx,eax
		and  ebx,0ffeh              ;retain don't care bits
		or   ebx,0001h              ;add present bit
		or   ebx,MON_ADDRESS        ;add monitor base address
		mov  edx,ecx                ;add page offset
		shl  edx,12
		or   ebx,edx
		cmp  ebx,eax
		je   PTE_OKAY

		umov [ebp][ecx*4],ebx       ;attempt to modify page table 
		umov eax,[ebp][ecx*4]       ;entry to the correct value
		cmp  ebx,eax
		jne  IDLE_ENTRY             ;must be ROM, give up

PTE_OKAY:	dec  ecx
		jge  CHECK_PTE
PAGING_OKAY:

;------------------------------------------------------------------------------
; Check DPL of user's stack segment and switch to level 0 stack if necessary.
;------------------------------------------------------------------------------
CHECK_PRIV:	mov  eax,LD_SS.AR
		and  eax,6000h
		je   CHECK_PRIV_DONE        ;running privilege level 0

		mov  eax,LD_TSS.BASE        ;set SS/ESP from SS0/ESP0 in TSS
		add  eax,4
		umov ecx,[eax]
		mov  LR_ESP,ecx
		add  eax,4
		umov ecx,[eax]
		and  ecx,0fffch
		mov  LR_SS,ecx

		bt   ecx,2
		jb   LDT_SS0
GDT_SS0:	cmp  ecx,LD_GDT.LIMIT
		jae  IDLE_ENTRY             ;selector exceeds GDT limit
		add  ecx,LD_GDT.BASE
		jmp  READ_SS0
LDT_SS0:	cmp  ecx,LD_LDT.LIMIT
		jae  IDLE_ENTRY             ;selector exceeds GDT limit
		add  ecx,LD_LDT.BASE
READ_SS0:	umov eax,[ecx]
		mov  word ptr LD_SS.LIMIT,ax
		shr  eax,16
		mov  word ptr LD_SS.BASE,ax
		add  ecx,4
		umov eax,[ecx]
		mov  LD_SS.AR,eax
		mov  byte ptr LD_SS.BASE+2,al
		shr  eax,16
		mov  byte ptr LD_SS.BASE+3,ah
		and  al,0fh
		mov  byte ptr LD_SS.LIMIT+2,al

		;if running V86-mode task, clear VM bit so monitor runs in
		;protected mode and set V86-mode DS,ES,FS,GS to null selectors
		;so they will not be restored in protected mode (SS and CS
		;contain valid selectors as usual).
		btr  LR_EFLAGS,EFLAGS_VM
		jnb  CHECK_PRIV_DONE
		mov  LR_DS,0
		mov  LR_ES,0
		mov  LR_FS,0
		mov  LR_GS,0

CHECK_PRIV_DONE:

;------------------------------------------------------------------------------
; Okay to transition into foreground monitor in real or protected mode.
;------------------------------------------------------------------------------
FG_PROT_TRANS:	mov  LD_CS.LIMIT,0ffffh
		mov  LD_CS.AR,0ff0f9bffh
		mov  eax,MON_SELECTOR

FG_REAL_TRANS:	mov  LR_CS,eax
		mov  LR_EIP,offset FG_IDLE_ENTRY
		mov  eax,MON_ADDRESS
		mov  LD_CS.BASE,eax

		mov  eax,LD_GDT.LIMIT       ;create user gdtr/idtr values
		mov  UR_GDTR.TLIMIT,ax
		mov  eax,LD_GDT.BASE
		mov  UR_GDTR.TBASE,eax
		mov  eax,LD_IDT.LIMIT
		mov  UR_IDTR.TLIMIT,ax
		mov  eax,LD_IDT.BASE
		mov  UR_IDTR.TBASE,eax

		;configure DR7 during foreground monitor execution;  the TP bit
		;is set so debug traps (icebp) trap back to ICE mode;  the GD
		;bit is set to protect debug registers from being modified by
		;target system interrupt handlers;  all debug breakpoints are
		;disabled so the monitor can access memory without generating
		;a access breakpoint debug trap;  state of task and brance
		;trace messages is left unchanged
		or   LR_DR7,03000h
		and  LR_DR7,0F000h

		btr  LR_EFLAGS,EFLAGS_TF    ;unconditionally clear TF flag 
		bts  MON_FLAGS,MF_FGMON
		cmp  HW_FGMON,0
		mov  edi,0
		loadall

;-----------------------------------------------------------------------------
; Foreground monitor entry
;
; This is the entry point for the foreground monitor after transitioning
; from background mode.  All registers, except CS, EIP and EFLAGS, have been
; restored to their original user values and the processor is running in
; either real or protected mode based on the state of the processor prior
; to monitor entry.
;-----------------------------------------------------------------------------
FG_IDLE_ENTRY:

;-----------------------------------------------------------------------------
;                           **USER_MODIFICATION**
; Add any code to be executed once on foreground monitor entry.  This is a
; good place to put code to disable any watchdog timers.  Check the comment
; at the top of this file for restrictions to any code placed here!
;-----------------------------------------------------------------------------

;-----------------------------------------------------------------------------
; Idle Loop 
;
; The monitor waits until emulation firmware requests a command.  If the
; monitor is running in foreground, this wait loop can be interrupted by
; the target system.  All registers, except CS, EIP and EFLAGS, have been
; restored to their original user values and the processor is running in
; either real or protected mode based on the state of the processor prior
; to monitor entry.
;
; Once a command it received, target interrupts are blocked and the monitor
; transitions into protected mode using its own GDT to execute the command.
; When finished, the monitor reenters the idle loop by transitioning back to
; into real or protected mode based on the user's context.  Registers used
; during command execution are restored and interrupts are reenabled.
;------------------------------------------------------------------------------
IDLE_ENTRY:	cmp  cs:HW_FGINTR,0         ;unblocks interrupts
IDLE_LOOP:

;-----------------------------------------------------------------------------
;                           **USER_MODIFICATION**
; Add any code to be executed on every iteration of the monitor's idle loop.
; If you have anything that must be done periodically to prevent problems
; (such as an OUT to a watchdog port to prevent a timeout from occuring),
; do it here.  Since this part of the code is used for background as well
; as foreground monitors, your code must look like this:
;		bt cs:MON_FLAGS,M_FGMON     ;is monitor in background mode?
;		jnb IDLE_LOOP_CONTINUE      ;background mode -- continue with idle loop
;		<rest of code goes here>
; Also, check the comment at the top of this file for restrictions to any
; code placed here!
;-----------------------------------------------------------------------------

IDLE_LOOP_CONTINUE:
		cmp  cs:CMD_REQUEST,0
		jz   IDLE_LOOP
		cmp  cs:HW_FGINTR,0         ;blocks interrupts
		nop                         ;wait for pending interrupts to
		nop                         ;be processed.
		nop
		nop
		nop
		nop
		nop
		nop

IDLE_EXIT:	lgdtd cs:MON_GDTR           ;transition into protected mode,
		mov  eax,cr0                ;no paging using monitor's GDT
		bts  eax,0                 
		btr  eax,31
		mov  cr0,eax
		jmp  far ptr DOCOMMAND

DOCOMMAND:	mov  ax,seg CMD_REQUEST     ;now in monitor protected mode
		mov  ds,ax
		mov  es,ax
		mov  ss,ax
		mov  sp,offset MON_STACK
		lidtd cs:MON_IDTR

		mov  CMD_STATUS,CS_SUCCESS  ;execute command
		mov  si,CMD_REQUEST
		shl  si,1
		cmp  si,CMDLIMIT
		jb   CMDVALID
		mov  si,0                    
CMDVALID:	call CMDTABLE[si]
CMDCONTINUE:
		mov  CMD_REQUEST,0
		call USERMODE
		jmp  IDLE_ENTRY

;------------------------------------------------------------------------------
; Procedure   USERMODE
; Parameters  NONE
;
; Restore all user registers except CS, EIP and EFLAGS and switch back to the
; user's address mode in effect prior to monitor entry.  Upon return, the
; monitor will be running in real-address mode or protected-address mode using
; the user's GDT.  The monitor stack can no longer be used and monitor data
; can only be read using CS register overrides.
;
; This functionality is required in foreground mode prior to enabling target
; system interrupts in the monitor's idle and cmb wait loops.  This procedure
; returns immediately if called in background mode.
;
; WARNING:  This code is bit tricky and self-modifies.  In order to return to
; the caller without using a stack, the monitor pops the return address into
; a variable and uses a jump near indirect to return.  In order to switch 
; from protected-address mode using the monitor's GDT to real- or protected-
; address mode using the user's GDT, the monitor must execute a intersegment
; jump using an immediate address.  Because the monitor is relocatable, the
; segment portion of the immediate address is modified at runtime.  I tried
; to use an indirect, intersegment jump, but the processor does not update
; the access rights byte for CS correctly when switching back to real mode. 
;
; In protected-address mode, the monitor uses verr/verw to conditionally
; restore DS/ES/SS segment registers.  If these registers are uninitialized
; and/or represent invalid GDT selectors, they can't and won't be restored.
;------------------------------------------------------------------------------
USERMODE	PROC NEAR
		pop  ds:UMODE_RETIND     ;pop return address
		bt   MON_FLAGS,MF_FGMON   
		jnb  UMODE_RETURN        ;bgmon - return immediately

		mov  eax,LR_CS                      ;change segment address in
		mov  word ptr ds:UMODE_JFP16+3,ax   ;far jmp instruction below

		lidtd UR_IDTR            ;restore user's descriptor tables
		lgdtd UR_GDTR            ;and addressing mode
		mov  eax,LR_CR0
		mov  cr0,eax
UMODE_JFP16:	jmp  far ptr UMODE_FG    ;only use CS-override from now on!

UMODE_FG:	mov  ebx,cs:LR_DS        ;restore DS/ES in real-mode or in
		bt   eax,0               ;protected-mode iff selectors denote
		jnb  DS_OKAY             ;readable data segments;  otherwise
		verr bx                  ;restore registers to null selector
		jz   DS_OKAY
		mov  bx,0
DS_OKAY:	mov  ds,bx
		mov  ebx,cs:LR_ES
		bt   eax,0
		jnb  ES_OKAY
		verr bx
		jz   ES_OKAY
		mov  bx,0
ES_OKAY:	mov  es,bx
		mov  ebx,cs:LR_SS        ;restore SS in real-mode or in
		bt   eax,0               ;protected-mode iff selector denotes
		jnb  SS_OKAY             ;a writable data segment; otherwise
		verw bx                  ;we can't restore SS to anything
		jnz  SS_NOTOKAY
SS_OKAY:	mov  ss,bx
SS_NOTOKAY:
		mov  eax,cs:LR_EAX
		mov  ebx,cs:LR_EBX
		mov  ecx,cs:LR_ECX
		mov  edx,cs:LR_EDX
		mov  esp,cs:LR_ESP
		mov  ebp,cs:LR_EBP
		mov  esi,cs:LR_ESI
		mov  edi,cs:LR_EDI

UMODE_RETURN:	jmp  cs:[UMODE_RETIND]   ;return using jump near indirect

		align 2
UMODE_RETIND	dw ?                     ;return address is stored here

USERMODE	ENDP

;------------------------------------------------------------------------------
; Subroutine BADCMD
;
; Description:  Returns an error when an invalid command is requested
; Parameters:   none
;------------------------------------------------------------------------------
BADCMD		PROC NEAR
		mov  CMD_STATUS,CS_BADCMD
		ret
BADCMD		ENDP

;------------------------------------------------------------------------------
; Subroutine MONPOLL
; Parameters n/a
;
; Does nothing except returns to complete the command handshake.
;------------------------------------------------------------------------------
CMD_MONPOLL	PROC NEAR
		ret
CMD_MONPOLL	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_MONEXIT
;
; Wait for CMB coordinated start of multiple emulators (if configured) then
; exit the monitor.  In foreground mode, the user's registers and addressing
; mode are restored prior entering the CMB wait loop to allow target system
; interrupts to be serviced during the wait.  After the CMB wait loop, a
; breakpoint instruction is executed to transition into background mode.  The
; monitor entry code will recognize the breakpoint was caused during monitor
; exit and will resume execution with the instruction following the breakpoint.
;------------------------------------------------------------------------------
CMD_MONEXIT	PROC NEAR

		mov  CMD_REQUEST,0          ;flag command completion
		call USERMODE
		cmp  cs:HW_ERYTRANS,0       ;flag ready to run
		cmp  cs:HW_FGINTR,0         ;enable interrupts

CMB_LOOP:	bt   cs:HW_CMBGO_L,0
		jnb  CMB_READY              ;cmb ready - continue exit
		bt   cs:HW_CMBBRK_L,0
		jb   CMB_LOOP
		cmp  cs:HW_ERYTRANS,0       ;cmb break - return to idle loop
		jmp  IDLE_LOOP              ;with interrupts already enabled

CMB_READY:	cmp  cs:HW_FGINTR,0         ;block interrupts
		nop                         ;wait for pending interrupts to 
		nop                         ;be processed
		nop
		nop
		nop
		nop
		nop
		nop

		bt   cs:MON_FLAGS,MF_FGMON
		jnb  RESTORE_CREGS

;-----------------------------------------------------------------------------
;                        **USER_MODIFICATION**
; At this point we are about to exit the monitor and return to user space.
; If you have disabled any watchdog timers (etc) at FG_IDLE_ENTRY, you should
; re-enable them here.  Check the comment at the top of this file for
; restrictions to any code placed here!
;-----------------------------------------------------------------------------

		icebp

RESTORE_CREGS:	mov  eax,UR_CR2             ;user registers --> CR?/TR? registers
		mov  CR2,eax
		mov  eax,UR_CR3
		mov  CR3,eax
		mov  eax,UR_TR6
		and  eax,0fffff000h		; 6/1/96: force miss of current TR6 entrie's linear address.
										; Siemens kludge
		mov  TR6,eax
		mov  eax,UR_TR7
		mov  TR7,eax

		btr  UR_DR6,DR6_BK          ;clr BK bit being restored
		mov  edi,offset UR_CR0      ;exit with user's loadall copy
		jmp  RESTORE_DREGS

; If the emulator is using the debug registers, the emulator will always break
; into the monitor after a target reset to reinitialize the debug registers.
; Unless there is a pending break request, the monitor exits immediately after
; initializing the debug registers without switching to foreground mode or 
; waiting for CMB.

DBG_MONEXIT:    bts  UR_DR6,DR6_BK          ;set BK bit for break handler
		btr  LR_DR6,DR6_BK          ;clr BK bit being restored
		mov  eax,UR_DR7             ;restore emulator's DR7 value
		mov  LR_DR7,eax
		mov  eax,MON_FLAGS
;	or   eax,(MF_RSTMON+MF_MONENTRY)   ;Problem here, using equates=immediates
;     or   eax,(MF_MONENTRY)
;	mov  MON_FLAGS,eax          ;set monitor entry flags
      bts  MON_FLAGS,MF_MONENTRY

		mov  edi,offset LR_CR0      ;exit with entry loadall data
		cmp  HW_ERYTRANS,0          ;flag ready to run

RESTORE_DREGS:	mov  eax,UR_DR0             ;user registers --> DR? registers
		mov  DR0,eax
		mov  eax,UR_DR1
		mov  DR1,eax
		mov  eax,UR_DR2
		mov  DR2,eax
		mov  eax,UR_DR3
		mov  DR3,eax

		loadall                     ;exit background to user program

CMD_MONEXIT	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_MEMREAD
; Parameters ADDRESS,ACCSIZE,COUNT
;
; Copy one or more bytes/words/dwords from emulation or target memory to the
; the monitor's data buffer.  
;------------------------------------------------------------------------------
CMD_MEMREAD	PROC NEAR
		mov  esi,ADDRESS
		mov  edi,offset DATABUF
		mov  ax,FLATSEG
		mov  ds,ax
		lea  eax,CS:FGRW_JMPTABLE     ;select jump table for foreground

      bt   CS:MON_FLAGS,MF_TMPBKGSMM
      jb   BKG_RD

		bt   CS:MON_FLAGS,MF_FGMON    ;or background mode read
		jb   RDWR_START
BKG_RD:
		lea  eax,CS:BGRD_JMPTABLE
		jmp  RDWR_START

CMD_MEMREAD	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_MEMWRITE
; Parameters ADDRESS,ACCSIZE,COUNT
;
; Copy one or more bytes/words/dwords from the monitor's data buffer to user
; memory (emulation or target). 
;------------------------------------------------------------------------------
CMD_MEMWRITE	PROC NEAR
		mov  edi,ADDRESS
		mov  esi,offset DATABUF
		mov  ax,FLATSEG
		mov  es,ax
		lea  eax,FGRW_JMPTABLE        ;select jump table for foreground
		bt   MON_FLAGS,MF_FGMON       ;or background mode memory write
		jb   RDWR_START
		lea  eax,BGWR_JMPTABLE
		jmp RDWR_START

CMD_MEMWRITE	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_IOREAD
; Parameters ADDRESS,COUNT,ACCSIZE,DATABUF
;
; Read one or more bytes/words/dwords from a target I/O port to the monitor's
; data buffer. 
;------------------------------------------------------------------------------
CMD_IOREAD	PROC NEAR
		mov  dx,word ptr ADDRESS
		mov  di,offset DATABUF
		lea  eax,IORD_JMPTABLE
		jmp  RDWR_START

CMD_IOREAD	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_IOWRITE
; Parameters ADDRESS,COUNT,ACCSIZE,DATABUF
;
; Transfer one or more bytes/words/dwords from the monitor's data buffer to
; a target I/O port. 
;------------------------------------------------------------------------------
CMD_IOWRITE	PROC NEAR
		mov  dx,word ptr ADDRESS
		mov  si,offset DATABUF
		lea  eax,IOWR_JMPTABLE
		jmp  RDWR_START

CMD_IOWRITE	ENDP


;------------------------------------------------------------------------------
; Subroutine CMD_SMMREAD
; Parameters ADDRESS,ACCSIZE,COUNT
;
; Copy one or more bytes/words/dwords from SMM memory (emulation or target)
; to the monitor's data buffer.  
;------------------------------------------------------------------------------
CMD_SMMREAD	PROC NEAR
      bt   MON_FLAGS,MF_FGMONUSE 
      jnb  START_SMMRDACCESS 
      CALL SETUP_TMPBKG      ;foreground monitor, need to get to background
                             ; monitor to make SMM access
START_SMMRDACCESS:
      mov  eax,DR7           ;Bit 10 in DR7(UM) must be set to access
      bts  eax,DR7_UM        ; SMM memory from background monitor
      mov  DR7,eax
      CALL CMD_MEMREAD   
      mov  eax,DR7           ;Restore(clear) bit 10 in DR7(UM) 
      btr  eax,DR7_UM       
      mov  DR7,eax
      bt   MON_FLAGS,MF_FGMONUSE
      jnb  NOT_FGSMMRDEND   
      CALL RETURN_TMPBKG     ;foreground monitor, return from temp use
                             ;  of background monitor
NOT_FGSMMRDEND:
      ret
CMD_SMMREAD ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_SMMWRITE
; Parameters ADDRESS,ACCSIZE,COUNT
;
; Copy one or more bytes/words/dwords from the monitor's data buffer to user
; SMM memory (emulation or target).
;------------------------------------------------------------------------------
CMD_SMMWRITE   PROC NEAR
      bt   MON_FLAGS,MF_FGMONUSE
      jnb  START_SMMWRACCESS
      CALL SETUP_TMPBKG      ;foreground monitor, need to get to background
                             ; monitor to make SMM access
START_SMMWRACCESS:
      mov  eax,DR7            ;Bit 10 in DR7(UM) must be set to access
      bts  eax,DR7_UM         ; SMM memory from background monitor
      mov  DR7,eax
      CALL CMD_MEMWRITE
      mov  eax,DR7            ;Restore(clear) bit 10 in DR7(UM) 
      btr  eax,DR7_UM        
      mov  DR7,eax
      bt   MON_FLAGS,MF_FGMONUSE
      jnb  NOT_FGSMMWREND
      CALL RETURN_TMPBKG     ;foreground monitor, return from temp use
                             ;  of background monitor
NOT_FGSMMWREND:
      ret
CMD_SMMWRITE   ENDP

;------------------------------------------------------------------------------
; Subroutine SETUP_TMPBKG
; Setup for temporaily use of background monitor to access SMM memory
;
;------------------------------------------------------------------------------
SETUP_TMPBKG  PROC NEAR
      bts  MON_FLAGS,MF_TMPBKGSMM ;set flag to use background mon temporaily

      mov  CX,LA_NUMDWORDS        ;loadall image --> temp regs
      mov  SI,offset LR_CR0
      mov  DI,offset TR_CR0
      cld
      rep  movsd

      icebp                  ;go to background (temporaily)
 
      lgdtd cs:MON_GDTR      ;transition into protected mode,
      mov  eax,cr0           ;no paging using monitor's GDT
      bts  eax,0
      btr  eax,31
      mov  cr0,eax
      jmp  far ptr CMD_SMMACCESS

CMD_SMMACCESS:
      mov  ax,seg CMD_REQUEST ;now in monitor protected mode
      mov  ds,ax
      mov  es,ax
      mov  ss,ax
      mov  eax,LR_ESP      ;Use stack pointer from above icebp  since
      mov  sp,ax           ;there is already a return for using CMD_SMMREAD
      ret
SETUP_TMPBKG   ENDP

;------------------------------------------------------------------------------
; Subroutine RETURN_TMPBKG
; Return from temporaily use of background monitor to access SMM memory
;
;------------------------------------------------------------------------------
RETURN_TMPBKG  PROC NEAR
      btr  MON_FLAGS,MF_TMPBKGSMM ;clear flag to use background mon temporaily

      mov  LR_EIP,offset FG_CONT
      cmp  HW_FGMON,0
      mov  edi,offset LR_CR0

      loadall                ;return to foreground monitor
FG_CONT:
      bts  MON_FLAGS,MF_FGMON ;restore MF_FGMON

      mov  CX,LA_NUMDWORDS        ;temp image --> initial regs
      mov  SI,offset TR_CR0
      mov  DI,offset LR_CR0
      cld
      rep  movsd

      ret
RETURN_TMPBKG   ENDP

;------------------------------------------------------------------------------
; Subroutine RDWR_START
; Parameters ES:[EDI],DS:[ESI],EAX,ACCSIZE,COUNT
;
; Generic routine to access user memory or I/O ports.
;------------------------------------------------------------------------------
RDWR_START	PROC NEAR
		sub  ebx,ebx                 ;get code address from jump
		mov  bx,CS:ACCSIZE           ;table based on access size
		mov  bx,CS:[eax][ebx*2]

; Jump to the selected access routine then check the HW_ACCVIO memory mapped
; I/O port to see if an error or abort was detected by the emulation drivers.

		mov  cx,CS:COUNT             ;number of accesses
		cld                          ;clear DF so ESI/EDI increment
RDWR_LOOP:	jmp  bx
RDWR_CHECK:	bt   CS:HW_ACCVIO,0
		jb   RDWR_ACCVIO
		loop RDWR_LOOP
		mov  ax,seg CMD_STATUS
		mov  ds,ax
		ret                          ;accesses completed successfully

RDWR_ACCVIO:    mov  ax,seg CMD_STATUS       ;access aborted - return error
		mov  ds,ax
		mov  CMD_STATUS,CS_ACCVIO
		ret

; The following jump tables are used to transfer a single byte/word/dword
; between user memory or I/O port and the monitor's transfer buffer.  All
; jump tables are indexed by the access size (scaled by 2).  Execution
; differs based on reads/writes and background/foreground monitor mode.
; For memory transfers in background mode, a special "umov" instruction is
; required to access foreground user memory from background mode memory.

; Jump table for foreground mode memory access to read or write a single
; byte/word/dword to oor from user memory and the monitor transfer buffer.

		ALIGN 2
FGRW_JMPTABLE   DW  0,FGRW_BYTE,FGRW_WORD,0,FGRW_DWORD

FGRW_BYTE:	movs byte ptr ES:[EDI],byte ptr DS:[ESI]
		jmp  RDWR_CHECK
FGRW_WORD:	movs word ptr ES:[EDI],word ptr DS:[ESI]
		jmp  RDWR_CHECK
FGRW_DWORD:	movs dword ptr ES:[EDI],dword ptr DS:[ESI]
		jmp  RDWR_CHECK

; Jump table for background mode memory reads to transfer a single
; byte/word/dword from user memory to the monitor transfer buffer.

		ALIGN 2
BGRD_JMPTABLE	DW  0,BGRD_BYTE,BGRD_WORD,0,BGRD_DWORD

BGRD_BYTE:	umov al,[esi]               ;read 8-bit byte(s)
		add  esi,1
		stosb
		jmp  RDWR_CHECK
BGRD_WORD:	umov ax,[esi]               ;read 16-bit word(s)
		add  esi,2
		stosw
		jmp  RDWR_CHECK
BGRD_DWORD:	umov eax,[esi]              ;read 32-bit dword(s)
		add  esi,4
		stosd
		jmp  RDWR_CHECK

; Jump table for background mode memory writes to transfer a single
; byte/word/dword from the monitor transfer buffer to user memory.

		ALIGN 2
BGWR_JMPTABLE	DW  0,BGWR_BYTE,BGWR_WORD,0,BGWR_DWORD

; Note. There is a special case involved dealing with reading from
; the transfer buffer when bs8 is asserted for BGWR_BYTE operation at an 
; odd address. As a result we always read WORDS from transfer buffer and 
; then write the appropriate byte to processor memory.
BGWR_BYTE:	
      bt   esi,0            ;Check for odd transfer buffer address
      jnb  BGWR_BYTE1       ;If not then continue at BGWR_BYTE1 
      sub  esi,1            ;We have an odd address, so adjust
      lodsw                 ; to read word at even address      
      add  esi,1            ; and
      shr  eax,8            ; put appropriate byte into postion.
      jmp  BGWR_BYTE2
BGWR_BYTE1:
      lodsw                     
BGWR_BYTE2:
		umov ES:[edi],al      ;Write byte to processor memory.
		add  edi,1      
      sub  esi,1           
		jmp  RDWR_CHECK
BGWR_WORD:	lodsw                       ;background mode 16-bit write(s)
		umov ES:[edi],ax
		add  edi,2
		jmp  RDWR_CHECK
BGWR_DWORD:	lodsd                       ;background mode 32-bit write(s)
		umov ES:[edi],eax
		add  edi,4
		jmp  RDWR_CHECK

; Jump table for background or foreground mode I/O reads to transfer a single
; byte/word/dword from an I/O port to the monitor transfer buffer.

		ALIGN 2
IORD_JMPTABLE   DW  0,IORD_BYTE,IORD_WORD,0,IORD_DWORD

IORD_BYTE:	insb
		jmp  RDWR_CHECK
IORD_WORD:	insw
		jmp  RDWR_CHECK
IORD_DWORD:	insd
		jmp  RDWR_CHECK

; Jump table for background or foreground mode I/O writes to transfer a single
; byte/word/dword from the monitor transfer buffer to an I/O port.

		ALIGN 2
IOWR_JMPTABLE   DW  0,IOWR_BYTE,IOWR_WORD,0,IOWR_DWORD

IOWR_BYTE:	outsb
		jmp  RDWR_CHECK
IOWR_WORD:	outsw
		jmp  RDWR_CHECK
IOWR_DWORD:	outsd
		jmp  RDWR_CHECK

RDWR_START	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_FPUREAD
;
; Description: 
; Parameters:
;------------------------------------------------------------------------------
CMD_FPUREAD	PROC NEAR
		mov CMD_STATUS,CS_BADCMD
		ret
CMD_FPUREAD	ENDP

;------------------------------------------------------------------------------
; Subroutine CMD_FPUWRITE
;
; Description: 
; Parameters:
;------------------------------------------------------------------------------
CMD_FPUWRITE	PROC NEAR
		mov CMD_STATUS,CS_BADCMD
		ret
CMD_FPUWRITE	ENDP

;------------------------------------------------------------------------------
; Monitor Exception Handlers
;------------------------------------------------------------------------------
EXCEPTION_0:	mov  bx,0
		jmp  EXCEPTION
EXCEPTION_1:	mov  bx,1
		jmp  EXCEPTION
EXCEPTION_2:	mov  bx,2
		jmp  EXCEPTION
EXCEPTION_3:	mov  bx,3
		jmp  EXCEPTION
EXCEPTION_4:	mov  bx,4
		jmp  EXCEPTION
EXCEPTION_5:	mov  bx,5
		jmp  EXCEPTION
EXCEPTION_6:	mov  bx,6
		jmp  EXCEPTION
EXCEPTION_7:	mov  bx,7
		jmp  EXCEPTION
EXCEPTION_8:	mov  bx,8
		jmp  EXCEPTION
EXCEPTION_9:	mov  bx,9
		jmp  EXCEPTION
EXCEPTION_10:	mov  bx,10
		jmp  EXCEPTION
EXCEPTION_11:	mov  bx,11
		jmp  EXCEPTION
EXCEPTION_12:	mov  bx,12
		jmp  EXCEPTION
EXCEPTION_13:	mov  bx,13
		jmp  EXCEPTION
EXCEPTION_14:	mov  bx,14
		jmp  EXCEPTION
EXCEPTION_15:	mov  bx,15
		jmp  EXCEPTION
EXCEPTION_16:	mov  bx,16

EXCEPTION:	mov  ax,seg CMD_STATUS
		mov  ds,ax
		mov  CMD_STATUS,bx
		mov  CMD_REQUEST,0
		call USERMODE
		jmp  IDLE_ENTRY

;------------------------------------------------------------------------------
; Monitor Reset Entry - Do Not Customize
;
; This is the entry point for the monitor in background memory.  The processor
; state is similar to that of coming out of reset, however emulation hardware
; images the reset value of CS:EIP (f000:fff0) to this address. 
;------------------------------------------------------------------------------
		ORG 1ff0h

		;jmp far ptr16:16
		db  0eah
		dw  offset CHECK_REENTRY,0

CODESEG		ENDS

		END
