From 2d04cacc5322951f187bb17e017c12920ac8ebe2 Mon Sep 17 00:00:00 2001 From: Mark Zbikowski Date: Thu, 25 Apr 2024 21:24:10 +0100 Subject: MZ is back! --- v4.0/src/DEV/XMAEM/INDEACC.INC | 218 ++++++ v4.0/src/DEV/XMAEM/INDEDAT.INC | 535 ++++++++++++++ v4.0/src/DEV/XMAEM/INDEDES.MAC | 173 +++++ v4.0/src/DEV/XMAEM/INDEDMA.ASM | 660 ++++++++++++++++++ v4.0/src/DEV/XMAEM/INDEEMU.ASM | 653 +++++++++++++++++ v4.0/src/DEV/XMAEM/INDEEXC.ASM | 799 +++++++++++++++++++++ v4.0/src/DEV/XMAEM/INDEGDT.ASM | 379 ++++++++++ v4.0/src/DEV/XMAEM/INDEI15.ASM | 503 +++++++++++++ v4.0/src/DEV/XMAEM/INDEIDT.ASM | 512 ++++++++++++++ v4.0/src/DEV/XMAEM/INDEINI.ASM | 1474 +++++++++++++++++++++++++++++++++++++++ v4.0/src/DEV/XMAEM/INDEINS.MAC | 923 ++++++++++++++++++++++++ v4.0/src/DEV/XMAEM/INDEMAUS.ASM | 73 ++ v4.0/src/DEV/XMAEM/INDEMSG.ASM | 96 +++ v4.0/src/DEV/XMAEM/INDEMSUS.ASM | 76 ++ v4.0/src/DEV/XMAEM/INDEMSUS.INC | 64 ++ v4.0/src/DEV/XMAEM/INDEOVP.MAC | 111 +++ v4.0/src/DEV/XMAEM/INDEPAT.ASM | 74 ++ v4.0/src/DEV/XMAEM/INDEXMA.ASM | 781 +++++++++++++++++++++ v4.0/src/DEV/XMAEM/INDEXMAE.SYS | 8 + v4.0/src/DEV/XMAEM/MAKEFILE | 57 ++ v4.0/src/DEV/XMAEM/XMAEM.ARF | 13 + v4.0/src/DEV/XMAEM/XMAEM.LC | 19 + v4.0/src/DEV/XMAEM/XMAEM.LNK | 13 + v4.0/src/DEV/XMAEM/XMAEM.MAK | 66 ++ v4.0/src/DEV/XMAEM/XMAEM.SKL | 16 + 25 files changed, 8296 insertions(+) create mode 100644 v4.0/src/DEV/XMAEM/INDEACC.INC create mode 100644 v4.0/src/DEV/XMAEM/INDEDAT.INC create mode 100644 v4.0/src/DEV/XMAEM/INDEDES.MAC create mode 100644 v4.0/src/DEV/XMAEM/INDEDMA.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEEMU.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEEXC.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEGDT.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEI15.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEIDT.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEINI.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEINS.MAC create mode 100644 v4.0/src/DEV/XMAEM/INDEMAUS.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEMSG.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEMSUS.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEMSUS.INC create mode 100644 v4.0/src/DEV/XMAEM/INDEOVP.MAC create mode 100644 v4.0/src/DEV/XMAEM/INDEPAT.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEXMA.ASM create mode 100644 v4.0/src/DEV/XMAEM/INDEXMAE.SYS create mode 100644 v4.0/src/DEV/XMAEM/MAKEFILE create mode 100644 v4.0/src/DEV/XMAEM/XMAEM.ARF create mode 100644 v4.0/src/DEV/XMAEM/XMAEM.LC create mode 100644 v4.0/src/DEV/XMAEM/XMAEM.LNK create mode 100644 v4.0/src/DEV/XMAEM/XMAEM.MAK create mode 100644 v4.0/src/DEV/XMAEM/XMAEM.SKL (limited to 'v4.0/src/DEV/XMAEM') diff --git a/v4.0/src/DEV/XMAEM/INDEACC.INC b/v4.0/src/DEV/XMAEM/INDEACC.INC new file mode 100644 index 0000000..01a28b2 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEACC.INC @@ -0,0 +1,218 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEACC * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation * +* * +* DESCRIPTIVE NAME: Access rights byte definitions for 80386 XMA emulator * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : This file defines the access rights bytes used in * +* descriptors that define the code and data segments. * +* * +* MODULE TYPE : INC * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEACC) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870530 D : NEW FOR WSP RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULE'S LIBRARY FILE TYPE TO "INC" * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +PAGE +; RECORD DEFINITIONS FOR ACCESS RIGHTS BYTES +; ====== =========== === ====== ====== ===== +; +; Data Segments - Interpretation of access rights for DS/ES/SS + + +; Define the bit fields for the access rights byte + +DATA_ACC RECORD PRESENT:1,DPL:2,SEG_TYPE:1,XQTBLE:1,EXPDIR:1,WRTBLE:1,ACCESSED:1 + + ; PRESENT (1 bit) 0 = segment not present in memory + ; 1 = segment present in memory + ; + ; DPL (2 bits) Descriptor privilege level -- 0 to 3 + ; + ; SEG_TYPE (1 bit) 0 = system segment + ; 1 = application segment + ; + ; XQTBL (1 bit) 0 = segment is not executable (i.e., data) + ; 1 = segment is executable (i.e., code) + ; + ; EXPDIR (1 bit) 0 = expand up, i.e., normal data segment + ; 1 = expand down, i.e., stack segment + ; + ; WRTBLE (1 bit) 0 = data segment is read only + ; 1 = data segment is read/write + ; + ; ACCESSED (1 bit) 0 = segment has not been accessed + ; 1 = segment has been accessed + + +; Privilege level 3 read/write data segment access rights byte + +CPL3_DATA_ACCESS EQU 11110011B ; Present + ; DPL = 3 + ; Application segment + ; Not executable (i.e. data) + ; Expand up + ; Readable/Writable + ; Accessed + +; Privilege level 0 read/write data segment access rights byte + +CPL0_DATA_ACCESS EQU 10010011B ; Present + ; DPL = 0 + ; Application segment + ; Not executable (i.e. data) + ; Expand up + ; Readable/Writable + ; Accessed + + +NULL_ACCESS EQU 00000000B ; Null (Not present) + + +; Descriptor privilege levels. These can be ANDed or ORed with the access +; rights byte to get the desired DPL for the descriptor. + +DPL3 EQU 01100000B +DPL2 EQU 01000000B +DPL1 EQU 00100000B +DPL0 EQU 00000000B + +PAGE +; Code Segments - Interpretation of access rights for CS. +; Identical to data segments except for two fields. + + +CODE_ACC RECORD DATA_HI:5,CONFORM:1,READABLE:1,DATA_LO:1 + + ; DATA_HI (5 bits) Same five bits as described for data segments + ; PPRESENT (1), DPL (2), SEG_TYPE (1) and + ; XQTBL (1) + ; + ; CONFORM (1 bit) 0 = Non-conforming - cannot be called by + ; someone with a different CPL + ; 1 = Conforming - can be called by anyone + ; regardless of CPL. CPL remains the same + ; + ; READABLE (1 bit) 0 = code segment is execute only + ; 1 = data segment is executable and readable + ; + ; DATA_LO (1 bit) Same bit as described for data segments + ; ACCESSED (1) + + +; Privilege level 3 conforming readable code segment access rights byte +; BUT... +; We switched the DPL to 0. Why? Because the DPL of the current code segment +; determines the current privilege level (CPL). Whatever is running at the +; time can only access data with a DPL >= CPL. So if the DPL for the code +; segment were 3 and we tried to access data with a DPL of 0 we would get a +; fault. By setting the DPL of the code segment to 0 we can access all data +; and will never be bothered by faults. + +CPL3_CODE_ACCESS EQU 10011111B ; Present + ; DPL = 0 + ; Application segment + ; Executable (i.e. code) + ; Conforming + ; Readable + ; Accessed + +; Access rights to be able to read and write to a code segment. Code segments +; may not be written to. So this access byte will define the segment as a +; read/write data segment. It is the same as CPL3_DATA_ACCESS. + +COMP_CODE_ACCESS EQU CPL3_DATA_ACCESS + +; Privilege level 0 non-conforming readable code segment access rights byte + +CPL0_CODE_ACCESS EQU 10011011B ; Present + ; DPL = 0 + ; Application segment + ; Executable (i.e. code) + ; Non-conforming + ; Readable + ; Accessed + +PAGE +; System Segments - Interpretation of access rights for TR/LDTR as well as +; the four gate types. The access rights byte for the +; GDTR and IDTR is undefined, meaning anyone can access +; the table (although not necessarily the segments +; defined in the table). The uppermost 4 bits are +; identical to those for data segments; the type field +; occupies the lowermost 4 bits. + + +SYSTEM_ACC RECORD DATA_H:4,DESC_TYPE:4 + + ; DATA_H (4 bits) Same four bits as described for data segments + ; PPRESENT (1), DPL (2) and SEG_TYPE (1) + ; + ; DESC_TYPE(4 bits) 0000 Intel Reserved + ; 0001 Available 286 TSS + ; 0010 LDT + ; 0011 Busy 286 TSS + ; 0100 Call Gate + ; 0101 Task Gate + ; 0110 286 Interrupt Gate + ; 0111 286 Trap Gate + ; 1000 Intel Reserved + ; 1001 Available 386 TSS + ; 1010 Intel Reserved + ; 1011 Busy 386 TSS + ; 1100 386 Call Gate + ; 1101 Intel Reserved + ; 1110 386 Interrupt Gate + ; 1111 386 Trap Gate + ; + + +; These equates cover the topmost 4 bits for system segment access +; rights bytes. They define the privilege level. + +LDT_ACCESS EQU 11100000B ; Present, DPL = 3 for LDTs + +TSS_ACCESS EQU 10000000B ; Present, DPL = 0 for TSSs + +GATE_ACCESS EQU 10000000B ; Present, DPL = 0 for Gates + + +; These are for the type field. When "OR"ed with one of the above, +; you have a complete access rights byte. + +LDT_DESC EQU 00000010B OR LDT_ACCESS + +FREE_TSS EQU 00000001B OR TSS_ACCESS +FREE_TSS_386 EQU 00001001B OR TSS_ACCESS + +BUSY_TSS EQU 00000011B OR TSS_ACCESS + +CALL_GATE EQU 00000100B OR GATE_ACCESS + +TASK_GATE EQU 00000101B OR GATE_ACCESS + +INT_GATE EQU 00000110B OR GATE_ACCESS + +TRAP_GATE EQU 00000111B OR GATE_ACCESS + diff --git a/v4.0/src/DEV/XMAEM/INDEDAT.INC b/v4.0/src/DEV/XMAEM/INDEDAT.INC new file mode 100644 index 0000000..6c58eef --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEDAT.INC @@ -0,0 +1,535 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEDAT * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation * +* * +* DESCRIPTIVE NAME: Sytem data structures and equates for 386 XMA emulator * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : This file defines the overlays and constants for most of * +* the system data. The following structures are defined * +* here: * +* The resister save area on our stack * +* 80386 Task State Segment (TSS) * +* The Global Descriptor Table (GDT) * +* 80286 Segment Descriptor * +* 80286 Gate Descriptor * +* 80286 Task State Segment (TSS) * +* * +* MODULE TYPE : INC * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEDAT) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870529 D : NEW FOR WSP RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULES'S LIBRARY FILE TYPE TO "INC" * +* $P2=P0000410 410 870918 D : RELOCATE DATA TO MAKE ROOM FOR I/O BIT MAP * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +; BPSTACK is an overlay for our stack when we receive control from an +; interrupt that we want to handle. The entry points for these interrupts +; push a 32 but error code and the interrupt number (16 bits) onto the stack. +; Then control goes to INDEEXC which saves the current register values on the +; stack. BP is set to point to the beginning of the register save area. This +; is the mapping of the resulting stack. + +BPSTACK STRUC + +BP_ES DW 0 ; Interrptee's ES +BP_DI DW 0 ; Interruptee's EDI (32 bit DI) + DW 0 ; +BP_SI DW 0 ; Interruptee's ESI (32 bit SI) + DW 0 ; +BP_BP DW 0 ; Interruptee's EBP (32 bit BP) + DW 0 ; +BP_PASP DW 0 ; Interruptee's ESP (32 bit SP) +BP_PSP2 DW 0 ; (ESP as it was before the PUSHA) +BP_BX DW 0 ; Interruptee's EBX (32 bit BX) + DW 0 ; +BP_DX DW 0 ; Interruptee's EDX (32 bit DX) + DW 0 ; +BP_CX DW 0 ; Interruptee's ECX (32 bit CX) + DW 0 ; +BP_AX DW 0 ; Interruptee's EAX (32 bit AX) + DW 0 ; +BP_DS DW 0 ; Interruptee's DS +BP_EX DW 0 ; Exception ID +BP_EC DW 0 ; Error Code + DW 0 + +; The following values are placed on our stack by the 80386 + +BP_IP DW 0 ; Interruptee's EIP (32 bit IP) +BP_IP2 DW 0 +BP_CS DW 0 ; Interruptee's CS (16 bit CS plus 16 bit trash) +BP_CS2 DW 0 +BP_FL DW 0 ; Interruptee's flags (32 bits) +BP_FL2 DW 0 +BP_SP DW 0 ; Interruptee's ESP - saved on an inter-level +BP_SP2 DW 0 ; interrupt +BP_SS DW 0 ; Interruptee's SSP - also saved on inter-level +BP_SS2 DW 0 ; interrupt +BP_VMES DW 0 ; Virtual mode ES +BP_VME2 DW 0 +BP_VMDS DW 0 ; Virtual mode DS + DW 0 +BP_VMFS DW 0 ; Virtual mode FS + DW 0 +BP_VMGS DW 0 ; Virtual mode GS + DW 0 +BP_STK DW 0 ; The rest of the stack + +BPSTACK ENDS + +BP_START EQU 0 ; Offset from BP of the start of the stack info + +SUBTTL 80386 TSS - Task State Segment +PAGE +;************************************************* +; * +; Start of Debugger's 386 TSS * +; * +;************************************************* + +TSS386 STRUC + DW 0 ; Intel reserved +ETSS_BACK_LINK DW 0 ; Back link to previous TSS +ETSS_SP0 DW 0 ; ESP for privilege level 0 + DW 0 +ETSS_SS0 DW 0 ; SS for privilege level 0 + DW 0 ; Intel reserved +ETSS_SP1 DD 0 ; ESP for privilege level 1 +ETSS_SS1 DW 0 ; SS for privilege level 1 + DW 0 ; Intel reserved +ETSS_SP2 DD 0 ; ESP for privilege level 2 +ETSS_SS2 DW 0 ; SS for privilege level 2 + DW 0 ; Intel reserved +ETSS_CR3 DD 0 ; CR3 - Page directory base register +ETSS_IP DW 0 ; Task's EIP + DW 0 +ETSS_FL DW 0 ; Task's Eflags +ETSS_FL2 DW 0 +ETSS_AX DD 0 ; Task's EAX +ETSS_CX DD 0 ; Task's ECX +ETSS_DX DD 0 ; Task's EDX +ETSS_BX DD 0 ; Task's EBX +ETSS_SP DW 0 ; Task's ESP + DW 0 +ETSS_BP DD 0 ; Task's EBP +ETSS_SI DD 0 ; Task's ESI +ETSS_DI DD 0 ; Task's EDI +ETSS_ES DW 0 ; Task's ES + DW 0 ; Intel reserved +ETSS_CS DW 0 ; Task's CS + DW 0 ; Intel reserved +ETSS_SS DW 0 ; Task's SS + DW 0 ; Intel reserved +ETSS_DS DW 0 ; Task's DS + DW 0 ; Intel reserved +ETSS_FS DW 0 ; Task's FS + DW 0 ; Intel reserved +ETSS_GS DW 0 ; Task's GS + DW 0 ; Intel reserved +ETSS_LDT DW 0 ; Selector for task's LDT + DW 0 ; Intel reserved +ETSS_TRAP DW 0 ; Trap bit - bit 0 (IBM bit 15) +ETSS_BM_OFFSET DW 0 ; I/O bit map base +ETSS_AVL DD 0 +TSS386 ENDS +TSS_386_LEN EQU 68H ; Length of the TSS +TSS_BM_LEN EQU 0FFFH-TSS_386_LEN ; Length of the I/O bit map + ; The bit map is in the same segment as the TSS + ; therefore the descriptor fot the TSS segment + ; must have enough room to include the bit map. + +SUBTTL GDT - Global Descriptor Table +PAGE + +SUBTTL GDT - GLOBAL DESCRIPTOR TABLE + +; THE GLOBAL DESCRIPTOR TABLE DEFINITION +; === ====== ========== ===== ========== +; + +GDT_LEN EQU 512 * 8 ; 512 entries total + +GDT_DEF STRUC + +UNUSED_ENTRY DQ 0 ; Seg reg = 0 illegal - this entry is + ; not accessible +GDT_PTR DQ 0 ; This entry points to this GDT table +MON_IDT_PTR DQ 0 ; System interrupt descriptor table +RSDA_PTR DQ 0 ; The real system data area (as opposed + ; to the virtual SDA's). XMA pages + ; start here +HUGE_PTR DQ 0 ; Used to address 0-1M as data + +LA_PTR DQ 0 ; Pointer to the LOADALL area (000800H) + +C_BWCRT_PTR DQ 0 ; Compatible blanck and white display +C_CCRT_PTR DQ 0 ; Compatible color display +E_CCRT_PTR DQ 0 ; Enhanced color display (16 bytes) +E_CCRT_PTR2 DQ 0 + +SYS_ROM_CS DQ 0 ; CS for system IDT, ROM resident +SYS_ROM_DS DQ 0 ; DS selector to access above as data +SYS_PATCH_CS DQ 0 ; CS for system IDT, RAM patch area +SYS_PATCH_DS DQ 0 ; DS selector to access above as data + +V_ROM_CS DQ 0 ; CS - virtual IDT, ROM resident +V_ROM_DS DQ 0 ; DS selector to access above as data +V_PATCH_CS DQ 0 ; CS - virtual IDT, RAM patch area +V_PATCH_DS DQ 0 ; DS selector to access above as data + +ES_TEMP DQ 0 ; Dynamic pointer for ES +CS_TEMP DQ 0 ; Dynamic pointer for CS +SS_TEMP DQ 0 ; Dynamic pointer for SS +DS_TEMP DQ 0 ; Dynamic pointer for DS + +; Monitor descriptors + +MON_LDTR DQ 0 +MON_DS DQ 0 +MON_ES DQ 0 +MON_SS DQ 0 +MON_CS DQ 0 +MON_TR_SS DQ 0 ; Task register value for single step +MON_TR_BP DQ 0 ; " " " " breakpoint +MON_TR_HWI DQ 0 ; Read/write (needs special hardware) +MON_TR_OI DQ 0 ; " " " " control-break + +KBD_OWNER_SDA DQ 0 ; Fast path to keyboard owner's SDA + +VTIMER_VECTOR DB 16 DUP (8 DUP(0)) ; 16 VM timer-related descriptors + +; Below are the 32 system exception service routine descriptors. + +SEX_TSS DB 32 DUP (8 DUP(0)) + +; Below are the 16 hardware interrupt service routine TSS descriptor +; pairs. The first is suitable for TR loading (i.e. target selector +; in the corresponding task gate) and the other is a TSS-as-data +; descriptor. + +HWI_TSS DB 16 DUP (16 DUP(0)) + +; This is the descriptor pair for the dispatcher (same format as HWI). + +DISPATCH_TASK DQ 0 + DQ 0 + +HWI_LDT DQ 0 + +BASICS_SEG DQ 0 ; BASIC's segment (F600) + +BIOSS_SEG DQ 0 ; BIOS's segment (F000) + +; +; +; Any other GDT things go here. There are around 300 slots left. +; +; + +RSVD_GDT DQ 0 + DB (GDT_LEN - (RSVD_GDT + 8) - (18 * 32)) DUP (0) + + ; The above leaves space for the + ; descriptors needed for 18 VMs + +PM_VM DB 32 DUP (0) ; Program Manager's virtual machine + ; entries. This will function as + ; the highest priority machine. +USER_CS DQ 0 ; Debugee's code segment +USER_SS DQ 0 ; Debugee's stack segment +USER_DS DQ 0 ; Debugee's data segment +USER_ES DQ 0 ; Debugee's extra segment +USER_TSS DQ 0 ; Debugee's Task State Segment +USER_TSS_DAT DQ 0 ; Debugee's TSS as a data segment +USER_LDT DQ 0 ; Debugee's LDT definition +USER_LDT_DAT DQ 0 ; Debugee's LDT as a data segment + +USER_VMS DB 14 DUP (32 DUP(0)) ; 16 user VMs + +SCRUBBER DB 32 DUP (0) ; RAM scrubber - dispatched when no other + ; machine has work to do +last_gdt_byte db 0 ; marker for last GDT byte + 1 +GDT_DEF ENDS + +SUBTTL Virtual Machine Entry +PAGE +; Virtual Machine Entry Definition - consists of 4 descriptors +; +; - Pointer to TSS, loadable into the Task Register +; - Pointer to TSS, loadable into a regular segment register +; - Pointer to the LDT, loadable into the LDT Register +; - Pointer to the LDT, loadable into a regular segment register +; +; The first of each pair is for loading into the associated special +; system registers, TR and LDTR. The second allows dynamic access +; and modification of descriptors by the system. + +VM_GDT_ENTRY STRUC + +VM_TR DQ 0 ; TR value for this machine's TSS +TSS_PTR DQ 0 ; Data pointer to this machine's TSS +VM_LDTR DQ 0 ; LDTR value for this machine's LDT +LDT_PTR DQ 0 ; Data pointer to this machine's LDT + +VM_GDT_ENTRY ENDS + +SUBTTL 80286 Segment Descriptor +PAGE +; DESCRIPTOR TABLE ENTRY STRUCTURES +; ========== ===== ===== ========== +; +; Segment Descriptor +; +; The following type of descriptor applies to data segments, code +; segments, local, global, and interrupt descriptor table segments, +; and task state segments. + +DATA_DESC STRUC + +SEG_LIMIT DW 0 ; Segment limit (1 - 65535 bytes) +BASE_LO_WORD DW 0 ; 24 bit segment physical address +BASE_HI_BYTE DB 0 ; (0 - (16M-1)) +DATA_ACC_RIGHTS DB 0 ; Access rights byte +DATA_RESERVED DW 0 ; Intel reserved + +DATA_DESC ENDS + +SHADOW_ID EQU DATA_RESERVED ; For garbage collection + +SUBTTL 80286 Gate Descriptor +PAGE +; 80286 Gate Descriptor + +; The following type of descriptor applies to task gates, call gates, +; interrupt gates, and trap gates. + +GATE_DESC STRUC + +ENTRY_POINT DW 0 ; Destination routine entry point + ; within the target segment. + ; This is unused for task gates. +CS_SELECTOR DW 0 ; Selector for destination segment. + ; For task gates, this must point + ; to a valid task state segment + ; descriptor. For call, trap, and + ; interrupt gates, this must point + ; to a valid code segment descriptor. + ; The segment defined in that + ; descriptor plus the aforementioned + ; entry-point defines the actual + ; routine address. +WORD_COUNT DB 0 ; For call gates only - number of + ; words to copy from caller's + ; stack to callee's stack +GATE_ACC_RIGHTS DB 0 ; Access rights byte +GATE_RESERVED DW 0 ; Intel reserved + +GATE_DESC ENDS + +; Record for the fields in a selector + +SELECTOR RECORD INDEX:13,TI:1,RPL:2 ; Standard 286 selector format + +SUBTTL TSS - Task State Segment +PAGE +; TASK STATE SEGMENT +; ==== ===== ======= +; +; There are currently two styles of task state segment (TSS) - one for +; VMs and one for the hardware interrupt service routines. There may +; be more later, depending on the needs and/or whims of the Authors +; of this and follow-on products. They will all have one thing in +; common - they begin with a 44 byte 286-required machine state data +; area. The first structure below is that area's definition. All +; subsequent colors of TSS will start with space for that data, defined +; like so: +; +; first_field_name DB (TYPE TASK_STATE_286) DUP (0) +; +; As a convenience, we use the highest part of the TSS for that +; particular task's stack (or in some cases, stacks - for privilege +; levels 0, 1, and 2). The intermediate area is used for task-specific +; data. + + +TASK_STATE_286 STRUC + +BACK_LINK DW 0 ; TSS selector from whence we came +VM_SP0 DW 0 ; SS:SP for CPL 0 +VM_SS0 DW 0 +VM_SP1 DW 0 ; SS:SP for CPL 1 +VM_SS1 DW 0 +VM_SP2 DW 0 ; SS:SP for CPL 2 +VM_SS2 DW 0 +VM_IP DW 0 ; IP - next instruction to execute +VM_FL DW 0 ; Flag word +VM_AX DW 0 ; +VM_CX DW 0 ; General +VM_DX DW 0 ; +VM_BX DW 0 ; +VM_SP DW 0 ; Purpose +VM_BP DW 0 ; +VM_SI DW 0 ; +VM_DI DW 0 ; Registers +VM_ES DW 0 ; +VM_CS DW 0 ; Segment +VM_SS DW 0 ; Selectors +VM_DS DW 0 ; +VM_LDT DW 0 ; Selector for this task's LDT + +TASK_STATE_286 ENDS + +; Note that the actual stacks for CPLs 2, 1, and 0 are part of the +; task state segment. CPL 3's stack is in its own address space. + +SUBTTL Length Equates +PAGE +; Length equates + +LA_LEN EQU GDT_LEN ; For new monitor + +SIDT_LEN EQU 256*8 ; Length of the IDT -- 256 interrupt + ; entries, 8 bytes each + +TSS_LEN EQU 68H + 2000H ; TSS structure length -- 68H for regs + ; 2000H for I/O bit map @P2C + +LDT_LEN EQU 00800H ; LDT structure length (2K) + +VM_ENTRY_LENGTH EQU TYPE VM_GDT_ENTRY ; Length of a VM entry + +VM_ENTRY_WIDTH EQU 5 ; Shift count for priority. Each + ; VM entry = 32 bytes, and SHL 5 + ; is the same as MUL by 32. +NEXT_DESC EQU TYPE DATA_DESC ; Length of a descriptor + +DESC_LEN EQU TYPE DATA_DESC ; Length of a descriptor + +DESC_WIDTH EQU 3 ; Shift count for desc len (8) is 3 + +PM_VM_SIZE EQU 01000H ; Virtual Manager's VM size + +MCRT_SIZE EQU 4*1024 ; Monochrome display size + +CCRT_SIZE EQU 16*1024 ; Compatible color display size + +ECCRT_SIZE EQU 0FFFFH ; Size of each portion of the enhanced + ; color display buffer + +MAX_SEG_LEN EQU 0FFFFH ; Maximum segment length = 64K + +NULL_SEG_LEN EQU 00000H ; Null segment length = 0 + +THIRTY_TWO_K EQU 08000H ; Unit of virtual machine size + + +SUBTTL Location Equates +PAGE + + +; Location equates + +; These items are all pre-allocated, and have no SBDs. After these, +; storage structures are all obtained through the memory manager. + +;----------------------------------------------------------------------------- + +; From here to the end marker, these guys must stay in order as this is +; how they are allocated. + +GDT_LOC EQU 0C100H ; GDT location relative to CS @P2C + +SIDT_LOC EQU (GDT_LOC + GDT_LEN) ; The system IDT is next + +DISPATCH_LOC EQU (SIDT_LOC + SIDT_LEN) + +; End marker + +; Monitor equate used in m_state to tell the estep routine that we're +; single stepping from within ego. +GO_MODE EQU 1 ; +; Monitor equate used in m_state to tell the hwi_int routine that we're +; breaking on a memory read address that's executed +MEMX EQU 2 ; +; Monitor equate used in m_state to tell mon_go that a breakpoint has been +; set so enable the breakpoint card +CARD_ENABL EQU 4 ; +S_MODE EQU 2000H ; Selector or segment mode flag + +BOOT_IP EQU 07C00H ; PC1 IP value for boot + +BOOT_CS EQU 00000H ; PC1 CS value for boot + +BOOT_SS EQU 00030H ; PC1 SS value for boot + +BOOT_SP EQU 07FFH ; PC1 SP value for boot + +BOOT_FL EQU 00200H ; Interrupts enabled + +CS_LO_CACHE EQU 00000H ; Code segment + +CS_HI_CACHE EQU 000H ; compatible cache + +SS_LO_CACHE EQU 00300H ; Stack segment + +SS_HI_CACHE EQU 000H ; compatible cache + +OTHER_CACHE EQU 00000H ; GP zero cache + +SUBTTL Display Equates +PAGE + +; The next equates are system definitions for display buffer addresses. + + +MCRT@_LO EQU 0000H ; + ; Monochrome display address +MCRT@_HI EQU 0BH ; (0B0000H) + +CCRT@_LO EQU 8000H ; + ; Compatible color display address +CCRT@_HI EQU 0BH ; (0B8000H) + +ECCRT@_LO_LO EQU 0000H ; + ; Enhanced color display address - +ECCRT@_LO_HI EQU 0AH ; lower 64K (0A0000H) + +ECCRT@_HI_LO EQU 0000H ; + ; Enhanced color display address - +ECCRT@_HI_HI EQU 0CH ; upper 64K (0C0000H) + + +; Code segment addresses + + +CSEG@_LO EQU 0000H ; + ; ROM code segment address +CSEG@_HI EQU 0EH ; (0E0000H) + +NSEG@_LO EQU 0000H ; + ; Null segment address +NSEG@_HI EQU 00H ; + +VIRTUAL_ENABLE EQU 0000000000000001B + +SUBTTL diff --git a/v4.0/src/DEV/XMAEM/INDEDES.MAC b/v4.0/src/DEV/XMAEM/INDEDES.MAC new file mode 100644 index 0000000..68d1776 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEDES.MAC @@ -0,0 +1,173 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEDES * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft * +* * +* DESCRIPTIVE NAME: Macros for setting up 80386 descriptors * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : DESCR_DEF - Define storage for a descriptor * +* DESCR_INIT - Initialize a descriptor that has already * +* been defined * +* * +* MODULE TYPE : MAC * +* * +* REGISTER USAGE : 80286 Standard * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEDES) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870604 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULE'S LIBRARY FILE TYPE TO "MAC" * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +; DESCR_DEF: Define a descriptor in-line using DWs and DBs. +; +; Input: For Segment descriptors, TYPE = SEG. +; For Gate descriptors, TYPE = GATE. +; Other parameters are as described by comments below. +; +; Output: DWs (define words) and DBs (define bytes) using the passed +; values. Useful for copying descriptors directly out of the code +; space, as when their values are forever static. +; +; Example: DESCR_DEF SEG, TSS0_LEN, TSS0_LOC, 0, CPL0_DATA_ACCESS +; +; This defines a segment format (limit/base/access) descriptor +; based on the parameters, which are usually equated values. Note +; that the low word of the address is TSS0_LOC and the high byte +; is 0. + + +DESCR_DEF MACRO TYPE,PARM1,PARM2,PARM3,ARB + + IFIDN <&TYPE>, +; +; Segment descriptor definition +; + DW &PARM1 ; Segment limit + DW &PARM2 ; Segment base address - low word + DB &PARM3 ; Segment base address - high byte + DB &ARB ; Access rights byte + DW 0 ; Intel reserved + + ENDIF + + IFIDN <&TYPE>, +; +; Gate descriptor definition +; + DW &PARM1 ; Destination offset + DW &PARM2 ; Destination segment selector + DB &PARM3 ; Word count for stack-to-stack copy + ; (only for call gates when PL changes) + DB &ARB ; Access rights byte + DW 0 ; Intel reserved + + ENDIF + + ENDM + +PAGE + +; DESCR_INIT: Initialize a descriptor dynamically. +; +; NOTE: ES must already point to the BASE of table +; where you want the descriptor to go. +; +; +; Input: For Segment descriptors, TYPE = SEG. +; For 4K granularity Segment descriptors, TYPE = BSEG. +; For Gate descriptors, TYPE = GATE. +; If the FIELD parameter is supplied, a MOV DI,FIELD is generated. +; Otherwise, its current value is used. Other parameters are as +; described by comments below. +; +; +; Output: DI ends up as DI + 8 (i.e. pointing at next descriptor. This +; is useful if you are initializing several consecutive +; descriptors). +; +; The passed data is stored at ES:DI using string store. This +; macro would be used when creating descriptors dynamically, as +; when allocating virtual machines. +; +; Example: DESCR_INIT GATE,INT13_PTR,INT13_IP,INT13_CS,0,TRAP_GATE_ACCESS +; +; This stores the parameters in gate format (offset/ selector/word +; count/access) through ES:DI, where DI is first loaded with +; INT13_PTR. + + +DESCR_INIT MACRO TYPE,FIELD,PARM1,PARM2,PARM3,ARB + + PUSH AX ; Save the utility register + + IFNB <&FIELD> + + MOV DI,&FIELD ; Set up index value to proper descriptor + ADD DI,GDT_LOC + + ENDIF + + + IFIDN <&TYPE>, +; +; Segment descriptor initialization +; + MOV AX,&PARM1 ; PARM1 = Segment Limit; load into AX + STOSW ; and store + MOV AX,&PARM2 ; PARM2 = BASE_LO_W; load into AX + STOSW ; and store + MOV AL,&PARM3 ; PARM3 = BASE_HI_B; load into AL, and + MOV AH,&ARB ; ARB = Access Rights Byte; load into AH + STOSW ; and store both + MOV AX,0 ; Make sure the Intel + STOSW ; reserved word is zero + + ENDIF + + + IFIDN <&TYPE>, +; +; Big (4k granularity) segment descriptor initialization +; + MOV AX,&PARM1 ; PARM1 = Segment Limit; load into AX + STOSW ; and store + MOV AX,&PARM2 ; PARM2 = BASE_LO_W; load into AX + STOSW ; and store + MOV AL,&PARM3 ; PARM3 = BASE_HI_B; load into AL, and + MOV AH,&ARB ; ARB = Access Rights Byte; load into AH + STOSW ; and store both + MOV AX,0080H ; 4k granularity bit + STOSW + + ENDIF + + + IFIDN <&TYPE>, +; +; Gate descriptor initialization +; + MOV AX,&PARM1 ; PARM1 = Destination offset; load into AX + STOSW ; and store + MOV AX,&PARM2 ; PARM2 = Destination Selector;load into AX + STOSW ; and store + MOV AL,&PARM3 ; PARM3 = Word Count; load into AL, and + MOV AH,&ARB ; ARB = Access Rights Byte; load into AH + STOSW ; and store both + MOV AX,0 ; Make sure the Intel + STOSW ; reserved word is zero + + ENDIF + + POP AX ; Restore AX + + ENDM diff --git a/v4.0/src/DEV/XMAEM/INDEDMA.ASM b/v4.0/src/DEV/XMAEM/INDEDMA.ASM new file mode 100644 index 0000000..84d73dc --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEDMA.ASM @@ -0,0 +1,660 @@ +PAGE 60,132 +TITLE INDEDMA - 386 XMA Emulator - DMA Emulation + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEDMA * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation * +* * +* DESCRIPTIVE NAME: DMA handler for the 80386 XMA emulator * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : This module intercepts any I/O going to the DMA address * +* ports. We can't let the DMA requests go to the virtual * +* addresses. On the real XMA card the addresses on the * +* bus lines are translated by the card so that it accesses * +* the correct memory. But with our emulation the addresses * +* are translated before they hit the bus lines. The DMA * +* addresses go straight to the bus lines without being * +* translated. This would result in DMA reading and writing * +* data to the wrong memory location. Not good. Therefore * +* we intercept the I/O that is going to the DMA address * +* ports. We run these addresses through our paging mech- * +* anism and then write the real addresses to the DMA * +* address ports. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINTS : DMAIN - Entry point for "IN" instructions * +* DMAOUT - Entry point for "OUT" instructions * +* MANPORT - Entry point to issue an OUT to the manufacturing* +* port to re-IPL the system * +* * +* LINKAGE : Jumped to by INDEXMA * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Jump to POPIO to return to the V86 task * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : DISPLAY - Entry point in INDEEXC to signal an error * +* POPIO - Entry point in INDEEMU to return to the V86 * +* task * +* PGTBLOFF - Word in INDEI15 that contains the offset of * +* the page tables * +* SGTBLOFF - Word in INDEI15 that contains the offset of * +* the page directory * +* NORMPAGE - Double Word in INDEI15 that contains the * +* entry that goes into the first page directory * +* entry so that it points to the normal page * +* tables * +* BUFF_SIZE - Word in INDEI15 that contains the size of the * +* MOVEBLOCK buffer * +* MAXMEM - Word in INDEI15 that contains the total * +* number of K in the box * +* WORD_FLAG - Byte in INDEXMA that indicates whether the * +* I/O instruction was for a word or a byte * +* * +* SUB-ROUTINES : XLATE - Translate the virtual DMA address to a real DMA * +* address * +* * +* MACROS : DATAOV - Add prefix for the next instruction so that it * +* accesses data as 32 bits wide * +* ADDROV - Add prefix for the next instruction so that it * +* uses addresses that are 32 bits wide * +* LJB - Long jump if below * +* LJA - Long jump if above * +* LJAE - Long jump if above or equal * +* LJNE - Long jump if not equal * +* * +* CONTROL BLOCKS : INDEDAT.INC - System data structures * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEDMA) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870101 D : NEW FOR RELEASE 1.1 * +* $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; Include system data structures and equates + + IF1 ; Only include macros on the first pass + INCLUDE INDEOVP.MAC ; Override prefix macros + INCLUDE INDEINS.MAC ; 386 instruction macros + ENDIF + .LIST ; Turn the listing back on + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME DS:PROG + ASSUME ES:NOTHING + ASSUME SS:NOTHING + +INDEDMA LABEL NEAR + + EXTRN DISPLAY:NEAR ; Entry point in INDEEXC to signal an error + EXTRN POPIO:NEAR ; Entry point in INDEEMU to return to the V86 + ; task + EXTRN PGTBLOFF:WORD ; Word in INDEI15 that contains the offset of + ; the page tables + EXTRN SGTBLOFF:WORD ; Word in INDEI15 that contains the offset of + ; the page directory + EXTRN NORMPAGE:WORD ; Double Word in INDEI15 that contains the + ; entry that goes into the first page direct- + ; ory entry so that it points to the normal + ; page tables + EXTRN BUFF_SIZE:WORD ; Word in INDEI15 that contains the size of the + ; MOVEBLOCK buffer + EXTRN MAXMEM:WORD ; Word in INDEI15 that contains the total + ; number of K in the box + EXTRN WORD_FLAG:BYTE ; Byte in INDEXMA that indicates whether the + ; I/O instruction was for a word or a byte + +; Let these entry points be known to other modules + + PUBLIC INDEDMA + PUBLIC DMAIN + PUBLIC DMAOUT + PUBLIC MANPORT + +PAGE + +; Define control blocks for each of the DMA channels 0 to 7. The control +; blocks have information on where the user wanted to do DMA, where we will +; actually do the DMA, the channel number and the page port. The following +; is an overlay for the control blocks. After that the actual control +; blocks are defined. + +DMACB STRUC ; DMA control block + +DMACHN DB 0 ; Channel number +DMALSB DB 0 ; Least significant address byte +DMAMSB DB 0 ; Most significant address byte (16 bits) +DMAPAGE DB 0 ; Page - Hi-order of 24-bit address +DMALR DB 0 ; Real Least significant address byte +DMAMR DB 0 ; Real Most significant address byte (16 bits) +DMAPR DB 0 ; Real Page - Hi-order of 24-bit address +DMAPP DB 0 ; Compatability mode page port + +DMACB ENDS + +DMASTART EQU 0 +DMAENTRYLEN EQU DMAPP+1-DMASTART + +; Now, the channel control blocks + +DMATABLE DB 0 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 87H ; Page port + + DB 1 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 83H ; Page port + + DB 2 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 81H ; Page port + + DB 3 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 82H ; Page port + + DB 4 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 8FH ; Page port + + DB 5 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 8BH ; Page port + + DB 6 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 89H ; Page port + + DB 7 ; Channel number + DB DMAENTRYLEN-2 DUP (0) ; The other stuff + DB 8AH ; Page port + +; And now some more variables + +DMACURRENT DB 0 ; Channel we're working on +DMABYTE DB 0 ; This flag is toggled between 0 and 1 + ; to indicated whether the least + ; significant or most significant byte + ; of the DMA address is being written +DMA_ADV_CHN DB 0 ; Advanced mode channel number + +PAGE + +; Define the jump table. There are 32 entries in the table that correspond to +; the first 32 ports, 0 to 20H. The code uses the port number as an index into +; this table which then gives control to the appropriate routine for that port. +; The table is initialized so that all entries jump to the code that will pass +; the I/O back to the real port. Then the entries for the ports we want to +; handle are set to the corresponding routines for those ports. + +OUT_TABLE: + .XLIST + REPT 32 + JMP DOOUT + ENDM + .LIST +OUT_TABLE_END: + +; Set the entries for the ports we want to handle. + + ORG OUT_TABLE+(00H*3) + JMP CHN_0 + ORG OUT_TABLE+(02H*3) + JMP CHN_1 + ORG OUT_TABLE+(04H*3) + JMP CHN_2 + ORG OUT_TABLE+(06H*3) + JMP CHN_3 + ORG OUT_TABLE+(0CH*3) + JMP RESET_BYTE_PTR + ORG OUT_TABLE+(18H*3) + JMP CHK_FUNCTION + ORG OUT_TABLE+(1AH*3) + JMP CHK_BASE_REG + + ORG OUT_TABLE_END + +PAGE + +; Control comes here from INDEXMA when it determines that the "IN" instruction +; is not for one of the XMA ports. On entry, WORD_FLAG is already set for word +; or byte operation. IP points to next instruction minus 1. DX has the port +; value in it. + +DMAIN PROC NEAR + CMP WORD_FLAG,0 ; Is this "IN" for a word? + JNE GETWORDREAL ; Yes. Then go get a word. + + IN AL,DX ; Else we'll just get a byte + MOV BYTE PTR SS:[BP+BP_AX],AL ; And put it into the user's AL reg. + JMP INEXIT ; Go return to the user + +GETWORDREAL: + IN AX,DX ; Get a word from the port + MOV WORD PTR SS:[BP+BP_AX],AX ; Put it into the user's AX register + MOV WORD_FLAG,0 ; Reset the word flag + +INEXIT: + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction, or past + ; the port value in the case of I/O + ; with an immediate port value + JMP POPIO ; Return to the V86 task + +PAGE + +; Control comes here from INDEXMA when it determines that the "OUT" instruction +; is not for one of the XMA ports. On entry, WORD_FLAG is already set for word +; or byte operation. IP points to next instruction minus 1. DX has the port +; value in it. + +DMAOUT: + +; Check for DMA page registers in compatibility mode + + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data + MOV DS,AX ; segment + CMP WORD_FLAG,0 ; Is this a word operation? + LJNE DISPLAY ; No? Sorry. We don't support word DMA + ; yet. We'll just have to signal an + ; error. + LEA BX,DMATABLE ; Point BX to the base of our channel + ; control blocks + CMP DX,0081H ; Is this out to the channel 2 page port + LJB NOT_DMA_PAGE ; If the port number is less than 81H + ; then the "OUT" is not to a DMA page + JA CHK_CHN_0 ; If the port number is above 81H, then + ; go check if it's below 87H, the page + ; port for channel 0 + ADD BX,DMAENTRYLEN*2 ; If it's not below or above, then it + ; must be port 81H! Point BX to the + ; control block for channel 2 + JMP CHNCOM ; Continue + +CHK_CHN_0: + CMP DX,0087H ; Is it the page port for channel 0? + JA CHK680 ; Nope. It's above that. Go check for + ; the Roundup IPL port + JE CHNCOM ; It IS the page port for channel 0. + ; BX already points to the control + ; block for channel 0. + CMP DX,0083H ; Is it the page port for channel 1? + JB SET_CHN_3 ; No. It's below that. Then it must + ; be the page port for channel 3! + LJA DISPLAY ; No. It's above it. We don't know any + ; ports between 83H and 87H. Go + ; signal an error. + ADD BX,DMAENTRYLEN*1 ; Yes. It's the page port for channel 1 + ; Point BX to the control block for + ; channel 1. + JMP CHNCOM ; And continue + +; The port is greater than 87H + +CHK680: + CMP DX,680H ; Is it the manufacturing port (for IPL) + ; on the Roundup? + JE MANPORT ; Yes. Go IPL the system. + JMP DOOUT ; No. Pass the "OUT" on to the real + ; port. + +; The "OUT" is to the page port for channel 3 + +SET_CHN_3: + ADD BX,DMAENTRYLEN*3 ; Point BX to the control block for + ; channel 3 + +; Check to see if the value written to the page port is greater than 0FH. +; Why? Let me tell you. Addresses are 20 bits, right? The user puts the +; lower 16 bits into the channel port in two eight bit writes. The value +; written to the page port is the upper eight bits of the address. But to +; address 1M you only need 20 bits. Therefore, only the lower four bits are +; valid if the address is to remain within the 1M address limit. So if the +; value to be written to the page port is 10H or greater it is invalid, so +; we will signal an error. + +CHNCOM: + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value that is to be written + ; to the page port + CMP AL,10H ; Is it 10H or greater? + JB CHNCONT ; Nope. We're still OK. + +; Oops. It's an invalid page port value. Time to signal an error. But wait. +; If we just jump to DISPLAY as usual the code will just return to the V86 +; task. This is not good since we haven't Revised the DMA and it will end +; up going to the wrong address. What we really want to do is kill the system. +; To do this we will issue an interrupt 6, invalid instruction. The exception +; handler checks to see from whence the interrupt came. If it came from the +; emulator then it assumes something is terribly wrong and issues an NMI. +; This is what we want. So we'll issue an INT 6 instead of a JMP DISPLAY. + +INVALID_PAGE: + INT 6 ; Signal and error + JMP INVALID_PAGE ; Do it again in case control comes + ; back here + +; At this point were still OK. BX points to the control block for the channel. +; Let's translate the address we currently have to its real address and send +; it out to the DMA channel. + +CHNCONT: + MOV BYTE PTR [BX+DMAPAGE],AL ; Put the page port value into the + ; control block + CALL XLATE ; Create the real address entries in + ; the control block + MOV DL,BYTE PTR [BX+DMACHN] ; Get the channel number from the c.b. + SHL DX,1 ; Convert it to a port address + MOV AL,BYTE PTR [BX+DMALR] ; Get the LSB of the real address + OUT DX,AL ; "OUT" it to the address port + JMP $+2 ; Wait a bit + MOV AL,BYTE PTR [BX+DMAMR] ; Get the MSB of the real address + OUT DX,AL ; "OUT" it to the address port + JMP $+2 ; Wait a bit + MOV DL,BYTE PTR [BX+DMAPP] ; Get the page port + MOV AL,BYTE PTR [BX+DMAPR] ; Get the real page number + OUT DX,AL ; Do the "OUT" to the DMA page port + JMP OUTEXITDMA ; That's it + +PAGE + +; This is where we come when we want to simply send the "OUT" to the real port. + +DOOUT: + CMP WORD_FLAG,0 ; Is this an "OUT" for a word? + JNE PUTWORDREAL ; Aye. Go put out a word. + + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Nay. It is for a byte. Get the + ; byte from the user's AL register + OUT DX,AL ; And send it out to the port + JMP OUTEXITDMA ; Time to leave + +PUTWORDREAL: + MOV AX,WORD PTR SS:[BP+BP_AX] ; Get the word form the user's AX + OUT DX,AX ; And thrust it out to the port + MOV WORD_FLAG,0 ; Reset the word flag + +OUTEXITDMA: + + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction, or past + ; the port value in the case of I/O + ; with an immediate port value + JMP POPIO ; Return to the V86 task + +PAGE + +; It's not an "OUT" to one of the DMA page ports. + +NOT_DMA_PAGE: + CMP DX,(OUT_TABLE_END-OUT_TABLE)/3 ; Is the port within the range + ; covered by our jump table, i.e., + ; 0 to 20H + JAE NOTCOWBOY ; Nope. Let's go check if it's the IPL + ; port on the AT + MOV AL,DL ; Yes, it's handled by our jump table + MOV AH,3 ; Convert the port number to an index + MUL AH ; into the jump table by multiplying + ; by 3. Jump table entry are 3 bytes. + LEA CX,OUT_TABLE ; Get the offset of the jump table + ADD AX,CX ; And add it on to the index + JMP AX ; Jump to the jump table entry for this + ; port + +NOTCOWBOY: + CMP DX,80H ; Is it the manufacturing (IPL) port for + ; the AT? + JNE DOOUT ; Negative. Then let's just do a plain + ; vanilla "OUT" to the real port. +MANPORT: + MOV AL,0FEH ; It's the IPL port! Send out a FEH to + OUT 064H,AL ; reset the system. + +HALT: HLT ; In case that trick didn't work @P1C + JMP HALT ; we'll just wait here until @P1C + ; somebody hits the big red switch. + +PAGE + +; This is the entry for an "OUT" to port 0CH. An "OUT" to this port will +; reset the controller so that the next out will be to the least significant +; byte of the address register. The way you set the lower 16 bits of the +; address is by sending the two bytes to the same port, first the least +; significant byte (LSB) and then the most significant byte (MSB). The port +; knows that successive bytes to the port mean successively higher bytes of +; the address. We emulate this with our DMABYTE flag. Since the user will +; only be sending two bytes to an address port this flag will be toggled +; between 0 and 1 indicating that the "OUT" was to the LSB if 0 and to the +; MSB if 1. A write to port 0CH tells the controller that the next write +; will be for the LSB. We emulate this by setting our DMABYTE flag to 0. + +RESET_BYTE_PTR: + MOV DMABYTE,0 ; Reset the byte flag + JMP DOOUT ; Send the "OUT" to the real port + +PAGE + +; The following entries handle the "OUT"s to the address ports for channels +; 0 to 3. These are ports 00, 02, 04 and 06 respectively. As mentioned +; above the address written to these ports is done in two steps. First the +; least significant byte (LSB) is written, the the most significant byte (MSB) +; is written. This results in a 16 bit value in the address port. Combine +; this with the byte in the page port and you have a 24 bit DMA address. The +; code below emulates this by putting the byte that is "OUT"ed to the LSB if +; DMA BYTE is 0 and to the MSB if DMABYTE is 1. DMABYTE is toggled between 0 +; and 1 each time there is a write to the port. So the bytes go alternately +; to the LSB and the MSB. + +; "OUT" is to port 00, the address port for channel 0. + +CHN_0: + LEA BX,DMATABLE ; Point BX to the control block for + ; channel 0 + JMP MOVBYTE ; Go get the byte + +; "OUT" is to port 02, the address port for channel 1. + +CHN_1: + LEA BX,DMATABLE+(1*DMAENTRYLEN) ; Point BX to the control block for + ; channel 1 + JMP MOVBYTE ; Go get the byte + +; "OUT" is to port 04, the address port for channel 2. + +CHN_2: + LEA BX,DMATABLE+(2*DMAENTRYLEN) ; Point BX to the control block for + ; channel 2 + JMP MOVBYTE ; Go get the byte + +; "OUT" is to port 06, the address port for channel 3. + +CHN_3: + LEA BX,DMATABLE+(3*DMAENTRYLEN) ; Point BX to the control block for + ; channel 3 + +MOVBYTE: + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the byte from the user's AL + XOR DMABYTE,1 ; Toggle the DMABYTE flag + JZ MOVMSB ; Was the flag set to 1? If so, go + ; put the byte to the MSB + MOV BYTE PTR [BX+DMALSB],AL ; Else it was 0 so put the byte in the + ; LSB for this channel + JMP OUTEXITDMA ; And exit + +MOVMSB: + MOV BYTE PTR [BX+DMAMSB],AL ; Put the byte in the MSB for this + ; channel + JMP OUTEXITDMA ; And exit + +PAGE + +; This is the entry point for an "OUT" to port 18H. It has something to do +; with the advanced DMA channels 4 thru 7. If the function number in the high +; nibble of AL is 2, set the base register, then we'll save the advanced channel +; number given in the low nibble of AL. If the function is not 2 then we will +; inhibit the setting of the base register. + +CHK_FUNCTION: + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value to be "OUT"ed to 18H + SHR AL,4 ; Shift the function number into AL + CMP AL,2 ; Is this the function to set the base + ; register? + JE SAVE_CHN ; Yup. Go save the channel number. + MOV DMABYTE,3 ; Nope. Inhibit setting the base reg. + JMP DOOUT ; Send the "OUT" to the real port + +SAVE_CHN: + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value in AL again + AND AL,07H ; Mask off the function number leaving + ; the channel number + MOV DMA_ADV_CHN,AL ; And save it + JMP RESET_BYTE_PTR ; Go reset the byte flag + +PAGE + +; This is the entry for an "OUT" to port 1AH. + +CHK_BASE_REG: + CMP DMABYTE,3 ; Are we inhibiting setting the base + ; register? + LJAE DOOUT ; Yes. Then just sent the "OUT" to the + ; real port + LEA BX,DMATABLE ; Point BX to the channel control blocks + MOV AL,DMA_ADV_CHN ; Get the current advanced channel + MOV AH,DMAENTRYLEN ; and multiply by the size of a + MUL AH ; control block. Now AX is the index + ; for the current control block + ADD BX,AX ; Add this on to the base and BX points + ; to the control block + SUB AX,AX ; Purge AX + MOV AL,DMABYTE ; Get the byte flag + ADD BX,AX ; And add it on to BX + MOV CL,BYTE PTR SS:[BP+BP_AX] ; Get the out value into CL + +; Now put it in the control block. Notice that BX is the base of the control +; block plus the byte flag. Now we add on the offset for the LSB entry. A +; little pondering of this code will reveal that for DMABYTE = 0 the byte in +; CL goes to the LSB entry, for DMABYTE = 1 it goes to the MSB entry and for +; DMABYTE = 2 it goes to the page entry. + + MOV BYTE PTR [BX+DMALSB],CL ; Save the byte in the control block + INC DMABYTE ; Increment our byte counter + CMP DMABYTE,3 ; Was the page entry written? + LJNE OUTEXITDMA ; Nope. Let's just exit. + + SUB BX,AX ; The page was written. Point BX back + ; to the start of the control block. + CMP CL,10H ; Does the page point to over 1M? + LJAE INVALID_PAGE ; Yes. Better signal an error. + + CALL XLATE ; The page is OK. Translate the virtual + ; address to the real address + MOV AL,BYTE PTR [BX+DMALR] ; Get the LSB of the real address + OUT 1AH,AL ; "OUT" it to the port 1AH + JMP $+2 ; Wait a bit + MOV AL,BYTE PTR [BX+DMAMR] ; Get the MSB of the real address + OUT 1AH,AL ; "OUT" it to the port 1AH + JMP $+2 ; Wait a bit + MOV AL,BYTE PTR [BX+DMAPR] ; Get the real page number + OUT 1AH,AL ; Do the "OUT" to port 1AH + JMP OUTEXITDMA ; That's all + +PAGE + +; XLATE is a procedure to translate the virtual DMA address to the real DMA +; address. It takes the virtual address that was "OUT"ed to the DMA ports +; and follows it through the page tables to get the real address. It puts +; the real address into the current channel control block. + +XLATE PROC + +; Calcuate the page fram offset of the real address, the lower 12 bits. + + MOV AX,WORD PTR [BX+DMALSB] ; Get the virtual LSB and MSB + AND AH,0FH ; Wipe out the top nibble. This leaves + ; us with only the offset into the 4K + ; page frame. This real address will + ; have the same offset into the page + ; frame + MOV WORD PTR [BX+DMALR],AX ; So save this in the real LSB + +; Pick up page table address. + + MOV SI,SGTBLOFF ; Point ST to the first page directory + ; entry + DATAOV ; Get the address of the current page + LODSW ; table + SUB AL,AL ; Clear the access rights byte. This + ; makes it a real offset. + DATAOV ; Point SI to the page table + MOV SI,AX + MOV AX,WORD PTR [BX+DMAMSB] ; Get the MSB and page + SHR AX,4 ; Shift the top four bits off the end + ; of the register these four bits are + ; not used. We are dealing with a 24 + ; bit address where only 20 bits are + ; used. + SHL AX,4-2 ; Shift back 2 bits. This puts zeroes + ; in the high bits while converting + ; the address to a page table index. + ; Page table entries are 4 bytes long, + ; hence, shift left 2 bits. + ADD SI,AX ; Add the page table index on to the + ; offset of the page table. SI now + ; points to the correct page table + ; entry. + ADD SI,1 ; Step over the access rights byte + + MOV AX,HUGE_PTR ; Load DS with a selector that accesses + MOV DS,AX ; all of memory as data + ADDROV ; Load the address of the page frame + LODSW ; into EAX + MOV CX,SYS_PATCH_DS ; Point DS back to our data segment + MOV DS,CX + +; Now AX contains the address of the page frame shifted right 8 bits. Remember +; that we incremented SI to skip the access rights? This gave us the page +; frame offset with out the lower byte. The lSB real address was already set +; above, as well as the low nibble of the real MSB. AX now contains the page +; and MSB of the real address. The low nibble of the MSB was already set so +; we just OR on the high nibble of the MSB. Then we set the real page. Then +; we're all done. + + OR BYTE PTR [BX+DMAMR],AL ; Turn on high 4 bits of the real MSB + MOV BYTE PTR [BX+DMAPR],AH ; Set the real page + + RET + +XLATE ENDP + +DMAIN ENDP + +PROG ENDS + + END diff --git a/v4.0/src/DEV/XMAEM/INDEEMU.ASM b/v4.0/src/DEV/XMAEM/INDEEMU.ASM new file mode 100644 index 0000000..9534ac2 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEEMU.ASM @@ -0,0 +1,653 @@ +PAGE 60,132 +TITLE INDEEMU - 386 XMA EMULATOR - Sensitive Instruction Emulator + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEEMU * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation * +* * +* DESCRIPTIVE NAME: 386 XMA emulator - emulate sensitive instructions * +* * +* STATUS (LEVEL) : VERSION (0) LEVEL (1.0) * +* * +* FUNCTION : When the I/O privilege level (IOPL) is less than 3 then * +* the processor flags an exception and gives control to * +* the emulator whenever the virtual 8086 (V86) task tries * +* to execute a sensitive instruction. The set of sensitive * +* instructions includes: STI, CLI, INT3, INTO, IRET, INT, * +* PUSHF, POPF, LOCK, IN and OUT. This moudle will emulate * +* these intructions for the V86 task. It will also set the * +* IOPL to 3. This will keep the processor from raising * +* further exceptions for these instructions. This in turn * +* improves performance because the emulator will not be * +* given control each time one of these instructions is * +* executed by the V86 task. * +* * +* This module also has a small piece of code to handle * +* exception 7, coprocessor not available. This exception * +* is raised when the EM (EMulation), MP (Monitor Processor),* +* and TS (Task Switch) bits in CR0 are on. When this * +* happens it turns off these bits and retries the instruc- * +* tion that faulted. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : EMULATE * +* * +* LINKAGE : Jumped to by INDEEXC * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : IRET to the V86 task * +* * +* EXIT ERROR : Jump to error routine in INDEEXC * +* * +* EXTERNAL * +* REFERENCES : POPREGS - Entry point in INDEEXC to pop the registers P1C* +* off the stack and IRET to the V86 task. * +* DISPLAY - Entry point in INDEEXC for the error routine * +* that does the NMI to the error handler. * +* INT15 - Entry point to INDEI15, the INT 15 handler. * +* XMAIN - Entry point in INDEXMA to handle IN for a byte * +* at the port address in DX * +* INW - Entry point in INDEXMA to handle IN for a word * +* at the port address in DX * +* INIMMED - Entry point in INDEXMA to handle IN for a byte * +* at the immediate port address given * +* INWIMMED- Entry point in INDEXMA to handle IN for a word * +* at the immediate port address given * +* XMAOUT - Entry point in INDEXMA to handle OUT for a byte * +* at the port address in DX * +* OUTW - Entry point in INDEXMA to handle OUT for a word * +* at the port address in DX * +* XMAOUTIMMED - Entry point in INDEXMA to handle OUT for a * +* byte at the immediate port address given * +* XMAOUTWIMMED - Entry point in INDEXMA to handle OUT for a * +* word at the immediate port address given * +* MANPORT - Entry point in INDEDMA to issue an out to the * +* port that will reIPL the system * +* * +* SUB-ROUTINES : None * +* * +* MACROS : DATAOV - Create a prefix for the following instruction * +* so that it accesses data 32 bits wide * +* ADDROV - Create a prefix for the following instruction * +* so that it uses addresses that are 32 bits wide * +* CMOV - Move to or from a control register * +* * +* CONTROL BLOCKS : INDEDAT.INC * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEEMU) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870523 D : NEW FOR RELEASE 1.1 * +* $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC + + IF1 ; Only include macros on the first pass + INCLUDE INDEOVP.MAC + INCLUDE INDEINS.MAC + ENDIF + .LIST ; Turn on the listing + + PUBLIC INDEEMU + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME DS:NOTHING + ASSUME ES:NOTHING + ASSUME SS:NOTHING + +INDEEMU LABEL NEAR + + ; The following entries are in other modules + + EXTRN XMAIN:NEAR ; Byte IN from port # in DX +IN_INST EQU XMAIN ; @P1C + EXTRN INW:NEAR ; Word IN from port # in DX + EXTRN INIMMED:NEAR ; Byte IN from immediate port # + EXTRN INWIMMED:NEAR ; Word IN from immediate port # + EXTRN XMAOUT:NEAR ; Byte OUT to port # in DX +OUT_INST EQU XMAOUT ; @P1C + EXTRN OUTW:NEAR ; Word OUT to port # in DX + EXTRN XMAOUTIMMED:NEAR ; Byte OUT to immediate port # +OUTIMMED EQU XMAOUTIMMED ; + EXTRN OUTWIMMED:NEAR ; Word OUT to immediate port # + EXTRN DISPLAY:NEAR ; Signal the error handler + EXTRN MANPORT:NEAR ; ReIPL the system + EXTRN INT15:NEAR ; Handle INT 15 + EXTRN POPREGS:NEAR ; Pop the registers and IRET to V86 task @P1C + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The following is a jump table for each of the instructions. There are 256 ;; +;; entries, each three bytes long, one for each possible op-code. The op- ;; +;; code is used as an index into the table. Each entry is a jump instruction ;; +;; instruction telling where to jump for each particular op-code. The table ;; +;; is initialized such that all in- structions jump to the routin for ;; +;; unexpected op-codes. Then the entries for the instructions we want to ;; +;; emulate are set to jump to the appropriate routine. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +TABLE: + .XLIST + REPT 256 ; Initialize the table so that all instructions + JMP UNEXPECTED ; jump to UNEXPECTED + ENDM + .LIST + +; Now set up the entries for the instructions we want to emulate + +TABLE_END: + ORG TABLE+(0FBH*3) ; 0FBH is the op-code for STI. + JMP STI_INST ; @P1C + ORG TABLE+(0FAH*3) ; 0FAH is the op-code for CLI. + JMP CLI_INST ; @P1C + ORG TABLE+(0F0H*3) ; 0F0H is the op-code for LOCK. + JMP LOCK_INST ; @P1C + ORG TABLE+(0EFH*3) ; 0EFH is the op-code for OUT for a word. + JMP OUTW ; + ORG TABLE+(0EEH*3) ; 0EEH is the op-code for OUT for a byte. + JMP OUT_INST ; @P1C + ORG TABLE+(0EDH*3) ; 0EDH is the op-code for IN for a word. + JMP INW + ORG TABLE+(0ECH*3) ; 0ECH is the op-code for IN for a byte. + JMP IN_INST ; @P1C + ORG TABLE+(0E7H*3) ; 0E7H is the op-code for OUT for a word to + JMP OUTWIMMED ; an immediate port value. + ORG TABLE+(0E6H*3) ; 0E6H is the op-code for OUT for a byte to + JMP OUTIMMED ; an immediate port value. + ORG TABLE+(0E5H*3) ; 0E5H is the op-code for IN for a word to + JMP INWIMMED ; an immediate port value. + ORG TABLE+(0E4H*3) ; 0E4H is the op-code for IN for a byte to + JMP INIMMED ; an immediate port value. + ORG TABLE+(0CFH*3) ; 0CFH is the op-code for IRET. + JMP IRET_INST ; @P1C + ORG TABLE+(0CEH*3) ; 0CEH is the op-code for INTO. + JMP INTO_INST ; @P1C + ORG TABLE+(0CDH*3) ; 0CDH is the op-code for INT. + JMP INT_INST ; @P1C + ORG TABLE+(0CCH*3) ; 0CCH is the op-code for INT3. + JMP INT3 ; + ORG TABLE+(09DH*3) ; 09DH is the op-code for POPF. + JMP POPF_INST ; @P1C + ORG TABLE+(09CH*3) ; 09CH is the op-code for PUSHF. + JMP PUSHF_INST ; @P1C + ORG TABLE+(00FH*3) ; 00FH is the op-code for POP CS. + JMP MANPORT ; Expedient until 0F opcode properly emulated + + ORG TABLE_END + +VALUE3 DB 3 + + PUBLIC EMULATE + PUBLIC POPIO + PUBLIC INTCOM + +EMULATE PROC NEAR + + CLD + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Get the op-code that faulted from real memory. Use it as an index into ;; +;; the jump table to go to the appropriate routine. ;; +;; ;; +;; Note: The DATAOV macro creates a prefix that makes the instruction that ;; +;; immediately follows access all data as 32 bits wide. Similarly, ;; +;; the ADDROV macro creates a prefix that makes the instruction that ;; +;; immediately follows use addresses that are 32 bits wide. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + MOV AX,HUGE_PTR ; Load DS with a selector that will + MOV DS,AX ; access all of memory as data + + MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86 + MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP + + DATAOV + MOV SI,SS:[BP+BP_IP] ; Get the V86 IP into our SI. The high + DATAOV ; order word is zeroes. + MOV AX,SS:[BP+BP_CS] ; Get the V86 CS into AX. Again, the + DATAOV ; high order word is zeroes. + SHL AX,4 ; Multiply CS by 16 to convert it to an + DATAOV ; offset. + ADD SI,AX ; Add on IP. Now SI contains the offset + ; from 0 of the instruction that + ; faulted. + ADDROV + LODSB ; Get the op-code into AL + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MUL VALUE3 ; Multiply the op-code by 3 to get an + LEA BX,TABLE ; index into the jump table + ADD AX,BX ; Add on the offset of the base of the + ; table + JMP AX ; Jump to the entry in the table. This + ; entry will then jump us to the + ; routine that handles this op-code. + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Woops! We got an op-code that we did not expect to fault. First let's ;; +;; check if this instruction faulted because the coprocessor was not avail- ;; +;; able. This will be signalled by an exception code of 07. If this is the ;; +;; case then reset the the following bits in CR0: EM (EMulation) says that ;; +;; coprocessor functions are emulated by software when set to 0; MP (monitor ;; +;; Processor), when set to 1 raises an exception 7 when TS (Task Switched) ;; +;; is set to 1 and a WAIT instruction is executed. TS is set every time ;; +;; there is a task switch. ;; +;; ;; +;; If it was not an execption 7 then we'll check the I/O privilege level ;; +;; (IOPL). An IOPL other less than 3 will cause all I/O and some sensitive ;; +;; instructions to fault. We really don't want to be bothered by all these ;; +;; faulting instructions so we'll set the IOPL to 3 which will allow anyone ;; +;; to do I/O and the sensitive instructions. This will improve performance ;; +;; since the V86 task will be interrupted less often. But first we'll check ;; +;; to see if the IOPL is already 3. If so then we got trouble. Most likely ;; +;; it's an invalid op-code. In this case we'll signal the error handler in ;; +;; INDEEXC. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +UNEXPECTED: + CMP SS:WORD PTR [BP+BP_EX],0007H + ; Check if it's an 07 exception -- co- + ; processor not available + JNE TRYIOPL3 ; If no then try setting the IOPL to 3 + + MOV AX,8000H ; Set the paging enabled bit + DATAOV + SHL AX,16 ; It's the one all the way on the left + MOV AX,0001H ; Set protect mode on. Leave all other + ; bits off. + CMOV CR0,EAX ; Reset CR0 + JMP POPREGS ; Return to the V86 task @P1C + +; Try setting the IOPL to 3 + +TRYIOPL3: + MOV BX,AX ; Save the faulty op-code in BX + MOV AX,SS:WORD PTR [BP+BP_FL] ; Get the V86 flags and check if + AND AX,3000H ; IOPL is already set to 3 + CMP AX,3000H + JE WHOOPS ; If we're already at IOPL 3 the some- + ; things fishy. Time to signal an + ; error. + OR SS:WORD PTR [BP+BP_FL],3000H + ; Otherwise set IOPL to 3 and return to + JMP POPREGS ; the V86 task and let it try to @P1C + ; execute the instruction again. + +; We got trouble. + +WHOOPS: + +; Convert jump address back to opcode in al + + MOV AX,BX ; Put the jump table index back into AX + LEA BX,TABLE ; Subtract the offset of the base of the + SUB AX,BX ; jump table + DIV VALUE3 ; Divide AX by 3 to get the opcode back. + JMP DISPLAY ; Go to the error routine in INDEEXC + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the LOCK instruction. This is an instruction we really don't want ;; +;; to emulate so we will set the IOPL to 3 so that further LOCKs won't bother ;; +;; us. If the exception code is for "invalid op-code" then we will just jump ;; +;; to the routine above to set the IOPL to 3. Otherwise we will just step IP ;; +;; past the LOCK instruction thus treating it as a NOP. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +LOCK_INST: ; @P1C + CMP SS:WORD PTR [BP+BP_EX],0006H ; Check if it's an invalid op code + JNE TRYIOPL3 ; Try setting the IOPL to 3 + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction + JMP POPIO ; thus treating it as a NOP + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the STI, enable interupts, instruction. This is pretty simple to ;; +;; do. Just get the V86 task's flags and flip on the enable interrupts bit. ;; +;; And while we're at it we'll set the IOPL to 3. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +STI_INST: ; @P1C + OR WORD PTR SS:[BP+BP_FL],3200H ; Set the enable interrupts bit + ; and set IOPL to 3 + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past STI instruction + JMP POPIO + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the CLI, disable interrupts, instruction. Just as in STI above, ;; +;; all that is needed is to turn of the enable interrups bit. And again, set ;; +;; the IOPL to 3 so that we won't get control again. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +CLI_INST: ; @P1C + AND WORD PTR SS:[BP+BP_FL],3DFFH ; Set interrupts disabled + OR WORD PTR SS:[BP+BP_FL],3000H ; Insure IOPL = 3 for speed + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past instruction + JMP POPIO + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the INT3 instruction. To do this we put a 3 in the exception code ;; +;; and jump to the portion of the code that emulates the INT instruction. ;; +;; That code uses the exception code to get the interrupt vector from real ;; +;; memory and gives control to the V86 task at the interrupt address. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +INT3: + MOV WORD PTR SS:[BP+BP_EX],3 ; Put a 3 in the exception field + ; This will cause the INTCOM + ; section to go to interrupt 3 + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past INT3 inscruction + JMP INTCOM ; Go get the vector from real + ; memory + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the INTO instruction. This is done just like the INT3 above. It ;; +;; puts a 4 in the exception code and jumps to the code in the INT emulator ;; +;; that will get the real address of the interrupt. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +INTO_INST: ; @P1C + MOV WORD PTR SS:[BP+BP_EX],4 ; Put a 4 in the exception field + ; This will cause the INTCOM + ; section to go to interrupt 4 + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past INTO inscruction + JMP INTCOM ; Go get the vector from real + ; memory + +PAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the IRET instruction. Get CS, IP and the flags off of the V86 ;; +;; task's stack and place them in the register values on our stack. When we ;; +;; return control to the V86 task these values will be taken off of our stack ;; +;; and placed in the V86 task's registers. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IRET_INST: ; @P1C + DATAOV ; Get the user's ESP (32 bit SP) + MOV AX,SS:[BP+BP_SP] ; and save it in ESI. + DATAOV + MOV SI,AX + ADD AX,6 ; Add 6 to the user's SP. This + MOV SS:WORD PTR [BP+BP_SP],AX ; skips over the IP, CS and + ; flags on the user's stack. + ; This puts SP where it would be + ; after the IRET. It assumes + ; there are at least six bytes + ; on the stack. + DATAOV + MOV AX,SS:[BP+BP_SS] ; Get the user's SS and multiply + DATAOV ; by 16. This converts the + SHL AX,4 ; segment value to an offset. + DATAOV ; Add this on to the ESP value in + ADD SI,AX ; ESI and now ESI is the offset + ; from 0 of the user's stack. + ADDROV + LODSW ; Get the user's EIP into EAX + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV WORD PTR SS:[BP+BP_IP],AX ; Put IP into the register values + ; on our stack + ADDROV + LODSW ; Get the user's CS into EAX + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV WORD PTR SS:[BP+BP_CS],AX ; Put CS into the register values + ; on our stack + ADDROV + LODSW ; Get the user's flags (32 bits) + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + AND AX,3FFFH ; Clean up the flags + OR AX,3000H ; Set IOPL to 3 + MOV WORD PTR SS:[BP+BP_FL],AX ; Put the flags into the register + ; values on our stack + JMP POPREGS ; Go return to the V86 task @P1C + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the INT instruction. Step the V86 task's CS and IP past the INT ;; +;; instruction. Push the flags, CS and IP in the task's stack. Get the ;; +;; interrupt number and use it to find the appropriate interrupt vector in ;; +;; low memory. Set the task's CS and IP to the interrupt vector and return ;; +;; control to the task. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +INT_INST: ; @P1C + +; Get the interrupt number from the instruction. It is the second byte of the +; instruction. DS:SI was used to get the op-code. Now DS:SI points to the next +; byte of the instruction. All we have to do is get it. + + ADDROV + LODSB ; Get the interrupt number + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AH,0 ; Clear the high byte + MOV WORD PTR SS:[BP+BP_EX],AX ; Save the interrupt number in + ; the exception code field + +; Step IP past the INT instruction. + + ADD WORD PTR SS:[BP+BP_IP],2 ; STEP IP PAST INT INSTRUCTION + +; Check for INT 15. This is handled by INDEI15. + +INTCONT: + CMP AL,15H ; Is it interrupt 15? + JNE INTCOM ; If not, continue + JMP INT15 ; Else go to INDEI15 + +; Now use the interrupt number to get the appropriate interrupt vector from +; low core. + +INTCOM: + MOV AX,HUGE_PTR ; Load ES with the selector that + MOV ES,AX ; accesses all of memory as data + DATAOV + MOV DI,SS:[BP+BP_SP] ; Load EDI with the user's ESP + ; Now ES:EDI points to the user's + ; stack + SUB DI,6 ; Decrement "SP" to make space for + ; the flags, CS snd IP + MOV SS:WORD PTR [BP+BP_SP],DI ; Set the user's new SP + + DATAOV + MOV AX,SS:[BP+BP_SS] ; Get the user's SS and shift it + DATAOV ; left four bits to convert it + SHL AX,4 ; to an offset + DATAOV ; Add it to EDI so that EDI now + ADD DI,AX ; contains the physical offset + ; of the user's stack + +; Now put the flags, CS and IP on the V86 task's stack. They are put on in the +; order IP, CS, flags. This is backwards from the INT push order of flags, CS +; and then IP. This is because we are moving forward through memory (CLD) +; whereas the stack grows backwards through memory as things apushed on to it. + + MOV AX,SS:[BP+BP_IP] + ADDROV + STOSW ; Put IP on the V86 task's stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_CS] + ADDROV + STOSW ; Put CS on the V86 task's stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_FL] ; Get the v86 task's flags + OR AX,3000H ; Set IPOL to 3 while we're here + ADDROV + STOSW ; Put the flags on the v86 task's + ; stack + + ADDROV ; INTEL BUG # A0-119 + NOP ; INTEL BUG # A0-119 + AND AX,3CFFH ; Clean up flags for our IRET + MOV WORD PTR SS:[BP+BP_FL],AX + +; Use the interrupt number to get the CS and IP of the interrupt routine + + MOV SI,SS:[BP+BP_EX] ; Get the interrupt number + SHL SI,2 ; Multiply by 4 since interrupt + ; vectors are 4 bytes long + LODSW ; Get the IP for the vector + MOV WORD PTR SS:[BP+BP_IP],AX ; Put it in the V86 task's IP + LODSW ; Get the CS for the vector + MOV WORD PTR SS:[BP+BP_CS],AX ; Put it in the V86 task's CS + + JMP POPREGS ; Go return to the V86 task @P1C + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the PUSHF instruction. Get the V86 task's flags and put them on ;; +;; its stack. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +PUSHF_INST: ; @P1C + MOV AX,HUGE_PTR ; Load ES with the selector that + MOV ES,AX ; accesses all of memory as data + + DATAOV + MOV DI,SS:[BP+BP_SP] ; Load EDI with the V86 task's SP + SUB DI,2 ; Decrement "SP" by one word to + ; make room for the flags + MOV SS:WORD PTR [BP+BP_SP],DI ; Store the new V86 task's SP + DATAOV + MOV AX,SS:[BP+BP_SS] ; Get the user's SS and shift it + DATAOV ; left four bits to convert it + SHL AX,4 ; to an offset + DATAOV ; Add it to EDI so that EDI now + ADD DI,AX ; contains the physical offset + ; of the user's stack + MOV AX,SS:[BP+BP_FL] ; Get the v86 task's flags + OR AX,3000H ; Set IPOL to 3 so that we won't + ADDROV ; be bothered anymore + STOSW ; Put the flags on the stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past PUSHF instruction + + JMP POPIO ; Go return to the V86 task + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Emulate the POPF instruction. Get the next word off of the V86 task's ;; +;; stack, set IOPL to 3 and put it in the V86 task's flags. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POPF_INST: ; @P1C + MOV AX,HUGE_PTR ; Big segment selector + MOV DS,AX ; Stack seg + DATAOV ; Create 32-bit operand prefix for next instruction + MOV AX,SS:[BP+BP_SP] ; stack ptr + DATAOV ; Create 32-bit operand prefix for next instruction + MOV SI,AX ; SI = stack ptr + ADD AX,2 + MOV SS:WORD PTR [BP+BP_SP],AX ; NEW STACK POINTER + DATAOV ; Create 32-bit operand prefix for next instruction + MOV AX,SS:[BP+BP_SS] ; Convert ss to 20 bit address + DATAOV ; Create 32-bit operand prefix for next instruction + SHL AX,4 + DATAOV ; Create 32-bit operand prefix for next instruction + ADD SI,AX ; Now have 32-bit offset from 0 + ADDROV ; Use 32-bit offset + LODSW ; GET REAL MODE FLAGS + ADDROV ; INTEL BUG # A0-119 + NOP ; INTEL BUG # A0-119 + AND AX,0FFFH ; CLEAN UP FLAGS FOR OUR IRET +; A POPF at level 3 will not change IOPL - WE WANT TO KEEP IT AT IOPL = 3 + OR AX,3000H ; SET IOPL = 3 + MOV WORD PTR SS:[BP+BP_FL],AX + ADD WORD PTR SS:[BP+BP_IP],1 ; STEP IP PAST INSTRUCTION + JMP POPIO ; CHECK FOR SINGLE STEP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The following entry point, POPIO, is the exit routine for situations when ;; +;; a single step condition would be lost by a normal IRET to the V86 task. ;; +;; You see, in real mode the single step interrupt gets control whenever the ;; +;; single step flag is on. However, we just got control and emulated the ;; +;; instruction. If we just return to the V86 task at CS:IP then the step ;; +;; between the instruction we just emulated and the next instruction will be ;; +;; missed by the single step routine. Therefore we check the V86 task's flags;; +;; to see if the single step flag is on. If so, then we give control to the ;; +;; singel step interrupt. Otherwise we just IRET to the V86 task's CS:IP. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +POPIO: + CMP SS:WORD PTR [BP+BP_EX],1 ; First check if the reason we got + ; control was because of a + ; single step + JE POPCONT ; If so, then we don't have to + ; give control to the single + ; step routine 'cause we already + ; did it. + TEST WORD PTR SS:[BP+BP_FL],0100H ; Was the single step flag on? + JZ POPCONT ; If not then just IRET + MOV SS:WORD PTR [BP+BP_EX],1 ; Otherwise put a 1 (single step + JMP INTCOM ; interrupt number) in the + ; exception code and go to + ; INTCOM to give control to the + ; interrupt +POPCONT: + +; Restore the registers. On entry, in INDEEXC, the registers were pushed as: +; DS, all registers, ES. + + POP ES ; Restore ES + DATAOV + POPA ; Restore all the registers (32 bits wide) + POP DS ; Restore DS + ADD SP,(BP_IP-BP_EX); Move SP past the exception ID an error code + ; that were put on our stack when the 386 + ; gave us control for the exception. + ; SS:SP now points to the V86's IP, CS, flags + ; for the IRET + DATAOV ; IP, CS, and flags are saved 32 bits wide + IRET ; Give control back to the V86 task + +EMULATE ENDP + +PROG ENDS + + END + diff --git a/v4.0/src/DEV/XMAEM/INDEEXC.ASM b/v4.0/src/DEV/XMAEM/INDEEXC.ASM new file mode 100644 index 0000000..a8faaa5 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEEXC.ASM @@ -0,0 +1,799 @@ +PAGE 60,132 +TITLE INDEEXC - 386 XMA EMULATOR - System Exception Handler + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEEXC * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator System Exception Handler * +* * +* STATUS (LEVEL) : VERSION (0) LEVEL (1.0) * +* * +* FUNCTION : This module gets control whenever an interrupt 00 - 07, * +* 09 - 0E or 15 occurs. This is because this module's * +* entry point was placed in the IDT entries for these * +* interrupts. It determines what course of action to take * +* on the interrupt/exception. * +* * +* First thing it does is is to check to see who caused the * +* exception. If the exception came from the virtual 8086 * +* (V86) task then it will try to emulate the interrupt if * +* necessary. If the exception came from the emulator it- * +* self then we may have problems. If it was a general * +* protection exception (INT 0D) then it just ignores it and * +* passes control back to the V86 task. If it was a page * +* fault (INT 14) then it assumes that whatever is running in* +* the V86 task came up with a bad address so it terminates * +* the application since it is obviously bad. If it is * +* neither of these two errors then something has gone bad * +* in the emulator. When this happens it signals the error * +* handler to prompt the user to take a dump or reIPL the * +* system. * +* * +* The old error routine used to display a panel with the * +* contents of the registers and the stack. The new error * +* routine just forces the V86 task to run the NMI code. * +* The old routine was left in place for debugging purposes. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : VEXCPT13 * +* * +* LINKAGE : This entry point is placed in the IDT for each interrupt * +* we want to handle. Whenever one of those interrupts is * +* execupted, control comes here. * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : IRET to the virtual 8086 task * +* * +* EXIT ERROR : Force the V86 task to execute an NMI * +* * +* EXTERNAL * +* REFERENCES : EMULATE - Entry point for INDEEMU * +* INT15 - Entry point for INDEI15 * +* * +* SUB-ROUTINES : HEXD - Display the double word in EAX * +* HEXW - Display the word in AX * +* HEXB - Display the byte in AL * +* * +* MACROS : DATAOV - Create a prefix for the following instruction * +* so that it accesses data 32 bits wide * +* ADDROV - Create a prefix for the following instruction * +* so that it uses addresses that are 32 bits wide * +* * +* CONTROL BLOCKS : INDEDAT.INC * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEEXC) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870523 D : NEW FOR RELEASE 1.1 * +* $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; Include system data + + IF1 ; Only include macros on the first pass of + INCLUDE INDEOVP.MAC ; of the assembler + ENDIF + .LIST ; Turn on the listing + +SEX_ATTR EQU 04B00H +STACK_ATTR EQU 00700H +BLANK EQU 00020H +BP_START EQU 0 + + PUBLIC INDEEXC + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME DS:NOTHING + ASSUME ES:NOTHING + ASSUME SS:NOTHING + +INDEEXC LABEL NEAR + + ; External entry points + + EXTRN EMULATE:NEAR ; Entry point to INDEEMU + EXTRN INT15:NEAR ; Entry point to INDEI15 + + ; External variables + + EXTRN CRT_SELECTOR:WORD ; Selector for the display buffer (INDEI15) + EXTRN XMATID:BYTE ; Current bank ID (INDEXMA) + +PAGE + +VEXCPT13 LABEL NEAR + + PUBLIC SEX + PUBLIC POPREGS ; P1C + PUBLIC HEXD + PUBLIC HEXW + PUBLIC HEXB + PUBLIC VEXCPT13 + PUBLIC DISPLAY + +SEX PROC NEAR + + CLD ; All moves go forward + +; Save the registers on the stack. These are the registers of the task that +; got interrupted. + +SAVE_REGS: + PUSH DS ; Save DS + + DATAOV ; Save all the registers (32 bits wide). They are + PUSHA ; pushed in the order: AX, CX, DX, BX, original + ; SP (before the PUSHA), BP, SI, DI. + + PUSH ES ; Save ES + MOV BP,SP ; Point BP to the start of the register save area + +PAGE +; First let's check to see who caused the exception, the V86 task or us. This +; is done by checking the flags of the routine that was interrupted. The VM +; flag is set for every routine that is running in V86 mode. There are really +; only two entities in the system, the emulator and the V86 task. The V86 task +; has the VM bit set when it is running, the emulator does not. So we can read +; this bit to determine who was interrupted. + + MOV AX,SS:WORD PTR [BP+BP_FL2] ; Get hi-order word of the flags + TEST AL,02H ; Check the VM bit + JZ DISPLAY ; Uh oh! It's us. + JMP LONGWAY ; It's the V86 task + +PAGE +; The following entry point, DISPLAY, is know to other modules. They jump here +; when they encounter a severe error and want to call the error handler. + +DISPLAY: +; JMP DODISP ; Just display the registers. + ; Comment out for final product. + +; Check if it was a general protection exception. If so, then we'll just pass +; control back to the V86 task and let it worry about it. + + MOV BP,SP ; Point BP to the saved registers + CMP SS:WORD PTR [BP+BP_EX],0DH ; Was it a general protection + ; exception + JNE DISPCONT ; If not, the continue + JMP POPREGS ; Else just return to the V86 @P1C + ; task + +; Check if it was a page fault. Page faults only occur when the page that is +; addressed is marked not present. When the emulator sets up memory it marks +; all pages as present. And this is true because the emulator does no page +; swapping. It messes with the page tables but it doesn't remove pages from +; memory. Therefore, if the page is not present then whatever is running in +; the V86 task came up with some wierd non-existant address. This guy obviously +; has gone west or doesn't know what he's doing. So we forcr the application +; to be terminated. + +DISPCONT: + CMP SS:WORD PTR [BP+BP_EX],0EH ; Was it a page fault? + JNE CHKVM ; Nope. Continue checking. + JMP PAGE_FAULT ; Yup. Assume application had bad + ; addresses. Therefore, termin- + ; ate the application. + +; Lastly we'll check who had the error, them or us. We need to check again +; because anybody can jump to the DISPLAY label above. If the V86 task had +; the exception then we will just terminate whatever was running in the V86 +; task. If the emulator had the exception, then obviously WE don't know +; what WE'RE doing. Oops! In this case we will force an NMI and bring down +; the whole system. If the emulator isn't healthy then nothing else should +; run either. It may sould selfish, but if the emulator is damaged then the +; rest of the system will die soon anyway. + +CHKVM: + MOV AX,SS:WORD PTR [BP+BP_FL2] ; Check the VM bit in the flags + TEST AL,02H ; to see who was running + JZ ERROR ; It was us. Better Force an NMI. + JMP TERM_APP ; It was them. Terminate whatever + ; is running. + +PAGE +ERROR: + +;----------------------------------------------------------------------------D1A +; We're in big trouble now. Something has gone west and there's no way to D1A +; get back home. At this point we give up and send up a flare. We signal D1A +; the error handler by forcing the V86 task to execute the NMI interrupt. D1A +; We put a marker of 0DEADH at the fixed location 0:4F2. This is in the D1A +; BIOS communication area. The error handler will look here for our marker D1A +; to determine if the NMI came from the emulator. If it finds it, it will D1A +; put up a severe error with our return code and ask the user to take a dump D1A +; or reIPL. The code following this new code is old code to display a debug D1A +; panel with the contents of the registers and stack. It is left here for D1A +; debugging but will not be executed when the new code is in place. D1A +;----------------------------------------------------------------------------D1A + ; D1A + MOV AX,HUGE_PTR ; Load ES with a selector that @D1A + MOV ES,AX ; accesses all of memory as data @D1A + ; D1A + MOV DI,4F2H ; Put our 0DEADH marker at the @D1A + MOV WORD PTR ES:[DI],0DEADH ; fixed location 0:4F2. D1A + MOV WORD PTR SS:[BP+BP_EX],2; Put a 2, the NMI interrupt number,@D1A + ; in the exception field. The D1A + ; code after LONGWAY4 will use D1A + ; this number to get the interrupt D1A + ; vector. D1A + JMP LONGWAY4 ; Go do the NMI @D1A + +PAGE +; The following code will not be executed. It is left as a debugging tool. + +DODISP: +; Blank the display screen + MOV DI,CRT_SELECTOR ; Load ES with the selector for the + MOV ES,DI ; display buffer + XOR DI,DI ; DI points to the start of the buffer + MOV CX,80*15 ; Only clear 15 lines + MOV AX,STACK_ATTR+BLANK ; AH = white on black attribute + ; AL = ASCII for a blank + REP STOSW ; Write 15 rows of while blanks on black + +; Highlite the display area + + XOR DI,DI ; DI points to the start of the buffer + MOV CX,80*6 ; Highlite 6 lines + MOV AX,SEX_ATTR+BLANK ; AH = white on red attribute + ; AL = ASCII for a blank + REP STOSW ; Highlight the 6 lines + +; Display the registers one at at time + + MOV CX,21 ; 18 regs + excpt id + task id + error code + MOV SI,SYS_PATCH_DS ; Load DS with the selector for our data + MOV DS,SI ; area + MOV SI,OFFSET REG_TABLE ; DS:SI now points to the reg table + SUB AX,AX ; Clear AH + MOV AL,XMATID ; Load AL with the current XMA bank ID + MOV WORD PTR SS:[BP+BP_PSP2],AX ; The bank ID gets displayed as + ; the task ID + +; Display one register + +DO_REG: + +; Calculate the offset into the display buffer + + LODSB ; Get the row coordinate + MOV AH,160 ; Multiply by 160 byte to a row + MUL AH ; (80 bytes of character, attribute) + ADD AL,DS:BYTE PTR [SI] ; Add on the number of columns + ADC AH,0 ; Don't forget the carry + ADD AL,DS:BYTE PTR [SI] ; Add the columns again (remember - + ADC AH,0 ; character, attribute) + INC SI ; Point to next entry in the reg table + MOV DI,AX ; Load DI with the offset into the + ; display buffer + +DO_ID: + +; Put the register name on the screen. + + MOV BX,SEX_ATTR ; Load the attribute byte into AH + MOV AH,BH + LODSB ; Get the character to display + CMP AL,0 ; Are we at the end of the string yet? + JZ DID_ID ; Yup. Then go display the register + ; value. + STOSW ; Else put the next character of the + ; register name on the screen + JMP DO_ID ; Go get the next character + +DID_ID: + +; Put the register value on the screen. + + MOV BP,SP ; BP points the start of the register + ; save area + LODSW ; Get the offset of this register's + ; save area + ADD BP,AX ; Add to BP. BP ponts to the register + ; value. + LODSW ; Get the length of this register's + MOV DX,AX ; save area + CMP DX,2 ; If the length is not two words + JNE MORE ; Then go to the one word code + + DATAOV ; Grab all 32 bits of the register + MOV AX,SS:WORD PTR [BP] + ADD BP,4 ; Point BP past the register value + CALL HEXD ; Display the 32 bit value + JMP LOOPREG ; Jump over the one word code + +MORE: + MOV AX,SS:WORD PTR [BP] ; Get the word value into AX + ADD BP,2 ; Step BP past the register value + CALL HEXW ; Display the 16 bit value + +LOOPREG: + LOOP DO_REG ; Go do another register + +PAGE +; +; Let's go put up the stack for everyone to see !!! +; + MOV BP,SP ; Reset BP to point to the beginning + ; of the register save area + +; If the V86 task faulted, display its stack. Else display our stack. + + MOV AX,SS:WORD PTR [BP+BP_FL2] ; Alright, whose fault was it? + TEST AL,02H ; Check the VM flag + JZ NOTVM86 ; Gulp! It's us. + +; It was the V86 task that faulted. Set DS:SI to point to their stack. + + MOV AX,HUGE_PTR ; Load DS with a slector that accesses + MOV DS,AX ; all of memory as data + DATAOV + SUB SI,SI ; Clear all 32 bits of ESI + MOV SI,SS:[BP+BP_SP] ; Load SI wiht the V86 task's SP + DATAOV + SUB AX,AX ; Clear all 32 bits of EAX + MOV AX,SS:[BP+BP_SS] ; Get the V86 task's SS + DATAOV ; Shift it left 4 bits to convert it + SHL AX,4 ; to an offset + DATAOV ; Add it on to SP. Now SI contains + ADD SI,AX ; the offest of the stack from 0 + + MOV BP,0FFFFH ; I don't know what this code does but + DATAOV ; I left it anyway. The following + ; comment is the only clue. + SHL BP,16 ; Make stack seg limit very large + JMP COMSTACK + +; It was us that faulted. Set DS:SI to point to our stack. + +NOTVM86: + MOV AX,SS ; Load DS with our own SS + MOV DS,AX + DATAOV + SUB SI,SI ; Clear all 32 bits of ESI + MOV SI,SP ; Now DS:SI points to our stack + +; DS:SI points to the beginning of a stack. Now display it. + +COMSTACK: + MOV DI,1120 ; Load DI with the offset into the + ; display buffer of where we want + ; to display the stack + MOV CX,70H ; Display 70H words of the stack + MOV BX,STACK_ATTR ; Load BH with the attribute byte + +DISP_STACK: + + ADDROV ; Get a word off of the stack + LODSW + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + CALL HEXW ; Display the word + MOV AX,STACK_ATTR+BLANK ; Put a blank after each word + STOSW ; Put that on the screen + LOOP DISP_STACK ; Do the rest of the stack + +; Wait for the operator to press the system request key to go to +; the monitor, or the escape key to perform the reset operation + +WAIT_HERE: + + IN AL,064H ; Poll the keystroke port + TEST AL,000000001B ; Did the user hit a key? + JZ WAIT_HERE ; Nope. Go check again. + + IN AL,060H ; Get the keystroke + CMP AL,054H ; System request key? + JNE CHK_ESC ; No. Check for Esc key. + + CMP SS:WORD PTR [BP+BP_EX],0EH ; It was system request key. Now + ; check for a page fault. + JE PAGE_FAULT ; If so, go remove the extra return code + ; from the stack and terminate the + ; application running in the V86 task. + JMP POPREGS ; Else just return to the V86 task @P1C + +CHK_ESC: + CMP AL,001H ; Was the Esc key hit? + JNE CHKPRT ; Nope. Go check for Print Screen and + ; Ctrl-Alt-Del. + MOV BP,SP ; Point BP to the register save area + CMP SS:WORD PTR [BP+BP_EX],0EH ; Check for a page fault + JE PAGE_FAULT ; If so, go remove the extra return + ; code from the stack and terminate + ; the application running in the + ; V86 task. + MOV AX,SS:WORD PTR [BP+BP_FL2] ; Else, Esc key hit and no page fault + TEST AL,02H ; Check who faulted, them or us + JZ DO_RESET ; If it's us, then reIPL. + +TERM_APP: + MOV SS:WORD PTR [BP+BP_EX],21H ; If it's them, termintate whatever + MOV SS:WORD PTR [BP+BP_AX],4CFFH ; is running by forcing a DOS + ; termintate. Return code is FF. + JMP DO_MONITOR ; Go pass the interrupt to the V86 + ; task + +PAGE_FAULT: +; +; On a page fault the 80386 processor puts an extra error code on our stack. +; (How handy!) We now need to remove the extra error code so that when we pop +; the registers off our stack at the end we end up with our stack possitioned +; correctly for the IRET. To do this, we move everything on the stack that is +; below the extra error code up four bytes. The error code takes up four bytes. +; + + STD ; Shift into reverse, 'cause stacks + ; grow down + MOV CX,(BP_EC-BP_START)/2 ; Load CX with the number of words + MOV DI,(BP_EC+2-BP_START) ; Point DI to the last word of the + ADD DI,BP ; extra error code + MOV SI,(BP_EC-2-BP_START) ; Point SI to the last word of the + ADD SI,BP ; exception code + MOV AX,SS ; Set up the selectors + MOV ES,AX + MOV DS,AX +STACK_LOOP: + LODSW ; Get a word off the stack + STOSW ; And move it up four bytes + LOOP STACK_LOOP ; Do that trick again + + CLD ; Shift back into forward + ADD BP,4 ; Scoot BP up four bytes to point to + ; pur new register save area + MOV SP,BP ; Adjust SP, too + JMP TERM_APP ; Go kill whatever is running in the V86 + ; task +CHKPRT: + CMP AL,053H ; Was the Del (as in Ctrl-Alt-Del) key + ; pressed? + JE DO_RESET ; If so, then reIPL + + CMP AL,037H ; Was the print screen key pressed? + JNE WAIT_HERE ; Nope. Must be an invalid key. Go get + ; another keystroke. + + MOV BP,SP ; It was a print screen. Reset BP to + ; point to our register save area. + MOV AX,SS:WORD PTR [BP+BP_FL2] ; If is was us that had the problem + TEST AL,02H ; then we don't allow print screen + ; because the system is not healthy + JZ WAIT_HERE ; Go get another key + + MOV SS:WORD PTR [BP+BP_EX],05H ; If it was them then we can do a + JMP DO_MONITOR ; print screen. Force the V86 + ; task to do an INT 5 (Prt Sc). + +; +; Reset the system, i.e. reIPL. Put a 1234 in the BIOS reset flag at 472H. +; This will keep BIOS from running through the whole POST upon reIPL. +; + +DO_RESET: + MOV AX,HUGE_PTR ; Load ES with a selector that accesses all + MOV ES,AX ; of memory as data + DATAOV + SUB DI,DI ; Clear EDI (32 bit DI) + MOV DI,472H ; Load the offset of the BIOS reset flag + MOV AX,1234H + ADDROV + STOSW ; Put 1234 in the BIOS reset flag + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AL,0FEH ; Now OUT a FE to port 64H. This will cause + OUT 064H,AL ; the machine to reIPL. + +HALT: HLT ; Just in case we don't reIPL, this halt @P1C + JMP HALT ; loop will keep the processor from doing @P1C + ; anything else + +DO_MONITOR: + +; If the exception camefrom the V86 task then pass the interrupt to the +; real mode interrupt vector. + + MOV BP,SP ; Reset BP to point to our register save area + ; on the stack + MOV AX,SS:WORD PTR [BP+BP_FL2] ; Check if it was the V86 task that + TEST AL,02H ; faulted + JNZ LONGWAY ; If so, pass the interrupt on + JMP POPREGS ; Otherwise just return @P1C + +PAGE + +; We come here if the check up front said it was the V86 task that faulted. + +LONGWAY: + MOV SS:WORD PTR [BP+BP_SP2],0 ;Purify high-order words of SP, SS + MOV SS:WORD PTR [BP+BP_SS2],0 ; and IP + MOV SS:WORD PTR [BP+BP_IP2],0 + +; Test for interrupt versus exception. + + CMP SS:WORD PTR [BP+BP_EX],13 ; Check if it was a general + ; protection exception + JNE LONGWAY2 ; If not, continue checking + JMP EMULATE ; If so, then go to INDEEMU to + ; emulate the instruction +LONGWAY2: + CMP SS:WORD PTR [BP+BP_EX],6 ; Was it an invalid op-code + ; exception? + JB LONGWAY4 ; If lower, then pass the + ; interrupt back to the V86 task + CMP SS:WORD PTR [BP+BP_EX],7 ; Was it a coprocessor not avail- + ; able exception? + JA LONGWAY3 ; If greater then do more checking + JMP EMULATE ; Emulation needed for interrupts + ; 6 and 7 +LONGWAY3: + CMP SS:WORD PTR [BP+BP_EX],15H ; Check if it was INT 15 + JNE LONGWAY4 ; Nope, pass interrupt back to + ; the V86 task + JMP INT15 ; Emulation needed for INT 15 + +LONGWAY4: + +; Pass the interrupt back to the V86 task. + + MOV AX,HUGE_PTR ; Load ES with a selector that accesses + MOV ES,AX ; all of memory as data + DATAOV + SUB DI,DI ; Clear all 32 bits of EDI + MOV DI,SS:[BP+BP_SP] ; Load DI with the V86 task's SP + SUB DI,6 ; Decrement "SP" to make room for the + ; push of IP, CS and the flags. + ; Note that this assumes there are at + ; least 6 bytes keft on the stack. + MOV SS:WORD PTR [BP+BP_SP],DI ; Put the new SP into the V86 register + ; save area + DATAOV + SUB AX,AX ; Clear all 32 bits of EAX + MOV AX,SS:[BP+BP_SS] ; Load AX with the V86 task's SS + DATAOV ; Shift it left four bits to convert + SHL AX,4 ; it to an offset + DATAOV ; Add it on to SP. Now DI contains + ADD DI,AX ; the offest of the stack from 0 + +; Now put the V86 task's IP, CS and flags on the stack. They are put on in +; reverse order because the stack grows down, but we are going up as we put +; the stuff on the stack. + + MOV AX,SS:[BP+BP_IP] ; Get the V86 task's IP + ADDROV + STOSW ; Put it on his stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_CS] ; Get the V86 task's CS + ADDROV + STOSW ; Put it on his stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_FL] ; Get the V86 task's flags + ADDROV + STOSW ; Put them on his stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + AND AX,3CFFH ; Clean up the flags for our IRET + MOV WORD PTR SS:[BP+BP_FL],AX + + MOV SI,SS:[BP+BP_EX] ; Get the interrupt vector + SHL SI,2 ; Multiply by four because interrupt + ; vectorsare four bytes long + MOV AX,HUGE_PTR ; Load DS with a selector that accesses + MOV DS,AX ; all of memory as data + LODSW ; Get the IP for the interrupt + MOV WORD PTR SS:[BP+BP_IP],AX ; Store it in the V86 task's IP + LODSW ; Get the CS for the interrupt + MOV WORD PTR SS:[BP+BP_CS],AX ; Store it in the V86 task's CS + +PAGE +POPREGS: ; @P1C + +; Pop the saved registers off of our stack and IRET to the V86 task. + + POP ES ; Restore ES + DATAOV ; Restore all the registers + POPA ; (32 bit registers) + POP DS ; Restore DS + ADD SP,(BP_IP-BP_EX) ; Step SP past the error code placed + ; on our stack by the 80386 + DATAOV + IRET ; IRET to the V86 task + +SEX ENDP + +SUBTTL HEXD - Convert DWORD in EAX to ASCII string at ES:DI +PAGE +; +; INPUT: EAX = hex double word to display +; BH = attribute byte +; ES:DI = location in the display buffer where the characters are +; to be placed +; +; OUTPUT: DI is incremented past last character displayed +; Characters are placed on the screen +; + +HEXD PROC NEAR + DATAOV + PUSH AX ; Save EAX on the stack + DATAOV + SHR AX,24 ; Shift the high order byte into AL + CALL HEXB ; Convert the byte in AL to ASCII at ES:DI + DATAOV + POP AX ; Restore the original EAX + PUSH AX ; Save the low word of EAX (i.e. AX) + DATAOV + SHR AX,16 ; Shift the second highest byte into AL + CALL HEXB ; Convert the byte in AL to ASCII at ES:DI + POP AX ; Restore the low word of EAX (i.e. AX) + PUSH AX ; And save it again + XCHG AH,AL ; Move the thrid highest byte into AL + CALL HEXB ; Convert the byte in AL to an ASCII string + POP AX ; Restore AX + CALL HEXB ; And conver the last byte to ASCII at ES:DI + RET + +HEXD ENDP + +SUBTTL HEXW - Convert WORD in AX to ASCII string at ES:DI +PAGE +; +; INPUT: AX = hex word to display +; BH = attribute byte +; ES:DI = location in the display buffer where the characters are +; to be placed +; +; OUTPUT: DI is incremented past last character +; Characters are placed on the screen +; + +HEXW PROC NEAR + + PUSH AX ; Save the value in AX on the stack + XCHG AH,AL ; Move the high byte into AL + CALL HEXB ; Convert the byte in AL to a string at ES:DI + POP AX ; Restore AX + CALL HEXB ; Convert the low byte to ASCII at ES:DI + RET + +HEXW ENDP + +SUBTTL HEXD - Convert BYTE in AL to ASCII string at ES:DI +PAGE +; +; INPUT: AL = hex byte to display +; BH = attribute byte +; ES:DI = location in the display buffer where the characters are +; to be placed +; +; OUTPUT: DI is incremented past last character +; Characters are placed on the screen +; + +HEXB PROC NEAR + + PUSH AX ; Save the value in AX + AND AL,0F0H ; Clear the low nibble of AL + SHR AL,1 ; Shift the high nibble into the low nibble + SHR AL,1 + SHR AL,1 + SHR AL,1 + ADD AL,030H ; Add '0' to convert to ASCII + CMP AL,03AH ; Was this hex digit greater than 9? + JC OK1 ; Nope. It's OK, so go display it. + ADD AL,7 ; Yup. Then convert to 'A' to 'F'. +OK1: MOV AH,BH ; Move the attribute into AH + STOSW ; Put the character & attribute into the display + ; buffer at ES:DI + POP AX ; Restore AX + AND AL,00FH ; Clear the high nibble of AL + ADD AL,030H ; Convert the low nibble to ASCII as before + CMP AL,03AH ; Hex digit greater than 9? + JC OK2 ; Nope. It's OK, so go display it. + ADD AL,7 ; Yup. Then convert to 'A' to 'F'. +OK2: MOV AH,BH + STOSW + RET + +HEXB ENDP + + PAGE + +REG MACRO NAME,ROW,COL,L + DB &ROW ; Display of register &NAME starts in + DB &COL ; row &ROW and column &COL + DB '&NAME:' ; Name to display for register &NAME + DB 0 ; End of string marker + DW BP_&NAME ; Offset of value of register &NAME + ; that we saved on our stack + DW &L ; Number of words in the register + ENDM + +SUBTTL Register table +PAGE +REG_TABLE LABEL NEAR +; +; Declare data used for displaying the registers on the screen. For each +; register there is a structure that contains the row and column of where the +; display of the register starts, the text or register name ended with a 0, the +; offset into the stack where the value in the register was saved, and the +; number of words in the register. +; + +; First, lets fake a register to put the exception message on the screen. + + DB 1 ; Row 1 + DB 10 ; Column 10 + DB 'System Exception - ' ; Text + DB 0 ; End of text + DW BP_EX ; Offset to hex value on the stack + DW 1 ; Number of words of data + +; Now, fake one to put the task id (bank ID) on the screen. + + DB 1 ; Row 1 + DB 50 ; Column 50 + DB 'Task ID - ' ; Text + DB 0 ; End of text + DW BP_PSP2 ; Offset to hex value on the stack + DW 1 ; Number of words of data + +; Now, lets do the registers + + REG CS,3,1,1 + REG IP,3,9,2 + REG SS,3,21,1 + REG SP,3,29,2 + REG DS,3,41,1 + REG SI,3,49,2 + REG ES,3,61,1 + REG DI,3,69,2 + + REG AX,4,1,2 + REG BX,4,13,2 + REG CX,4,25,2 + REG DX,4,37,2 + REG BP,4,49,2 + REG EC,4,61,2 + + REG FL,5,1,2 + REG VMDS,5,18,1 + REG VMES,5,33,1 + REG VMFS,5,48,1 + REG VMGS,5,63,1 + +PROG ENDS + + END + diff --git a/v4.0/src/DEV/XMAEM/INDEGDT.ASM b/v4.0/src/DEV/XMAEM/INDEGDT.ASM new file mode 100644 index 0000000..e945373 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEGDT.ASM @@ -0,0 +1,379 @@ +PAGE 60,132 +TITLE INDEGDT - 386 XMA Emulator - Build Global Descriptor Table + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEGDT * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Build the Global Descriptor Table (GDT) * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : Build the Global Descriptor Table (GDT) for the 80386 * +* XMA emulator * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : GDT_BLD * +* * +* LINKAGE : Called NEAR from INDEINI * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Return to INDEINI * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* SUB-ROUTINES : SREG_2_24BITS - Convert the 16 bit value in AX to a 24 * +* bit value in DH and AX. * +* ADDOFF - Add the 16 bit offset in AX to the 24 bit value * +* in DH and BX. * +* * +* MACROS : DESCR_DEF - Declare a descriptor entry * +* DESCR_INIT - Initialize a descriptor * +* * +* CONTROL BLOCKS : INDEDAT - System data structures * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEGDT) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870530 D : NEW FOR RELEASE 1.1 * +* $P1=P0000293 410 870731 D : LIMIT LINES TO 80 CHARACTERS * +* $P2=P0000312 410 870804 D : CHANGE COMPONENT FROM MISC TO LOAD * +* $P3=P0000410 410 870918 D : RELOCATE DATA AREAS TO MAKE ROOM FOR BIT MAP * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; Include system data structures + INCLUDE INDEACC.INC ; Include access byte definitions + + IF1 ; Only include macros on the first pass + INCLUDE INDEDES.MAC ; Descriptor macros + ENDIF + .LIST ; Turn on the listing + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME SS:NOTHING + ASSUME DS:PROG + ASSUME ES:NOTHING + +INDEGDT LABEL NEAR + + PUBLIC INDEGDT + PUBLIC GDT_BLD + PUBLIC GDT_DATA_START + PUBLIC SREG_2_24BITS + +; The following data define a pre-initialized GDT. They represent +; all structures which will exist when the emulator comes up. +; THESE MUST BE INITIALIZED IN THE ORDER IN WHICH THEY APPEAR IN THE +; GDT_DEF STRUCTURE DEFINITION AS IT APPEARS IN INDEDAT.INC. This data +; area will be copied from the code segment to the final GDT resting +; place during initialization. + +GDT_DATA_START LABEL WORD + +; First entry is unusable + + DESCR_DEF SEG, 0, 0, 0, 0 + +; The descriptor for GDT itself + + DESCR_DEF SEG, GDT_LEN, GDT_LOC, 3, CPL0_DATA_ACCESS +PAGE + +; The system IDT descriptor + + DESCR_DEF SEG, SIDT_LEN, SIDT_LOC, 3, CPL0_DATA_ACCESS + +; The real system data area descriptor (XMA pages) + + DESCR_DEF SEG, 0FFFFH, 0000H, 11H, CPL0_DATA_ACCESS + +; The virtual IDT descriptor (not needed so use for all memory) + + DESCR_DEF SEG, MAX_SEG_LEN, NSEG@_LO, NSEG@_HI, CPL0_DATA_ACCESS + +; The LOADALL descriptor + + DESCR_DEF SEG, 0, 0, 0, 0 +PAGE + +; Compatible monochrome display + + DESCR_DEF SEG, MCRT_SIZE, MCRT@_LO, MCRT@_HI, CPL0_DATA_ACCESS + +; Compatible color display + + DESCR_DEF SEG, CCRT_SIZE, CCRT@_LO, CCRT@_HI, CPL0_DATA_ACCESS + +; Enhanced color display - one entry for each 64K P1C + + DESCR_DEF SEG, ECCRT_SIZE, ECCRT@_LO_LO, ECCRT@_LO_HI, CPL0_DATA_ACCESS + +; Second part of enhanced display P1C + + DESCR_DEF SEG, ECCRT_SIZE, ECCRT@_HI_LO, ECCRT@_HI_HI, CPL0_DATA_ACCESS +PAGE + +; Code segment for ROM code, system IDT + + DESCR_DEF SEG, MAX_SEG_LEN, CSEG@_LO, CSEG@_HI, CPL0_CODE_ACCESS + +; Data segment for ROM code, system IDT + + DESCR_DEF SEG, MAX_SEG_LEN, CSEG@_LO, CSEG@_HI, CPL0_CODE_ACCESS + +; Temporarily, the monitor is installed using the patch +; segment descriptors in the GDT + +MSEG@_LO EQU 00000H +MSEG@_HI EQU 008H + + +; Code segment for patch code, system IDT + + DESCR_DEF SEG, MAX_SEG_LEN, MSEG@_LO, MSEG@_HI, CPL0_CODE_ACCESS + +; Data segment for patch code, system IDT + + DESCR_DEF SEG, MAX_SEG_LEN, MSEG@_LO, MSEG@_HI, CPL0_DATA_ACCESS +PAGE + +; Code segment for ROM code, virtual IDT + + DESCR_DEF SEG, MAX_SEG_LEN, CSEG@_LO, CSEG@_HI, CPL0_CODE_ACCESS + +; Data segment for ROM code, virtual IDT + + DESCR_DEF SEG, MAX_SEG_LEN, CSEG@_LO, CSEG@_HI, CPL0_CODE_ACCESS + +; Code segment for patch code, virtual IDT + + DESCR_DEF SEG, NULL_SEG_LEN, NSEG@_LO, NSEG@_HI, NULL_ACCESS + +; Data segment for patch code, virtual IDT + + DESCR_DEF SEG, NULL_SEG_LEN, NSEG@_LO, NSEG@_HI, NULL_ACCESS +PAGE + +; Temporary descriptors for ES, CS, SS, and DS + + DESCR_DEF SEG, MAX_SEG_LEN, NSEG@_LO, NSEG@_HI, CPL0_DATA_ACCESS + + DESCR_DEF SEG, MAX_SEG_LEN, NSEG@_LO, NSEG@_HI, CPL0_CODE_ACCESS + + DESCR_DEF SEG, MAX_SEG_LEN, NSEG@_LO, NSEG@_HI, CPL0_DATA_ACCESS + + DESCR_DEF SEG, MAX_SEG_LEN, NSEG@_LO, NSEG@_HI, CPL0_DATA_ACCESS +PAGE + +; These DQ's pad out the space between the last MultiPC descriptor and the +; PMVM descriptors. They don't need initialization. + + DQ 9 DUP (0) ; These 9 are for the Monitor descriptors + +; This is for the keyboard owner (not used) + + DQ 0 + +; These 16 are for the virtual timer support + + DQ 16 DUP (0) + +; The following 32 descriptors are for exception condition task gates. + + REPT 32 + DQ 0 + ENDM + +; The following 16 pairs are for the hardware interrupt handling tasks. + + REPT 16 + DQ 0 + DQ 0 + ENDM + +; The following pair is for the DISPATCH task + + DESCR_DEF SEG, TSS_LEN, DISPATCH_LOC, 3, + DESCR_DEF SEG, TSS_LEN, DISPATCH_LOC, 3, CPL0_DATA_ACCESS + + DESCR_DEF SEG, 0, 0, 0,LDT_DESC + +; BASIC's segment + + DESCR_DEF SEG, 07FFFH, 06000H, 0FH, CPL0_DATA_ACCESS + +; BIOS's segment + + DESCR_DEF SEG, 01FFFH, 00000H, 0FH, CPL0_DATA_ACCESS + + DQ 13 DUP (0) ; Reserved guys + +; The following guys are junk ! Used before the first TSS allocated. These +; have to be in the PMVM location in the GDT. + + DESCR_DEF SEG, TSS_LEN, 0C000H, 0, FREE_TSS ; For the TR + + DESCR_DEF SEG, TSS_LEN, 0C000H, 0, CPL0_DATA_ACCESS ; TSS as data + + DESCR_DEF SEG, GDT_LEN, 0D000H, 0, LDT_DESC ; For the LDTR + + DESCR_DEF SEG, GDT_LEN, 0D000H, 0, CPL0_DATA_ACCESS ; LDT as data + +GDT_DATA_END LABEL WORD + +COPY_LEN EQU GDT_DATA_END-GDT_DATA_START + +; +; End of pre-allocated GDT +; + +PAGE + +GDT_BLD PROC NEAR + +; Copy the pre-initialized GDT into its proper location in memory + + CLI ; All string operations go forward + + MOV SI,OFFSET GDT_DATA_START ; DS:SI points to the pre-initialized + ; GDT above + MOV CX,COPY_LEN/2 ; CX = number of words to copy + MOV DI,GDT_LOC ; ES:DI points to the GDT location + REP MOVSW ; Copy GDT into its final resting place + +; Now let's initialize some of the GDT entries + +; Set up the descriptors for our code segment and data segment + + MOV AX,CS ; Get our CS + CALL SREG_2_24BITS ; And convert it to a 24 bit offset + MOV BX,AX ; Get the monitor offset + +ALLSET: + DESCR_INIT SEG, SYS_PATCH_CS, 07FFFH, BX, DH, CPL0_CODE_ACCESS + DESCR_INIT SEG, SYS_PATCH_DS, 0FFFFH, BX, DH, CPL0_DATA_ACCESS + +; Initialize the descriptor for the GDT + + MOV AX,ES ; Get the segment of the GDT + CALL SREG_2_24BITS ; Convert to a 24 bit offset + MOV DL,DH ; Save a copy of the hi byte + MOV BX,GDT_LOC ; Add on the offset of the GDT + CALL ADDOFF + + DESCR_INIT SEG, GDT_PTR, GDT_LEN, BX, DH, CPL0_DATA_ACCESS + +; Initialize the descriptor for the IDT + + MOV BX,SIDT_LOC ; DH,AX already contains the segment + ; converted to 24 bits so all we + CALL ADDOFF ; have to do is add on the offset + + DESCR_INIT SEG, MON_IDT_PTR, SIDT_LEN, BX, DH, CPL0_DATA_ACCESS + +; Initialize the descriptor that accesses all of memory as data + + DESCR_INIT BSEG, HUGE_PTR, 0FFFFH, 0, 0, CPL0_DATA_ACCESS + +; Initialize the descriptors for the V86 task's LDT + + MOV BX,DISPATCH_LOC ; @P3C + CALL ADDOFF + + DESCR_INIT SEG,SCRUBBER.VM_LDTR,00FFFH,BX,DH,LDT_DESC + DESCR_INIT SEG,SCRUBBER.LDT_PTR,00FFFH,BX,DH,CPL0_DATA_ACCESS + +; Initialize the descriptors for the V86 task's TR + + MOV BX,DISPATCH_LOC ; @P3C + CALL ADDOFF + + DESCR_INIT SEG,SCRUBBER.VM_TR,TSS_LEN,BX,DH,FREE_TSS_386 ; @P3C + DESCR_INIT SEG,SCRUBBER.TSS_PTR,TSS_LEN,BX,DH,CPL0_DATA_ACCESS; @P3C + + RET + +GDT_BLD ENDP + +PAGE +; SREG_2_24BITS converts the segment register 16 bit value in the AX register +; into a 24 bit value in DH and AX +; +; Input : AX = segment register value +; +; Output: AX = 24 bit low word +; DH = 24 bit high byte + + +SREG_2_24BITS PROC NEAR + +; Put the high four bits of AH into the low four bits of DH + + MOV DH,AH ; Get the high byte of the segment value + ; into DH + AND DH,0F0H ; Strip off the low nibble + SHR DH,4 ; Shift right four bits + +; Now shift AX left four bits + + AND AH,00FH ; Strip high nibble from AH + ; This keeps the carry flag from being + ; set which could effect later ADDs. + SHL AX,4 ; Shift AX + + RET + +SREG_2_24BITS ENDP + +PAGE +; ADDOFF adds the 16 bit offset in BX to the 24 bit value in DL and AX. The +; result is left in DH and BX. +; +; Input : DL = 24 bit high byte +; AX = 24 bit low word +; BX = offset to be added +; +; Output: DH = 24 bit high byte +; BX = 24 bit low word + +ADDOFF PROC NEAR + + MOV DH,DL ; Restore the high byte that was saved + ; in DL + ADD BX,AX ; Add on the offset + ADC DH,0 ; Add the carry, if any, to the high + ; byte + RET + +ADDOFF ENDP + +PROG ENDS + + END diff --git a/v4.0/src/DEV/XMAEM/INDEI15.ASM b/v4.0/src/DEV/XMAEM/INDEI15.ASM new file mode 100644 index 0000000..047648d --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEI15.ASM @@ -0,0 +1,503 @@ +PAGE 60,132 +TITLE INDEI15 - 386 XMA Emulator - Interrupt 15 handler + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEI15 * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Interrupt 15H handler for the 80386 XMA emulator * +* * +* STATUS (LEVEL) : Version (0) Level (2.0) * +* * +* FUNCTION : This module emulates the MOVEBLOCK functions of interrupt * +* 15H. The MOVEBLOCK functions are specified by an AH value* +* of 87H to 89H. The user can use the MOVEBLOCK functions * +* to copy a block of memory to another block of memory. * +* This includes copying to and from memory above 1M. This * +* is really where this function comes in handy. The user * +* can reserve memory above 1M for use by the MOVEBLOCK * +* functions by specifying the number of K to be reserved as * +* a parameter on the line to load the emulator in the * +* CONFIG.SYS file. * +* * +* DEVICE=INDXMAEM.SYS bbb * +* * +* "bbb" is the number of K to reserve for MOVEBLOCK * +* functions * +* * +* We allocate a buffer for the MOVEBLOCK functions at the * +* top of available memory. Any functions dealing with this * +* buffer area must be handles by us. * +* * +* Function 87H is the actual MOVEBLOCK function. The user * +* passes a 32 bit source address and a 32 bit destination * +* address in a parameter list pointed to by ES:SI. CX * +* contains the number of words to copy. We need to * +* intercept this call and check the source and destination * +* addresses. If either or both of these addresses is above * +* 1M then we need to adjust the addresses so that they * +* access the MOVEBLOCK buffer up at the top of memory. You * +* see, the user thinks the extended memory starts right * +* after the 1M boundary. We want to make it look like the * +* MOVEBLOCK buffer sits right after the 1M boundary. So we * +* monkey with the user's addresses so that they access the * +* MOVEBLOCK buffer instead of real memory after 1M, because * +* that memory is us. * +* * +* Function 88H queries how many K are above the 1M * +* boundary. We can't tell him how much is really there * +* because some of it is us and our XMA pages. So for this * +* function we will just return the size of the MOVEBLOCK * +* buffer. This function was moved to a real mode P3C* +* interrupt handler in module INDE15H. P3C* +* * +* Function 89H is reserved for the MOVEBLOCK functions but * +* is not in use right now. So for this function we just * +* return a bad return code in AH and set the carry flag. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : INT15 * +* * +* LINKAGE : Jumped to from INDEEXC * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Go to POPIO in INDEEMU to IRET to the V86 task * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : EMULATE - Entry point for INDEEMU * +* POPIO - Entry in INDEEMU to check for single step * +* interrupts, pop the registers and IRET to the * +* V86 task * +* POPREGS - Entry point in INDEEXC to pop the registers * +* off the stack and IRET to the V86 task P2C* +* * +* SUB-ROUTINES : None * +* * +* MACROS : DATAOV - Add prefix for the next instruction so that it * +* accesses data as 32 bits wide * +* ADDROV - Add prefix for the next instruction so that it * +* uses addresses that are 32 bits wide * +* * +* CONTROL BLOCKS : INDEDAT.INC * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEI15) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870603 D : NEW FOR RELEASE 1.1 * +* $P1=P0000293 410 870731 D : LIMIT LINES TO 80 CHARACTERS * +* $P2=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES * +* $D1=D0007100 410 870817 D : CHANGE TO EMULATE XMA 2 * +* $P3=P0000xxx 120 880331 D : MOVE FUNCTION 88H HANDLING TO INDE15H * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; Include the system data structures + + IF1 ; Only include the macros on the first pass + INCLUDE INDEOVP.MAC + ENDIF + .LIST ; Turn on the listing + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME DS:PROG + ASSUME ES:NOTHING + ASSUME SS:NOTHING + +INDEI15 LABEL NEAR + + EXTRN EMULATE:NEAR ; Entry point for INDEEMU + EXTRN POPIO:NEAR ; Entry in INDEEMU to check for single + ; step interrupts and return to the + ; V86 task + EXTRN POPREGS:NEAR ; Entry in INDEEXC to return to the P2C + ; V86 task + + PUBLIC INDEI15 + PUBLIC INT15 + PUBLIC TTTABLE + +PAGE + +INT15 PROC NEAR + + CLD ; All string operations go forward + +; We handle the INT 15H functions for MOVEBLOCK interface. These functions +; are specified by an AH value of 87H to 89H. Function 87H is the MOVEBLOCK +; function. Function 88H queries the size of memory above 1M. Function 89H +; is reserved but not supported so we return a return code of 86H. + + CMP SS:BYTE PTR [BP+BP_AX+1],87H ; Is AH asking for function 87H? + JB NOTMINE ; No. It's too low. It's out of our + ; range so we'll pass it on to the + ; real vector. + JE MOVEBLK ; It is 87H! Let's go do the MOVEBLOCK. + + CMP SS:BYTE PTR [BP+BP_AX+1],89H ; Is AH asking for function 89H? + JNE NOTMINE ; No. It's not our function so @P3C + ; so we'll pass it on to the real + ; vector. + ; @P3D + MOV SS:BYTE PTR [BP+BP_AX+1],86H ; It's 89H. Sorry we don't support + ; that function. Put an 86H return + ; code in AH. + OR WORD PTR SS:[BP+BP_FL],1 ; Set the carry flag + JMP POPIO ; And return to the V86 task + +; Hey, it's not MY interrupt. + +NOTMINE: + JMP DOINT ; Go pass the interrupt back to the + ; real INT 15H vector + + +; Function 88H code to query the size of memory above 1M was moved to 3@P3D +; INDE15H. + +PAGE +; The user wants to move a block of memory. Now the source and target of the +; MOVEBLOCK can each be below 1M or above 1M. For addresses above 1M we must +; make adjustments so that the MOVEBLOCK is done to and/or from the MOVEBLOCK +; buffer in high memory. The user passes a parameter list which is pointed +; at by ES:SI. At offset 0EH is a 32 bit pointer to the source block. At +; offset 1AH is a 32 bit pointer to the destination block. CX contains the +; number of words to move. Here we go! + +MOVEBLK: + MOV AX,HUGE_PTR ; Load DS and ES with a selector that + MOV DS,AX ; accesses all of memory as data + MOV ES,AX + +; Get the user's ES:SI and convert it to a 32 bit offset in ESI. + + DATAOV ; Purge ESI + SUB SI,SI + MOV SI,SS:[BP+BP_SI] ; Load SI with the user's SI + + DATAOV ; Purge EBX + SUB BX,BX + MOV BX,10H ; Set EBX to 1M by loading it with 10H + DATAOV ; and shifting it left 16 bits to + SHL BX,16 ; multiply by 64K + + DATAOV ; Sterilize EAX + SUB AX,AX + MOV AX,SS:[BP+BP_VMES] ; Load AX with the user's ES + DATAOV ; Shift it left four bits to convert it + SHL AX,4 ; to an offset + + DATAOV ; Add the ES offset on to SI. Now ESI + ADD SI,AX ; is the offset from 0 of the user's + ; parameter list. + DATAOV ; Add 1AH to SI. Now it points to the + ADD SI,1AH ; 32 bit destination address. + + DATAOV + ADDROV ; Get the 32 bit destination address + LODSW ; into EAX + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + DATAOV ; Clear the top eight bits of any + SHL AX,8 ; residual gunk. Only 24 bit ;P1C + DATAOV ; addresses (16M) are allowed anyway. + SHR AX,8 ; Shift the bits off the left end and + ; shift zeroes back in. + DATAOV ; Move this clean value into EDI + MOV DI,AX ; EDI now has the destination address + + DATAOV ; Check if this address is over 1M. If + CMP DI,BX ; so then it's going to our MOVEBLOCK + ; buffer. + JB OK1 ; It's below 1M? OK. Leave it alone. + +; The destination is above 1M so we have to modify the destination address so +; that it points to our MOVEBLOCK buffer. + + PUSH DS ; Save DS + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data + MOV DS,AX ; segment + + DATAOV ; Clean up EAX + SUB AX,AX + MOV AX,MAXMEM ; Load the total number of K on the box + SUB AX,BUFF_SIZE ; Subtract the MOVEBLOCK buffer size + SUB AX,1024 ; Subtract 1M (/1K) for 0 to 1M. This + ; leaves AX with the number of K from + ; 1M to the MOVEBLOCK buffer. + POP DS ; Restore DS + DATAOV ; Multiply EAX by 1K (shift left 10) to + SHL AX,10 ; convert it to an offset from 1M of + ; the MOVEBLOCK buffer + DATAOV ; Add this to EDI. EDI now points to + ADD DI,AX ; a location within (hopefully) the + ; MOVEBLOCK buffer as if the buffer + ; were located at the 1M boundary. + +; Now let's get the source address + +OK1: + DATAOV ; Subtract 0C from ESI to point ESI to + SUB SI,0CH ; offset 0E in the parameter list + + DATAOV + ADDROV ; Get the 32 bit source address into + LODSW ; EAX + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + DATAOV ; Clear the top eight bits of any + SHL AX,8 ; residual gunk. Only 24 bit address + DATAOV ; (16M) are allowed. Shift the gunky + SHR AX,8 ; bits off the left end and shift + ; zeroes back in. + DATAOV ; Move this clean value into ESI + MOV SI,AX ; ESI now has the source address + + DATAOV ; Check if this address is over 1M. If + CMP SI,BX ; so then it's goin' to our MOVEBLOCK + ; buffer. + JB OK2 ; It's below 1M? OK. Let it be. + +; The source is above 1M so we have to modify the source address so that it +; points to our MOVEBLOCK buffer. + + PUSH DS ; Save DS + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data + MOV DS,AX ; segment + + DATAOV ; Sanitize up EAX + SUB AX,AX + MOV AX,MAXMEM ; Load the total number of K on the box + SUB AX,BUFF_SIZE ; Subtract the MOVEBLOCK buffer size + SUB AX,1024 ; Subtract 1M (/1K) for 0 to 1M. This + ; leaves AX with the number of K from + ; 1M to the MOVEBLOCK buffer. + POP DS ; Restore DS + DATAOV ; Multiply EAX by 1K (shift left 10) to + SHL AX,10 ; convert it to an offset from 1M of + ; the MOVEBLOCK buffer + DATAOV ; Add this to ESI. ESI now points to + ADD SI,AX ; a location within (hopefully) the + ; MOVEBLOCK buffer as if the buffer + ; were located at the 1M boundary. + +; Our pointers are all set. Let's get CX and do the copy for the guy. + +OK2: + MOV CX,SS:[BP+BP_CX] ; Get the user's CX + TEST CL,01H ; Is this an even number? + JZ MOV4 ; If so, we can make the copy faster + ; by moving double words + ADDROV ; Nope. It's odd. We'll just do the + REP MOVSW ; copy with words. + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + JMP MOVCOM ; Skip over the double word copy + +MOV4: + SHR CX,1 ; Divide the count by two since we'll + ; be copying double words + DATAOV ; Do it 32 bits wide + ADDROV ; Use the 32 bit ESI and EDI + REP MOVSW ; Ready? ZOOOOM! + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + +; Now let's set the flags and return code in AH to show that every thing went +; A-OK. + +MOVCOM: + MOV SS:BYTE PTR [BP+BP_AX+1],0 ; Set a zero return code in AH + AND WORD PTR SS:[BP+BP_FL],0FFFEH ; Reset the carry flag + OR WORD PTR SS:[BP+BP_FL],40H ; Set the zero flag + + JMP POPIO ; Return to the V86 task + +PAGE + +; It's not a MOVEBLOCK function so we'll just pass the interrupt on to the real +; interrupt handler. + +DOINT: + MOV AX,HUGE_PTR ; Load ES with a selector that accesses + MOV ES,AX ; all of memory as data + DATAOV ; Load EDI with the user's ESP + MOV DI,SS:[BP+BP_SP] + + SUB DI,6 ; Decrement "SP" by six to make room + ; for our simulated interrupt that + ; will put the flags, CS and IP on + ; the stack. This assumes that there + ; are indeed six bytes left on the + ; stack. + MOV SS:WORD PTR [BP+BP_SP],DI ; Set the user's new SP + + DATAOV ; Get the user's SS into our AX + MOV AX,SS:[BP+BP_SS] + DATAOV ; Shift "SS" left four bits to convert + SHL AX,4 ; it to an offset + DATAOV ; Add this to "SP" in DI to make EDI + ADD DI,AX ; a 32 bit offset from 0 of the user's + ; stack + +; Now put the flags, CS and IP on the V86 task's stack. They are put on in the +; order IP, CS, flags. This is backwards from the INT push order of flags, CS +; and then IP. This is because we are moving forward through memory (CLD) +; whereas the stack grows backwards through memory as things pushed on to it. + + MOV AX,SS:[BP+BP_IP] ; Get the user's IP + ADDROV ; And put it on his stack + STOSW + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_CS] ; Get the user's CS + ADDROV ; And put it on his stack + STOSW + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+BP_FL] ; Get the user's flags + OR AX,3000H ; Set the IOPL to 3 so we get fewer + ; faults + ADDROV ; And put them on his stack + STOSW + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + AND AX,3CFFH ; Clean up the flags for our IRET + MOV WORD PTR SS:[BP+BP_FL],AX ; Put them on our stack + + MOV SI,SS:[BP+BP_EX] ; Get the interrupt number + SHL SI,2 ; Multiply by four 'cause interrupt + ; vectors are four bytes long + MOV AX,HUGE_PTR ; Load DS with a selector that accesses + MOV DS,AX ; all of memory as data + LODSW ; Get the IP of the interrupt vector + ; from the interrupt vector table + MOV WORD PTR SS:[BP+BP_IP],AX ; Put it in the IP saved on our stack + LODSW ; Get the CS of the interrupt vector + ; from the interrupt vector table + MOV WORD PTR SS:[BP+BP_CS],AX ; Put it in the CS saved on our stack + + JMP POPREGS ; Now when we do an IRET we will @P2C + ; actually be giving control to the + ; real INT 15H vector. +INT15 ENDP + +PAGE +; Macros used to define data areas + +; DDL - Define a label and make it public + +DDL MACRO NAME,SIZE + PUBLIC &NAME +&NAME LABEL &SIZE + ENDM + + +; DDW - Define a word and make it public + +DDW MACRO NAME,VALUE + IFNB <&NAME> ;; If a name was given then make it public + PUBLIC &NAME + ENDIF + IFNB <&VALUE> ;; If a value was given then initialize the +&NAME DW &VALUE ;; variable to that value + ELSE ;; Else initialize it to 0 +&NAME DW 0 + ENDIF + ENDM + + +; Now lets define some data. Remember, these are all PUBLIC even though they +; are not listed at the top of the program as being such. It's easy to lose +; these guys. + + DDW REAL_SP,0 ; Our initial SP when we come up in real mode + + DDW REAL_SS,0 ; Our initial SS when we come up in real mode + + DDW REAL_CS,0 ; Our initial CS when we come up in real mode + + DDW PGTBLOFF,0 ; The offset of the normal page tables + + DDW SGTBLOFF,0 ; The offset of the page directory + + DDW NORMPAGE,0 ; The entry for the first page directory entry + DDW ,0 ; which points to the first normal page table. + ; A 32 bit value + DDW XMAPAGE,7 ; Page directory entry that points to the first + DDW ,0011H ; XMA page table at 11000:0. Access and present + ; bits set. It, too, is a 32 bit value. + DDW BUFF_SIZE,0 ; Size of the MOVEBLOCK buffer. Initialized to 0. + + DDW MAXMEM,1000H ; Total amount of K in the box. Initialized to 4M. + + DDW CRT_SELECTOR,C_BWCRT_PTR ; Selector for the display buffer + +; And now, the world famous Translate Table!! YEAAAA!! +; +; The first 160 entries (0 to 640K) are initialized to blocks 0 to '9F'X. D1A +; This is to emulate the XMA 2 device driver which uses these blocks to back D1A +; the memory on the mother board from 0 to 640K which it disabled. It sets D1A +; up the translate table for bank 0 such that it maps the XMA memory from 0 D1A +; to 640K to conventional memory at 0 to 640K. So we emulate that here by D1A +; initializing the firs 160 entries in the translate table. D1A + +TTTABLE: + BLOCK = 0 ; Start with block number 0 @D1A + REPT 20 ; Do 20 times (20 x 8 = 160) @D1A + DW BLOCK,BLOCK+1,BLOCK+2,BLOCK+3,BLOCK+4,BLOCK+5,BLOCK+6,BLOCK+7 + ; Define eight translate table entries @D1A + ; initialized to the block number D1A + BLOCK = BLOCK + 8 ; Increment the block number to the next set @D1A + ENDM ; of eight translate table entries @D1A + + DW 96 + 256*15 DUP(0FFFH) ; The rest of the translate table @D1C + +TTTABLE_END: ; Label to mark the end of the translate table + + +; Define our stack for when we're in protect mode + + DDW MON_STACK_BASE,<500 DUP(0)> + DDL SP_INIT,WORD ; Top of the stack. The initial SP points here. + +PROG ENDS + + END diff --git a/v4.0/src/DEV/XMAEM/INDEIDT.ASM b/v4.0/src/DEV/XMAEM/INDEIDT.ASM new file mode 100644 index 0000000..28e9161 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEIDT.ASM @@ -0,0 +1,512 @@ +PAGE 60,132 +TITLE INDEIDT - 386 XMA Emulator - Build Interrupt Descriptor Table + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEIDT * +* * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Build the Interrupt Descriptor Table (IDT) * +* * +* STATUS (LEVEL) : Version (0) Level (2.0) * +* * +* FUNCTION : Build the Interrupt Descriptor Table for the 80386 XMA * +* emulator. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : SIDT_BLD (not to be confused with SIDTBLD) * +* * +* LINKAGE : Called by INDEINI * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Return to INDEINI * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : VEXCP13 - Entry point for INDEEXC * +* * +* SUB-ROUTINES : BLD_IDT - Put the entries into the IDT * +* * +* MACROS : DATAOV - Create a prefix for the following instruction * +* so that it accesses data 32 bits wide * +* ADDROV - Create a prefix for the following instruction * +* so that it uses addresses that are 32 bits wide * +* * +* CONTROL BLOCKS : INDEDAT * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEIDT) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870530 D : NEW FOR RELEASE 1.1 * +* $P1=P0000312 410 870803 D : CHANGE COMPONENT FROM MISC TO LOAD * +* $P2=P0000xxx 120 880517 D : HANDLE INT 0D FROM V86 TASK, I.E., OPTICAL DISK * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; System data structures and equates + + IF1 ; Only include macros on the first pass + INCLUDE INDEOVP.MAC + ENDIF + .LIST ; Turn on the listing +PAGE + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME SS:NOTHING + ASSUME DS:NOTHING + ASSUME ES:NOTHING + +INDEIDT LABEL NEAR + +; Let these entry points be known to other modules. + + PUBLIC INDEIDT + PUBLIC SIDT_BLD + +; This is the entry point to INDEEXC. + + EXTRN VEXCPT13:NEAR + +PAGE +; Define the stack structure for our fast path. For all the interrupts that we +; don't want to handle we just pass the interrupt back to the real interrupt +; vector. The entry points for these vectors push the interrupt vector offset +; (interrupt number * 4) onto the stack and then call FASTPATH to pass the +; interrupt to the real vector. FASTPATH pushes BP, AX, DI and SI on to the +; stack. The following structure is a map of the stack after these registers +; are pushed. This structure allows us to access info on the stack. + +SPSTACK STRUC + +SP_SI DW 0 ; Saved ESI (32 bit SI) + DW 0 +SP_DI DW 0 ; Saved EDI (32 bit DI) + DW 0 +SP_AX DW 0 ; Saved EAX + DW 0 +SP_BP DW 0 ; Saved BP (only 16 bits) +SP_EX DW 0 ; Interrupt vector offset (interrupt number * 4) + +; The following information is saved by the 80386 + +SP_IP DW 0 ; Interruptee's EIP (32 bit IP) +SP_IP2 DW 0 +SP_CS DW 0 ; Interruptee's CS (16 bit CS and 16 bit junk) +SP_CS2 DW 0 +SP_FL DW 0 ; Interruptee's Eflags (32 bit flags) +SP_FL2 DW 0 +SP_SP DW 0 ; Interruptee's ESP +SP_SP2 DW 0 +SP_SS DW 0 ; Interruptee's SS +SP_SS2 DW 0 +SP_VMES DW 0 ; Interruptee's ES + DW 0 +SP_VMDS DW 0 ; Interruptee's DS + DW 0 +SP_VMFS DW 0 ; Interruptee's FS + DW 0 +SP_VMGS DW 0 ; Interruptee's GS + DW 0 +SP_STK DW 0 ; The rest of the stack + +SPSTACK ENDS + +SP_START EQU 0 ; Offset from BP of the start of the save area + ; BP is set ot point to the start of the save area + +PAGE + +SIDTBLD PROC NEAR + +; Generate the entry points for all (yes, ALL) 256 interrupt vectors. For +; interrupt 0D (general Protection exception) we will check if it was the V86 +; task that faulted. If so, then we will just pass the interrupt back to the +; V86 task. Else, we will go to our exception handler since the interrupt +; happened because of the emulator. +; +; For interrupts 00, 01, 02, 03, 04, 05, 06, 07, 09, 0A, 0B, 0C, 0E and 15 we +; will go to our exception handler. +; +; For all other interrupts we will go to the FASTPATH routine which will pass +; the interrupt back to the V86 interrupt vector. +; +; Note: For interrupts that go to our exception handler we push a 32 bit error +; code and then push the interrupt number. For the FASTPATH interrupts +; we push the interrupt vector offset (interrupt number *4). This results +; in different stack structures depending on how the interrupt is handled. +; So be careful when you're trying to figure out what's on the stack. +; + +; Interrupt 0D + + IRP V,<0D> +VEC&V: + PUSH 0&V&H ; Push the interrupt number (0D) + PUSH BP ; Save BP + DATAOV ; @P2A + PUSH AX ; Save EAX, all 32bits of it. @P2A + DATAOV ; @P2A + PUSH DI ; Save EDI @P2A + DATAOV ; @P2A + PUSH SI ; Save ESI @P2A + MOV BP,SP ; Point BP to the save area @P2A + + ; Now we must check if the INT 0D came from the V86 task P2A + ; or if it was a General Protection exception. In the P2A + ; case of a General Protection exception the 80386 puts P2A + ; an error code on the stack after pushing the EFLAGS, CS P2A + ; and EIP. The error code is 32 bits wide. If the V86 P2A + ; task issues an INT 0D, an error code is NOT placed on P2A + ; the stack. In this case we want to pass the interrupt P2A + ; back to the V86 task instead of going to our exception P2A + ; handler. The way we check for an error code is by P2A + ; checking how much ESP has been decremented since the P2A + ; start of the interrupt. The original ESP is saved in P2A + ; the TSS. Our stack definition above does not include P2A + ; an error code. So if ESP has been decremented more than P2A + ; the size of our structure, we can know that an error P2A + ; code is on the stack and then go to our exception P2A + ; handler. P2A + + MOV AX,SCRUBBER.TSS_PTR ; Load DS with the selector @P2A + MOV DS,AX ; that accesses the TSS as data @P2A + MOV SI,0 ; Base for reading the TSS @P2A + DATAOV ; @P2A + MOV AX,[SI].ETSS_SP0 ; Get the original SP before the @P2A + DATAOV ; interrupt @P2A + SUB AX,SP ; Subtract the current stack @P2A + ; pointer @P2A + CMP AX,SP_STK ; Check for an error code @P2A + ; @P2D + JG SKIP&V ; If there's an error code, go @P2C + ; handle the exception P2C + MOV WORD PTR [BP+SP_EX],0&V&H*4 ; If there is no error @P2A + ; code then multiply the vector P2A + ; number by four for the FASTPATH P2A + ; code. P2A + JMP PASS_ON ; Give the interrupt back to the @P2C + ; V86 task. +SKIP&V: DATAOV ; @P2A + POP SI ; Restore ESI from off our stack @P2A + DATAOV ; @P2A + POP DI ; Restore EDI @P2A + DATAOV ; @P2A + POP AX ; Restore EAX @P2A + POP BP ; Take BP off the stack. This leaves + ; the interrupt number that we pushed + ; above and the error code that was + ; pushed by the 386 on the INT 0D. + JMP VEXCPT13 ; Go to the exception handler. + + ENDM + +PAGE +; For interrupts 00, 01, 02, 03, 04, 05, 06, 07, 09, 0A, 0B, 0C, 0E and 15 +; push a dummy error code of 0 and then the interrupt number. Then go to the +; exception handler. + + IRP V,<00,01,02,03,04,05,06,07,09,0A,0B,0C,0E,15> +VEC&V: + PUSH 0 ; Push a dummy error code of 0 + PUSH 0 ; 32 bits wide +SKIP&V: + PUSH 0&V&H ; Push the interrupt number + JMP VEXCPT13 ; Go to the exception handler + ENDM + +PAGE +; For the rest of the interrupts push the interrupt vector offset (interrupt +; number * 4) and go to the fast path routine. +; +; INT 08H is given the FASTPATH. It's the double fault interrupt so we are +; dead any way. This interrupt is normally used for the timer interrupt. +; +; INT 10H, BIOS video calls, is given the fastest code path by putting it just +; before the FASTPATH routine. + + IRP V,<08,0F,11,12,13,14,16,17,18,19,1A,1B,1C,1D,1E,1F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<20,21,22,23,24,25,26,27,28,29,2A,2B,2C,2D,2E,2F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<30,31,32,33,34,35,36,37,38,39,3A,3B,3C,3D,3E,3F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<40,41,42,43,44,45,46,47,48,49,4A,4B,4C,4D,4E,4F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<50,51,52,53,54,55,56,57,58,59,5A,5B,5C,5D,5E,5F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<60,61,62,63,64,65,66,67,68,69,6A,6B,6C,6D,6E,6F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<70,71,72,73,74,75,76,77,78,79,7A,7B,7C,7D,7E,7F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<80,81,82,83,84,85,86,87,88,89,8A,8B,8C,8D,8E,8F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V,<90,91,92,93,94,95,96,97,98,99,9A,9B,9C,9D,9E,9F> +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + IRP V, +VEC&V: + PUSH 0&V&H*4 ; Push the interrupt vector offset + JMP FASTPATH ; Go to the fastpath routine + ENDM + +VEC10: + PUSH 010H*4 ; Push the interrupt vector offset + +PAGE +FASTPATH: + PUSH BP ; Save BP + DATAOV + PUSH AX ; Save EAX, all 32bits of it. + DATAOV + PUSH DI ; Save EDI + DATAOV + PUSH SI ; Save ESI + MOV BP,SP ; Point BP to the save area + +PASS_ON: ; @P2C + CLD ; All string operations go forward + + MOV AX,HUGE_PTR ; Load DS and ES with a selector that + MOV DS,AX ; accesses all of memory as data + MOV ES,AX + DATAOV + SUB DI,DI ; Clear EDI + MOV DI,SS:[BP+SP_SP] ; Load DI with the interruptee's SP + SUB DI,6 ; Decrement "SP" to simulate the pushing + ; of the flags, CS and IP on an INT. + MOV SS:WORD PTR [BP+SP_SP],DI ; Replace the user's SP + + DATAOV + SUB AX,AX ; Clear EAX + MOV AX,SS:[BP+SP_SS] ; Load AX with the user's SS register + DATAOV ; Shift "SS" left four bits to convert + SHL AX,4 ; it to an offset + DATAOV ; Add on "SP" to get a 32 bit offset + ADD DI,AX ; from 0 of the user's stack. + +; Put the user's IP, CS and flags onto his stack. This is done in reverse +; order because we are moving forward in memory whereas stacks grow backward. + + MOV AX,SS:[BP+SP_IP] ; Get the user's IP + ADDROV + STOSW ; And put it on the stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+SP_CS] ; Get the user's CS + ADDROV + STOSW ; And put it on the stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + MOV AX,SS:[BP+SP_FL] ; Get the user's flags + ADDROV + STOSW ; And put them on the stack + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + AND AX,3CFFH ; Clean up the flags for our IRET by + MOV WORD PTR SS:[BP+SP_FL],AX ; setting IOPL to 3 + +; Replace the interruptee's CS:IP with the CS:IP of the interrupt vector. When +; we IRET back to the V86 task control will go to the interrupt routine. + + MOV SI,SS:[BP+SP_EX] ; Get the interrupt vector offset + LODSW ; Get the IP of the interrupt vector + MOV WORD PTR SS:[BP+SP_IP],AX ; Replace the user's IP + LODSW ; Get the CS of the interrupt vector + MOV WORD PTR SS:[BP+SP_CS],AX ; Replace the user's CS + + DATAOV + POP SI ; Restore ESI from off our stack + DATAOV + POP DI ; Restore EDI + DATAOV + POP AX ; Restore EAX + POP BP ; Restore BP + ADD SP,(SP_IP-SP_EX) ; Step SP past the interrupt vector + ; offset + DATAOV + IRET ; Give control back to the interruptee + +PAGE + +; Build a talbe of the offsets of all the interrupt entry points. This table +; is used as input to the procedure that builds the IDT. + +SIDT_OFFSETS LABEL WORD + + IRP V,<00,01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F> + DW OFFSET VEC&V + ENDM + IRP V,<10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F> + DW OFFSET VEC&V + ENDM + IRP V,<20,21,22,23,24,25,26,27,28,29,2A,2B,2C,2D,2E,2F> + DW OFFSET VEC&V + ENDM + IRP V,<30,31,32,33,34,35,36,37,38,39,3A,3B,3C,3D,3E,3F> + DW OFFSET VEC&V + ENDM + IRP V,<40,41,42,43,44,45,46,47,48,49,4A,4B,4C,4D,4E,4F> + DW OFFSET VEC&V + ENDM + IRP V,<50,51,52,53,54,55,56,57,58,59,5A,5B,5C,5D,5E,5F> + DW OFFSET VEC&V + ENDM + IRP V,<60,61,62,63,64,65,66,67,68,69,6A,6B,6C,6D,6E,6F> + DW OFFSET VEC&V + ENDM + IRP V,<70,71,72,73,74,75,76,77,78,79,7A,7B,7C,7D,7E,7F> + DW OFFSET VEC&V + ENDM + IRP V,<80,81,82,83,84,85,86,87,88,89,8A,8B,8C,8D,8E,8F> + DW OFFSET VEC&V + ENDM + IRP V,<90,91,92,93,94,95,96,97,98,99,9A,9B,9C,9D,9E,9F> + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM + IRP V, + DW OFFSET VEC&V + ENDM +PAGE +SIDT_BLD: + +; Build the system IDT. The system IDT will contain 256 interrupt gates. + + MOV AX,CS ; Set DS:SI to point to the table of + MOV DS,AX ; interrupt entry points + MOV SI,OFFSET SIDT_OFFSETS + + MOV DI,SIDT_LOC ; Set ES:DI to point to the beginning + ; of the IDT + MOV BX,SYS_PATCH_CS ; Load BX with the selector for the + ; segment of the interrupt routines. + ; It's our code segment. + +; DX contains the second highest word of the interrupt descriptor. + + MOV DH,0EEH ; Set DPL to 3 to reduce the number of + ; exceptions + MOV DL,0 ; The word count field is unused + + MOV CX,256 ; 256 interrupt gates + + CALL BLD_IDT ; Go build the IDT + + RET ; Return to INDEINI + +PAGE + +; This loop builds descriptors in the IDT. DS:SI points to a table of 16 bit +; offsets for the interrupt entry points. ES:DI points to the start of the IDT. +; BX contains the segment selector of the interrupt entry points. DX contains +; the DPL of the interrupt gates. + +BLD_IDT: + MOVSW ; Get an interrupt routine entry point + ; and put it in the offset field + MOV AX,BX ; Get the segment selector + STOSW ; and put it in the selector field + MOV AX,DX ; Get the interrupt gate DPL + STOSW ; and put it in the access rights field + MOV AX,0 ; Zero out the reserved portions + STOSW + LOOP BLD_IDT ; Repeat for all interrupt vectors + + RET + +SIDTBLD ENDP + +PROG ENDS + + END diff --git a/v4.0/src/DEV/XMAEM/INDEINI.ASM b/v4.0/src/DEV/XMAEM/INDEINI.ASM new file mode 100644 index 0000000..19c8b79 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEINI.ASM @@ -0,0 +1,1474 @@ +PAGE 60,132 +TITLE INDEINI - 386 XMA EMULATOR - Initialization + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEINI * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA EMULATOR INITIALIZATION * +* * +* STATUS (LEVEL) : VERSION (0) LEVEL (2.0) * +* * +* FUNCTION : Do all the initialization needed for the 386 XMA emulator.* +* * +* The 386 XMA emulator is installed by putting the follow- * +* ing command in the CONFIG.SYS file: * +* * +* DEVICE=\386XMAEM.SYS bbb * +* * +* where "bbb" is the number of K reserved for the MOVEBLOCK * +* function. If EMS is used, this command must appear * +* before the command to load EMS. * +* * +* This module first of all does all the stuff to set up * +* the device driver linkage to DOS. The driver is a * +* character device. The only command it recognizes is P3C* +* "initialize". When it receives the initialize command * +* it does all the set up for the emulator. For information * +* on device drivers see the DOS Technical Reference. * +* * +* Then it checks to see if we're on a model_80 and the * +* emulator has not been previously installed. If this is * +* the case, then it procedes to do the following: * +* Get the MOVEBLOCK buffer size from the parameter list * +* Save the maximum XMA block number in the header * +* Relocate to high memory * +* Initialize the page directory and page tables * +* Call INDEIDT to initialize the IDT * +* Call INDEGDT to initialize the GDT * +* Switch to virtual mode * +* Initialize the TSS for the virtual 8086 task * +* Initialize the XMA page tables * +* Enable paging * +* * +* This module also contains code to handle the Generic D2A* +* IOCTL call which is used to query the highest valid D2A* +* XMA block number. This code is left resident. D2A* +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 STANDARD * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* LINKAGE : Invoked as a DOS device driver * +* * +* INPUT PARMS : The number of 1K blocks reserved for the MOVE BLOCK * +* service can be specified after the DEVICE command in the * +* CONFIG.SYS file. 0K is the default. * +* * +* RETURN PARMS : A return code is returned to DOS in the device header * +* at offset 3. * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Return to DOS after device driver is loaded * +* * +* EXIT ERROR : Return to DOS after putting up error messages * +* * +* EXTERNAL * +* REFERENCES : SIDT_BLD - Entry point for INDEIDT to build the IDT * +* GDT_BLD - Entry point for INDEGDT to build the GDT * +* WELCOME - The welcome message * +* GOODLOAD - Message saying we loaded OK * +* NO_80386 - Error message for not running on a model_80 * +* WAS_INST - Error message for protect mode in use * +* SP_INIT - Initial protect mode SP * +* REAL_CS - Place to save our real mode CS * +* REAL_SS - Place to save our real mode SS * +* REAL_SP - Place to save our real mode SP * +* PGTBLOFF - Offset of the page tables * +* SGTBLOFF - Offest of the page directory * +* NORMPAGE - Normal page directory entry * +* XMAPAGE - Page directory entry for the first XMA page D1A* +* BUFF_SIZE- Size of the MOVEBLOCK buffer * +* MAXMEM - Maximum amount of memory on the box * +* CRT_SELECTOR - Selector for the display buffer * +* * +* SUB-ROUTINES : GATE_A20 - Gate on or off address bit 20 * +* GET_PARMS - Get the MOVEBLOCK buffer size specified on * +* the command in CONFIG.SYS and convert to * +* binary. * +* * +* MACROS : DATAOV - Add prefix for the next instruction so that it * +* accesses data as 32 bits wide * +* ADDROV - Add prefix for the next instruction so that it * +* uses addresses that are 32 bits wide * +* CMOV - Move to and from control registers * +* JUMPFAR - Build an instruction that will jump to the * +* offset and segment specified * +* * +* CONTROL BLOCKS : INDEDAT.INC * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEINI) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870521 D : NEW FOR RELEASE 1.1. CHANGES TO THE ORIGINAL * +* CODE ARE MARKED WITH D0A. * +* $P1=P0000281 410 870730 D : SAVE 32 BIT REGISTERS ON model_80 * +* $P2=P0000312 410 870804 D : CHANGE COMPONENT FROM MISC TO LOAD * +* $P3=P0000335 410 870811 D : HEADER INFORMATION ALL SCREWED UP * +* $D1=D0007100 410 870810 D : CHANGE TO EMULATE XMA 2 * +* CHANGE ID STRING TO "386XMAEMULATOR10" * +* $P4=P0000649 411 880125 D : A20 NOT ENABLED WHEN PASSWORD SET * +* $P5=P0000650 411 880128 D : COPROCESSOR APPLICATIONS FAIL * +* $P6=P0000740 411 880129 D : IDSS CAPTURED DCR 87 CODE. REMOVE IT. * +* $D2=D0008700 120 880206 D : SUPPORT DOS 3.4 IOCTL CALL * +* $P7=P0000xxx 120 880331 D : FIX INT 15. LOAD AS V86 MODE HANDLER. * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC + + IF1 ; Only include the macros in the first pass + INCLUDE INDEOVP.MAC ; of the assembler + INCLUDE INDEINS.MAC + ENDIF + .LIST ; Turn on the listing + + ; Let these variables be known to external procedures + + PUBLIC POST + PUBLIC INDEINI + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME SS:NOTHING + ASSUME DS:PROG + ASSUME ES:NOTHING + +INDEINI LABEL NEAR + + ; These variables are located in INDEI15 + + EXTRN SP_INIT:WORD ; Initial protect mode SP + EXTRN REAL_CS:WORD ; Place to save our real mode CS + EXTRN REAL_SS:WORD ; Place to save our real mode SS + EXTRN REAL_SP:WORD ; Place to save our real mode SP + EXTRN PGTBLOFF:WORD ; Offset of the page tables + EXTRN SGTBLOFF:WORD ; Offest of the page directory + EXTRN NORMPAGE:WORD ; Normal page directory entry. Points to the + ; page table that maps the real 0 to 4M. + EXTRN XMAPAGE:WORD ; Page directory entry for the first XMA + ; page table (page table for bank 0) @D1A + EXTRN BUFF_SIZE:WORD ; Size of the MOVEBLOCK buffer + EXTRN MAXMEM:WORD ; Maximum amount of memory on the box + EXTRN CRT_SELECTOR:WORD ; Selector for the display buffer + + ; These are the messages + + EXTRN WELCOME:BYTE ; The welcome message + EXTRN GOODLOAD:BYTE ; Message saying we loaded OK + EXTRN NO_80386:BYTE ; Error message for not running on a model_80 + EXTRN WAS_INST:BYTE ; Error message for protect mode in use + Extrn Small_Parm:Byte ; Parm value < 64 and > 0 ;an000; dms; + Extrn No_Mem:Byte ; Parm value > memory available ;an000; dms; + + ; These entries are located in external procedures + + EXTRN SIDT_BLD:NEAR ; Build the interrupt descriptor table (IDT) + EXTRN GDT_BLD:NEAR ; Build the global descriptor table (GDT) + +; General equates + +DDSIZE EQU GDT_LOC ; Size of the device driver +HIGH_SEG EQU 0FFF0H ; The segment we relocate to +MEG_SUPPORTED EQU 24 ; Must be a multiple of 4 +XMA_PAGES_SEL EQU RSDA_PTR ; Selector for XMA pages +DISPSTRG EQU 09H ; DOS display string function number D0A +GET_VECT EQU 35H ; DOS get vector function number P7A +SET_VECT EQU 25H ; DOS set vector function number P7A +model_80 EQU 0F8H ; Model byte for the Wangler D0A +XMA640KSTRT EQU 580H ; Start address of XMA block for D0A + ; 640K (16000:0 / 1K) D0A P3C + +; ASCII character equates + +TAB EQU 09H ; ASCII tab +LF EQU 0AH ; ASCII line feed +CR EQU 0DH ; ASCII carriage return + +SUBTTL Structure Definitions +PAGE +;------------------------------------------------------------------------------; +; Request Header (Common portion) ; +;------------------------------------------------------------------------------; + +RH EQU DS:[BX] ; The Request Header structure is based off + ; of DS:[BX] + +RHC STRUC ; Fields common to all request types + DB ? ; Length of Request Header (including data) + DB ? ; Unit code (subunit) +RHC_CMD DB ? ; Command code +RHC_STA DW ? ; Status + DQ ? ; Reserved for DOS +RHC ENDS ; End of common portion + +; Status values for RHC_STA + +STAT_DONE EQU 0100H ; Function complete status (high order byte)@P3C +STAT_CMDERR EQU 8003H ; Invalid command code error +STAT_GEN EQU 800CH ; General error code D0A + +;------------------------------------------------------------------------------; +; Request Header for INIT command ; +;------------------------------------------------------------------------------; + +RH0 STRUC + DB (TYPE RHC) DUP (?) ; Reserve space for the header + +RH0_NUN DB ? ; Number of units + ; Set to 1 if installation succeeds, + ; Set to 0 to cause installation failure +RH0_ENDO DW ? ; Offset of ending address +RH0_ENDS DW ? ; Segment of ending address +RH0_BPBO DW ? ; Offset of BPB array address +RH0_BPBS DW ? ; Segment of BPB array address +RH0_DRIV DB ? ; Drive code (DOS 3 only) +RH0 ENDS + +RH0_BPBA EQU DWORD PTR RH0_BPBO ; Offset & segment of BPB array address. + ; On the INIT command the BPB points to + ; the characters following the "DEVICE=" + ; in the CONFIG.SYS file. + +;---------------------------------------------------------------------------D2A; +; Request Header for Generic IOCTL Request D2A; +;---------------------------------------------------------------------------D2A; + +RH19 STRUC + DB (TYPE RHC) DUP (?) ; Reserve space for the header @D2A + +RH19_MAJF DB ? ; Major function @D2A +RH19_MINF DB ? ; Minor function @D2A +RH19_SI DW ? ; Contents of SI @D2A +RH19_DI DW ? ; Contents of DI @D2A +RH19_RQPK DD ? ; Pointer to Generic IOCTL request packet @D2A +RH19 ENDS + +SUBTTL Device Driver Header +PAGE +POST PROC NEAR + + ; Declare the device driver header + + ORG 0 ; Device header must the very first thing in the + ; device driver + DD -1 ; Becomes pointer to next device header + DW 0C040H ; Character device, does IOCTL @P3C @D2C + DW OFFSET STRATEGY ; Pointer to device "strategy" routine + DW OFFSET IRPT ; Pointer to device "interrupt handler" + DB "386XMAEM" ; Device name @D0C + + ; End of device driver header + +;------------------------------------------------------------------------------; +; Request Header (RH) address, saved here by "strategy" routine ; +;------------------------------------------------------------------------------; + +RH_PTRA LABEL DWORD +RH_PTRO DW ? ; Offset of the request header +RH_PTRS DW ? ; Segment of the request header + ; Character ID "386XMAEMULATOR10" deleted 2@D2D +HI_XMA_BLK DW ? ; The highest XMA block number @D0A +EXT_MEM DW ? ; Number of K of extended memory @P7A + ; D0A +RBX DW ? ; Temporary save area for register BX @P1A +ISmodel_80 DB -1 ; model_80 flag. Set to 1 if on a model_80 @P1A + ; Set to 0 if not on a model_80 @D1C + +SUBTTL Device Strategy +PAGE +;------------------------------------------------------------------------------; +; Device "strategy" entry point ; +; ; +; Retain the Request Header address for use by Interrupt routine ; +;------------------------------------------------------------------------------; + +STRATEGY PROC FAR + + MOV CS:RH_PTRO,BX ; Save the offset of the request header + MOV CS:RH_PTRS,ES ; Save the segment of the request header + RET + +STRATEGY ENDP + +SUBTTL Device Interrupt Intry Point +PAGE + +;------------------------------------------------------------------------------; +; Table of command processing routine entry points ; +;------------------------------------------------------------------------------; +CMD_TABLE LABEL WORD + DW OFFSET INIT_P1 ; 0 - Initialization + DW OFFSET MEDIA_CHECK ; 1 - Media check + DW OFFSET BLD_BPB ; 2 - Build BPB + DW OFFSET INPUT_IOCTL ; 3 - IOCTL input + DW OFFSET INPUT ; 4 - Input + DW OFFSET INPUT_NOWAIT ; 5 - Non destructive input no wait + DW OFFSET INPUT_STATUS ; 6 - Input status + DW OFFSET INPUT_FLUSH ; 7 - Input flush + DW OFFSET OUTPUT ; 8 - Output + DW OFFSET OUTPUT_VERIFY ; 9 - Output with verify + DW OFFSET OUTPUT_STATUS ;10 - Output status + DW OFFSET OUTPUT_FLUSH ;11 - Output flush + DW OFFSET OUTPUT_IOCTL ;12 - IOCTL output + DW OFFSET DEVICE_OPEN ;13 - Device OPEN + DW OFFSET DEVICE_CLOSE ;14 - Device CLOSE + DW OFFSET REMOVABLE_MEDIA ;15 - Removable media + DW OFFSET INVALID_FCN ;16 - Invalid IOCTL function @D2A + DW OFFSET INVALID_FCN ;17 - Invalid IOCTL function @D2A + DW OFFSET INVALID_FCN ;18 - Invalid IOCTL function @D2A + DW OFFSET GENERIC_IOCTL ;19 - Generic IOCTL function @D2A + DW OFFSET INVALID_FCN ;20 - Invalid IOCTL function @D2A + DW OFFSET INVALID_FCN ;21 - Invalid IOCTL function @D2A + DW OFFSET INVALID_FCN ;22 - Invalid IOCTL function @D2A + DW OFFSET GET_LOG_DEVICE ;23 - Get Logical Device @D2A +MAX_CMD EQU ($-CMD_TABLE)/2 ; Highest valid command follows + DW OFFSET SET_LOG_DEVICE ;24 - Set Logical Device @D2A + +;------------------------------------------------------------------------------; +; Device "interrupt" entry point ; +;------------------------------------------------------------------------------; +IRPT PROC FAR ; Device interrupt entry point + +; First we must save all the registers that we use so that when we return to +; DOS the registers are not changed. + + PUSH DS ; Save the segment registers modified + PUSH ES + + CMP CS:ISmodel_80,-1; Did we already check what machine we are @D2A + JNE DID_CHECK ; running on? @D2A + + MOV CS:RBX,BX ; Save BX @P1A + MOV BX,0FFFFH ; Check the model byte at FFFF:000E @D0A @P1M + MOV ES,BX ; to see if we're running on a @D0A @P1M + MOV BX,0EH ; model_80 (PS/2 model 80). @D0A @P1M + CMP BYTE PTR ES:[BX],model_80 ; @P1A + MOV BX,CS:RBX ; Restore BX @P1A @D2M + JNE NO_model_80 + + MOV CS:ISmodel_80,1 ; Set the flag saying we're on a @P1A @D2M + JMP DID_CHECK ; model_80 @D2A + +NO_model_80: + MOV CS:ISmodel_80,0 ; Set the flag saying we're not on a @D2M + ; model_80 + +DID_CHECK: ; D2A + CMP ISmodel_80,1 ; Are we on a model_80? @D2A + JE PUSH32 ; If so, go save the 32 bit registers @P1A + +; Push 16 bit registers onto the stack. + + PUSH AX + PUSH BX + PUSH CX + PUSH DX + PUSH DI + PUSH SI + PUSH BP + + JMP PUSHED ; @P1A + +; Push 32 bit registers onto the stack P1A + ; @D2D +PUSH32: DATAOV ; Save all the 32 bit registers. The @P1A + PUSHA ; model_80's BIOS uses 32 bit registers, @P1A + ; so we must not trash the high order P1A + ; words as well as the low order words. P1A + +PUSHED: CLD ; All moves go forward + + LDS BX,CS:RH_PTRA ; Get the request header address passed to the + ; "strategy" routine into DS:BX + + MOV AL,RH.RHC_CMD ; Get the command code from the Request Header + CBW ; Zero AH (if AL > 7FH, next compare will + ; catch that error) + + CMP AL,MAX_CMD ; If command code is too high + JA IRPT_CMD_HIGH ; Then jump to error routine + + ADD AX,AX ; Double command code for table offset since + ; table entries are words + MOV DI,AX ; Put into index register for CALL + ; @D2D +; +; At entry to command processing routine: +; +; DS:BX = Request Header address +; CS = 386XMAEM code segment address +; AX = 0 +; + CALL CS:CMD_TABLE[DI] ; Call routine to handle the command + JMP IRPT_CMD_EXIT + + +IRPT_CMD_HIGH: ; JMPed to if RHC_CMD > MAX_CMD + MOV AX,STAT_CMDERR ; Return "Invalid Command" error code + OR AX,STAT_DONE ; Add "done" bit to status word @P3C + MOV RH.RHC_STA,AX ; Store status into request header + +IRPT_CMD_EXIT: ; Return from command routine + +; Restore the registers before returning to DOS. + + CMP CS:ISmodel_80,1 ; Are we on a model_80? @P1A + JE POP32 ; Yes. Then pop the 32 bit registers. @P1A + +; Pop 16 bit registers off of the stack. + + POP BP + POP SI + POP DI + POP DX + POP CX + POP BX + POP AX + + JMP POPPED ; @P1A + +; Pop 32 bit registers off of the stack. P1A + ; P1A +POP32: DATAOV ; @P1A + POPA ; @P1A + +; Pop the segment registers off of the stack. + +POPPED: POP ES + POP DS + + RET +IRPT ENDP + +SUBTTL Command Routines +PAGE + +MEDIA_CHECK: ; +BLD_BPB: ; +INPUT_IOCTL: ; IOCTL input +INPUT: ; +INPUT_NOWAIT: ; Non-destructive input no wait +INPUT_STATUS: ; Input status +INPUT_FLUSH: ; Input flush +OUTPUT: ; +OUTPUT_VERIFY: ; +OUTPUT_IOCTL: ; IOCTL output +OUTPUT_STATUS: ; Output status +OUTPUT_FLUSH: ; Output flush +DEVICE_OPEN: ; +DEVICE_CLOSE: ; +REMOVABLE_MEDIA: ; +INVALID_FCN: ; @D2A +GET_LOG_DEVICE: ; @D2A +SET_LOG_DEVICE: ; @D2A + + MOV AX,STAT_GEN ; Return general error code @D2A + OR AX,STAT_DONE ; Add "done" bit to status word @D2A + MOV RH.RHC_STA,AX ; Store status into request header @D2A + + RET + +SUBTTL Generic IOCTL Service Routine +PAGE + +;------------------------------------------------------------------------------; +; This routine handles the Generic IOCTL call. The Emulator provides an D2A; +; interface through the Generic IOCTL call to query the number of XMA D2A; +; blocks available. When the function code in the parameter list is 0 the D2A; +; Emulator will return the number of XMA blocks available. There are no D2A; +; other functions uspported at this time. D2A; +;------------------------------------------------------------------------------; + +GIP EQU ES:[DI] ; @D2A + +GEN_IOCTL_PARM STRUC ; @D2A + +GIOPLEN DW ? ; Length of the parameter list @D2A +GIOPFCN DW ? ; Function code @D2A +GIOPBLK DW ? ; Number of XMA blocks available @D2A + +GEN_IOCTL_PARM ENDS ; @D2A + +MAXFCN EQU 0 ; Highest function number allowed @D2A + +; Return codes D2A + +GOODRET EQU 0 ; Good return code @D2A +BADLEN EQU 1 ; Bad parameter list length @D2A +BADFCN EQU 2 ; Bad function number @D2A + +GENERIC_IOCTL: ; D2A + + LES DI,RH.RH19_RQPK ; Point ES:DI to the Generic IOCTL @D2A + ; request packet D2A + +; First check to make sure the parameter list is long enough to return the D2A +; number of XMA blocks. D2A + + CMP GIP.GIOPLEN,4 ; Do we have at least four bytes? @D2A + JAE GIP_CHKFCN ; Yup. Go to check function number. @D2A + + MOV GIP.GIOPFCN,BADLEN ; Nope. Sorry. Return the error @D2A + JMP GIP_DONE ; code and go to the end. @D2A + +; Check if the function number in the parameter list is a valid function. D2A + +GIP_CHKFCN: ; D2A + CMP GIP.GIOPFCN,MAXFCN ; Is the function code less than or @D2A + ; equal to the maximum supported? D2A + JLE GIP_CONT ; Yes. Good boy. You get to continue. @D2A + + MOV GIP.GIOPFCN,BADFCN ; No. Shamey, shamey. Set the bad @D2A + JMP GIP_DONE ; return code and go to the end. @D2A + +; Parameter list is OK. Let's return the number of XMA blocks. D2A + +GIP_CONT: ; D2A + MOV GIP.GIOPFCN,GOODRET ; Set a good return code @D2A + MOV AX,CS:HI_XMA_BLK ; Get the number of XMA blox @D2A + MOV GIP.GIOPBLK,AX ; Put it in the paramter list @D2A + + +GIP_DONE: ; D2A + MOV RH.RHC_STA,STAT_DONE ; Store done status and good return @D2A + ; code into request header D2A + RET ; @D2A + +INT15F88 PROC FAR ; P7A + +; The following is the interrupt chaining structure specified in the PC AT P7A +; Technical Reference. P7A + + JMP SHORT BEGIN ; P7A + +CHAINOFF DW 0 ; Offest of the previous INT 15 vect. @P7A +CHAINSEG DW 0 ; Segment of the previous INT 15 vect.@P7A +SIGNATURE DW 424BH ; Says we're doing chaining @P7A +FLAGS DB 0 ; @P7A +FIRST EQU 80H ; @P7A + JMP SHORT RESET ; @P7A +RESERVED DB 7 DUP (0) ; @P7A + +; OK. Let's see if the user asked for function 88H, query memory size. P7A +; The function number is specified in the AL register. If it's P7A +; function88h, then put the memory size in AX and IRET to the caller. P7A +; Else, just pass the interrupt on to the guy who was installed in the INT P7A +; 15 vector before us. P7A + +BEGIN: CMP AH,88H ; Is it function 88H? @P7A + JNE NOT_MINE ; It's not ours to handle @P7A + + MOV AX,CS:EXT_MEM ; Put the number of K into AX @P7A + IRET ; Return to the caller @P7A + +NOT_MINE: JMP CS:DWORD PTR CHAINOFF + ; Pass the interrupt on to the @P7A + ; previously installed vector @P7A + +RESET: RET ; This, too, is part of the interrupt@P7A + ; chaining structure. We will just@P7A + ; return on a call to reset. Note @P7A + ; that this is a far return. @P7A +INT15F88 ENDP ; @P7A + +LEAVE_RES LABEL NEAR ; Leave code up to here resident.@D0A @D2M + +SUBTTL Initialize Routine +PAGE +INIT_P1: + + PUSH ES ; Save our code segment at the @D0A + MOV DI,0 ; fixed location 0:4F4. This @P3C + MOV ES,DI ; gives us a quick way to find CS @P3C + MOV DI,4F4H ; and also enables us to break on @P3C + MOV ES:[DI],CS ; a write to 0:4F4 which helps us @P3C + POP ES ; find the code on the ICE386. @D0A + MOV AH,DISPSTRG ; Display the welcome message. @D0A + MOV DX,OFFSET WELCOME ; @D0A + PUSH DS ; Save DS since DS:BX points to the @D0A + PUSH CS ; request header @D0A + POP DS ; DS:DX points to the message @D0A + INT 21H ; Display the message @D0A + POP DS ; Restore DS @D0A + ; @P3D + MOV RH.RH0_ENDS,CS ; Set the segment and offset of the end + MOV RH.RH0_ENDO,OFFSET LEAVE_RES ; of code to leave resident + MOV RH.RHC_STA,STAT_DONE ; Store "done" status into request + ; header + CMP CS:ISmodel_80,1 ; Check if we're on a model_80 @D0A @P1C + JE CONT ; If so, then continue @D0A + ; D0A + MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A + MOV AX,STAT_GEN ; Return general error code @D0A + OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C + MOV RH.RHC_STA,AX ; Store status into request header @D0A + ; D0A + MOV AH,DISPSTRG ; Display the message that we are @D0A + MOV DX,OFFSET NO_80386 ; not on a model_80 @D0A + PUSH CS ; @D0A + POP DS ; @D0A + INT 21H ; @D0A + ; D0A + RET ; D0A + ; D0A +CONT: ; @D0M + SMSW AX ; Get machine status register + TEST AL,1 ; Check if the processor is already in + ; protect mode. If so, then someone + ; else (maybe us) has already taken + ; over protect mode. + JZ STILLOK ; If not, keep going @D0C + + MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A + MOV AX,STAT_GEN ; Return general error code @D0A + OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C + MOV RH.RHC_STA,AX ; Store status into request header @D0A + ; D0A + MOV AH,DISPSTRG ; Display the message that protect @D0A + MOV DX,OFFSET WAS_INST ; mode is taken. @D0A + PUSH CS ; DS:DX points to the message @D0A + POP DS ; @D0A + INT 21H ; @D0A + + RET ; +STILLOK: ; D0A + PUSH 0DEADH ; Push stack delimiter + ; Don't have to set character ID @D2D + CALL GET_PARMS ; Get the MOVEBLOCK buffer size if + jnc StillOK1 + MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A + MOV AX,STAT_GEN ; Return general error code @D0A + OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C + MOV RH.RHC_STA,AX ; Store status into request header @D0A + ; D0A + pop ax + ret ; exit program + +StillOK1: + ; one was specified + CLI ; Disable interrupts + + PUSH CS ; Now we can point DS to our own @P3A + POP DS ; code segment @P3A + ; 2@D2D + MOV AX,CS + MOV REAL_CS,AX ; Save real CS for when we @P3C + MOV AX,SS ; switch to protect mode + MOV REAL_SS,AX ; Save real SS @P3C + MOV AX,SP + MOV REAL_SP,AX ; Save real SP @P3C + +;------------------------------------------------------------------------------; +; Enable address line A20 ; +;------------------------------------------------------------------------------; + + ; 3@P4D + CALL GATE_A20 + + INT 11H ; Get the BIOS equipment flags + AND AL,30H ; Bits 5 and 6 on means it's a mono + CMP AL,30H + JE LEAVEBW + MOV CRT_SELECTOR,C_CCRT_PTR ; Set the CRT selector to color display +LEAVEBW: + MOV AH,88H ; Get number of 1k blocks above 1M + INT 15H + ADD AX,1024 ; Add 640K for the memory below 640K @P7C + MOV MAXMEM,AX ; Save for later + +; Get the maximum XMA block number and save it in the header up front D0A +; All memory is treated as XMA memory. D1C + ; D0A + SUB AX,BUFF_SIZE ; Can't use the MOVEBLOCK buffer for @D0A + ; XMA memory. AX = number of K D0A + ; available. D0A + SUB AX,(1024-640) +128 ; Subtract 128k for the Emulator code ;an000; dms; + SHR AX,2 ; Divide by four to get the number of @D0A + ; 4K blocks D0A + DEC AX ; Subtract 1. This converts the @P3A + ; number of blocks available to the P3A + ; highest block number available. P3A + ; Block numbers are zero based. P3A + MOV HI_XMA_BLK,AX ; Save it in the header D0A + +;------------------------------------------------------------------------------; +; Now lets relocate ourselves to high memory. ; +;------------------------------------------------------------------------------; + + MOV AX,HIGH_SEG ; Set ES to the highest segment value + MOV ES,AX + MOV DI,0 ; ES:DI points to the place to relocate to + MOV AX,CS + MOV DS,AX + MOV SI,0 ; DS:SI points to our code to be moved + MOV CX,DDSIZE/2 ; Length of code / 2 since moving words + CLD + REP MOVSW ; Copy myself to high memory + + JUMPFAR NEXT,HIGH_SEG ; Jump to my relocated code +NEXT: + + MOV AX,HIGH_SEG ; Set DS to be the same as CS + MOV DS,AX +;------------------------------------------------------------------------------; +; The machine is still in real mode. Zero out GDT and IDT ram. ; +;------------------------------------------------------------------------------; + + MOV DI,GDT_LOC ; DI points to GDT location + + MOV CX,(GDT_LEN+SIDT_LEN)/2 ; Set GDT and IDT to zero + MOV AX,0 ; Store zeroes for now + REP STOSW + +;------------------------------------------------------------------------------; +; Use good-old real-mode selectors to set up the page tables. The ; +; page directory is a 4K block that is placed just before the ; +; beginning of the GDT and on a 4K boundary. Note that the DATAOV ; +; macro creates a prefix for the following instruction so that its ; +; data references are 32 bits wide. ; +;------------------------------------------------------------------------------; + + DATAOV + SUB AX,AX ; Clear EAX (32 bit AX reg.) + MOV AX,HIGH_SEG ; Get the current code segment + DATAOV + SUB BX,BX ; Clear EBX (32 bit BX reg.) + MOV BX,GDT_LOC/16 ; Load the offset of the GDT, converted + ; to paragraphs + DATAOV ; Add it on to the current code segment + ADD AX,BX ; to get the segment address of the GDT. + ; This will be over 1M, so use 32 bits. + AND AX,0FF00H ; Round down to nice 4k boundary + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,4096/16 ; Load with the size of the page directory + ; converted to paragraphs + DATAOV ; Subtract the number of paragraphs needed + SUB AX,BX ; for the page directory + DATAOV + SHL AX,4 ; Convert from paragraphs to bytes + CMOV CR3,EAX ; Load the address of the page directory + ; into CR3 + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,HIGH_SEG ; Load our current code segment + DATAOV + SHL BX,4 ; Convert from paragraphs to bytes + DATAOV + SUB AX,BX ; Subtract from the address of the page + ; directory to get the offset of the + ; directory in our code segment + MOV SGTBLOFF,AX ; Save for later + +; Now let's clear the page directory + + MOV CX,2048 ; Length is 4K/2 since storing words + DATAOV + MOV DI,AX ; ES:EDI points to beginning of directory + MOV AX,0 + REP STOSW ; Clear the page directory! + +;------------------------------------------------------------------------------; +; Initialize the first directory entries to our page tables ; +;------------------------------------------------------------------------------; + + CMOV EAX,CR3 ; Get back CR3 - the address of the page dir. + DATAOV + MOV DI,SGTBLOFF ; Point ES:EDI to first entry in directory + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,MEG_SUPPORTED/4*4096 ; Load the size of the page tables. + ; Each page table maps 4M of memory, so divide + ; the number of Meg supported by 4 to get the + ; number of page tables. Each page table is + ; 4K in size, so multiply by 4K. + DATAOV + SUB AX,BX ; Subtract the size needed for the page tables + ; from the address of the page directory to + ; get the address of the first page table. + ADD AX,7 ; Set the present bit and access rights. + ; This converts the address to a valid entry + ; for the page directory. + DATAOV + MOV NORMPAGE,AX ; Save for later + MOV CX,MEG_SUPPORTED/4 ; Load the number of page tables into CX + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,1000H ; Set up 4k increment +; +; Now we load the page directory. EAX contains the address of the first +; page table, EBX contains 4K, CX contains the number of page tables, and +; ES:EDI (32 bit DI reg.) points to the first page directory entry. Now what +; we do is stuff EAX into the 32bits pointed to by EDI. EDI is then auto- +; incremented by four bytes, because of the 32 bit stuff, and points to the +; next page directory entry. (Page directory and page table entries are four +; bytes long.) Then we add the 4K in EBX to the address in EAX making EAX +; the address of the next page table. This is done for the number of page +; table entries in CX. Pretty slick, huh? +; +LPT: + DATAOV ; Stuff the page table address into the + STOSW ; page directory + DATAOV ; Add 4K to the page table address in EAX + ADD AX,BX ; so that it contains the address of the + ; next page table + LOOP LPT ; Do it again + +; Now calcuate the offset from our code segment of the page tables + + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,HIGH_SEG ; Load our current code segment + DATAOV + SHL BX,4 ; Convert paragraphs to bytes + DATAOV ; Load EAX with the address of the first + MOV AX,NORMPAGE ; page table + DATAOV + SUB AX,BX ; Convert EAX to an offset + AND AL,0F8H ; AND off the access rights + MOV PGTBLOFF,AX ; Save for later + +;------------------------------------------------------------------------------; +; Initialize the page tables ; +;------------------------------------------------------------------------------; + + MOV DI,PGTBLOFF ; ES:DI points to the first page table + DATAOV + SUB AX,AX ; Zero EAX + ADD AX,7 ; Set the present and access rights + MOV CX,MEG_SUPPORTED/4*1024 ; Load CX with the number of page table + ; entries to initialize. As mentioned + ; above, the number of page tables = + ; number of Meg / 4. There are 1K + ; entries per table so multiply by 1K + DATAOV + SUB BX,BX ; Clear EBX + MOV BX,1000H ; Set up 4k increment +; +; As with the page directory, we use a tight loop to initialize the page tables. +; EAX contains the address of the first page frame, which is 0000, plus the +; access rights. EBX contains a 4K increment. ES:DI points to the first entry +; in the first page table. CX contains the number of page table entries to +; initialize. The stuff and increment works the same as for the page directory +; with an added touch. Note that this does all the page tables in one fell +; swoop. When we finish stuffing the last address into the first page table +; the next place we stuff is into the first entry in the second page table. +; Since our page tables are back to back we can just zoom up the page tables +; incrementing by 4K as we go and thus initialize all the page tables in one +; fell swoop. +; +BPT: + DATAOV ; Stuff the page frame address into the + STOSW ; page table + DATAOV + ADD AX,BX ; Next 4k page frame + LOOP BPT + +;------------------------------------------------------------------------------; +; Now set up the first 64K over 1M to point to point to the first 64K ; +; in low memory to simulate the segment wrap over 1M. ; +; For now will set it up to point to itself and try to get DOS to load ; +; the device driver up there. Will find out if anyone tries to alter ; +; it because it will be marked for system use only. ; +;------------------------------------------------------------------------------; + + MOV DI,1024 ; 1M offset into page table + ADD DI,PGTBLOFF ; Page table offset + MOV AX,10H ; Set EAX to contain 1M address by loading + DATAOV ; it with 10H and shifting it 16 bits to + SHL AX,16 ; get 00100000. (Same as 10000:0) + ADD AX,5 ; Present, system use, read only + MOV CX,16 ; 16 entries = 64k +BPT2: + DATAOV + STOSW ; Stuff the address in the page table + DATAOV + ADD AX,BX ; Next 4k page frame + LOOP BPT2 + +PAGE +;------------------------------------------------------------------------------; +; Build the Global Descriptor Table and load the GDT register. ; +;------------------------------------------------------------------------------; + CALL GDT_BLD + + MOV DI,GDT_PTR ; Get the offset of the GDT descriptor + ADD DI,GDT_LOC ; located in the GDT + MOV BP,DI ; Transfer the offset to BP + LGDT ES:FWORD PTR[BP] ; Put the descriptor for the GDT into + ; the GDT register + +PAGE +;------------------------------------------------------------------------------; +; Build and initialize the system Interrupt Descriptor Table, ; +; then load the IDT register. ; +;------------------------------------------------------------------------------; + CALL SIDT_BLD + + MOV DI,MON_IDT_PTR ; Get the offset of the IDT descriptor + ADD DI,GDT_LOC ; located in the GDT + MOV BP,DI ; Transfer the offset to BP + + LIDT ES:FWORD PTR[BP] ; Put the descriptor for the IDT into + ; the IDT register + +PAGE +;------------------------------------------------------------------------------; +; At this point we prepare to switch to virtual mode. The first ; +; instruction after the LMSW that causes the switch must be a ; +; jump far to set a protected mode segment selector into CS. ; +;------------------------------------------------------------------------------; + + MOV AX,VIRTUAL_ENABLE ; Machine status word needed to + LMSW AX ; switch to virtual mode + + JUMPFAR DONE,SYS_PATCH_CS ; Must purge pre-fetch queue + ; and set selector into CS +DONE: +PAGE +;------------------------------------------------------------------------------; +; Initialize all the segment registers ; +;------------------------------------------------------------------------------; + + MOV AX,SYS_PATCH_DS ; Load DS, ES, and SS with the selector + MOV DS,AX ; for our data area. This is the same + MOV ES,AX ; as our code area but has read/write + ; access. + MOV SS,AX + MOV SP,OFFSET SP_INIT + + PUSH 0002H ; Clean up our flags. Turn off all bits + POPF ; except the one that is always on. + +;------------------------------------------------------------------------------; +; Load the LDTR to avoid faults ; +;------------------------------------------------------------------------------; + + MOV AX,SCRUBBER.TSS_PTR ; Load DS with the data descriptor for + MOV DS,AX ; the virtual machine's TSS + MOV AX,SCRUBBER.VM_LDTR ; Get the LDTR for virtual machine + MOV DS:VM_LDT,AX ; Set LDTR in TSS + LLDT AX ; Set the LDTR. Temporary for now. + +; Have to always have space allocated for the dispatch task TSS + + MOV AX,SCRUBBER.VM_TR ; Low mem gets clobbered without this @P5C + LTR AX ; Set current Task Register + ; This TSS is located right after the IDT + +PAGE +;------------------------------------------------------------------------------; +; Now we initialize the TSS (Task State Segment) for the one and only ; +; virtual 8086 task. This task encompasses everything that runs in real ; +; mode. First we clear the TSS and its I/O bit map. Then we initialize ; +; the bit map for all the I/O ports we want to trap. Then we set up the ; +; registers for the V86 task. These registers are given the same values ; +; as we got on entry. IP is set to point to TEST_EXIT. ; +;------------------------------------------------------------------------------; + + MOV AX,SCRUBBER.TSS_PTR ; Load ES and DS with the descriptor + MOV DS,AX ; for the VM's TSS with read/write + MOV ES,AX ; access rights + CLD + MOV DI,0 ; Point ES:DI to the beginning of the TSS + MOV AX,0 ; Clear AX + MOV BX,0 ; Clear BX + MOV CX,TSS_386_LEN ; Load CX with the length of the TSS + REP STOSB ; Clear the TSS + MOV CX,TSS_BM_LEN ; Load CX with the length of the I/O bit + ; map. The bit map immediately follows + ; the TSS and is in the TSS segment. + REP STOSB ; Clear the bit map + MOV AL,0FFH ; Intel requires this byte + STOSB + +; +; Now set up the bit map. Turn on bits for I/O ports that we want to trap. +; + + MOV DI,0+TSS_386_LEN ; Set bits 0,2,4,6 to 1 - DMA ports + MOV AL,055H + STOSB + MOV DI,1+TSS_386_LEN ; Set C to 1 - DMA port + MOV AL,010H + STOSB + MOV DI,3+TSS_386_LEN ; Set 18,1A to 1 - DMA ports + MOV AL,005H + STOSB + MOV DI,16+TSS_386_LEN ; Set 80-8f to 1s - DMA page ports + MOV AL,0FFH ; + manufacturing port for ctl-alt-del + STOSB + STOSB + MOV DI,0680H/8+TSS_386_LEN ; Set Roundup manuf. port to 1 + MOV AL,001H + STOSB + MOV DI,31A0H/8+TSS_386_LEN ; Set 31a0-31a7 to 1s (XMA) + MOV AL,0FFH + STOSB + + MOV WORD PTR [BX].ETSS_BM_OFFSET,TSS_386_LEN + ; Put the bit map offset in the TSS + MOV WORD PTR [BX].ETSS_SP0,OFFSET SP_INIT + ; Put our SP as the SP for privilege + ; level 0 + MOV WORD PTR [BX].ETSS_SS0,SYS_PATCH_DS + ; Put our SS as the SS for privilege + ; level 0 + +; Next we set up the segment registers + + MOV WORD PTR [BX].ETSS_GS,SEG PROG ; GS - our code segment + MOV WORD PTR [BX].ETSS_FS,SEG PROG ; FS - our code segment + MOV WORD PTR [BX].ETSS_DS,SEG PROG ; DS - our code segment + MOV WORD PTR [BX].ETSS_ES,SEG PROG ; ES - our code segment + +; Next the SS,SP + + MOV AX,CS:REAL_SS ; Set the real mode SS as the SS for the task + MOV WORD PTR [BX].ETSS_SS,AX + MOV AX,CS:REAL_SP ; Set the real mode SP as the SP for the task + MOV WORD PTR [BX].ETSS_SP,AX + +; The flags register + + MOV WORD PTR [BX].ETSS_FL2,2 ; Set the VM flag. Task is a V86 task. + MOV WORD PTR [BX].ETSS_FL,0202H ; Set interrupts enabled + +; Set up CS and IP + + MOV AX,CS:REAL_CS ; Set the real mode CS as the CS for the task + MOV WORD PTR [BX].ETSS_CS,AX ; This is the CS we got when we loaded + ; in low memory, before relocating + MOV AX,OFFSET PROG:TEST_EXIT ; Set IP to the label TEST_EXIT below. + MOV WORD PTR [BX].ETSS_IP,AX + +; The LDTR + + MOV WORD PTR [BX].ETSS_LDT,SCRUBBER.VM_LDTR + +; And finally, CR3, the page directory base register + + CMOV EAX,CR3 ; Get CR3 + DATAOV + MOV WORD PTR [BX].ETSS_CR3,AX ; Save it in the TSS + +PAGE +;------------------------------------------------------------------------------; +; Now initialize our wonderful XMA page tables. Each table maps 4M. ; +; There is one table for each XMA bank since 4M is enough to map the ; +; 1M address space. All the XMA tables are initialized to point to ; +; the real memory at 0 to 4M. This is done by just copying the page ; +; table entry for 0 to 4M that was initialized above. ; +;------------------------------------------------------------------------------; + + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data + MOV DS,AX + MOV SI,PGTBLOFF ; DS:SI point to the real page table for 0-4M + MOV AX,XMA_PAGES_SEL ; Load ES with the selector for the XMA pages + MOV ES,AX + SUB DI,DI ; ES:DI point to the first XMA page table + MOV CX,2048 ; Copy 4K / 2 since we're copying words + REP MOVSW ; Copy the first XMA page table +; +; Now ES:DI points to the second XMA page table. Set DS:SI to point to the +; first XMA page table as the source for the copy. Now we can put a count +; of 15 page tables in CX. After each page is copied it is used as the source +; for the next page. This method lets us zip up the page tables initializing +; them all to be the same as the original page table for 0 - 4M. +; + MOV AX,XMA_PAGES_SEL ; Load DS with the selector for the XMA page + MOV DS,AX ; tables + SUB SI,SI ; DS:SI points to the first XMA page table + MOV CX,2048*15 ; Copy 15 more page tables + REP MOVSW ; Copy to the other 15 XMA ID'S page tables + +; D1A +; Set the first page directory entry to point to the page table for bank 0. D1A +; This is another way of saying, "Let's make bank 0 the active bank." We D1A +; are now emulating the XMA 2 card along with its initialization device D1A +; driver, INDXMAA.SYS. When the device driver exits, it leaves the XMA 2 D1A +; card enabled and set to bank 0. Therefore, we must do the same. D1A +; D1A + ; D1A + MOV AX,SYS_PATCH_DS ; Load DS and ES with our data segment @D1A + MOV DS,AX ; selector @D1A + MOV ES,AX ; @D1A + MOV DI,SGTBLOFF ; Point ES:DI to the first page @D1A + ; directory entry D1A + DATAOV ; Load AX with the page directory entry @D1A + MOV AX,XMAPAGE ; for the first XMA page table @D1A + DATAOV ; Stuff the address of the page table @D1A + STOSW ; for bank 0 into the page directory @D1A + +PAGE +;------------------------------------------------------------------------------; +; And now, the moment you've all been waiting for -- TURN ON THE PAGING ; +; MECHANISM!!! ; +;------------------------------------------------------------------------------; + + CMOV EAX,CR0 ; Get CR0 @P5A + MOV BX,8000H ; Set up BX to OR on the Paging Enable bit @P5C + DATAOV + SHL BX,16 ; It's the one all the way on the left @P5C + DATAOV ; @P5A + OR AX,BX ; Set the paging enabled bit @P5A + OR AL,02H ; Set co-processor bit on @P5A + AND AL,0F7H ; Turn off Task Switch bit @P5C + CMOV CR0,EAX ; Here we go... + +; Make sure high order bits of ESP are zero - a1 errata + + MOV AX,SP ; Save SP in AX 'cause it changes when we do... + PUSH 0 ; this PUSH. Push 0 for high 16 bits of ESP + PUSH AX ; Push low 16 bits of SP + DATAOV + POP SP ; Pop 32 bit ESP! + +PAGE +;------------------------------------------------------------------------------; +; Now we give control back to the V86 task by setting up the stack P5C; +; for an IRET back to the V86 task. This requires putting the V86 P5C; +; task's segment registers, SS and ESP, and the EFLAGS, CS and IP on P5C; +; the stack. The 80386 puts all these values on the stack when it P5C; +; interrupts out of V86 mode, so it expects them there on an IRET P5C; +; back to V86 mode. But really we are giving control back to ; +; ourself. The CS:IP on the stack point to the label TEST_EXIT ; +; below, but it is in the copy of the emulator that was originally ; +; loaded, not the copy that was relocated to high memory and is now ; +; running in protect mode. This clever trick will result in the ; +; original copy of the emulator returning to DOS which will continue ; +; to load the rest of the system. The system will come up completely ; +; unaware that it is running in a small universe of a V86 task which ; +; is being monitored by the XMA emulator. ; +;------------------------------------------------------------------------------; + + + MOV AX,SCRUBBER.TSS_PTR ; Load DS with the descriptor for the @P5A + MOV DS,AX ; VM's TSS with read/write access @P5A + MOV BX,0 ; VM's TSS with read/write access @P5A +; P5A +; Set up our stack for an IRET to the V86 task. This is an inter-level P5A +; IRET to a V86 task so we need the V86 task's SS, ESP, ES, DS, FS and GS P5A +; as well as his EFLAGS, EIP and CS. P5A +; P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_GS ; Put V86 task's GS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_FS ; Put V86 task's FS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_DS ; Put V86 task's DS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_ES ; Put V86 task's ES on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_SS ; Put V86 task's SS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_SP ; Put V86 task's ESP on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_FL ; Put V86 task's EFLAGS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_CS ; Put V86 task's CS on the stack @P5A + DATAOV ; @P5A + PUSH WORD PTR [BX].ETSS_IP ; Put V86 task's EIP on the stack @P5A + DATAOV ; @P5A + IRET ; @P5A + ; @P5D + +TEST_EXIT: ; We are now running in V86 mode + POP AX ; Pop the stack until our DEAD delimiter is + CMP AX,0DEADH ; found + JNE TEST_EXIT + +; Replace the interrupt 15 vector with our handler (INT15F88). P7A + + MOV AH,GET_VECT ; Get the current vector at interrupt 15H @P7A + MOV AL,15H ; @P7A + INT 21H ; @P7A + + MOV CS:CHAINSEG,ES ; Save it in the chaining header in @P7A + MOV CS:CHAINOFF,BX ; INT15F88 @P7A + + MOV AH,SET_VECT ; Set the entry point of INT15F88 as the @P7A + MOV AL,15H ; new interrupt 15 vector @P7A + PUSH CS ; @P7A + POP DS ; @P7A + MOV DX,OFFSET INT15F88 ; @P7A + INT 21H ; @P7A + +; Copy the number of K for extended memory from BUFF_SIZE to EXT_MEM. This P7A +; is needed because BUFF_SIZE does not stay resident, EXT_MEM does. P7A + + MOV AX,BUFF_SIZE ; @P7A + MOV EXT_MEM,AX ; @P7A + +; Issue the message that says we installed successfully + + MOV AH,DISPSTRG ; Set AH to DOS display string function @D0A + MOV DX,OFFSET GOODLOAD ; @D0A + PUSH CS ; @D0A + POP DS ; DS:DX points to the message @D0A + INT 21H ; Display the message @D0A + + RET ; Return to IRPT which called INIT_P1 + +SUBTTL Gate A20 +PAGE +;------------------------------------------------------------------------------; +; GATE_A20 ; +; This routine controls a signal which gates address bit 20. ; +; Bit 2 of port 92H controls the enabling of A20. If bit 2 is on, P4C; +; then A20 is enabled. Conversely, if bit 2 is off, A20 is disabled. P4C; +; ; +;------------------------------------------------------------------------------; + +; Equates for the Gate A20 enable + +ENABLE_A20 EQU 02H ; Bit 2 of port 92H turns on A20 @P4C + +GATE_A20 PROC + + IN AL,92H ; Get the current value of port 92 @P4A + OR AL,ENABLE_A20 ; Turn on the bit to enable A20 @P4A + OUT 92H,AL ; Send it back out to port 92 @P4A + RET + ; 15@P4D +GATE_A20 ENDP + +SUBTTL GET_PARMS parameter line scan +PAGE +;------------------------------------------------------------------------------; +; GET_PARMS ; +; This procedure converts the numeric parameter following the DEVICE statement ; +; in the CONFIG.SYS file to a binary number and saves it in BUFF_SIZE. The ; +; number is rounded up to the nearest 16K boundary. ; +; ; +; Register usage: ; +; DS:SI indexes parameter string ; +; AL contains character from parameter string ; +; CX value from GET_NUMBER ; +; ; +;------------------------------------------------------------------------------; + + ASSUME DS:NOTHING ; DS:BX point to Request Header + +GET_PARMS PROC + + PUSH DS ; Save DS + push bx ; save bx ;an000; dms; + + LDS SI,RH.RH0_BPBA ; DS:SI point to all text after "DEVICE=" + ; in CONFIG.SYS + XOR AL,AL ; Start with a null character in AL. + +;------------------------------------------------------------------------------; +; Skip until first delimiter is found. There may be digits in the path string.; +; ; +; DS:SI points to \pathstring\386XMAEM.SYS nn nn nn ; +; The character following 386XMAEM.SYS may have been changed to a null (00H). ; +; All letters have been changed to uppercase. ; +;------------------------------------------------------------------------------; + +GET_PARMS_A: + CALL GET_PCHAR ; Get a character from the parameter string + JZ Get_Parms_Null ; The zero flag is set if the end of the line + ; is found. If so, then exit. + +; Check for various delimeters + + OR AL,AL ; Null + JZ GET_PARMS_B + CMP AL,' ' ; Blank + JE GET_PARMS_B + CMP AL,',' ; Comma + JE GET_PARMS_B + CMP AL,';' ; Semi-colon + JE GET_PARMS_B + CMP AL,'+' ; Plus sign + JE GET_PARMS_B + CMP AL,'=' ; Equals + JE GET_PARMS_B + CMP AL,TAB ; Tab + JNE GET_PARMS_A ; Skip until delimiter or CR is found + +GET_PARMS_B: ; Now pointing to first delimiter + CALL SKIP_TO_DIGIT ; Skip to first digit + JZ Get_Parms_C ; Found EOL, no digits remain + + CALL GET_NUMBER ; Extract the digits and convert to binary + jmp Get_Parms_Found ; Parm found + +Get_Parms_Null: + + xor cx,cx ; set cx to 0 ;an000; dms; + +Get_Parms_Found: + + mov bx,cx ; put cx value in bx ;an000; dms; + + cmp cx,0 ; 0 pages requested? ;an000; dms; + jne Get_Parm_Max ; allocate maximum number ;an000; dms; + MOV CS:BUFF_SIZE,0; Store buffer size + jmp Get_Parms_C + +Get_Parm_Max: + + cmp bx,64 ; >= 64 pages requested? ;an000; dms; + jnb Get_Parms_64_Pg ; yes - continue ;an000; dms; + mov dx,offset Small_Parm ; Parm < 64 and > 0 ;an000; dms; + mov ah,Dispstrg ; Display the welcome message. ;an000; dms; + push ds ; Save DS ;an000; dms; + push cs ; ;an000; dms; + pop ds ; DS:DX points to the message ;an000; dms; + int 21h ; Display the message ;an000; dms; + pop ds ; Restore DS ;an000; dms; + stc ; flag an error occurred ;an000; dms; + jmp Get_Parms_C ; exit routine ;an000; dms; + +Get_Parms_64_Pg: + + mov ax,bx ; prepare to adjust to Kb value ;an000; dms; + mov cx,10h ; 16Kb per page ;an000; dms; + xor dx,dx ; clear high word ;an000; dms; + mul cx ; get Kb value ;an000; dms; + + mov bx,ax ; store page Kb value in bx ;an000; dms; + add bx,128 ; adjust for emulator code + + mov ah,88h ; get number of 1k blocks above 1Mb ;an000; dms; + int 15h ; + + sub ax,bx ; get number of blocks to allocate for extended ;an000; dms; + jnc Get_Parms_Ext ; set extended memory value in buff size ;an000; dms; + mov dx,offset No_Mem ; not enough memory for parm + mov ah,Dispstrg ; Display the welcome message. ;an000; dms; + push ds ; Save DS ;an000; dms; + push cs ; ;an000; dms; + pop ds ; DS:DX points to the message ;an000; dms; + int 21h ; Display the message ;an000; dms; + pop ds ; Restore DS ;an000; dms; + stc ; flag an error ;an000; dms; + jmp Get_Parms_C ; exit routine ;an000; dms; + +Get_Parms_Ext: + + MOV CS:BUFF_SIZE,ax ; Store buffer size + clc + +GET_PARMS_C: + pop bx ; restore bx ;an000; dms; + POP DS ; Restore DS + + RET + +;------------------------------------------------------------------------------; +; GET_PCHAR -- Get a character from the parameter string into AL ; +;------------------------------------------------------------------------------; + +GET_PCHAR PROC + CMP AL,CR ; Carriage return already encountered? + JE GET_PCHAR_X ; Don't read past end of line + LODSB ; Get character from DS:SI, increment SI + CMP AL,CR ; Is the character a carriage return? + JE GET_PCHAR_X ; Yes, leave the zero flag set to signal end + ; of line + CMP AL,LF ; No, is it a line feed? This will leave the + ; zero flag set if a line feed was found. +GET_PCHAR_X: + RET + +GET_PCHAR ENDP + +;------------------------------------------------------------------------------; +; CHECK_NUM -- Check if the character in AL is a numeric digit, ASCII for ; +; 0 - 9. The zero flag is set if the character is a digit, ; +; otherwise it is reset. ; +;------------------------------------------------------------------------------; + +CHECK_NUM PROC + CMP AL,'0' ; If character is less than a "0" then it is not + JB CHECK_NUM_X ; a number, so exit + + CMP AL,'9' ; If character is greater than a "9" then it is + JA CHECK_NUM_X ; not a number, so exit + + CMP AL,AL ; Set the zero flag to show it is a number +CHECK_NUM_X: + RET ; Zero flag is left reset if character is not +CHECK_NUM ENDP ; a number + +;------------------------------------------------------------------------------; +; SKIP_TO_DIGIT -- Scan the parameter string until a numeric character is ; +; found or the end of the line is encountered. If a numeric ; +; character is not found then the zero flag is set. Else if ; +; a character was found then the zero flag is reset. ; +;------------------------------------------------------------------------------; + +SKIP_TO_DIGIT PROC + CALL CHECK_NUM ; Is the current character a digit? + JZ SKIP_TO_DIGIT_X ; If zero flag is set then it is a number + + CALL GET_PCHAR ; Get the next character from the line + JNZ SKIP_TO_DIGIT ; Loop until first digit or CR or LF is found + RET ; Fall through to here if digit not found + +SKIP_TO_DIGIT_X: + CMP AL,0 ; Digit found, reset the zero flag to show digit + RET ; was found +SKIP_TO_DIGIT ENDP + +;------------------------------------------------------------------------------; +; GET_NUMBER -- Convert the character digits in the parameter string to a ; +; binary value. The value is returned in CX, unless the ; +; calculation overflows, in which case return a 0. The next ; +; character after the digits is left in AL. ; +;------------------------------------------------------------------------------; + +C10 DW 10 +GN_ERR DB ? ; Zero if no overflow in accumulation + +GET_NUMBER PROC ; Convert string of digits to binary value + XOR CX,CX ; Clear CX, the resulting number + MOV CS:GN_ERR,CL ; No overflow yet + +GET_NUMBER_A: + SUB AL,'0' ; Convert the ASCII character in AL to binary + CBW ; Clear AH + XCHG AX,CX ; Previous accumulation in AX, new digit in CL + MUL CS:C10 ; DX:AX = AX*10 + OR CS:GN_ERR,DL ; Any overflow from AX goes into DX. Any non- + ; zero value in DL will signal an error + ADD AX,CX ; Add the new digit to ten times the previous + ; digits + XCHG AX,CX ; New number now in CX + CALL GET_PCHAR ; Get the next character + CALL CHECK_NUM ; Check if it is numeric + JZ GET_NUMBER_A ; If so, then go back and add this digit to the + ; result + CMP CS:GN_ERR,0 ; Did we overflow? + JE GET_NUMBER_B ; If not, we're done + XOR CX,CX ; Return a zero result if overflow +GET_NUMBER_B: + RET +GET_NUMBER ENDP + +GET_PARMS ENDP + +POST ENDP + +PROG ENDS + END + diff --git a/v4.0/src/DEV/XMAEM/INDEINS.MAC b/v4.0/src/DEV/XMAEM/INDEINS.MAC new file mode 100644 index 0000000..c4b13e5 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEINS.MAC @@ -0,0 +1,923 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEINS * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Instructions for the 80386 * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : These macros define instructions that are recognized by * +* the 80386 but that are not recognized by MASM 3.0. We * +* have to create these instructions ourselves because the * +* Macro Assembler won't. * +* * +* MODULE TYPE : MAC * +* * +* REGISTER USAGE : 80286 Standard * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEINS) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870604 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULE'S LIBRARY FILE TYPE TO "MAC" * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +; Some information about creating instructions + +; MODIFIER BYTE VALUES + +; MOD REG/OPX R/M +; --- ------- --- +; 11 111 111 + +; EAX AX AL 000 DISP0 00 +; ECX CX CL 001 DISPB 01 +; EDX DX DL 010 DISPW 10 +; EBX BX BL 011 DISPR 11 +; ESP SP AH 100 +; EBP BP CH 101 +; ESI SI DH 110 +; EDI DI BH 111 + +; ES:0 +; CS:1 +; SS:2 +; DS:3 +; FS:4 +; GS:5 + + +; OPX FIELD VALUES + +; 00F-000 OPCODE VALUES 00F-001 OPCODE VALUES +; --------------------- --------------------- + +; SLDT 000 SGDT 000 +; STR 001 SIDT 001 +; LLDT 010 LIDT 010 +; LTR 011 LGDT 011 +; VERR 100 SMSW 100 +; VERW 101 ? 101 +; ? 110 LMSW 110 +; ? 111 ? 111 + + +PAGE + +; Macros to move to and from the 80386 system registers and to and from the +; new segment registers FS and GS. + + +; CMOV - Move to and From Control Registers +; +; Examples: +; +; CMOV CRx,EREG +; CMOV EREG,CRx +; + +CMOV MACRO REG1,REG2 + + _CREGFD = 0 + _CRCNT = 0 + IRP TREG, + IFIDN <®1>,<&TREG> + _CREGFD = 1 + _CRCNT = 0 + ELSE + _CRCNT = _CRCNT + 1 + ENDIF + ENDM + IFE _CREGFD-1 + F_DREG <®2> + IF _DREGNUM LT 8 + DB 0FH + DB 22H + DB 0C0H + _CRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + _CREGFD = 0 + _CRCNT = 0 + IRP TREG, + IFIDN <®2>,<&TREG> + _CREGFD = 1 + _CRCNT = 0 + ELSE + _CRCNT = _CRCNT + 1 + ENDIF + ENDM + IFE _CREGFD-1 + F_DREG <®1> + IF _DREGNUM LT 8 + DB 0FH + DB 20H + DB 0C0H + _CRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + SYNTAX ERROR - CONTROL REGISTER EXPECTED + ENDIF + ENDIF + ENDM + + +; DMOV - Move to and From Debug Registers +; +; Examples: +; +; DMOV DRx,EREG +; DMOV EREG,DRx +; + +DMOV MACRO REG1,REG2 + + _DREGFD = 0 + _DRCNT = 0 + IRP TREG, + IFIDN <®1>,<&TREG> + _DREGFD = 1 + _DRCNT = 0 + ELSE + _DRCNT = _DRCNT + 1 + ENDIF + ENDM + IFE _DREGFD-1 + F_DREG <®2> + IF _DREGNUM LT 8 + DB 0FH + DB 23H + DB 0C0H + _DRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + _DREGFD = 0 + _DRCNT = 0 + IRP TREG, + IFIDN <®2>,<&TREG> + _DREGFD = 1 + _DRCNT = 0 + ELSE + _DRCNT = _DRCNT + 1 + ENDIF + ENDM + IFE _DREGFD-1 + F_DREG <®1> + IF _DREGNUM LT 8 + DB 0FH + DB 21H + DB 0C0H + _DRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + SYNTAX ERROR - DEBUG REGISTER EXPECTED + ENDIF + ENDIF + ENDM + +; TMOV - Move to and From Test Registers +; +; Examples: +; +; TMOV TRx,EREG +; TMOV EREG,TRx +; + +TMOV MACRO REG1,REG2 + + _TREGFD = 0 + _TRCNT = 0 + IRP TREG, + IFIDN <®1>,<&TREG> + _TREGFD = 1 + _TRCNT = 0 + ELSE + _TRCNT = _TRCNT + 1 + ENDIF + ENDM + _TRCNT = _TRCNT + 6 + IFE _TREGFD-1 + F_DREG <®2> + IF _DREGNUM LT 8 + DB 0FH + DB 26H + DB 0C0H + _TRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + _TREGFD = 0 + _TRCNT = 0 + IRP TREG, + IFIDN <®2>,<&TREG> + _TREGFD = 1 + _TRCNT = 0 + ELSE + _TRCNT = _TRCNT + 1 + ENDIF + ENDM + _TRCNT = _TRCNT + 6 + IFE _TREGFD-1 + F_DREG <®1> + IF _DREGNUM LT 8 + DB 0FH + DB 24H + DB 0C0H + _TRCNT*8 + _DREGNUM + ELSE + SYNTAX ERROR - EXTENDED REGISTER EXPECTED + ENDIF + ELSE + SYNTAX ERROR - TEST REGISTER EXPECTED + ENDIF + ENDIF + ENDM + +; SMOV - Move to/from FS/GS segment registers from/to general registers +; +; Examples: +; +; SMOV SReg, REG16 +; SMOV REG16, SReg +; + +SMOV MACRO REG1,REG2 + _SREGNUM = 8 + _SREG1 = 1 + IFIDN <®1>, + _SREGNUM = 4 + ELSE + IFIDN <®1>, + _SREGNUM = 4 + ENDIF + ENDIF + IF _SREGNUM GT 7 + IFIDN <®1>, + _SREGNUM = 5 + ELSE + IFIDN <®1>, + _SREGNUM = 5 + ENDIF + ENDIF + ENDIF + IF _SREGNUM GT 7 + _SREG1 = 0 + IFIDN <®2>, + _SREGNUM = 4 + ELSE + IFIDN <®2>, + _SREGNUM = 4 + ENDIF + ENDIF + IF _SREGNUM GT 7 + IFIDN <®2>, + _SREGNUM = 5 + ELSE + IFIDN <®2>, + _SREGNUM = 5 + ENDIF + ENDIF + ENDIF + ENDIF + IF _SREGNUM GT 7 + SYNTAX ERROR - FS OR GS SEGMENT REGISTER EXPECTED + ELSE + IF _SREG1 EQ 1 + DB 8EH + F_WREG <®2> + ELSE + DB 8CH + F_WREG <®1> + ENDIF + IF _WREGNUM GT 7 + SYNTAX ERROR - WORD REGISTER EXPECTED + ELSE + DB 0C0H + _SREGNUM*8 + _WREGNUM + ENDIF + ENDIF + ENDM + +F_MOD MACRO TYPE,DISP1,DISP2 + _DISP = 2 +;; 1 0 + IRP _TEST_, + IFIDN <&TYPE>,<&_TEST_> + _DISP = 0 + ELSE + _DISP = _DISP + 1 + ENDIF + ENDM + IFE (_DISP LT 2) +;; 1 0 + IRP _TEST_, + IFIDN <&TYPE>,<&_TEST_> + _DISP = 0 + ELSE + _DISP = _DISP + 1 + ENDIF + ENDM + ENDIF + IF _DISP LT 2 + IFB <&DISP1> + SYNTAX ERROR - IMMEDIATE OPERAND EXPECTED + ELSE + IFE _DISP-1 + _MOD = 2 + ELSE + _MOD = 1 + ENDIF + ENDIF + ELSE + _MOD = 0 + ENDIF + ENDM + +MK_DISP MACRO P3,P4 + IFE _DISP + DB &P3 + ELSE + DW &P3 + IFNB <&P4> + IFDIF <&P4>, + DW &P4 + ENDIF + ELSE + DW 0H + ENDIF + ENDIF + ENDM + +MK_IMMD MACRO P2,P3,P4,P5 + _IMMDCNT = 3 + IRP _PARM,<&P2,&P3,&P4,&P5> + IFE _IMMDCNT + IF _SIZE EQ 1 + DB &_PARM + ELSE + DW &_PARM + ENDIF + ELSE + IFE _IMMDCNT-1 + IF _SIZE EQ 4 + IFNB <&_PARM> + DW &_PARM + ELSE + DW 0H + ENDIF + ENDIF + ENDIF + ENDIF + _IMMDCNT = _IMMDCNT + 1 + IFIDN <&_PARM>, + _IMMDCNT = 0 + ENDIF + ENDM + ENDM + +F_BREG MACRO TBREG + _BREGNUM = 0 + IRP TREG, + IFIDN <&TBREG>,<&TREG> + _BREGNUM = 0 + ELSE + _BREGNUM = _BREGNUM + 1 + ENDIF + ENDM + IFE (_BREGNUM LT 8) + IRP TREG, + IFIDN <&TDREG>,<&TREG> + _BREGNUM = 0 + ELSE + _BREGNUM = _BREGNUM + 1 + ENDIF + ENDM + ENDIF + ENDM + +F_WREG MACRO TWREG + _WREGNUM = 0 + IRP TREG, + IFIDN <&TWREG>,<&TREG> + _WREGNUM = 0 + ELSE + _WREGNUM = _WREGNUM + 1 + ENDIF + ENDM + IFE (_WREGNUM LT 8) + IRP TREG, + IFIDN <&TWREG>,<&TREG> + _WREGNUM = 0 + ELSE + _WREGNUM = _WREGNUM + 1 + ENDIF + ENDM + ENDIF + ENDM + +F_DREG MACRO TDREG + _DREGNUM = 0 + IRP TREG, + IFIDN <&TDREG>,<&TREG> + _DREGNUM = 0 + ELSE + _DREGNUM = _DREGNUM + 1 + ENDIF + ENDM + IFE (_DREGNUM LT 8) + IRP TREG, + IFIDN <&TDREG>,<&TREG> + _DREGNUM = 0 + ELSE + _DREGNUM = _DREGNUM + 1 + ENDIF + ENDM + ENDIF + ENDM + +F_BASE MACRO TBASE + _BASE = 0 +;; 7 6 5 4 3 2 1 0 + IRP TREG,<[EDI],[ESI],[EBP],[ESP],[EBX],[EDX],[ECX],[EAX]> + IFIDN <&TBASE>,<&TREG> + _BASE = 0 + ELSE + _BASE = _BASE + 1 + ENDIF + ENDM + IFE (_BASE LT 8) +;; 7 6 5 4 3 2 1 0 + IRP TREG,<[EDI],[ESI],[EBP],[ESP],[EBX],[EDX],[ECX],[EAX]> + IFIDN <&TBASE>,<&TREG> + _BASE = 0 + ELSE + _BASE = _BASE + 1 + ENDIF + ENDM + ENDIF + ENDM + +F_INDEX MACRO INDX + _INDEX = 0 +;; 7 6 5 4 3 2 1 0 + IRP TREG,<[EDI,[ESI,[EBP,_XX_,[EBX,[EDX,[ECX,[EAX> + IFIDN <&INDX>,<&TREG> + _INDEX = 0 + ELSE + _INDEX = _INDEX + 1 + ENDIF + ENDM + IFE (_INDEX LT 8) +;; 7 6 5 4 3 2 1 0 + IRP TREG,<[EDI,[ESI,[EBP,_XX_,[EBX,[EDX,[ECX,[EAX> + IFIDN <&INDX>,<&TREG> + _INDEX = 0 + ELSE + _INDEX = _INDEX + 1 + ENDIF + ENDM + ENDIF + ENDM + +F_SCALE MACRO TSCALE + _SCALE = 0 +;; 3 2 1 0 + IRP TREG,<*8], *4], *2], *1]> + IFIDN <&TSCALE>,<&TREG> + _SCALE = 0 + ELSE + _SCALE = _SCALE + 1 + ENDIF + ENDM + ENDM + +PAGE + +; Macros to PUSH and POP the new segment registers FS and GS + + +; PUSH_FS - PUSH FS segment register + +PUSH_FS MACRO + DB 00FH + DB 0A0H + ENDM + + +; PUSH_GS - PUSH GS segment register + +PUSH_GS MACRO + DB 00FH + DB 0A8H + ENDM + + +; POP_FS - POP FS segment register + +POP_FS MACRO + DB 00FH + DB 0A1H + ENDM + + +; POP_GS - POP GS segment register + +POP_GS MACRO + DB 00FH + DB 0A9H + ENDM + +PAGE + +; Macros for multiplication instructions + +; RIMUL - Uncharacterized Signed Multiply (16-bit) +; Syntax: RIMUL REG,REG/MEM + +RIMUL MACRO REG,OPND + LOCAL L1,L2 + _2BYTEOP = 1 + DB 0FH + .XLIST +L1 LABEL BYTE + .LIST + CMP ®,&OPND + .XLIST +L2 LABEL BYTE + ORG OFFSET CS:L1 + .LIST + DB 0AFH + .XLIST + ORG OFFSET CS:L2 + .LIST + ENDM + +; ERIMUL - 32 bit Uncharacterized Signed Multiply +; Systax: ERIMUL REG,REG/MEM + +ERIMUL MACRO REG,OPND + DB 66H + RIMUL ®,<&OPND> + ENDM + +PAGE + +; Macros to load pointers with the segment in FS, GS or SS. That is, these are +; just like the instructions LDS and LES but for the FS, GS and SS registers. + +NEWLS MACRO OP,REG,OPND + LOCAL L1,L2 + DB 0FH + .XLIST +L1 LABEL BYTE + .LIST + LDS ®,DWORD PTR &OPND + .XLIST +L2 LABEL BYTE + ORG OFFSET CS:L1 + .LIST + DB &OP + .XLIST + ORG OFFSET CS:L2 + .LIST + ENDM + +; LFS REG,OPND + +LFS MACRO REG,OPND + NEWLS 0B4H,®,<&OPND> + ENDM + +; LGS REG,OPND + +LGS MACRO REG,OPND + NEWLS 0B5H,®,<&OPND> + ENDM + +; LSS REG,OPND + +LSS MACRO REG,OPND + NEWLS 0B2H,®,<&OPND> + ENDM + +; Now we do 32 bit versions of the above + +; ELFS REG,OPND + +ELFS MACRO REG,OPND + DB 66H + NEWLS 0B4H,®,<&OPND> + ENDM + +; ELGS REG,OPND + +ELGS MACRO REG,OPND + DB 66H + NEWLS 0B5H,®,<&OPND> + ENDM + +; ELSS REG,OPND + +ELSS MACRO REG,OPND + DB 66H + NEWLS 0B2H,®,<&OPND> + ENDM + +PAGE + +; Macros for some shift instructions + +; Shift Left Double R/M, Reg [CL = COUNT] [16-bit Operand] +; SHLD OPND,REG (Double left shift) [CL = COUNT] + +SHLD MACRO OPND,REG + SHDOP 0A5H,<&OPND>,® + ENDM + + +; Shift Right Double R/M, Reg [CL = COUNT] [16-bit Operand] +; SHRD OPND,REG (Double right shift) [CL = COUNT] + +SHRD MACRO OPND,REG + SHDOP 0ADH,<&OPND>,® + ENDM + + +; Shift Left Double R/M, Reg, Immd (8-bit) [16-bit Operand] +; SHLDI OPND,REG,IMMD-8 + +SHLDI MACRO OPND,REG,IMMD + SHDOP 0A4H,<&OPND>,® + DB &IMMD + ENDM + + +; Shift Right Double R/M, Reg, Immd (8-bit) [16-bit Operand] +; SHRDI OPND,REG,IMMD-8 + +SHRDI MACRO OPND,REG,IMMD + SHDOP 0ACH,<&OPND>,® + DB &IMMD + ENDM + +; Now 32 bit versions of the above + +; Shift Left Double R/M, Reg [CL = COUNT] [32-bit Operand] +; ESHLD OPND,REG (Double left shift) [CL = COUNT] + +ESHLD MACRO OPND,REG + DB 66H + SHDOP 0A5H,<&OPND>,® + ENDM + + +; Shift Right Double R/M, Reg [CL = COUNT] [32-bit Operand] +; ESHRD OPND,REG (Double right shift) [CL = COUNT] + +ESHRD MACRO OPND,REG + DB 66H + SHDOP 0ADH,<&OPND>,® + ENDM + + +; Shift Left Double R/M, Reg, Immd (8-bit) [32-bit Operand] +; ESHLDI OPND,REG,IMMD-8 (Double left shift) + +ESHLDI MACRO OPND,REG,IMMD + DB 66H + SHDOP 0A4H,<&OPND>,® + DB &IMMD + ENDM + + +; Shift Right Double R/M, Reg, Immd (8-bit) [32-bit Operand] +; ESHRDI OPND,REG,IMMD-8 (Double right shift) + +ESHRDI MACRO OPND,REG,IMMD + DB 66H + SHDOP 0ACH,<&OPND>,® + DB &IMMD + ENDM + + +SHDOP MACRO OP,OPND,REG + LOCAL L1,L2 + _2BYTEOP = 1 + DB 0FH + .XLIST +L1 LABEL BYTE + .LIST + OR ®,&OPND + .XLIST +L2 LABEL BYTE + ORG OFFSET CS:L1 + .LIST + DB &OP + .XLIST + ORG OFFSET CS:L2 + .LIST + ENDM + +PAGE + +; The following two instructions, CALLFAR and JUMPFAR, work in the +; MS Macro Assembler, but not for intersegment direct. The assembler +; generates segments based on 8088 values, and we need them based +; on protect-mode selector values. The assembler works just ducky for +; jump and call far indirect, since you go pick up the offset and +; segment at execution time. + +CALLFAR MACRO DISP,SEGMENT + + DB 09AH ; Call far direct + DW (OFFSET &DISP) ; to this offset + DW &SEGMENT ; in this segment + + ENDM + + +JUMPFAR MACRO DISP,SEGMENT + + DB 0EAH ; Jump far direct + DW (OFFSET &DISP) ; to this offset + DW &SEGMENT ; in this segment + + ENDM + +PAGE + +; Macros for extended jump instructions + +LJCOND MACRO OP,DISPL + _2BYTEOP = 1 +TEMP = $ + 4 + DB 0FH + DB &OP + DW (OFFSET &DISPL)-(&TEMP) + ENDM + +; LJO DISPL (Long Jump on Overflow) +LJO MACRO DISPL + LJCOND 80H,<&DISPL> + ENDM + + +; LJNO DISPL (Long Jump on NO Overflow) +LJNO MACRO DISPL + LJCOND 81H,<&DISPL> + ENDM + +; LJB DISPL (Long Jump on Below) +LJB MACRO DISPL + LJCOND 82H,<&DISPL> + ENDM + +; LJC DISPL (Long Jump on Carry) +LJC MACRO DISPL + LJCOND 82H,<&DISPL> + ENDM + +; LNAE DISPL (Long Jump on Not Above or Equal) +LNAE MACRO DISPL + LJCOND 82H,<&DISPL> + ENDM + +; LJNB DISPL (Long Jump on Not Below) +LJNB MACRO DISPL + LJCOND 83H,<&DISPL> + ENDM + +; LJNC DISPL (Long Jump on No Carry) +LJNC MACRO DISPL + LJCOND 83H,<&DISPL> + ENDM + +; LJAE DISPL (Long Jump on Above or Equal) +LJAE MACRO DISPL + LJCOND 83H,<&DISPL> + ENDM + +; LJE DISPL (Long Jump on Equal) +LJE MACRO DISPL + LJCOND 84H,<&DISPL> + ENDM + +; LJZ DISPL (Long Jump on Zero) +LJZ MACRO DISPL + LJCOND 84H,<&DISPL> + ENDM + +; LJNE DISPL (Long Jump on Not Equal) +LJNE MACRO DISPL + LJCOND 85H,<&DISPL> + ENDM + +; LJNZ DISPL (Long Jump on Not Zero) +LJNZ MACRO DISPL + LJCOND 85H,<&DISPL> + ENDM + +; LJBE DISPL (Long Jump on Below or Equal) +LJBE MACRO DISPL + LJCOND 86H,<&DISPL> + ENDM + +; LJNA DISPL (Long Jump on Not Above) +LJNA MACRO DISPL + LJCOND 86H,<&DISPL> + ENDM + +; LJNBE DISPL (Long Jump on Not Below or Equal) +LJNBE MACRO DISPL + LJCOND 87H,<&DISPL> + ENDM + +; LJA DISPL (Long Jump on Above) +LJA MACRO DISPL + LJCOND 87H,<&DISPL> + ENDM + +; LJS DISPL (Long Jump on Sign) +LJS MACRO DISPL + LJCOND 88H,<&DISPL> + ENDM + +; LJNS DISPL (Long Jump on No Sign) +LJNS MACRO DISPL + LJCOND 89H,<&DISPL> + ENDM + +; LJP DISPL (Long Jump on Parity) +LJP MACRO DISPL + LJCOND 8AH,<&DISPL> + ENDM + +; LJPE DISPL (Long Jump on Parity Even) +LJPE MACRO DISPL + LJCOND 8AH,<&DISPL> + ENDM + +; LJNP DISPL (Long Jump on No Parity) +LJNP MACRO DISPL + LJCOND 8BH,<&DISPL> + ENDM + +; LJPO DISPL (Long Jump on Parity Odd) +LJPO MACRO DISPL + LJCOND 8BH,<&DISPL> + ENDM + +; LJL DISPL (Long Jump on Less) +LJL MACRO DISPL + LJCOND 8CH,<&DISPL> + ENDM + +; LJNGE DISPL (Long Jump on Not Greater or Equal) +LJNGE MACRO DISPL + LJCOND 8CH,<&DISPL> + ENDM + +; LJNL DISPL (Long Jump on Not Less) +LJNL MACRO DISPL + LJCOND 8DH,<&DISPL> + ENDM + +; LJGE DISPL (Long Jump on Greater than or Equal) +LJGE MACRO DISPL + LJCOND 8DH,<&DISPL> + ENDM + +; LJLE DISPL (Long Jump on Less than or Equal) +LJLE MACRO DISPL + LJCOND 8EH,<&DISPL> + ENDM + +; LJNG DISPL (Long Jump on Not Greater than) +LJNG MACRO DISPL + LJCOND 8EH,<&DISPL> + ENDM + +; LJNLE DISPL (Long Jump on Not Less than or Equal) +LJNLE MACRO DISPL + LJCOND 8FH,<&DISPL> + ENDM + +; LJG DISPL (Long Jump on Greater than) +LJG MACRO DISPL + LJCOND 8FH,<&DISPL> + ENDM + +; JECXZ DISPL (Jump short on ECX Zero) +JECEXZ MACRO DISPL + DB 66H + JCXZ &DISPL + ENDM + diff --git a/v4.0/src/DEV/XMAEM/INDEMAUS.ASM b/v4.0/src/DEV/XMAEM/INDEMAUS.ASM new file mode 100644 index 0000000..1174510 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEMAUS.ASM @@ -0,0 +1,73 @@ +PAGE 60,132 +TITLE INDEMAUS - 386 XMA Emulator - Messages + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEMAUS * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator messages -- U.S. * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : Declare the U.S. messages for the 80386 XMA Emulator * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : N/A * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : None * +* * +* LINKAGE : The messages are made PUBLIC so that the initialization * +* module, INDEINI, can access them. * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : None * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* SUB-ROUTINES : None * +* * +* MACROS : None * +* * +* CONTROL BLOCKS : None * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEMAUS) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870629 D : NEW FOR RELEASE 1.1 * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +SUBTTL Messages ; D0A +PAGE ; D0A + ; D0A +PROG SEGMENT PARA PUBLIC 'PROG' ; D0A + ; D0A + ASSUME CS:PROG ; D0A + ASSUME SS:NOTHING ; D0A + ASSUME DS:PROG ; D0A + ASSUME ES:NOTHING ; D0A + ; D0A +INDEMAUS LABEL NEAR ; D0A + ; D0A + INCLUDE INDEMSUS.INC ; Use the US messages D0A + ; D0A +PROG ENDS ; D0A + END ; D0A diff --git a/v4.0/src/DEV/XMAEM/INDEMSG.ASM b/v4.0/src/DEV/XMAEM/INDEMSG.ASM new file mode 100644 index 0000000..1adf363 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEMSG.ASM @@ -0,0 +1,96 @@ +PAGE 60,132 +TITLE INDEMSG - 80386 XMA Emulator - Messages + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEMSG * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator messages -- U.S. * +* * +* STATUS (LEVEL) : Version (0) Level (1.10) * +* * +* FUNCTION : Declare the U.S. messages for the 80386 XMA Emulator * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : N/A * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : None * +* * +* LINKAGE : The messages are made PUBLIC so that the initialization * +* module, INDEINI, can access them. * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : None * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* SUB-ROUTINES : None * +* * +* MACROS : None * +* * +* CONTROL BLOCKS : None * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEMSG) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870629 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870805 D : RENAME MODULE TO INDEMSUS * +* $P2=P0000489 410 871002 D : RENAME MODULE TO INDEMSG. DECLARE MESSAGES HERE.* +* $P3=P0000649 411 880125 D : NEW VERSION OF THE EMULATOR * +* $P4=P0000741 411 880203 D : UPDATE COPYRIGHT * +* $D1=D0008700 120 880206 D : SUPPORT DOS 4.00 IOCTL CALL * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +SUBTTL Messages ; D0A +PAGE ; D0A + ; D0A +PROG SEGMENT PARA PUBLIC 'PROG' ; D0A + ; D0A + ASSUME CS:PROG ; D0A + ASSUME SS:NOTHING ; D0A + ASSUME DS:PROG ; D0A + ASSUME ES:NOTHING ; D0A + ; D0A +INDEMSG LABEL NEAR ; D0A + ; D0A +;---------------------------------------------------------------------------P2A; +; Declare messages that the emulator will display on the screen. These P2A; +; messages are declared with line lengths of 80 bytes to allow for World P2A; +; Trade translation. The messages are made public so that other modules P2A; +; can access them. P2A; +; Note that the messages are declared 80 bytes long to facilitate world trade translation. P2A; +;---------------------------------------------------------------------------P2A; + ; P2A + PUBLIC WELCOME ; P2A + PUBLIC GOODLOAD ; P2A + PUBLIC NO_80386 ; P2A + PUBLIC WAS_INST ; P2A + Public Small_Parm ;an000; dms; + Public No_Mem ;an000; dms; + ; P2A +CR EQU 13 ; ASCII for a carriage return ; @P2A +LF EQU 10 ; ASCII for a line feed ; @P2A + ; P2A +INCLUDE xmaem.cl1 + +PROG ENDS ; D0A + END ; D0A diff --git a/v4.0/src/DEV/XMAEM/INDEMSUS.ASM b/v4.0/src/DEV/XMAEM/INDEMSUS.ASM new file mode 100644 index 0000000..d73d016 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEMSUS.ASM @@ -0,0 +1,76 @@ +PAGE 60,132 +TITLE INDEMSG - 80386 XMA Emulator - Messages + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEMSUS * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator messages -- U.S. * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : Declare the U.S. messages for the 80386 XMA Emulator * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : N/A * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : None * +* * +* LINKAGE : The messages are made PUBLIC so that the initialization * +* module, INDEINI, can access them. * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : None * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* SUB-ROUTINES : None * +* * +* MACROS : None * +* * +* CONTROL BLOCKS : None * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEMSUS) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870629 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870805 D : RENAME MODULE TO INDEMSUS * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +SUBTTL Messages ; D0A +PAGE ; D0A + ; D0A +PROG SEGMENT PARA PUBLIC 'PROG' ; D0A + ; D0A + ASSUME CS:PROG ; D0A + ASSUME SS:NOTHING ; D0A + ASSUME DS:PROG ; D0A + ASSUME ES:NOTHING ; D0A + ; D0A +INDEMSG LABEL NEAR ; D0A + ; D0A + INCLUDE INDEMSUS.INC ; Use the US messages D0A + ; D0A + include copyrigh.inc ; DCL p1821 + ; +PROG ENDS ; D0A + END ; D0A diff --git a/v4.0/src/DEV/XMAEM/INDEMSUS.INC b/v4.0/src/DEV/XMAEM/INDEMSUS.INC new file mode 100644 index 0000000..e352729 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEMSUS.INC @@ -0,0 +1,64 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEMSUS * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator messages -- U.S. * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : Include file for U.S. messages for the 80386 XMA Emulator.* +* The messages are made PUBLIC so that the initialization * +* module, INDEINI, can access them. * +* * +* MODULE TYPE : INC * +* * +* MACROS : None * +* * +* CONTROL BLOCKS : None * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEMSUS) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870722 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULE'S LIBRARY FILE TYPE TO "INC" * +* $P2=P0036900 110 870826 D : CHG MSG NO. INDXE003 * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +;---------------------------------------------------------------------------D0A; +; Declare messages that the emulator will display on the screen. These D0A; +; messages are declared with line lengths of 80 bytes to allow for World D0A; +; Trade translation. The messages are made public so that other modules D0A; +; can access them. D0A; +; Note that the messages are declared 80 bytes long to facilitate world trade translation. D0A; +;---------------------------------------------------------------------------D0A; + ; D0A + PUBLIC WELCOME ; D0A + PUBLIC GOODLOAD ; D0A + PUBLIC NO_80386 ; D0A + PUBLIC WAS_INST ; D0A + ; D0A +CR EQU 13 ; ASCII for a carriage return ; @D0A +LF EQU 10 ; ASCII for a line feed ; @D0A + ; D0A +WELCOME DB "INDXE001 80386 XMA Emulator Version 1.00 " ; @D0A + DB " (C) Copyright 1988 Microsoft Corporation All rights reserved " ; @D0A + DB CR,LF,"$" ; @D0A + ; D0A +GOODLOAD DB "INDXE002 80386 XMA Emulator installed " ; @D0A + DB CR,LF,"$" ; @D0A + ; D0A +NO_80386 DB "INDXE003 80386 XMA Emulator not installed. This system unit " ; @D0A + DB " is not supported. The 80386 XMA Emulator requires " ; @D0A + DB " an IBM Personal System/2 Model 80. " ; @D0A + DB CR,LF,"$" ; @D0A + ; D0A +WAS_INST DB "INDXE004 80386 XMA Emulator not installed. " ; @D0A + DB " Protected mode already in use. " ; @D0A + DB CR,LF,"$" ; @D0A + ; D0A diff --git a/v4.0/src/DEV/XMAEM/INDEOVP.MAC b/v4.0/src/DEV/XMAEM/INDEOVP.MAC new file mode 100644 index 0000000..3f2e213 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEOVP.MAC @@ -0,0 +1,111 @@ +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEOVP * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Override prefix macros * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : DATAOV - Creates a prefix so that the next instruction * +* accesses data twice as wide as it normally would.* +* Bytes go to words, and words go to double words. * +* ADDROV - Creates a prefix so that the next instruction * +* uses 32 bit addresses instead of 16 bit. * +* SEGOV - Creates a segment override prefix for the next * +* instruction. * +* * +* MODULE TYPE : MAC * +* * +* REGISTER USAGE : 80286 Standard * +* * +* CHANGE ACTIVITY : * +* * +* $MAC(INDEOVP) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870604 D : NEW FOR RELEASE 1.1 * +* $P1=P0000311 410 870804 D : RENAME MODULE'S LIBRARY FILE TYPE TO "MAC" * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +PAGE +; DATAOV - Create a prefix for an instruction so that it accesses data twice +; as wide as it normally would have. If the instruction was to access +; bytes then it will access words. If it was to access words then it +; will access double words (32 bits). + +DATAOV MACRO + DB 066H + ENDM + +PAGE +; ADDROV - Create a prefix for an instruction so that it uses 32 bit addresses +; instead of 16 bit addresses. + +ADDROV MACRO + DB 067H + ENDM + +PAGE +; SEGOV - Segment Prefix Overrides +; This macro will create segment prefix overrides for all the segment registers +; on the 80386. It will also create prefixes for the data override and address +; override as listed in the DATOV and ADDROV macros above. +; +; Syntax: SEGOV ES | CS | SS | DS | FS | GS | DATA | ADDRESS +; + +SEGOV MACRO SR + IFIDN <&SR>, + DB 026H + ENDIF + IFIDN <&SR>, + DB 026H + ENDIF + IFIDN <&SR>, + DB 02EH + ENDIF + IFIDN <&SR>, + DB 02EH + ENDIF + IFIDN <&SR>, + DB 036H + ENDIF + IFIDN <&SR>, + DB 036H + ENDIF + IFIDN <&SR>, + DB 03EH + ENDIF + IFIDN <&SR>, + DB 03EH + ENDIF + IFIDN <&SR>, + DB 064H + ENDIF + IFIDN <&SR>, + DB 064H + ENDIF + IFIDN <&SR>, + DB 065H + ENDIF + IFIDN <&SR>, + DB 065H + ENDIF + IFIDN <&SR>, + DB 066H + ENDIF + IFIDN <&SR>, + DB 066H + ENDIF + IFIDN <&SR>,
+ DB 067H + ENDIF + IFIDN <&SR>,
+ DB 067H + ENDIF + ENDM + diff --git a/v4.0/src/DEV/XMAEM/INDEPAT.ASM b/v4.0/src/DEV/XMAEM/INDEPAT.ASM new file mode 100644 index 0000000..e14d1d8 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEPAT.ASM @@ -0,0 +1,74 @@ +PAGE 60,132 +TITLE INDEPAT - 80386 XMA Emulator - Patch area + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEPAT * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: 80386 XMA Emulator patch area * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : Declare a patch area for the 80386 XMA Emulator * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : N/A * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINT : None * +* * +* LINKAGE : None * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : None * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : None * +* * +* SUB-ROUTINES : None * +* * +* MACROS : None * +* * +* CONTROL BLOCKS : None * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEPAT) COMP(LOAD) PROD(3270PC) : * +* * +* $P0=P0000489 410 871002 D : NEW FOR WSP VERSION 1.1 * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + +SUBTTL Patch Area +PAGE + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME SS:NOTHING + ASSUME DS:PROG + ASSUME ES:NOTHING + + PUBLIC INDEPAT + +INDEPAT LABEL NEAR + + DB 512 DUP(0EEH) + +PROG ENDS + END diff --git a/v4.0/src/DEV/XMAEM/INDEXMA.ASM b/v4.0/src/DEV/XMAEM/INDEXMA.ASM new file mode 100644 index 0000000..c35dd24 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEXMA.ASM @@ -0,0 +1,781 @@ +PAGE 60,132 +TITLE INDEXMA - 386 XMA Emulator - XMA Emulation + +COMMENT # +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* * +* MODULE NAME : INDEXMA * +* * +* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * +* * +* DESCRIPTIVE NAME: Do the XMA emulation for the 80386 XMA Emulator * +* * +* STATUS (LEVEL) : Version (0) Level (1.0) * +* * +* FUNCTION : This module does the actual manipulation of the page * +* tables to emulate the XMA card. Using the 80386 * +* paging mechanism we let the 4K page frames represent * +* the 4K XMA blocks on the XMA card. We let the page * +* tables represent the translate table. * +* * +* The XMA "blocks" start at address 12000:0. The D1C* +* Emulator emulates the XMA 2 card with the INDXMAA D1C* +* device driver. On initial power up, the XMA 2 card is D1C* +* disabled. The INDXMAA device driver then disables the D1C* +* memory from 0K to 640K and backs it with memory from D1C* +* 0K to 640K on the XMA 2 card. The Emulator looks like D1C* +* it does the same thing. The XMA blocks for 0K to 640K D1C* +* are taken from the system board memory from 0K to D1C* +* 640K. This memory on the motherboard is treated as D1C* +* XMA memory. This emulates the INDXMAA device driver's D1C* +* mapping of 0K to 640K on the XMA card to real memory. D1C* +* The XMA "blocks" for 640K and up are located in high D1C* +* memory starting at 12000:0. These "blocks" run up to D1C* +* the start of the MOVEBLOCK buffer. The MOVEBLOCK D1C* +* buffer is a chunk of storage (in 16K multiples) at the D1C* +* end of available memory that is reserved for the D1C* +* MOVEBLOCK functions. D1C* +* * +* The page tables are used to emulate the translate * +* table. By setting the address of the XMA "block" into * +* the page table entry for a specific page frame we can * +* make that address access that particular XMA page * +* frame. To the user this looks just like the translate * +* table is active. * +* * +* The tricky part comes in disabling pages (blocks). On D1C* +* the XMA 2 card, when a translate table entry is D1C* +* disabled the addresses for that address range go to D1C* +* real memory. If the address is between 0K and 640K D1C* +* then any access of that storage gets nothing because D1C* +* there is no memory backed from 0K to 640K on the real D1C* +* system. All other addresses go to real memory. So D1C* +* when the user disables translation of a translate D1C* +* table entry we need to check what range that entry D1C* +* covers. If the entry points to somewhere between 0K D1C* +* and 640K then we will set the page table entry that D1C* +* corresponds to the translate table entry to point to D1C* +* non-existent memory. For all other addresses we will D1C* +* just point the page table entry back to the real D1C* +* memory at that address. D1C* +* * +* This module receives control on all "IN"s and "OUT"s * +* done by the user. If the "IN" or "OUT" is not to an * +* XMA port then it passes the I/O on to INDEDMA. If it * +* is for an XMA port then the request is handled here. * +* * +* This module keeps its own copies of the XMA registers * +* and the translate table. When any I/O comes for the * +* XMA card it updates its copies of the registers and * +* the translate table. Then it does any needed * +* modifications on the page tables to emulate the XMA * +* request. * +* * +* MODULE TYPE : ASM * +* * +* REGISTER USAGE : 80386 Standard * +* * +* RESTRICTIONS : None * +* * +* DEPENDENCIES : None * +* * +* ENTRY POINTS : INW - Emulate "IN" for a word with port number * +* in DX * +* INWIMMED - Emulate "IN" for a word with an immediate * +* port number * +* INIMMED - Emulate "IN" for a byte with an immediate * +* port number * +* XMAIN - Emulate "OUT" for a byte with port number * +* in DX * +* OUTW - Emulate "OUT" for a word with port number * +* in DX * +* OUTWIMMED - Emulate "OUT" for a word with an immediate * +* port number * +* XMAOUTIMMED - Emulate "OUT" for a byte with an immediate * +* port number * +* XMAOUT - Emulate "OUT" for a byte with port number * +* in DX * +* * +* LINKAGE : Jumped to by INDEEXC * +* * +* INPUT PARMS : None * +* * +* RETURN PARMS : None * +* * +* OTHER EFFECTS : None * +* * +* EXIT NORMAL : Go to POPIO in INDEEMU to IRET to the V86 task * +* * +* EXIT ERROR : None * +* * +* EXTERNAL * +* REFERENCES : POPIO:NEAR - Entry in INDEEMU to return to V86 task * +* HEXW:NEAR - Entry in INDEEXC to display word in AX * +* DMAIN:NEAR - Entry in INDEDMA to "IN" from DMA port * +* DMAOUT:NEAR - Entry in INDEDMA to "OUT" to DMA port * +* PGTBLOFF:WORD - Offset of the normal page tables * +* SGTBLOFF:WORD - Offset of the page directory * +* NORMPAGE:WORD - Entry for the 1st page directory entry * +* so that it points to the normal * +* page tables * +* XMAPAGE:WORD - Entry for the 1st page directory entry * +* that points to the XMA page tables * +* TTTABLE:WORD - The translate table * +* BUFF_SIZE:WORD - Size of the MOVEBLOCK buffer * +* MAXMEM:WORD - Number of kilobytes on this machine * +* * +* SUB-ROUTINES : TTARCHANGED - Put the block number at the translate table * +* entry in 31A0H into "ports" 31A2H and 31A4H * +* UPDATETT - Update the translate table and page tables * +* to reflect the new block number written to * +* either 31A2H or 31A4H * +* * +* MACROS : DATAOV - Add prefix for the next instruction so that it * +* accesses data as 32 bits wide * +* ADDROV - Add prefix for the next instruction so that it * +* uses addresses that are 32 bits wide * +* CMOV - Move to and from control registers * +* * +* CONTROL BLOCKS : INDEDAT.INC - system data structures * +* * +* CHANGE ACTIVITY : * +* * +* $MOD(INDEXMA) COMP(LOAD) PROD(3270PC) : * +* * +* $D0=D0004700 410 870530 D : NEW FOR RELEASE 1.1 * +* $P1=P0000293 410 870731 D : LIMIT LINES TO 80 CHARACTERS * +* $P2=P0000312 410 870804 D : CHANGE COMPONENT FROM MISC TO LOAD * +* $D1=D0007100 410 870810 D : CHANGE TO EMULATE XMA 2 * +* * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# + + .286P ; Enable recognition of 286 privileged instructs. + + .XLIST ; Turn off the listing + INCLUDE INDEDAT.INC ; Include system data structures + + IF1 ; Only include macros on the first pass + INCLUDE INDEOVP.MAC + INCLUDE INDEINS.MAC + ENDIF + .LIST ; Turn on the listing + +CRT_SELECTOR EQU 00030H ; Selector for mono display buffer +SEX_ATTR EQU 04B00H ; Attribute for turquoise on red +STACK_ATTR EQU 00700H ; Attribute for white o black +BLANK EQU 00020H ; ASCII for a blank +XMA_PAGES_SEL EQU RSDA_PTR ; Selector for the XMA pages +HIMEM EQU 120H ; Adjustment for XMA pages >= 640K. @D1C + ; They start at address 12000:0. + ; It's the block number corresponding + ; to address 12000:0. + ; @D1D + +PROG SEGMENT PARA PUBLIC 'PROG' + + ASSUME CS:PROG + ASSUME DS:PROG + ASSUME ES:NOTHING + ASSUME SS:NOTHING + +INDEXMA LABEL NEAR + +; The following entry points are in other modules + + EXTRN POPIO:NEAR ; Return to V86 task - in INDEEMU + EXTRN HEXW:NEAR ; Display word in AX - in INDEEXC + EXTRN DMAIN:NEAR ; "IN" from DMA port - in INDEDMA + EXTRN DMAOUT:NEAR ; "OUT" to DMA port - in INDEDMA + +; The following data are in INDEI15.ASM + + EXTRN PGTBLOFF:WORD ; Offset of the normal page tables + EXTRN SGTBLOFF:WORD ; Offset of the page directory + EXTRN NORMPAGE:WORD ; Entry for the 1st page directory entry + ; so that it points to the normal + ; page tables + EXTRN XMAPAGE:WORD ; Entry for the 1st page directory entry + ; that points to the XMA page tables + EXTRN TTTABLE:WORD ; The translate table + EXTRN BUFF_SIZE:WORD ; Size of the MOVEBLOCK buffer + EXTRN MAXMEM:WORD ; Number of kilobytes on this machine + +; Let the following entries be known to other modules + + PUBLIC INDEXMA + PUBLIC INW + PUBLIC INWIMMED + PUBLIC INIMMED + PUBLIC XMAIN + PUBLIC OUTW + PUBLIC OUTWIMMED + PUBLIC XMAOUTIMMED + PUBLIC XMAOUT + PUBLIC NOTXMAOUT + +; Let the following data be known to other modules + + PUBLIC WORD_FLAG + PUBLIC XMATTAR + PUBLIC XMATTIO + PUBLIC XMATTII + PUBLIC XMATID + PUBLIC XMACTL + +; The following XMA labels represent the XMA ports starting at 31A0H. +; THEY MUST BE KEPT IN THE FOLLOWING ORDER. + +XMATTAR DW 0 ; Port 31A0H - Translate table index +XMATTIO DW 0 ; Port 31A2H - XMA block number +XMATTII DW 0 ; Port 31A4H - Block number with auto-increment +XMATID DB 0 ; Port 31A6H - Bank ID +XMACTL DB 02H ; Port 31A7H - Control flags. Virtual @D1C + ; enable bit is initially on. + +; How about some flags? + +WORD_FLAG DB 0 ; If set to 1 then I/O is for a word. + ; Else, it's for a byte + +PAGE + +; Control comes here for an "IN" for a word with the port value in DX + +INW: + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our + MOV DS,AX ; data segment so we can set WORD_FLAG + MOV WORD_FLAG,1 ; Flag this as a word operation + JMP XMAIN ; Go do the "IN" + +; Control comes here for an "IN" for a word with an immediate port value + +INWIMMED: + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our + MOV DS,AX ; data segment so we can set WORD_FLAG + MOV WORD_FLAG,1 ; Flag this as a word operation + +; Control comes here for an "IN" for a byte with an immediate port value + +INIMMED: + + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "IN" instruction + +; Get the port address from the instruction. The port address is in the byte +; immediately following the "IN" op-code. We will load the port address into +; DX. This way when we join the code below it will look like the port address +; was in DX all along. + + MOV AX,HUGE_PTR ; Load DS with a selector that accesses + MOV DS,AX ; all of memory as data + + MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86 + MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP + + DATAOV ; Load ESI (32 bit SI) with the V86 + MOV SI,SS:[BP+BP_IP] ; task's IP + DATAOV + MOV AX,SS:[BP+BP_CS] ; Load EAX with the V86 task's CS + DATAOV ; and then shift left four bits to + SHL AX,4 ; convert it to an offset + DATAOV ; Add the CS offset to "IP" in SI + ADD SI,AX ; SI now contains CS:IP as a 32 bit + ; offset from 0 + ADDROV ; Get the byte after the "IN" instruc- + LODSB ; tion. This is the port address. + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + SUB DX,DX ; Clear DX to prepare for one byte move + MOV DL,AL ; DX now has the port address + +; Control comes here for an "IN" for a byte with the port value in DX + +XMAIN PROC NEAR + + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our + MOV DS,AX ; data segment + + CMP DX,31A0H ; Is the port address below 31A0H? + JB NOTXMAIN ; Yup. Then it's not XMA. + + CMP DX,31A7H ; Is the port address above 31A7H? + JA NOTXMAIN ; Yup. Then it's not XMA. + +; It's an XMA port so lets do the "IN" for the guy. + + AND XMATTAR,0FFFH ; First lets clear the high nibbles of + AND XMATID,0FH ; our ports. This insures that we + AND XMACTL,0FH ; have valid values in our ports. + + LEA SI,XMATTAR ; Point SI to the port requested by + ADD SI,DX ; first pointing it to port 31A0H + SUB SI,31A0H ; and then adding on the difference + ; between 31A0H and the requested port + CMP WORD_FLAG,0 ; Is this a word operation? + JNE GETWORD ; Yes. Then go get a word. + + LODSB ; Else get a byte from the "port" + MOV BYTE PTR SS:[BP+BP_AX],AL ; Put it in the V86 task's AL register + JMP INEXIT ; Th-th-that's all folks! + +; For non-XMA ports we just pass the "IN" on to INDEDMA + +NOTXMAIN: + JMP DMAIN + +; The "IN" is for a word + +GETWORD: + LODSW ; Get a word from the "port" + MOV WORD PTR SS:[BP+BP_AX],AX ; Put it in the V86 task's AX register + + MOV WORD_FLAG,0 ; Reset the word flag + + CMP DX,31A4H ; Is this an "IN" from the auto- + ; increment port? + JNE INEXIT ; Nope. Then just leave. + + INC XMATTAR ; The "IN" is from the auto-increment + ; port so increment the translate + CALL TTARCHANGED ; table index and call TTARCHANGED + ; to update the status of the "card" +INEXIT: + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction (past + ; the port value for immediate insts.) + JMP POPIO ; Go return to the V86 task + +PAGE + +; Control comes here for an "OUT" for a word with the port value in DX + +OUTW: + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our + MOV DS,AX ; data segment so we can set WORD_FLAG + MOV WORD_FLAG,1 ; Flag this as a word operation + JMP XMAOUT ; Go do the "OUT" + +; Control comes here for an "OUT" for a word with an immediate port value + +OUTWIMMED: + MOV AX,SYS_PATCH_DS ; Load DS with the selector for our + MOV DS,AX ; data segment so we can set WORD_FLAG + MOV WORD_FLAG,1 ; Flag this as a word operation + +; Control comes here for an "OUT" for a byte with an immediate port value + +XMAOUTIMMED: + + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction + +; Get the port address from the instruction. The port address is in the byte +; immediately following the "OUT" op-code. We will load the port address into +; DX. This way when we join the code below it will look like the port address +; was in DX all along. + + MOV AX,HUGE_PTR ; Load DS with a selector that accesses + MOV DS,AX ; all of memory as data + + MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86 + MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP + + DATAOV ; Load ESI (32 bit SI) with the V86 + MOV SI,SS:[BP+BP_IP] ; task's IP + DATAOV + MOV AX,SS:[BP+BP_CS] ; Load EAX with the V86 task's CS + DATAOV ; and then shift left four bits to + SHL AX,4 ; convert it to an offset + DATAOV ; Add the CS offset to "IP" in SI + ADD SI,AX ; SI now contains CS:IP as a 32 bit + ; offset from 0 + ADDROV ; Get the byte after the "OUT" instruc- + LODSB ; tion. This is the port address. + + ADDROV ; Intel bug # A0-119 + NOP ; Intel bug # A0-119 + + SUB DX,DX ; Clear DX to prepare for one byte move + MOV DL,AL ; DX now has the port address + +; Control comes here for an "OUT" for a byte with the port value in DX + +XMAOUT: + MOV AX,SYS_PATCH_DS ; Load DS and ES with the selector for + MOV DS,AX ; our data area + MOV ES,AX + + CMP DX,31A0H ; Is the port address below 31A0H? + JB NOTXMAOUT ; Yes. Then it's not XMA. + + CMP DX,31A7H ; Is the port address above 31A7H? + JA NOTXMAOUT ; Yes. Then it's not XMA. + + LEA DI,XMATTAR ; Point SI to the port requested by + ADD DI,DX ; first pointing it to port 31A0H + SUB DI,31A0H ; and then adding on the difference + ; between 31A0H and the requested port + CMP WORD_FLAG,0 ; Is this a word operation? + JNE PUTWORD ; Yes. Then go put a word. + + MOV AL,BYTE PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AL + STOSB ; register into the "port" + + CMP DX,31A6H ; Is this "OUT" to the bank ID port? + JE CHKCNTRL ; If so, go set the new bank + + CMP DX,31A7H ; Is the "OUT" to the control port? + JE CHKCNTRL ; Affirmative. Go handle control bits. + + CMP DX,31A1H ; Is this "OUT" to the TT index? + ; (high byte) + JBE TTAROUT ; Yup. Go update dependent fields. + + JMP OUTEXIT ; Any other ports just exit. + +; The "OUT" is for a word + +PUTWORD: + MOV AX,WORD PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AX + STOSW ; register into the "port" + + MOV WORD_FLAG,0 ; Reset the word flag + + CMP DX,31A0H ; Is the "OUT" to the TT index port? + JE TTAROUT ; Si. Go update the dependent fields. + + CMP DX,31A2H ; Is the "OUT" to set a block number? + JNE CHKA4 ; No. Go do some more checks. + + MOV XMATTII,AX ; The "OUT" is to 31A2H. Set the auto- + ; increment port to the same value. + ; The two ports should always be in + ; sync. + CALL UPDATETT ; Update the "translate table" with the + ; new block number + JMP OUTEXIT ; That's it. Let's leave. + +CHKA4: + CMP DX,31A4H ; Is "OUT" to the auto-increment port + JNE CHKCNTRL ; No. Then it must be to the bank ID/ + ; control byte port (31A6H). + MOV XMATTIO,AX ; The "OUT is to the auto-increment port + CALL UPDATETT ; Update the "translate table" + INC XMATTAR ; Increment the translate table index + CALL TTARCHANGED ; Update fields that depend on the + ; translate table index + JMP OUTEXIT ; And return to the V86 task + +; The translate table index was changed + +TTAROUT: + CALL TTARCHANGED ; Update fields that depend on the + ; setting of the translate table index + JMP OUTEXITDMA ; Skip flushing the page-translation + ; cache since the page tables have + ; not changed. + +; It's not an XMA "OUT" so pass it on to INDEDMA + +NOTXMAOUT: + JMP DMAOUT + +; The "OUT" is to the bank ID port (31A6H), the control port (31A7H) or both + +CHKCNTRL: + + TEST XMACTL,02H ; Is the virtual enable bit on? + JNZ SETXMA ; Aye. Go make the specified XMA bank + ; active. + DATAOV ; Nay. We simulate disabling the XMA + MOV AX,NORMPAGE ; card by making the normal page + ; tables active. + MOV DI,SGTBLOFF ; This is done by setting the first + DATAOV ; entry in the page directory to + STOSW ; point to the page table for normal + ; memory. + JMP OUTEXIT ; Return to the V86 task + +SETXMA: + AND XMATID,0FH ; Wipe out the high nibble of the bank + MOV AL,XMATID ; ID. XMA only has 16 banks. + DATAOV ; Now multiply by 4K (shift left 12 ;P1C + SHL AX,28 ; bits) to get the offset from the + DATAOV ; base of the XMA page tables of the + SHR AX,28-12 ; page table for the requested bank. + ; Page tables are 4K in length. In + ; the process of shifting we shift the + ; high order 16 bits off the left end + ; of EAX so that they are 0 when we + ; shift back. + DATAOV ; Add on the offset of the base of the + ADD AX,XMAPAGE ; page tables. EAX now has the offset + ; of the page table for the XMA bank. + MOV DI,SGTBLOFF ; Point to the first entry in the page + ; directory. + DATAOV ; Set the first entry in the page + STOSW ; directory to point to the XMA page + ; table + +; Since the page tables have changed we need to purge the page-translation +; cache. "For greatest efficiency in address translation, the processor +; stores the most recently used page-table data in an on-chip cache... The +; existence of the page-translation cache is invisible to applications +; programmers but not to systems programmers; operating-system programmers +; must flush the cache whenever the page tables are changed." +; -- 80386 Programmer's Reference Manual (C) Intel 1986 + +OUTEXIT: + CMOV EAX,CR3 ; Get the page directory base register + NOP ; 386 errata B0-110 + CMOV CR3,EAX ; Write it back to reset the cache + NOP ; 386 errata B0-110 + +OUTEXITDMA: + ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction + JMP POPIO ; Return to the V86 task + +PAGE + +; TTARCHANGED updates all the fields that depend on the translate table index +; in port 31A0H. This is mainly getting the translate table entries for the +; specified index and putting them in the block number ports 31A2H and 31A4H. + +TTARCHANGED PROC + + MOV AX,XMATTAR ; Get the new translate table index + AND AX,0FFFH ; The high nibble is not used + SHL AX,1 ; Change it to a word index. The + ; translate table entries are words. + LEA SI,TTTABLE ; Point SI to the translate table base + ADD SI,AX ; Add on the offset into the table + LODSW ; Get the XMA block number for the + ; specified translate table entry + MOV XMATTIO,AX ; Put it into "port" 31A2H + MOV XMATTII,AX ; Put it into "port" 31A4H + + RET + +TTARCHANGED ENDP + +PAGE + +; UPDATETT will update the "translate table" and the corresponding page +; tables when an XMA block number is written to either port 31A2H or the +; auto-increment port 31A4H. A write to either of these ports means to set +; the XMA block specified at the translate table entry indicated in port +; 31A0H. +; +; The Emulator is set up to look like an XMA 2 card with the INDXMAA device D1C +; driver. When the system comes up the XMA card is initially disabled. D1C +; INDXMAA then backs memory from 0K to 640K on the system board with memory D1C +; from 0K to 640K on the XMA card. To emulate this, the Emulator treats D1C +; real memory from 0K to 640K as XMA blocks from 0K to 640K on the XMA card. D1C +; This both saves memory and requires no code to back the real memory from D1C +; 0K to 640K with XMA memory on initialization. The Emulator therefore only D1C +; needs to allocate XMA memory for the XMA blocks over 640K. The XMA memory D1C +; for over 640K starts at 12000:0. The XMA blocks 00H to 9FH will be mapped D1C +; to the motherboard memory at 0K to 640K. The XMA blocks A0H and up will D1C +; be mapped to the memory at 12000:0 and up. D1C +; +; Bits 15 (IBM bit 0) and 11 (IBM bit 4) of the XMA block number have +; special meanings. When bit 15 is on it means that the block number is a +; 15 bit number. This is in anticipation of larger block numbers in the +; future. Current block numbers are 11 bits. When bit 11 is on it means +; that the XMA translation for this translation table entry should be +; disabled. The memory for this 4K block should be mapped back to real +; memory. +; +; We also check to make sure that the XMA block is not above the XMA memory +; limit. XMA memory ends where the MOVEBLOCK buffer starts. If the XMA +; block is above the end of XMA memory then the page table entry for that +; address is set to point to non-existent memory. +; +; When address translation is disabled for addresses above 640K then the D1C +; page table entry for that address is set to point back to real memory. D1C +; For disabled pages in the range 0K to 640K the page table entry is set to D1C +; point to non-existent memory. D1C + +UPDATETT PROC + + MOV AX,XMATTAR ; Get the index of the TT entry that + ; is to be changed + AND AX,0FFFH ; Clear the high four bits. They are + ; not used. + SHL AX,1 ; Change to a word offset since the TT + ; entries are words. + LEA DI,TTTABLE ; Point DI to the translate table base + ADD DI,AX ; Add on the offset of the entry that + ; is to be changed + MOV AX,XMATTIO ; Get the block number to be written + STOSW ; Store the block number in the TT + +; Convert bank number to a page address. +; The following code works only with paging enabled at 256k boundary. +; It is intended to support up to 128M at 4k granularity. +; It interprets the high order bits as a superset of XMA. +; Following is a truth table for bits 11 (XMA inhibit bit) and 15 ("enable-hi"). +; 15 11 +; 0 0 = enabled 11 bit address +; 0 1 = disabled address +; 1 x = enabled 15 bit address + + TEST AH,80H ; Is this a 15 bit block number? + JZ SMALL ; Far from it. Go do stuff for 11 bit + ; block numbers. + +; We have a 15 bit address + + CMP AX,0FFFFH ; If it's FFFFH then we treat it the + ; the same as 0FFFH which means + JE DISABLEPAGE ; disable the page + + AND AX,7FFFH ; Turn off the 15 bit address bit + JMP BOTH ; leaving a valid block number for + ; our calculations later + +SMALL: + TEST AH,08H ; Is the disable bit on? + JNZ DISABLEPAGE ; Yes. Go disable the page. + + AND AX,07FFH ; No. Turn off the high nibble and the + ; disable bit leaving a valid block + ; number for our upcoming calculations +BOTH: + CMP AX,640/4 ; Is this block number for 640K or over? + JB NOADJUST ; Yup. There's no adjustment @D1C + ; needed for blocks between 0K and + ; 640K since we use real memory for + ; these blocks. + ; XMA 1 emulation code deleted 3@D1D + ADD AX,HIMEM-(640/4) ; Add on the adjustment needed for @D1C + ; blocks above 640K to point to + ; the XMA blocks starting at 12000:0. + ; But don't forget to subtract the + ; block number for 640K. This makes + ; the block number 0 based before we + ; add on the block number for 12000:0. +NOADJUST: + DATAOV ; Shift the high order 16 bits of EAX + SHL AX,16 ; off the left end of the register. + DATAOV ; Now shift the block number back four + SHR AX,16-12 ; bits. This results in a net shift + ; left of 12 bits which converts the + ; block number to an offset, and it + ; clears the high four bits. + OR AL,7 ; Set the access and present bits. This + ; converts our offset to a valid page + ; table entry. + DATAOV ; Save the page table entry in EBX for + MOV BX,AX ; now + +; Now we must make sure the offset of our XMA page frame is within the address +; space of the XMA pages, that is, it is below the start of the MOVEBLOCK +; buffer. + + DATAOV ; Clear all 32 bits of EAX + SUB AX,AX + MOV AX,MAXMEM ; Load up the number of K on the box + SUB AX,BUFF_SIZE ; Subtract the number of K reserved + ; for the MOVEBLOCK buffer + DATAOV ; Multiply by 1K (shift left 10) to + SHL AX,10 ; convert it to an offset + DATAOV ; Is the XMA page address below the + CMP BX,AX ; MOVEBLOCK buffer address? + JB ENABLED ; Yup. Whew! Let's go set up the page + ; table entry for this XMA block. + JMP EMPTY ; Nope. Rats! Well, we'll just have + ; to point this TT entry to unbacked + ; memory. + +; We come here when we want to disable translation for this translate table +; entry. For TT entries for 640K and over we just point the translate table +; entry back to real memory. For TT entries between 0K and 640K we point +; the translate table to unbacked memory. This memory on the motherboard +; was disabled under real XMA so we emulate it by pointing to unbacked +; memory. + +DISABLEPAGE: + ; XMA 1 emulation code deleted 2@D1D + CMP BYTE PTR XMATTAR,640/4 ; Is the address at 640K or above? + JNB SPECIAL ; Aye. Go point back to real memory. + +; The address is between 256K and 640K. Let's set the page table entry to +; point to non-exiatent memory. + +EMPTY: + DATAOV ; Clear EAX + SUB AX,AX + MOV AX,MAXMEM ; Get the total number of K on the box + DATAOV ; Multiply by 1024 to convert to an + SHL AX,10 ; offset. AX now points to the 4K + ; page frame after the end of memory. + OR AL,7 ; Turn on the accessed and present bits + ; to avoid page faults + DATAOV ; Save the page table entry in EBX + MOV BX,AX + JMP ENABLED ; Go set up the page table + +; If the address is above 640K then the translate table (page table) entry D1C +; is set to point back to real memory. +SPECIAL: + MOV AX,XMATTAR ; Get the index of the TT entry that is + ; to be disabled + DATAOV ; Dump the high 24 bits off the left end + SHL AX,24 ; of the register + DATAOV ; Now shift it back 12 bits. This + SHR AX,24-12 ; results in a net shift left of 12 + ; bits which multiplies the TT index + ; by 4K while at the same time clear- + ; ing the high order bits. EAX is now + ; the offset of the memory pointed to + ; by the TT entry. + OR AL,7 ; Turn on the accessed and present bits + ; to make this a page table entry + DATAOV ; Save the page table entry in EBX + MOV BX,AX + +; Now let's put the new page table entry in EBX, which represents the XMA block, +; into the page table. +ENABLED: + MOV AX,XMATTAR ; Get the index of the TT entry + +; Now we want ot convert the index of the TT entry to an offset into our XMA +; page tables. The bank number is now in AH and the 4K block number of the +; bank is in AL. To point to the right page table we need to multiply the bank +; number by 4K (shift left 12 bits) since page tables are 4K in length. The +; bank number is already shifted left 8 bits by virtue of it being in AH. It +; needs to be shifted left four more bits. In order to access the right entry +; in the page table, the block number in AL must be multiplied by 4 (shifted +; left two bits) because the page table entries are 4 bytes in length. So, +; first we shift AH left two bits and then shift AX left two bits. In the end +; this shifts AH, the bank ID, left four bits and shifts AL, the block number, +; two bits, which is what we wanted. A long explanation for two instructions, +; but they're pretty efficient, don't you think? + + SHL AH,2 ; Shift the bank ID left two bits + SHL AX,2 ; Shift the bank ID and the block number + ; left two bits + MOV DI,AX ; Load DI with the offset of the page + ; table entry that is to be changed + PUSH ES ; Save ES + MOV AX,XMA_PAGES_SEL ; Load ES with the selector for our + MOV ES,AX ; XMA page tables. Now ES:DI points + ; to the page table entry to be + ; changed + DATAOV ; Get the new value for the page table + MOV AX,BX ; entry which was saved in EBX. + DATAOV ; Stuff it into the page table - all + STOSW ; 32 bits of it. + + POP ES ; Restore ES + + RET ; And return + +UPDATETT ENDP + +XMAIN ENDP + +PROG ENDS + + END diff --git a/v4.0/src/DEV/XMAEM/INDEXMAE.SYS b/v4.0/src/DEV/XMAEM/INDEXMAE.SYS new file mode 100644 index 0000000..d0fd31a --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEXMAE.SYS @@ -0,0 +1,8 @@ +MZ%  ++V386XMAEM#86XMAEMULATOR11...(û&?t.(PSQRWVU.*.(f`.G<w3.6 +G.>*t +]^_ZY[Xfaû& PD!G OG(G.>*tG G FE!tG G 9F!ð3.h.Ok.A.?.=5$0<0tS8Q+O-&Ȏؾhؿѹ f+f+ۻ f%f+ۻf+f"f+ۻff+ãEf f>Ef+ۻ`f+fGf+ۻfff+ۻffGf+$C>Cf+f+ۻff>Cfff@ы&VO<ы&^`h؎м=6jظ*иظ؎h󪹗hUikx8GfhG=6GhG\GXGTGH.?GP.=G8G&G$.AGL|G G` fGh؋6C++x󥸀f"jPf\X=u D!u du`Q+d$Yw24t0 +t< t<,t<;t <+t<=t< u*t6.O< t< t< +<0r<9w:tu< +3.,0.& .t.>t3f`F4t~$ u~$uF4t &F$.>S3 3 Kh޾+.f FFK