        Page    60,132
;-----------------------------------------------------------------------------
;                          BEGIN LISTING 3
;-----------------------------------------------------------------------------
;
; EMULOAD.ASM
;
;       Copyright (c) 1991, 1995-Present  Robert Collins
;
;       You have my permission to copy and distribute this software for
;       non-commercial purposes.  Any commercial use of this software or
;       source code is allowed, so long as the appropriate copyright
;       attributions (to me) are intact, *AND* my email address is properly
;       displayed.
;
;       Basically, give me credit, where credit is due, and show my email
;       address.
;
;-----------------------------------------------------------------------------
;
;       Robert R. Collins               email:  rcollins@x86.org
;
;-----------------------------------------------------------------------------
;
;       This utility uses '386 LOADALL to emulate '286 LOADALL.
;       All 16-bit registers are zero-extended to 32-bit
;       registers.  All 24-bit physical addresses are zero-
;       extended to 32-bit registers.  '386-specific registers
;       not used in '286 LOADALL are either set to the current
;       values (Debug registers), or zeroed (segment registers).
;
;---------------------------------------------------------------
;
;       This program assumes that you have run the '386 LOADALL
;       test prior to installing this TSR.  Obviously if LOADALL
;       has been removed from the '386 mask, then this program
;       will never work.  Likewise, it is easier for me to
;       document the need to run the LOADALL test program, than
;       to incorporate it into this code.
;
;---------------------------------------------------------------
;
;       EMULOAD returns ERROR codes to DOS that can be
;       intecepted by the batch file command 'IF ERRORLEVEL'.
;       The following ERRORLEVEL codes are generated by this
;       program:
;         0 = EMULOAD driver now installed in memory
;         1 = Attempted removal of the EMULOAD driver from
;             memory failed because EMULOAD was not in already
;             in memory.
;         2 = The EMULOAD driver was already in memory when an
;             attempt was made to install it again.
;         3 = Bogus command line argument(s).
;         4 = Help requested.
;         5 = The EMULOAD driver was sucessfully removed from
;             memory.
;         6 = Can't install the EMULOAD driver because this
;             computer isn't an 80386.
;
;---------------------------------------------------------------
;
;       Compilation instructions:
;               MASM EMULOAD;           (MASM 5.1)
;               LINK EMULOAD;
;               EXE2BIN EMULOAD EMULOAD.COM
;               DEL EMULOAD.EXE
;
;       The resultant EMULOAD.COM file is 1473 bytes, while the
;       TSR portion is 1072 bytes.
;
;---------------------------------------------------------------
; Compiler directives
;---------------------------------------------------------------
        Title   EMULOAD
        .radix  16
        .8086

;---------------------------------------------------------------
; Interrupt vector segment
;---------------------------------------------------------------
ABS0    segment at 0
        org 6*4
        INT_6   dd      ?

        org 800h
        Loadall_286     dd      ?
ABS0    ends


;---------------------------------------------------------------
; Structure definitions
;---------------------------------------------------------------
Desc_cache2     STRUC                   ; 80286 Descriptor cache
        A15_A00 dw      ?               ;  register layout.
        A23_A16 db      ?
        _Type2  db      ?
        _Limit2 dw      ?
Desc_cache2     ENDS


Desc_cache3     STRUC                   ; 80386 Descriptor cache
        _Access db      0               ;  register layout
        _Type   db      ?
        _CS32   db      0
                db      0
        _Addr   dd      ?
        _Limit  dd      ?
Desc_cache3     ENDS



