;---------------------------------------------------;
;             AN EXAMPLE PROGRAM OF A               ;
;         V86 MODE MONITOR IMPLEMENTATION           ;
;               BY ALEXEI A. FROUNZE                ;
;                28TH OF MAY, 2000                  ;
;                                                   ;
;       !!! ABSOLUTELY NO ANY WARRANTIES !!!        ;
;                                                   ;
; ! AUTHOR CAN NOT BE RESPONSIBLE FOR ANY DAMAGE, ! ;
;           ! DATA OR HEALTH/LIFE LOSS !            ;
;                                                   ;
; E-Mail:   alexfru@chat.ru                         ;
; Homepage: http://alexfru.chat.ru                  ;
; Mirror:   http://members.xoom.com/alexfru         ;
;                                                   ;
; FEATURES:                                         ;
;   - 32-BIT PROTECTED MODE PROGRAMMING WITH 386+   ;
;   - EXCEPTIONS HANDLING                           ;
;   - TIMER & KEYBOARD HARDWARE INTERRUPTS HANDLING ;
;   - V86 MACHINE EMULATION                         ;
;   - LAUNCHES A COMMAND INTERPRETER (COMMAND.COM)  ;
;   - SUPPORTS BIOS INT15 FUNCTION 87H              ;
;                                                   ;
; SYSTEM REQUIREMENTS:                              ;
;   - 386 OR BETTER COMPUTER                        ;
;   - (S)VGA COLOR VIDEO SYSTEM                     ;
;   - DOS 5.0 OR BETTER (WITHOUT ANY MEMORY         ;
;     MANAGERS SUCH AS HIMEM.SYS AND EMM386.EXE)    ;
;---------------------------------------------------;

        .MODEL   LARGE

        INCLUDE MACROSES.INC                    ; MAIN DEFINITIONS

        NESTED_INTS     EQU     8               ; DEPTH OF NESTED IRQS FOR RM/VM

SIZE_STACK      EQU     1000H

;-----------------------------------------;
; 16-BIT REAL/PROTECTED NODE CODE SEGMENT ;
;-----------------------------------------;

.8086
CODE16  SEGMENT PARA USE16
CODE16_BEGIN    =       $
        ASSUME          CS:CODE16,DS:DATA,ES:DATA
START:
        MOV     AX, DATA
        MOV     DS, AX
        MOV     ES, AX                          ; LOAD DATA SEGMENTS

        LEA     DX, MSG_STARTUP
        MOV     AH, 9
        INT     21H

        CALL    DETECT
.386P
        @ENABLE_A20                             ; ENABLE A20 LINE
        IN      AL, PORT_INT_MASK_M
        MOV     INT_MASK_M, AL                  ; SAVE MASK OF HARDWARE INTS AT MASTER CNTRLR
        IN      AL, PORT_INT_MASK_S
        MOV     INT_MASK_S, AL                  ; SAVE MASK OF HARDWARE INTS AT SLAVE CNTRLR
        CLI                                     ; DISABLE MASKABLE INTS
        @DISABLE_NMI                            ; DISABLE NON-MASKABLE INTS

        INCLUDE SETUPGDT.INC                    ; FILL GDT
        INCLUDE SETUPIDT.INC                    ; FILL IDT
        LGDT    FWORD PTR GDT_GDT               ; LOAD GDTR
        LIDT    FWORD PTR IDTR                  ; LOAD IDTR

        MOV     EAX, CR0
        AND     EAX, 01111111111111111111111111110000B
        OR      EAX, 00000000000000000000000000000001B
        MOV     CR0, EAX                        ; SET UP PROTECTED MODE

        @JUMP                                   ; OVERLOAD CODE SELECTOR
        MOV     AX, DS_SEL
        MOV     DS, AX                          ; LOAD DS WITH DATA SELECTOR
        MOV     ES, AX                          ; LOAD ES WITH DATA SELECTOR
        MOV     AX, SS_SEL
        MOV     SS, AX                          ; LOAD SS WITH STACK SELECTOR

        XOR     AX, AX
        MOV     FS, AX                          ; LOAD FS WITH 0
        MOV     GS, AX                          ; LOAD GS WITH 0
        LLDT    AX                              ; LOAD LDTR WITH 0

        MOVZX   ESP, SP                         ; EXPAND SP TO ESP
        PUSH    CS                              ; PREPARE FOR RETURNING BACK
        PUSH    OFFSET START_BACK_TO_16         ; TO 16-BIT PM CODE
        MOV     DS:[ESP32], ESP

        LEA     EDI, ENTER_32
        MOV     EAX, CS32_SEL
        PUSH    EAX
        PUSH    EDI                             ; PUSH 32-BIT CODE ADDRESS

        INCLUDE SETUPTSS.INC                    ; FILL TSSs
        MOV     AX, TSS_MAIN_SEL
        LTR     AX                              ; LOAD TR REGISTER

        @SET_INT_CTRLR 20H, 28H                 ; REINITIALISE MASTER & SLAVE INT CONTROLLERS
        MOV     AL, 0                           ; MASK = 0
        OUT     PORT_INT_MASK_M, AL             ; UNMASK INTS
        OUT     PORT_INT_MASK_S, AL             ; UNMASK INTS
        @ENABLE_NMI                             ; ENABLE NON-MASKABLE INTS
        STI                                     ; ENABLE MASKABLE INTS

        @RETF                                   ; "RETURN" TO 32-BIT CODE

