💾 Archived View for spam.works › mirrors › textfiles › computers › 86bugs.lst captured on 2024-02-05 at 11:22:20.

View Raw

More Information

⬅️ Previous capture (2023-06-14)

-=-=-=-=-=-=-

Last Change 7/17/93.  Please send updates directly to Harald.


86BUGS.LST   revision 1.0
By Harald Feldmann (harald.feldmann@almac.co.uk), mail address:
Hamarsoft, p.o. box 91, 6114 ZH  Susteren, The Netherlands.
(Please retain my name and address in the document)

This file lists undocumented and buggy instructions of the Intel 80x86
family of processors. Some of the information was obtained from the book
"Programmer's technical reference, the processor and coprocessor; by
Robert L. Hummel; Ziff davis press. ISBN 1-56276-016-5 Which is highly
recommended. Note that Intel does not support the special features and
may decide to drop opcode variants and instructions in future products.

All mentioned trademarks and/or tradenames are owned by the respective
owners and are acknowledged.

Undocumented instructions and undocumented features of Intel and IIT
processors:

AAD:	    OPCODE: d5,0a     OPCODE VARIANT

	    This instruction regularly performs the following action:
		- unpacked BCD in AX	  example (AX = 0104h)
		- AL = AH * 10d + AL		  (AL = 0eh )
		- AH = 00			  (AH = 00h )

	    The normal opcode decodes as follows: d5,0a
	    The instruction itself is an instruction plus operand. By
	    replacing the second byte with any number in the range 00 -
	    ff we can build our own instruction AAD for various number
	    systems in those ranges. For example by coding d5,10 we
	    achieve an instruction that performs: AL = AH * 16d + AL.

	    Note: the variant is not supported on all 80x86-compatible
	    CPUs, notably the NEC V-series, because some hard-code the
	    divisor at 0Ah

AAM:	    OPCODE: d4,0a     OPCODE VARIANT

	    This instruction regularly performs the following action:
		- binary number in AL
		- AH = AL / 10d
		- AL = AL MOD 10d

	    Thus creating an unpacked BCD in AX.
	    The normal opcode decodes as follows: d4,0a
	    The instruction itself is an instruction plus operand. By
	    replacing the second byte with any number in the range 00 -
	    ff we can build our own instruction AAM for various number
	    systems in that range. For example by coding d4,07 we
	    achieve an instruction that performs: AH = AL / 07d, AL = AL
	    MOD 07d

	    The AAD and AAM opcode variants have been found in Future
	    Domain SCSI controller ROMS.


LOADALL:    OPCODE: 0f,05 (i80286) & 0f,07 (i80386 & i80486)
	    UNDOCUMENTED

	    Load _ALL_ processor registers. Does exactly as the name
	    suggests, separate versions for i80286 and i80386 exist. The
	    i80286 LOADALL instruction reads a block of 102 bytes into
	    the chip, starting at address 000800 hex. The i80286 LOADALL
	    takes 195 clocks to execute.
	    The sequence is as follows (Hex address, Bytes, Register):

		0800: 6 N/A
		0806: 2 MSW (Machine Status Word)
		0808: 14 N/A
		0816: 2 TR (Task Register)
		0818: 2 FLAGS (Flags)
		081a: 2 IP (Instruction Pointer)
		081c: 2 LDT (Local Descriptor Table)
		081e: 2 DS (Data Segment)
		0820: 2 SS (Stack Segment)
		0822: 2 CS (Code Segment)
		0824: 2 ES (Extra Segment)
		0826: 2 DI (Destination Index)
		0828: 2 SI (Source Index)
		082a: 2 BP (Base Pointer)
		082c: 2 SP (Stack Pointer)
		082e: 2 BX (BX register)
		0830: 2 DX (DX register)
		0832: 2 CX (CX register)
		0834: 2 AX (AX register)
		0836: 6 ES cache (ES descriptor _cache_)
		083c: 6 CS cache (CS descriptor _cache_)
		0842: 6 SS cache (SS descriptor _cache_)
		0848: 6 DS cache (DS descriptor _cache_)
		084e: 6 GDTR (Global Descriptor Table)
		0854: 6 LDT cache (Local Descriptor_cache_)
		085a: 6 IDTR (Interrupt Descriptor table)
		0860: 6 TSS cache (Task State Segment _cache_)

	    Descriptor cache entries are internal copies of the
	    original registers (the LDT cache is normally a copy of the
	    last regularly _loaded_ LDT). Note that after executing
	    LOADALL, the chip will use the _cache_ registers without
	    re-checking the caches against the regular registers. That
	    means that cache and register do not have to be the same.
	    Caches are updated when the original register is loaded
	    again. Both will then contain the same value.

	    Descriptor caches layout:
	    3 bytes	   24 bit physical address of segment
	    1 byte	   access rights byte, mapped as access right
			   byte in a regular descriptor. The present
			   bit now represents a valid bit. If this bit
			   is cleared (zero) the segment is invalid and
			   accessing it will trigger exception 0dh. The
			   DPL (Descriptor Privilege Level) fields of
			   the CS and SS descriptor caches determine
			   the CPL (Current Privilege Level).
	    2 bytes	   16 bit segment limit.
	    This layout is the same for the GDTR and IDTR registers,
	    except that the access rights byte must be zero.


	    i80386 LOADALL:
	    The i80386 variant loads 204 (dec) bytes from the address at
	    ES:EDI and resumes execution in the specified state.
	    No timing information available.

	    relative offset: Bytes:   Registers:
		0000:	     4	      CR0
		0004:	     4	      EFLAGS
		0008:	     4	      EIP
		000c:	     4	      EDI
		0010:	     4	      ESI
		0014:	     4	      EBP
		0018:	     4	      ESP
		001c:	     4	      EBX
		0020:	     4	      EDX
		0024:	     4	      ECX
		0028:	     4	      EAX
		002c:	     4	      DR6
		0030:	     4	      DR7
		0034:	     4	      TR
		0038:	     4	      LDT
		003c:	     4	      GS (zero extended)
		0040:	     4	      FS (zero extended)
		0044:	     4	      DS (zero extended)
		0048:	     4	      SS (zero extended)
		004c:	     4	      CS (zero extended)
		0050:	     4	      ES (zero extended)
		0054:	    12	      TSS descriptor cache
		0060:	    12	      IDT descriptor cache
		006c:	    12	      GDT descriptor cache
		0078:	    12	      LDT descriptor cache
		0084:	    12	      GS descriptor cache
		0090:	    12	      FS descriptor cache
		009c:	    12	      DS descriptor cache
		00a8:	    12	      SS descriptor cache
		00b4:	    12	      CS descriptor cache
		00c0:	    12	      ES descriptor cache

	    Descriptor caches layout:
	    1 byte	   zero
	    1 byte	   access rights byte, same as i80286
	    2 bytes	   zero
	    4 bytes	   32 bit physical base address of segment
	    4 bytes	   32 bit segment limit


