|
|
; ; ; Copyright (C) Microsoft Corporation, 1987-89 ; ; This Module contains Proprietary Information of Microsoft ; Corporation and should be treated as Confidential. ; title emoem.asm - OEM dependent code for 8087
;-------------------------------------------------------------------- ; ; OEM customization routines for 8087/80287 coprocessor ; ; This module is designed to work with the following ; Microsoft language releases: ; ; Microsoft C 3.00 and later ; Microsoft FORTRAN 77 3.30 and later ; Microsoft Pascal 3.30 and later ; ; This module supersedes the OEMR7.ASM module used in earlier ; versions of Microsoft FORTRAN 77 and Pascal. The documentation ; provided with the FORTRAN and Pascal releases refers to the old ; OEMR7.ASM module and is only slightly relevant to this module. ; ; The following routines need to be written to properly handle the ; 8087/808287 installation, termination, and interrupt handler ; ; __FPINSTALL87 install 8087 interrupt handler ; __FPTERMINATE87 deinstall 8087 interrupt handler ; __fpintreset reset OEM hardware if an 8087 interrupt ; ; ***** NEW INSTRUCTIONS ***** ; ; If you want a PC clone version, do nothing. The libraries are ; setup for working on IBM PC's and clones. ; ; These instructions only need to be followed if a non-IBM PC ; clone version is desired. ; ; This module should be assembled with the ; Microsoft Macro Assembler Version 4.00 or later as follows: ; ; masm -DOEM -r emoem.asm; ; ; Most hardware handles the 8087/80287 in one of the following ; three ways - ; ; 1. NMI - IBM PC and clones all handle the interrupt this way ; 2. single 8259 ; 3. master/slave 8259 ; ; Manufacturer specific initialization is supported for these 3 ; machine configurations either by modifying this file and replacing ; the existing EMOEM module in the math libraries or by patching ; the .LIB and .EXE files directly. ; ; LIB 87-+EMOEM; ; LIB EM-+EMOEM; ; ;--------------------------------------------------------------------
ifdef OEM if1 %out OEM version for non-clone support endif endif
;--------------------------------------------------------------------- ; Assembly constants. ;---------------------------------------------------------------------
NULL_JMP macro jmp $+2 endm
WASTE_TIME macro count local WasteLoop
push cx ifnb <count> mov cx, count else mov cx, 10h endif WasteLoop: push ax pop ax
loop WasteLoop pop cx
endm
; MS-DOS OS calls
OPSYS EQU 21H SETVECOP EQU 25H GETVECOP EQU 35H DOSVERSION EQU 30h ifndef _NOCTRLC CTLCVEC EQU 23h endif ;_NOCTRLC
extrn __WINFLAGS:abs WF_PMODE equ 1 WF_CPU286 equ 2 WF_CPU386 equ 4 WF_WIN286 equ 10h WF_WIN386 equ 20h
EMULATOR_DATA segment para public 'FAR_DATA' assume ds:EMULATOR_DATA
; User may place data here if DS is setup properly. ; Recommend keeping the data items in the code segment.
ifdef OEM extrn aoldIMR:byte ; 1st 8259 original IMR value extrn boldIMR:byte ; 2nd 8259 original IMR value endif ;OEM
extrn OldNMIVec:dword ; Old value in 8087 exception interrupt vector
ifndef _NOCTRLC extrn ctlc:dword ; Old value of Control-C vector (INT 23h) endif ;_NOCTRLC
EMULATOR_DATA ends
EMULATOR_TEXT segment para public 'CODE' assume cs:EMULATOR_TEXT
public __FPINSTALL87 ; DO NOT CHANGE THE CASE ON public __FPTERMINATE87 ; THESE PUBLIC DEFINITIONS
extrn __FPEXCEPTION87:near ; DO NOT CHANGE CASE extrn __FPEXCEPTION87P:near
ifdef _NO87INSTALL ; install/terminate routines for NO87 case for QuickC
public __FPINSTALLNO87 public __FPTERMINATENO87
endif ;_NO87INSTALL
ifdef OEM
;*********************************************************************** ; ; Hardware dependent parameters in the 8087 exception handler. ; ; For machines using 2 8259's to handle the 8087 exception, be sure that ; the slave 8259 is the 1st below and the master is the 2nd. ; ; The last 4 fields allow you to enable extra interrupt lines into the ; 8259s. It should only be necessary to use these fields if the 8087 ; interrupt is being masked out by the 8259 PIC. ; ; The ocw2's (EOI commands) can be either non-specific (20H) or ; specific (6xH where x=0 to 7). If you do not know which interrupt ; request line on the 8259 the 8087 exception uses, then you should issue ; the non-specific EOI (20H). Interrupts are off at this point in the ; interrupt handler so a higher priority interrupt will not be seen.
oeminfo struc oemnum db 0 ; MS-DOS OEM number (IBM is 00h) intnum db 2 ; IBM PC clone interrupt number share db 0 ; nonzero if original vector should be taken a8259 dw 0 ; 1st 8259 (A0=0) port # aocw2 db 0 ; 1st 8259 (A0=0) EOI command b8259 dw 0 ; 2nd 8259 (A0=0) port # bocw2 db 0 ; 2nd 8259 (A0=0) EOI command a8259m dw 0 ; 1st 8259 (A0=1) port # aocw1m db 0 ; 1st 8259 (A0=1) value to mask against IMR b8259m dw 0 ; 2nd 8259 (A0=1) port # bocw1m db 0 ; 2nd 8259 (A0=1) value to mask against IMR oeminfo ends
;----------------------------------------------------------------------- ; OEM specific 8087 information ; ; If the OEM number returned from the DOS version call matches, ; this information is automatically moved into the oem struc below.
oemtab label byte ; Table of OEM specific values for 8087
; OEM#, int, shr, a59, acw2,b59, bcw2,a59m,acw1,b59m,bcw1
;TI Professional Computer TI_prof oeminfo <028h,047h,000h,018h,020h,0000,0000,0000,0000,0000,0000>
db 0 ; end of table
; Unique pattern that can be searched for with the debugger so that ; .LIB or .EXE files can be patched with the correct values. ; If new values are patched into .LIB or .EXE files, care must be ; taken in insure the values are correct. In particular, words and ; bytes are intermixed in oeminfo structure. Remember words are ; stored low byte - high byte in memory on the 8086 family.
db '<<8087>>' ; older versions used '<8087>'
; Some manufacturer's machines can not be differentiated by the ; OEM number returned by the MS-DOS version check system call. ; For these machines it is necessary to replace the line below
oem1 oeminfo <> ; default values for IBM PC & clones
; with one of the following. If your machine has an 8087 capability ; and it is not in the list below, you should contact your hardware ; manufacturer for the necessary information.
;ACT Apricot ;oem1 oeminfo <000h,055h,000h,000h,020h,000h,000h,000h,000h,000h,000h>
;NEC APC3 and PC-9801 (OEM number returned by NEC MS-DOS's is different) ;oem1 oeminfo <000h,016h,000h,008h,066h,000h,067h,00Ah,0BFh,002h,07Fh>
;---------------------------------------------------------------------
endif ;OEM
page
;--------------------------------------------------------------------- ; ; Perform OEM specific initialization of the 8087. ;
__FPINSTALL87: push ds ; DS = EMULATOR_DATA
ifdef OEM push ds pop es ; CS = DS = ES mov ah,DOSVERSION int OPSYS ; bh = OEM# cld mov si,offset oemtab ; start of OEM 8087 info table mov di,offset oem1+1 mov cx,(size oem1)-1 OEMloop: lodsb ; get OEM# or al,al jz OEMdone ; OEM# = 0 - did not find OEM cmp al,bh ; correct OEM# je OEMfound add si,cx ; skip over OEM information jmp OEMloop
OEMfound: rep movsb ; move the information
OEMdone: ; done with automatic customization endif ;OEM
; Save old interrupt vector. ; Ask operating system for vector.
ifdef WINDOWS mov ax, word ptr [OldNMIVec] or ax, word ptr [OldNMIVec+2] jnz SetVector endif ;WINDOWS
ifdef OEM mov al,[oem1].intnum ; Interrupt vector number. mov ah,GETVECOP ; Operating system call interrupt. else mov ax,GETVECOP shl 8 + 2 ; get interrupt vector 2 endif ;OEM int OPSYS ; Call operating system.
mov word ptr [OldNMIVec],bx ; Squirrel away old vector. mov word ptr [OldNMIVec+2],es
; Have operating system install interrupt vectors.
SetVector: push cs ; Move current CS to DS for opsys calls. pop ds assume ds:EMULATOR_TEXT
mov dx,offset __fpinterrupt87 ; Load DX with 8087 interrupt handler. ifdef OEM mov ah,SETVECOP ; Set interrupt vector code in AH. mov al,[oem1].intnum ; Set vector number. else mov ax,SETVECOP shl 8 + 2 ; set interrupt vector 2 endif ;OEM int OPSYS ; Install vector.
mov ax, __WINFLAGS test ax, WF_PMODE jz SkipSettingIRQ13
.286p .287 fsetpm ; set PM just in case Windows didn't. .8086 .8087
push cs pop ds ; Move current CS to DS for opsys calls. assume ds:EMULATOR_TEXT mov dx, offset __fpIRQ13 ; Load DX with IRQ 13 interrupt handler. mov ax, SETVECOP shl 8 + 75h ; set interrupt vector 75h int OPSYS ; Install vector. SkipSettingIRQ13:
ifndef _NOCTRLC ; Intercept Control-C vector to guarentee cleanup
mov ax,GETVECOP shl 8 + CTLCVEC int OPSYS mov word ptr [ctlc],bx mov word ptr [ctlc+2],es mov dx,offset ctlcexit mov ax,SETVECOP shl 8 + CTLCVEC int OPSYS endif ;_NOCTRLC
ifdef OEM
; set up 8259's so that 8087 interrupts are enabled
mov ah,[oem1].aocw1m ; get mask for 1st 8259 IMR or ah,ah ; if 0, don't need to do this jz installdone ; and only 1 8259 mov dx,[oem1].a8259m ; get port number for 1st 8259 (A0=1) in al,dx ; read old IMR value mov [aoldIMR],al ; save it to restore at termination and al,ah ; mask to enable interrupt jmp short $+2 ; for 286's out dx,al ; write out new mask value
mov ah,[oem1].bocw1m ; get mask for 2nd 8259 IMR or ah,ah ; if 0, don't need to do this jz installdone ; mov dx,[oem1].b8259m ; get port number for 2nd 8259 (A0=1) in al,dx ; read old IMR value mov [boldIMR],al ; save it to restore at termination and al,ah ; mask to enable interrupt jmp short $+2 ; for 286's out dx,al ; write out new mask value
installdone:
endif ;OEM
assume ds:EMULATOR_DATA pop ds ret
page ; __FPTERMINATE87 ; ; This routine should do the OEM 8087 cleanup. This routine is called ; before the program exits. ; ; DS = EMULATOR_DATA
__FPTERMINATE87: push ds push ax push dx
ifdef OEM mov ah,SETVECOP mov al,[oem1].intnum else mov ax,SETVECOP shl 8 + 2 endif ;OEM lds dx,[OldNMIVec] int OPSYS
ifdef OEM
; reset 8259 IMR's to original state
push cs pop ds ; DS = CS assume ds:EMULATOR_TEXT cmp [oem1].aocw1m,0 ; did we have to change 1st 8259 IMR je term2nd8259 ; no - check 2nd 8259 mov al,[aoldIMR] ; get old IMR mov dx,[oem1].a8259m ; get 1st 8259 (A0=1) port # out dx,al ; restore IMR
term2nd8259: cmp [oem1].bocw1m,0 ; did we have to change 2nd 8259 IMR je terminatedone ; no mov al,[boldIMR] ; get old IMR mov dx,[oem1].b8259m ; get 2nd 8259 (A0=1) port # out dx,al ; restore IMR
terminatedone:
endif ;OEM
pop dx pop ax pop ds assume ds:EMULATOR_DATA ret
; Forced cleanup of 8087 exception handling on Control-C
ifndef _NOCTRLC ctlcexit: push ax push dx push ds call __FPTERMINATE87 ; forced cleanup of exception handler lds dx,[ctlc] ; load old control C vector mov ax,SETVECOP shl 8 + CTLCVEC int OPSYS pop ds pop dx pop ax jmp [ctlc] ; go through old vector endif ;_NOCTRLC
page ; __fpinterrupt87 ; ; This is the 8087 exception interrupt routine. ; ; All OEM specific interrupt and harware handling should be done in ; __fpintreset because __FPEXCEPTION87 (the OEM independent 8087 ; exception handler) may not return. __FPEXCEPTION87 also turns ; interrupts back on. ;
PENDINGBIT= 80h ; Bit in status word for interrupt pending
public __fpIRQ13 __fpIRQ13: cli
WASTE_TIME 70
push ax xor al, al NULL_JMP out 0f0h, al ; reset busy line. NULL_JMP mov al, 65h NULL_JMP out 0a0h, al ; EOI slave irq 5 NULL_JMP mov al, 62h NULL_JMP out 20h, al ; EOI master irq 2 NULL_JMP pop ax
sub sp, 2
push bp mov bp, sp
fnstsw [bp+2] WASTE_TIME push ax xor al, al NULL_JMP out 0f0h, al ; reset busy line. NULL_JMP pop ax
pop bp
fnclex ; 486 bug - must wait till after last ; "out f0" to clear fp exceptions ; or IGNNE# will be permanently active. WASTE_TIME push ax xor al, al NULL_JMP out 0f0h, al ; reset busy line. NULL_JMP pop ax
fnclex ; 486 bug - must wait till after last ; "out f0" to clear fp exceptions ; or IGNNE# will be permanently active. WASTE_TIME push ax xor al, al NULL_JMP out 0f0h, al ; reset busy line. NULL_JMP pop ax
; fnclex ;Now this is safe.
jmp __FPEXCEPTION87P
public __fpinterrupt87 __fpinterrupt87: assume ds:nothing nop
push bp mov bp, sp sub sp, 2
fnstsw word ptr [bp-2] ; Store out exceptions
push cx ; waste time mov cx, 3 self: loop self pop cx
test byte ptr [bp-2],PENDINGBIT ; Test for 8087 interrupt
mov sp, bp pop bp
jz not87int ; Not an 8087 interrupt.
ifdef OEM call __fpintreset ; OEM interrupt reset routine endif ;OEM
call __FPEXCEPTION87 ; 8087 error handling - may not return ; this routine turns interrupts back on
ifdef OEM cmp [oem1].share,0 ; Should we execute the old interrupt routine? jnz not87int ; if so then do it ; else return from interrupt
; If you fall through here to do further hardware resetting, things ; may not always work because __FPEXCEPTION87 does not always return ; This only happens when the 8087 handler gets an exception that is ; a fatal error in the language runtimes. I.e., divide by zero ; is a fatal error in all the languages, unless the control word has ; set to mask out divide by zero errors.
endif ;OEM
done8087: iret
not87int: sub sp, 4
push bp mov bp, sp
push ds push ax
mov ax, EMULATOR_DATA mov ds, ax assume ds:EMULATOR_DATA
mov ax, word ptr [OldNMIVec+2] ; segment of OldNMIVec mov [bp+4], ax
mov ax, word ptr [OldNMIVec] ; offset of OldNMIVec mov [bp+2], ax
pop ax pop ds mov sp, bp pop bp
retf ; jmp [OldNMIVec]. We should not return.
ifdef OEM
__fpintreset: push ax push dx mov al,[oem1].aocw2 ; Load up EOI instruction. or al,al ; Is there at least one 8259 to be reset? jz Reset8259ret ; no mov dx,[oem1].a8259 out dx,al ; Reset (master) 8259 interrupt controller. mov al,[oem1].bocw2 ; Load up EOI instruction. or al,al ; Is there a slave 8259 to be reset? jz Reset8259ret mov dx,[oem1].b8259 out dx,al ; Reset slave 8259 interrupt controller.
Reset8259ret: pop dx pop ax ret
endif ;OEM
ifdef _NO87INSTALL __FPINSTALLNO87: push bx push es push ax push dx push ds
mov ax,cs ; Move current CS to DS for opsys calls. mov ds,ax assume ds:EMULATOR_TEXT r ; Save old interrupt vector. ; Ask operating system for vector.
ifdef OEM mov al,[oem1].intnum ; Interrupt vector number. mov ah,GETVECOP ; Operating system call interrupt. else mov ax,GETVECOP shl 8 + 2 ; get interrupt vector 2 endif ;OEM int OPSYS ; Call operating system. mov word ptr [OldNMIVec],bx ; Squirrel away old vector. mov word ptr [OldNMIVec+2],es
; Have operating system install interrupt vectors.
mov dx,offset __fpinterruptno87 ; Load DX with fake 8087 interrupt handler. ifdef OEM mov ah,SETVECOP ; Set interrupt vector code in AH. mov al,[oem1].intnum ; Set vector number. else mov ax,SETVECOP shl 8 + 2 ; set interrupt vector 2 endif ;OEM int OPSYS ; Install vector.
pop ds assume ds:nothing pop dx pop ax pop es pop bx ret
__fpinterruptno87: jmp [OldNMIVec] ; will use CS: override
__FPTERMINATENO87: push ds push ax push dx
mov ax,cs mov ds,ax assume ds:EMULATOR_TEXT
ifdef OEM mov ah,SETVECOP mov al,[oem1].intnum else mov ax,SETVECOP shl 8 + 2 endif ;OEM lds dx,[OldNMIVec] assume ds:nothing int OPSYS
pop dx pop ax pop ds
ret
endif ;_NO87INSTALL
EMULATOR_TEXT ends
end
|