Loadall_struc2  STRUC                   ; 80286 LOADALL table
                        dw      3 dup (?)       ; RESERVED
        _286Msw         dw      ?               ; MSW
                        dw      7 dup (?)       ; RESERVED
        _286Tr          dw      ?               ; TR
        _Flags          dw      ?               ; FLAGS
        _286Ip          dw      ?               ; IP
        _286Ldt         dw      ?               ; LDT
        _286Ds          dw      ?               ; DS
        _286Ss          dw      ?               ; SS
        _286Cs          dw      ?               ; CS
        _286Es          dw      ?               ; ES
        _286Di          dw      ?               ; DI
        _286Si          dw      ?               ; SI
        _286Bp          dw      ?               ; BP
        _286Sp          dw      ?               ; SP
        _286Bx          dw      ?               ; BX
        _286Dx          dw      ?               ; DX
        _286Cx          dw      ?               ; CX
        _286Ax          dw      ?               ; AX
        ES_Desc286      dw      3 dup (?)       ; ES Desc. Cache
        CS_Desc286      dw      3 dup (?)       ; CS Desc. Cache
        SS_Desc286      dw      3 dup (?)       ; SS Desc. Cache
        DS_Desc286      dw      3 dup (?)       ; DS Desc. Cache
        Gdt_Desc286     dw      3 dup (?)       ; GDTR
        Ldt_Desc286     dw      3 dup (?)       ; LDTR
        Idt_Desc286     dw      3 dup (?)       ; IDTR
        TSS_Desc286     dw      3 dup (?)       ; TSSR
Loadall_Struc2  ENDS

Loadall_struc3  STRUC
        _Cr0            dd      ?               ; EAX
        _Eflags         dd      ?               ; EFLAGS
        _Eip            dd      ?               ; EIP
        _Edi            dd      ?               ; EDI
        _Esi            dd      ?               ; ESI
        _Ebp            dd      ?               ; EBP
        _Esp            dd      ?               ; ESP
        _Ebx            dd      ?               ; EBX
        _Edx            dd      ?               ; EDX
        _Ecx            dd      ?               ; ECX
        _Eax            dd      ?               ; EAX
        _Dr6            dd      ?               ; DR6
        _Dr7            dd      ?               ; DR7
        _Tr             dd      ?               ; TR
        _Ldt            dd      ?               ; LDT
        _Gs             dd      ?               ; GS
        _Fs             dd      ?               ; FS
        _Ds             dd      ?               ; DS
        _Ss             dd      ?               ; SS
        _Cs             dd      ?               ; CS
        _Es             dd      ?               ; ES
        TSS_Desc        dd      3 dup (?)       ; TSSR
        IDT_Desc        dd      3 dup (?)       ; IDTR
        Gdt_Desc        dd      3 dup (?)       ; GDTR
        Ldt_Desc        dd      3 dup (?)       ; LDTR
        GS_Desc         dd      3 dup (?)       ; GS Desc. Cache
        FS_Desc         dd      3 dup (?)       ; FS Desc. Cache
        DS_Desc         dd      3 dup (?)       ; DS Desc. Cache
        SS_Desc         dd      3 dup (?)       ; SS Desc. Cache
        CS_Desc         dd      3 dup (?)       ; CS Desc. Cache
        ES_Desc         dd      3 dup (?)       ; ES Desc. Cache
                        dd      0ah dup (?)     ; RESERVED
Loadall_Struc3  ENDS

INT_VEC         STRUC
        int_offset      dw      ?
        int_segment     dw      ?
INT_VEC         ENDS


;---------------------------------------------------------------
; Equate definitions
;---------------------------------------------------------------
        LOADALL286      equ     050fh
        CRLF            equ     <0dh,0ah>
        CRLF$           equ     <0dh,0ah,'$'>
        INT6            equ     [bp-4]


;---------------------------------------------------------------
; Macro definitions
;---------------------------------------------------------------
        LOADALL_386     MACRO
                db      0fh,07h
        ENDM


        PRINT_STRING    MACRO   MSG_NAME
        mov     ah,9
        mov     dx,offset MSG_NAME
        int     21h
        ENDM



_TEXT   SEGMENT PARA PUBLIC 'CODE'
        Assume  CS:_TEXT, DS:_TEXT, ES:_TEXT, SS:_TEXT
        Org     100h
        .386p
;---------------------------------------------------------------
  Emulate_286_Loadall   Proc    Far
;---------------------------------------------------------------
        jmp     EMULOAD         ; goto beginning instruction

Align 4
;---------------------------------------------------------------
; Local Data
;---------------------------------------------------------------
Loadall_tbl     Loadall_Struc3 <>