UNKNOWN:    OPCODE: 0f,04     UNDOCUMENTED

	    This instruction is likely to be an alias for the LOADALL on
	    the i80286. It is not documented and is even marked as
	    unused in the 'Programmer's technical reference'. Still it
	    executes on the i80286. >> info wanted <<


SETALC:     OPCODE: d6	      UNDOCUMENTED

	    This instruction copies the Carry Flag to the AL register.
	    In case of a CY, AL becomes ffh. When the Carry Flag is
	    cleared, AL becomes 00.


Floating Point special instructions:

FMUL4X4:    OPCODE: db,f1     IIT ONLY

	    This instruction is available only on the IIT (Integrated
	    Information Technology Inc.) math processors.
	    Takes 242 clocks.
	    The instruction performs a 4x4 matrix multiply in one
	    instruction using four banks of 8 floating point registers.
	    The operands must be loaded to a specific bank in a specific
	    order. The equation solved can be represented by:

	    Xn = (A00 * Xo) + (A01 * Xo) + (A02 * Xo) + (A03 * Xo)
	    Yn = (A10 * Yo) + (A11 * Yo) + (A12 * Yo) + (A13 * Yo)
	    Zn = (A20 * Zo) + (A21 * Zo) + (A22 * Zo) + (A23 * Zo)
	    Vn = (A30 * Vo) + (A31 * Vo) + (A32 * Vo) + (A33 * Vo)

	    Where Xo stands for the original X value and Xn for the
	    result. Operands must be loaded to the following registers
	    in the specified banks in the specified order.

		   Before FMUL4X4		 After FMUL4X4

			    bank	       bank
	    Register:	0    1	  2		 0

		ST(0)  Xo   A33  A31		Xn
		ST(1)  Yo   A23  A21		Yn
		ST(2)  Zo   A13  A11		Zn
		ST(3)  Vo   A03  A01		Vn
		ST(4)	    A32  A30		 ?
		ST(5)	    A22  A20		 ?
		ST(6)	    A12  A10		 ?
		ST(7)	    A02  A00		 ?



	    All four banks can be selected by using the bankswitching
	    instructions, but only bank 0, 1 and 2 make sense since bank
	    3 is an internal scratchpad. The separate banks can contain
	    8 floating points and may be re-used with normal
	    instructions. Each bank acts like an independent i80287,
	    except when bankswitched inbetween, in those cases where the
	    initial status is not maintained;

	    Pseudo- multichip operation can be performed in each bank
	    and even in multiple banks at the same time (although only
	    one instruction will operate on one register at any given
	    time), provided that the active register and top register
	    are not changed after switching from bank to bank.


	    EXAMPLE:
		FINIT			     ; reset control word
		FSBP1			     ; select bank 1
		FLD DWORD PTR es:[si]	     ; first original
		FLD DWORD PTR es:[si+4]      ; second original
		FLD DWORD PTR es:[si+8]      ; third original
		FSTCW WORD PTR [bx]	     ; save FPU control status
		FSBP2			     ; NOTE ! you will see three
					       active registers in this
					       bank when using a
					       debugger
		FINIT			     ; nothing visible
		FLD DWORD PTR [si]	     ; new value
		FLD DWORD PTR [si+4]	     ; second new value
		FADD ST,ST(1)		     ; two values visible
		FSTP DWORD PTR [si+8]	     ; one value visible
		FSBP1			     ; one original visible
		FLDCW WORD PTR [bx]	     ; restore FPU status to the
					       one active in bank 1,
					       causing original three
					       values to be visible
					       again in correct
					       sequence

		... simply continue with what you wanted to do with
		those numbers from es:[si], they are still there.

		FLD DWORD PTR [si+8]	     ; for instance...


	    This feature of the IIT chips can be used to perform complex
	    operations in registers with many components remaining the
	    same for a large dataset, only saving intermediary results
	    to ONE memory location, bankswitching to the next series of
	    operands, loading that ONE operand and continuing the
	    calculation with the next set of operands already in that
	    bank. This does require another read into the new bank but
	    may save time and memoryspace compared to memory based
	    operands or multiple pass algorithms with multiple arrays of
	    intermediary results.



