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 ;---------------------------------------------------------------