;
;
; 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.
WASTE_TIME 70 ;Fix timing problem??
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