BANKSWITCH INSTRUCTIONS:

FSBP0:	    OPCODE: db,e8     IIT ONLY
	    Selects the original bank. (default) (6 clocks)


FSBP1:	    OPCODE: db,eb     IIT ONLY

	    Selects bank 1 from FMUL4X4 instruction diagram (6 clocks)


FSBP2:	    OPCODE: db,ea     IIT ONLY

	    Selects bank 2 from FMUL4X4 instruction diagram (6 clocks)

FSBP3:	    OPCODE: db,e9     IIT ONLY	 UNDOCUMENTED
	    Selects the scratchpad bank3 used by the FMUL4X4 internally.
	    Not very useful but funny to look at... How-to: load
	    any value into bank 0,1 or 2 until you have a full 8
	    registers, then execute this bankswitch. Using a
	    debugger like CodeView you are now able to inspect the
	    bank3 registers. (most likely to take 6 clocks)



TRIGONIOMETRIC FUNCTIONS:

	    Apparently the IIT 2c87 recognises and executes some
	    i80387 trigoniometric functions. UNDOCUMENTED
	    FSIN (sine) and FCOS (cosine) have been tested and function
	    according to the Intel 80387 specifications. FSINCOS
	    (available on the Intel 80287XL, 80387 and up) does not
	    work.

FSIN:	    OPCODE: d9,fe     IIT 2c87+ (also Intel 80387+) UNDOCUMENTED
	    Calculates the sine of the input in radians in ST(0). After
	    calculation, ST(0) contains the sine. Takes approximately
	    120 clocks.

FCOS:	    OPCODE: d9,ff     IIT 2c87+ (also Intel 80387+) UNDOCUMENTED
	    Calculates the cosine of the input in radians in ST(0).
	    After calculation, ST(0) contains the cosine. Takes
	    approximately 120 clocks.


... CUT HERE FOR FIRST REVISION, next part is to be revised ...



Instructions by mnemonic mnemonic:
opcode: processor:  remark & remedy:

AAA	       i80286 & i80386 & i80486

CMPS			i80286
CMPXCHG 		i80486
FINIT
FSTSW
FSTCW


INS			i80286 &
			i80386 &
			i80486

INVD			i80486



MOV to SS	 n/a	early 8088  Some early 8088 would not properly
				    disable interrupts after a move to
				    the SS register. Workaround would
				    be to explicitly clear the
				    interrupts, update SS and SP and
				    then re-enable the interrupts.
				    Typically this would occur in a
				    situation where one would relocate
				    a stack in memory, more than 64Kb
				    from the original one, updating
				    both SS and SP like in:
				      MOV SS,AX  ; would disable
						   interrupts
						   automatically during
						   this and next
						   instruction.
				      MOV SP,DX  ; interrupts disabled
				      ...	 ; interrupts enabled.


multiple prefixes
with REPx		8088 & 8086 They would not properly restart at
				    the first prefix byte after an
				    interrupt. when more than one
				    prefix is used. e.g. LOCK REP MOVSW
				    CS:[bx]. A workaround is to test
				    after the instruction for CX==0,
				    here: LOCK REP MOVSW CS:[BX] OR
				    CX,CX JNZ here because of the CS
				    override, the REP and LOCK prefixes
				    would not be recognised to be part
				    of the instruction and the REP MOVSW
				    would be aborted. This also seems to
				    be the case for a REP MOVSW CS:[BX]
				    Note that this also implies that
				    REPZ, REPNZ are affected in SCASW
				    for instance.