emuload_msg     db      "80286 LOADALL EMULATOR utility.",CRLF
                db      "Version 1.0 Only for 80386 computers."
                db      CRLF
                db      "Copyright (c) 1991 Robert Collins."
                db      CRLF$
emu_msg_len     equ     $-emuload_msg

align 4
;---------------------------------------------------------------
; TSR Code begins here as an INT06 replacement.
;---------------------------------------------------------------
Int06:  push    bp
        mov     bp,sp
        push    si
        push    ds
        lds     si,[bp][2]              ; get CS:IP of bogus
                                        ;  opcode
        cmp     word ptr [si],LOADALL286; was it LOADALL?
        jne     @Not_LOADALL            ; nope
        mov     di,0
        mov     ds,di
        mov     di,cs
        mov     es,di
        mov     edi,offset Loadall_tbl

Assume  DS:ABS0, ES:_TEXT, SS:NOTHING
;---------------------------------------------------------------
; Convert 80286 registers to 80386 counterparts.  The sequencing
; order follows the 80386 LOADALL table.
;---------------------------------------------------------------
; While mapping MSW to CR0, bit5 in CR0 is documented as
; RESERVED on the '386 DX, and '1' on the '386 SX.  Bit6 is
; defined as 'NE' (Numeric Exception) on the '486.  If we wanted
; this code to work on the '486, then we should mask the lower
; nibble of MSW with CR0.  But the '486 doesn't have LOADALL,
; so this isn't necesary.  Next consider the Reserved bit5 on
; the '386 DX.  Since LOADALL completely redefines the CPU
; state, it is safe to clear this reserved bit instead of
; masking it with MSW.
;---------------------------------------------------------------
        mov     eax,cr0                 ; MSW   --> CR0
        mov     ax,Loadall_286._286Msw
        mov     Loadall_tbl._CR0,eax
        movzx   eax,Loadall_286._Flags  ; FLAGS --> EFLAGS
        mov     Loadall_tbl._EFLAGS,eax

;---------------------------------------------------------------
; Hereafter MOVZX isn't needed because the upper 16-bits are
; guaranteed to be 0.
;---------------------------------------------------------------
        mov     ax,Loadall_286._286IP   ; IP    --> EIP
        mov     Loadall_tbl._EIP,eax
        mov     ax,Loadall_286._286DI   ; DI    --> EDI
        mov     Loadall_tbl._EDI,eax
        mov     ax,Loadall_286._286SI   ; SI    --> ESI
        mov     Loadall_tbl._ESI,eax
        mov     ax,Loadall_286._286BP   ; BP    --> EBP
        mov     Loadall_tbl._EBP,eax
        mov     ax,Loadall_286._286SP   ; SP    --> ESP
        mov     Loadall_tbl._ESP,eax
        mov     ax,Loadall_286._286BX   ; BX    --> EBX
        mov     Loadall_tbl._EBX,eax
        mov     ax,Loadall_286._286DX   ; DX    --> EDX
        mov     Loadall_tbl._EDX,eax
        mov     ax,Loadall_286._286CX   ; CX    --> ECX
        mov     Loadall_tbl._ECX,eax
        mov     ax,Loadall_286._286AX   ; AX    --> EAX
        mov     Loadall_tbl._EAX,eax