START_BACK_TO_16:
        CLI                                     ; DISABLE MASKABLE INTS
        @DISABLE_NMI                            ; DISABLE NON-MASKABLE INTS
        MOV     AX, DS_SEL
        MOV     DS, AX                          ; SAFETY IS WHAT WE NEED!!!

        INCLUDE SET_RM1.INC                     ; BACK TO REAL MODE

        MOV     AL, INT_MASK_M
        OUT     PORT_INT_MASK_M, AL             ; RESTORE MASK OF HARDWARE INTS AT MASTER CNTRLR
        MOV     AL, INT_MASK_S
        OUT     PORT_INT_MASK_S, AL             ; RESTORE MASK OF HARDWARE INTS AT SLAVE CNTRLR
        @ENABLE_NMI                             ; ENABLE NON-MASKABLE INTS
        STI                                     ; ENABLE MASKABLE INTS
        @DISABLE_A20                            ; DISABLE A20 LINE

START_EXIT:
        LEA     DX, MSG_CONTACT
        MOV     AH, 9
        INT     21H
        MOV     AX, 4C00H
        INT     21H                             ; EXIT TO DOS
        INCLUDE DETECT.INC                      ; HARDWARE DETECTION CODE
        INCLUDE LCOMMAND.INC                    ; COMMAND.COM LAUNCHER

;-------------------------;
; V86 TASK CODE GOES HERE ;
;-------------------------;

V86_START:

; LAUNCH COMMAND.COM

        CALL    LCOMMAND
        MOV     AX, 0
        DIV     AX                                      ; "EXIT" ;)

SIZE_CSEG16     =       ($ - CODE16_BEGIN)
CODE16  ENDS

;--------------------------------------;
; HELPFUL CODE SEGMENT FOR V86 MONITOR ;
;--------------------------------------;

CODE_V86_IRET SEGMENT PARA USE16
        ASSUME CS:CODE_V86_IRET
V86_INT_X:
        INT     0
CODE_V86_IRET ENDS

;------------------------------------;
; 32-BIT PROTECTED MODE CODE SEGMENT ;
;------------------------------------;

CODE32  SEGMENT PARA USE32
CODE32_BEGIN    =       $
        ASSUME          CS:CODE32,DS:DATA,ES:DATA

;--------------------;
; MAIN TASK (RING 0) ;
;--------------------;

ENTER_32:
        MOV     AX, DS_SEL
        MOV     DS, AX
        MOV     WORD PTR DS:[TASK_ADDR+4], TSS_V86_SEL
        JMP     FWORD PTR DS:[TASK_ADDR]        ; JUMP TO THE V86 TASK

        @RETF                                   ; BACK TO 16-BIT CODE

;----------------------------------------------;
; MAIN ROUTINES AND HANDLERS ARE INCLUDED HERE ;
;----------------------------------------------;

        INCLUDE SCREENIO.INC                    ; SCREEN I/O ROUTINES
        INCLUDE EXC.INC                         ; EXCEPTION HANDLER
        INCLUDE TIMER.INC                       ; KEYBOARD INTERRUPT HANDLER
        INCLUDE KEYBOARD.INC                    ; ROUTINE FOR DISPLAYING TSS
        INCLUDE IRQS.INC                        ; TRAPPINGS TO V86
        INCLUDE SERVICE.INC                     ; SERVICE INTERRUPT
SIZE_CSEG32     =       ($ - CODE32_BEGIN)
CODE32  ENDS

;-----------------------------------------;
; COMMON REAL/PROTECTED MODE DATA SEGMENT ;
;-----------------------------------------;

DATA    SEGMENT PARA USE16
DATA_BEGIN      =       $
        INCLUDE GDT.INC                         ; GDT TABLE
        INCLUDE IDT.INC                         ; IDT TABLE
        INCLUDE TSS.INC                         ; TSS TABLES

;----------------------------;
; LAUNCH-TIME ERROR MESSAGES ;
;----------------------------;

MSG_STARTUP     DB      "Example of V86 monitor by Alexei A. Frounze (C) 2000",13,10,"$"
MSG_CONTACT     DB      "Contact information:",13,10
                DB      "E-mail  : alexfru@chat.ru",13,10
                DB      "Homepage: http://alexfru.chat.ru",13,10
                DB      "Mirror  : http://members.xoom.com/alexfru",13,10,"$"
MSG_NEEDED_386 DB       "80386 OR BETTER PROCESSOR IS NEEDED.",13,10,"$"
MSG_NEEDED_RM DB        "PROCESSOR IS ALREADY IN PROTECTED MODE.",13,10
        DB              "PROBABLY WINDOWS OR EMM386.EXE IS LOADED.",13,10
        DB              "LOAD PURE DOS IN REAL MODE.",13,10,"$"
