Since this is about low level operation, let's start with the fact that the CPU/FPU does not provide an
FSTSW AX instruction, only an
FNSTSW AX. When encountering
FSTSW AX, the assembler issues two instructions (*1):
9b for FWAIT and
DF E0 for FNSTSW AX
The important difference between the 8087 and 80287 is that the 287 no longer snoops the CPU bus, but acts now as an I/O device (*2) - which in turn can be used by other CPU as well. Addressing is done using chip select and a two bit address via:
- /NPS1, NPS2 - Numeric Processor Select - essentially the Chip Select signals for the 80286
- CMD0, CMD1 - Command 0/1 - essentially Port/Register address lines of the FPU
In case of the 80286 communication these are decoded as the word size I/O Ports 0F8h, 0FAh, and 0FCh (*3/*4). Note, that these addresses are only fixed within the CPU. External decoding is needed to map the 80287 to these addresses:
Address /NPS1 NPS0 CMD1 CMD0
0F8h L H L L
0FAh L H L H
0FCh L H H L
0FEh L H H H
Essentially coupling (and buffering) A1 and A2 to CMD0/1. Additionally the 80287 read/write signals (/NPRD, /NPWR) need to be connected to the CPU's /IORD, /IOWR (or better the signals decoded by the 82288).
The three Ports/Registers are used for 5 distinct transfers:
CMD1/0 (Port) R/W
00 (0F8h) W Opcode to 80287
00 (0F8h) R CW or SW from 80287
01 (0FAh) W Exception Pointer to 80287
10 (0FCh) W Data to 80287
10 (0FCh) R Data from 80287
A command transfer consists of one or more writes to the first port (0F8h) containing the FPU command to be executed. Depending on command the Status (SW) or Control (CW) Word can be read right after from the same port.
For data transfer the 80286 provides a DMA like mechanism called Processor Extension Data Channel. If an FPU instruction contains a data transfer the CPU sets up the PEDC address, length and direction from the instruction.
When the FPU is ready to transfer, it raises PEREQ (Processor Extension Request). In case of write to the FPU, the CPU will read the data from memory (*5), provide it on the data lines and pull PEACK (Processor Extension Acknowledge) low. With writes from FPU the same sequence happens. End of transfer (and reinitialization of the PEDC) is signalled by /BUSY going high.
These transfers go to and from Port 0FCh.
FSTSW is directly done as read of port 0F8h - after writing the FSTSW command to port 0F8h.
(Here it gets a bit fuzzy, as I can't find the code I'm looking for - Many years ago I attached a 287 to a 68k system, I did only find part of the notes so far. I'm still searching for a command table.)
*1 - Which opens Pandora's Box of Definitions:
FSTSW an instruction?
- Is it a macro?
- Is it something else?
Or - dogmatics behold - is Assembler maybe not a 1:1 representation of machine code?
*2 - The 8087 is really a second processor as it takes over the bus to perform its own read/write cycles - although, not completely independent, as it relies on the CPU to do read on the operand address first, but discards the data. The FPU captures the address for further use and, if a read is to be done, also the first data byte/word. After that it takes over the bus and does all follow up ready or write on its own.
The 287 in turn does not handle the bus but relies on the 286 to feed it with nice aligned 16 bit words. This is also the reason why a 287 can not operate with an 8088. It simply lacks a way to access memory in general and bytewise memory in particular.
*3 - The 386/387 change data size to 32-bit access using only addresses 0F8h and 0FCh.
*4 - The 386 added A31 to the address as selector for simply systems (noone will need 4 Gi address space), do 0F8/0FCh becomes 0800000F8h/FCh
*5 - One cycle when reading word aligned data, two if byte aligned - that's why word alignment does speed up a 80287 a tiny bit.