;---------------------------------------------------------------
; DR6 & DR7 aren't in the '286, so let's use the current values.
; By keeping the current values, guarantees that any ICE
; breakpoints, or debug register breakpoints are preserved.
; (ICE breakpoints use (at least) the upper two of the
; 'RESERVED' bits in DR7.
;---------------------------------------------------------------
        mov     eax,dr6                 ; Keep DR6
        mov     Loadall_tbl._DR6,eax
        mov     eax,dr7                 ; Keep DR7
        mov     Loadall_tbl._DR7,eax

        movzx   eax,Loadall_286._286TR  ; TR    --> TR
        mov     Loadall_tbl._TR,eax
        mov     ax,Loadall_286._286LDT  ; LDT   --> LDT
        mov     Loadall_tbl._LDT,eax

;---------------------------------------------------------------
; FS & GS aren't in the '286, so let's zero them out.
;---------------------------------------------------------------
        xor     ax,ax
        mov     Loadall_tbl._GS,eax     ; Clear GS
        mov     Loadall_tbl._FS,eax     ; Clear FS

        mov     ax,Loadall_286._286DS   ; DS    --> DS
        mov     Loadall_tbl._DS,eax
        mov     ax,Loadall_286._286SS   ; SS    --> SS
        mov     Loadall_tbl._SS,eax
        mov     ax,Loadall_286._286CS   ; CS    --> CS
        mov     Loadall_tbl._CS,eax
        mov     ax,Loadall_286._286ES   ; ES    --> ES
        mov     Loadall_tbl._ES,eax

;-----------------------------------------------------------
; Convert '286 descriptor cache register entries to '386
; format.
;-----------------------------------------------------------
        mov     esi,offset Loadall_286.TSS_Desc286
        mov     edi,offset Loadall_tbl.TSS_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.IDT_Desc286
        mov     edi,offset Loadall_tbl.IDT_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.GDT_Desc286
        mov     edi,offset Loadall_tbl.GDT_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.LDT_Desc286
        mov     edi,offset Loadall_tbl.LDT_Desc
        call    CVT_Desc

;-----------------------------------------------------------
; Fill in FS & GS descriptor cache entires with 0.
;-----------------------------------------------------------
        mov     Loadall_tbl.GS_Desc._Type,93h
        mov     Loadall_tbl.GS_Desc._Addr,0
        mov     Loadall_tbl.GS_Desc._Limit,0ffffh
        mov     Loadall_tbl.FS_Desc._Type,93h
        mov     Loadall_tbl.FS_Desc._Addr,0
        mov     Loadall_tbl.FS_Desc._Limit,0ffffh

;-----------------------------------------------------------
; Convert '286 descriptor cache register entries to '386
; format.
;-----------------------------------------------------------
        mov     esi,offset Loadall_286.DS_Desc286
        mov     edi,offset Loadall_tbl.DS_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.SS_Desc286
        mov     edi,offset Loadall_tbl.SS_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.CS_Desc286
        mov     edi,offset Loadall_tbl.CS_Desc
        call    CVT_Desc
        mov     esi,offset Loadall_286.ES_Desc286
        mov     edi,offset Loadall_tbl.ES_Desc
        call    CVT_Desc
        mov     edi,offset Loadall_tbl
        LOADALL_386
        HLT                     ; This instruction never
                                ;  gets executed

@Not_LOADALL:
        pop     ds
        pop     si
        pop     bp

Orig_int06:
        jmp     far ptr INT_6
Emulate_286_Loadall     endp


;---------------------------------------------------------------
  CVT_Desc      proc    near    ; Convert '286 descriptor table
;                               ; cache register format to '386
;                               ; format.
;---------------------------------------------------------------
; Input:   DS:ESI = Pointer to '286 descriptor cache entry
;          DS:EDI = Pointer to '386 descriptor cache entry
; Output:  None
; Register(s) modified:  EAX, EBX, ECX
;---------------------------------------------------------------
        mov     eax,[esi]               ; get 24-bit base &
                                        ;  access rights
        mov     ebx,eax                 ; make a copy
        movzx   ecx,[esi]._Limit2       ; get 16-bit limit
        rol     eax,8                   ; put access in AL
        and     ebx,00ffffffh           ; make 24-bit address
        mov     ES:[edi]._Type,al       ; store Access
        mov     ES:[edi]._Addr,ebx      ; store Address
        mov     ES:[edi]._Limit,ecx     ; store Limit
        ret
CVT_Desc        endp

TSR_End label   word
;---------------------------------------------------------------
; End of TSR program
;---------------------------------------------------------------


;---------------------------------------------------------------
; Local DATA used for initialization code only.
;---------------------------------------------------------------
bogus_msg1      db      "Unrecognized command line argument."
                db       CRLF$
bogus_msg2      db       "Not 80386 computer.",7,CRLF$

driver_msg1     db      "Resident driver installed."
                db       CRLF$
driver_msg2     db      "Resident driver already installed."
                db       7,CRLF$
driver_msg3     db      "Resident driver removed from memory."
                db       CRLF$
driver_msg4     db      "Resident driver was not already "
                db      "installed",7,CRLF$
help_msg        db       CRLF
                db      "Syntax:  EMULOAD",CRLF
                db      "         EMULOAD -R (to remove from "
                db      "memory)",CRLF$


ASSUME  DS:_TEXT
;---------------------------------------------------------------
  EMULOAD       proc    near    ; Beginning of initialization
;                               ; code as the NON-TSR part of
;                               ; the program.
;---------------------------------------------------------------
        cld                             ; clear direction flag
        Print_String    emuload_msg     ; Print initialization
                                        ;  message.

;---------------------------------------------------------------
; Check CPU type
;---------------------------------------------------------------
        call    CPU_TYPE        ; Get CPU type
        and     al,0fh          ; mask out CPU sub-type
        cmp     al,3            ; 80386?
        jz      short @F        ; yes
        Print_String    Bogus_msg2      ; Not 80386 computer
        mov     ax,4c06h                ; set function to DOS
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; Check command line argument
;---------------------------------------------------------------
@@:     xor     ax,ax                   ; clear AX
        mov     si,80h                  ; get start of PSP
        lodsb                           ; get command line len.
        or      ax,ax                   ; Any command line args?
        jz      short Installed?        ; nope
        mov     cx,ax                   ; put into counter
        mov     di,si                   ;
        mov     al,' '                  ; skip past superfluous
        repz    scasb                   ;   blank characters
        cmp     byte ptr [di],0dh       ; are we at the end?
        jz      short Installed?        ; yep
        cmp     byte ptr [di-1],'-'     ; check if it's a switch
        jnz     short @F                ; if not, then error
        mov     si,di                   ; get pointer
        lodsb                           ; get cmd line switch
        cmp     al,'r'                  ; remove driver?
        jz      short remove_driver     ; yep
        cmp     al,'R'                  ; remove driver?
        jz      short remove_driver     ; go remove driver
        cmp     al,'?'                  ; help?
        jnz     short @F                ; nope
        Print_String    help_msg        ; Print help message
        mov     ax,4c04h                ; set return code
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; Bogus command line argument
;---------------------------------------------------------------
@@:     Print_String    bogus_msg1      ; Invalid command line
        mov     ax,4c03h                ; set function code
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; Remove driver from memory
;---------------------------------------------------------------
remove_driver:
        call    check_installed         ; Driver installed?
        jnz     short @F                ; driver not installed
        mov     bp,sp                   ; create stack frame
        push    ds                      ; save (DS)
        mov     dx,ABS0                 ; get bottom of memory
        mov     ds,dx                   ; make segment register

ASSUME  DS:ABS0, ES:_TEXT
;---------------------------------------------------------------
; Restore original INT6 vector
;---------------------------------------------------------------
; We can restore the original INT6 by getting the vector from
; our current memory resident driver -- not the DS from the
; code now executing.  The original DS is the same as the code
; segment for our EMULOAD driver.  Hence we only need to get
; the original segment value from the memory resident image.
; And we get this by looking at the segment for INT6!
;---------------------------------------------------------------
        mov     es,int_6.int_segment            ; Original DS
        mov     ax,es:orig_int06[1].int_offset  ; Original INT6
        mov     bx,es:orig_int06[1].int_segment ;    "      "
        mov     int_6.int_offset,ax             ; Restore orig.
        mov     int_6.int_segment,bx            ;  INT6

;---------------------------------------------------------------
; Free memory pointed to by ES
;---------------------------------------------------------------
        mov     ah,49h                  ; DOS FREE_MEM function
        int     21h                     ; free allocated memory
        mov     ds,[bp-2]               ; get original (DS)

ASSUME  DS:_TEXT
;---------------------------------------------------------------
; Now split with TSR removed from memory.
;---------------------------------------------------------------
        Print_String    driver_msg3     ; Driver removed
        mov     ax,4c05h                ; set function to DOS
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; If EMULOAD was not in memory, then come here and split with
; the error code.
;---------------------------------------------------------------
@@:     Print_String    driver_msg4     ; Driver not installed
        mov     ax,4c01h                ; set function to DOS
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; Check for driver already installed
;---------------------------------------------------------------
Installed?:
        call    check_installed         ; check if driver is
        jnz     short @F                ;  already installed?

;---------------------------------------------------------------
; Driver already installed
;---------------------------------------------------------------
        Print_String    driver_msg2     ; Driver already inst.
        mov     ax,4c02h                ; set function to DOS
        int     21h                     ; exit to DOS

;---------------------------------------------------------------
; Driver not yet installed
;---------------------------------------------------------------
@@:     Print_String    driver_msg1     ; Driver now installed

;---------------------------------------------------------------
; Install driver into memory
;---------------------------------------------------------------
        xor     dx,dx                   ; Point to INT. vectors
        mov     ds,dx                   ; complete the move
ASSUME  ds:ABS0

;---------------------------------------------------------------
; Chain to INT6 by replacing and saving the original INT6
; vector.
;---------------------------------------------------------------
        mov     ax,int_6.int_offset             ; Orig. offset
        mov     bx,int_6.int_segment            ; Orig. segment
        mov     orig_int06[1].int_offset,ax     ; save old INT6
        mov     orig_int06[1].int_segment,bx    ;  vector.

;---------------------------------------------------------------
; Now replace the original INT6 vector.
;---------------------------------------------------------------
        mov     dx,offset cs:int06      ; Get new INT6 vector
        mov     int_6.int_offset,dx     ;  as CS:INT6
        mov     int_6.int_segment,cs    ;

ASSUME  DS:_TEXT
;---------------------------------------------------------------
; Terminate and Stay Resident
;---------------------------------------------------------------
        mov     dx,cs                   ; make DS=CS
        mov     ds,dx
        mov     es,ds:[2ch]             ; get DOS env. segment
        mov     ah,49h                  ; release memory func.
        int     21h                     ; release memory
        mov     dx,offset tsr_end       ; get ending address
        shr     dx,4                    ; divide by 16
        adc     dx,1                    ; check for remainder;
                                        ;  add 1
        mov     ax,3100h                ; set return code to DOS
        int     21h
EMULOAD         endp


ASSUME  ES:ABS0
;---------------------------------------------------------------
; Check to see if the EMULOAD driver is installed in memory.
; It is possible to check if a TSR program is already installed
; in memory by looking for a semaphore in the memory image.
; Luckily we can locate the memory image of our TSR by looking
; at the current INT6 vector.  The INT6 code segment is the
; segment of the TSR!  So this routine looks in this segment
; for the inital banner message:
;
;   80286 LOADALL EMULATOR utility.
;   Version 1.0 Only for 80386 computers.
;   Copyright (c) 1991 Robert Collins.
;
; If this message is found, then the TSR is in memory.  If
; another TSR has chained to the same INT6 vector, this
; technique will fail to find EMULOAD, as it very well should!
;---------------------------------------------------------------
  Check_installed       proc    near
;---------------------------------------------------------------
; Input:   None
; Output:  NZ if NOT installed
;          ZF if ALREADY installed
; Register(s) modified:  CX, SI, DI
;---------------------------------------------------------------
        push    es                      ; save (ES)
        mov     cx,ABS0                 ; get bios data segment
        mov     es,cx                   ; put in (ES)
        mov     cx,emu_msg_len          ; # of bytes to compare
        mov     si,offset emuload_msg   ; get address of message
        les     di,ES:INT_6             ; get INT6 vector
        sub     di,int06-emuload_msg    ; point to theoretical
                                        ;  start of message
        repz    cmpsb                   ; check data
        pop     es                      ; restore (ES)
        ret                             ; split
Check_installed endp


;---------------------------------------------------------------
; Include the CPU_TYPE procedure & LOADALL test
;---------------------------------------------------------------
        Include CPU_TYPE.ASM

_TEXT   ends
        end     Emulate_286_LOADALL

;---------------------------------------------------------------
;                           END LISTING 3
;---------------------------------------------------------------