MSG_FOUND_HIMEM DB      "HIMEM.SYS IS INSTALLED. DON'T LOAD IT PLEASE.",13,10,"$"
MSG_NEEDED_VGA DB       "VGA OR SVGA(VESA COMPATIBLE) VIDEO ADAPTER IS NEEDED.",13,10,"$"

; HEXADECIMAL TABLE FOR CONVERTING BCD TO TEXT
HEX_TAB DB              "0123456789ABCDEF"

;------------------;
; SYSTEM VARIABLES ;
;------------------;

INT_MASK_M DB           ?
INT_MASK_S DB           ?
ESP32   DD              ?

IDTR0   DF              0               ; DUMMY IDTR FOR SWITCHING TO REAL MODE
IDTR1   DF              3FFH

EXT_MEMORY DD           ?

TIMER_CNT DW            0
KEY_SCAN_CODE DB        0
KBD_SEQ DB              0

;------------------;
; FLAGS AND TABLES ;
;------------------;

; FLAG TABLE FOR EXCEPTIONS. 1 MEANS EXCEPTION HAS ERROR CODE, 0 - NO CODE.
EXC_ERR DB              0,0,0,0,0,0,0,0, 1,0,1,1,1,1,1,0, 0,1

; IRQ PASS TABLE #1. 1 MEANS PASSING IRQ TO V86 IS ENABLED, 0 - DISABLED.
PASS_IRQ LABEL          BYTE
        DB              1                       ; IRQ0  (Timer)
        DB              1                       ; IRQ1  (Keyboard)
        DB              1                       ; IRQ2  (Cascade)
        DB              1                       ; IRQ3  (Com 2/4)
        DB              1                       ; IRQ4  (Com 1/3)
        DB              1                       ; IRQ5  (LPT 2)
        DB              1                       ; IRQ6  (FDD)
        DB              1                       ; IRQ7  (LPT 1)
        DB              1                       ; IRQ8  (CMOS)
        DB              1                       ; IRQ9  (Redirected IRQ2)
        DB              1                       ; IRQ10 (Free)
        DB              1                       ; IRQ11 (Free)
        DB              1                       ; IRQ12 (Free)
        DB              1                       ; IRQ13 (FPU)
        DB              1                       ; IRQ14 (HDD)
        DB              1                       ; IRQ15 (Free)

; IRQ PASS TABLE #2. 0 MEANS NO PMODE IRQ HANDLER, 1 - PMODE HANDLER EXIST.
IRQ_ADDR LABEL          DWORD
        XXX = OFFSET TIMER_HANDLER
        DD              XXX
        XXX = OFFSET KEYBOARD_HANDLER
        DD              XXX
        DD              14 DUP(0)

;--------------------;
; EXCEPTION MESSAGES ;
;--------------------;

MSG_EXC DB              "EXCEPTION: ",0
MSG_EXC_ERR DB          "ERROR CODE: ",0
MSG_CSEIP DB            "CS=XXXXXXXX EIP=XXXXXXXX",0
MSG_CR0 DB              "CR0=XXXXXXXX",0

;-------------------------------;
; STACK VARS FOR V86 INTERRUPTS ;
;-------------------------------;

NESTED_SP DD            0
NESTED_STACK LABEL DWORD
        DD              NESTED_INTS*32 DUP(0)
INT_SP  DW              NESTED_INTS*1024

;------------------------------------;
; DATA FOR LAUNCHING THE COMMAND.COM ;
;------------------------------------;

DTA_OFS	DW		?
DTA_SEG	DW		?

CMD_LINE LABEL BYTE
CMD     DB              CMD_LEN
CMD_BEGIN =             $
;        DB              "/C DIR|MORE"
CMD_LEN =               $ - CMD_BEGIN
        DB              0DH,0

FCB1	DB		37 DUP (0)
FCB2	DB		37 DUP (0)

R_EXEC	S_EXEC		<0, CMD,DATA, FCB1,DATA, FCB2,DATA>

COMNAME	DB		128 DUP (0)		;FULL NAME OF COMMAND.COM
COMSPEC	DB		"COMSPEC="		;NAME OF SEARCHING VARIABLE

KEEP_SP	DW		?
KEEP_SS	DW		?

SIZE_DSEG       =       ($ - DATA_BEGIN)
DATA    ENDS

;-----------------------------------;
; REAL/PROTECTED MODE STACK SEGMENT ;
;-----------------------------------;

SSTACK  SEGMENT PARA STACK
        DB      SIZE_STACK DUP (?)
SSTACK  ENDS

;----------------------------;
; STACK SEGMENT FOR V86 INTS ;
;----------------------------;

STCK_INT_V86 SEGMENT PARA
        DB      NESTED_INTS*1024 DUP (?)
STCK_INT_V86 ENDS

;------------------------;
; TASK V86 STACK SEGMENT ;
;------------------------;

STCK_V86 SEGMENT PARA
        DB      SIZE_STACK DUP (?)
STCK_V86 ENDS

END     START
