Some undocumented instruction updates

This commit is contained in:
Jeff Parsons 2016-02-16 12:21:58 -08:00
commit 025b1abb93
19 changed files with 2531 additions and 1582 deletions

221
docs/x86/ops/AAM/AAM.ASM Normal file
View file

@ -0,0 +1,221 @@
;
; Saved on February 16, 2015 from http://www.rcollins.org/ftp/source/aam/aam.asm
;
.386p
;-----------------------------------------------------------------------------
;
; AAM.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
;
;-----------------------------------------------------------------------------
.model small
.code
.286
;-----------------------------------------------------------------------------
; Interrupt vector segment
;-----------------------------------------------------------------------------
ABS0 segment at 0
org 0*4
Orig_INT0 label word
ABS0 ends
;-----------------------------------------------------------------------------
; Local stack frame variable(s)
;-----------------------------------------------------------------------------
INT0 equ [bp-4]
;-----------------------------------------------------------------------------
; Instruction macro definition
;-----------------------------------------------------------------------------
AAMI MACRO VALUE
db 0d4h,VALUE
ENDM
;-----------------------------------------------------------------------------
TEST_AAM proc near ; Test AAM IMMED08 instruction functionality.
;-----------------------------------------------------------------------------
; Input: None
; Output: BX = Bit mask of results (3FF if all tests passed)
; [b15..b10] = Unused
; [b9] = 1, Carry Flag test passed
; [b8] = 1, Overflow Flag test passed
; [b7] = 1, Auxiliary carry Flag test passed
; [b6] = 1, INT0 exception passed
; [b5] = 1, ZF flag test passed
; [b4] = 1, NZ flag test passed
; [b3] = 1, NS flag test passed
; [b2] = 1, SF flag test passed
; [b1] = 1, PE flag test passed
; [b0] = 1, PO flag test passed
; Register(s) modified: AX, BX, CX, SI
;-----------------------------------------------------------------------------
xor bx,bx ; clear result flags
xor cx,cx
;-----------------------------------------------------------------------------
; Test EVEN and ODD parity by generating results in the low byte that
; contain even and odd parity respectively.
;-----------------------------------------------------------------------------
mov al,0fbh ; 251/252 leave remainder=251, whose
; parity=ODD.
AAMI 0FCh ; generate odd parity
jpe @F ; oops odd parity not set
or bl,1 ; set even parity flag
@@: AAMI 0F1h ; 251/241 leaves remainder=10, whose
; parity=EVEN
jpo @F ; oops even parity
or bl,2 ; set odd parity flag
;-----------------------------------------------------------------------------
; Test Sign flag by generating results in the low byte whose bit7=1. This
; is easily done by putting 80h in AL, and dividing by a number larger than
; 80h. The remainder will always be 80h, and therefore the sign flag is set.
;-----------------------------------------------------------------------------
@@: mov al,080h ; 128/255 leaves remainder=128, whose
AAMI 0ffh ; Sign flag=1 (bit7=1)
jns @F ; oops no SF!
or bl,4 ; set SF flag
@@: AAMI 80h ; 128/128 leaves remainder=0, whose
js @F ; sign flag=0 (bit7=0)
or bl,8 ; set NS flag
;-----------------------------------------------------------------------------
; Test ZERO flag by generating results in the low byte as ZERO, and NON-ZERO.
;-----------------------------------------------------------------------------
@@: mov al,0f0h ; 240/127 leaves remainder=113, which
AAMI 7Fh ; is obviously not 0.
jz @F ; oops, ZF!
or bl,10h ; set NF flag
@@: AAMI 113d ; 113/113 leaves remainder=0, which is
jnz @F ; obviously 0!
or bl,20h ; set ZF flag
;-----------------------------------------------------------------------------
; Test that AAM 0 (divide by 0) will generate the appropriate CPU exception
; (exception 0). This can be tested by setting up a simple INT0 handler, and
; try to divide by 0. If the execption occured, then success.
;-----------------------------------------------------------------------------
@@: enter 4,0 ; create stack frame
mov word ptr INT0,offset INT0_handler
mov INT0[2],cs ; save current CS to restore later
call set_INT0_vector ; set pointer to our INT6 handler
AAMI 0 ; generate INT0 exception
jcxz @F ; if CX=0, then an error occurred
or bl,40h ; set success flag
@@: call set_INT0_vector ; restore original INT0 vector
leave ; restore stack frame
;-----------------------------------------------------------------------------
; Test unaffected flags will cycle through every possible combination of
; AAM, and test that none of the "unaffected" flags are changed. For
; brevity of source code, I'm going to do one of the biggest no-no's in
; programming...I'm going to write self modifying code.
;-----------------------------------------------------------------------------
; First test the Auxiliary carry Flag (AF). If AF gets set, then the test
; fails.
;-----------------------------------------------------------------------------
mov si,offset @AF[1] ; get address of operand to AAM
mov cx,1 ; start with AAM 01
@@: mov al,ch
mov cs:[si],cl ; modify op code
jmp short @AF ; go
@AF: AAMI 00 ; starting sequence
lahf ; get flags register
test ah,10h ; auxiliary flag set?
jnz short @F ; yes
add ch,1 ; try next dividend
adc cl,0 ; try next divisor
jnc @B ; continue
or bl,80h ; set success flag
;-----------------------------------------------------------------------------
; Second, test the Overflow Flag (OF). If OF gets set, then the test fails.
;-----------------------------------------------------------------------------
@@: mov si,offset @OF[1] ; get address of operand to AAM
mov cx,1 ; start with AAM 01
@@: mov al,ch
mov cs:[si],cl ; modify op code
jmp short @OF ; go
@OF: AAMI 00 ; starting sequence
jo short @F ; test failed
add ch,1 ; try next dividend
adc cl,0 ; try next divisor
jnc @B ; continue
or bh,01h ; set success flag
;-----------------------------------------------------------------------------
; Finally, test the Carry Flag (CF). If CF gets set, then the test fails.
;-----------------------------------------------------------------------------
@@: mov si,offset @CF[1] ; get address of operand to AAM
mov cx,1 ; start with AAM 01
@@: mov al,ch
mov cs:[si],cl ; modify op code
jmp short @CF ; go
@CF: AAMI 00 ; starting sequence
jc short @F ; test failed
add ch,1 ; try next dividend
adc cl,0 ; try next divisor
jnc @B ; continue
or bh,02h ; set success flag
@@: ret ; split
Test_AAM endp
;-----------------------------------------------------------------------------
; Set the INT6 vector by exchanging it with the one currently on the stack.
;-----------------------------------------------------------------------------
set_INT0_vector:
push ds
push ABS0 ; save interrupt vector segment
pop ds ; make DS=INT vector segment
ASSUME DS:ABS0
mov dx,Orig_INT0; ; get offset if INT0 handler
xchg INT0,dx ; set new INT0 offset
mov Orig_INT0,dx
mov dx,Orig_INT0[2] ; get segment of INT0 handler
xchg INT0[2],dx ; set new INT0 segment
mov Orig_INT0[2],dx
pop ds ; restore segment register
ret ; split
ASSUME DS:NOTHING
;-----------------------------------------------------------------------------
; INT0 handler sets a semaphore (CX=FFFF) and adjusts the return address to
; point past the invalid opcode.
;-----------------------------------------------------------------------------
INT0_handler:
enter 0,0 ; create new stack frame
dec cx ; make CX=FFFF
add word ptr ss:[bp][2],2 ; point past invalid opcode
leave
iret
end

View file

@ -0,0 +1,51 @@
---
layout: page
title: "x86 Instructions: AAM"
permalink: /docs/x86/ops/AAM/
---
AAD (0xD4)
---
### Description
From [http://www.rcollins.org/secrets/opcodes/AAM.html](http://www.rcollins.org/secrets/opcodes/AAM.html):
Undocumented: Available to all Intel x86 processors.
Useful in production source code.
AAM
Flags: ASCII Adjust after Multiply
+-+-+-+-+-+-+-+-+-+ +----------+----------+
|O|D|I|T|S|Z|A|P|C| | 11010100 | DATA |
+-+-+-+-+-+-+-+-+-+ +----------+----------+
|0| | | |+|+|0|+|0| | D4 | IMM8 |
+-+-+-+-+-+-+-+-+-+ +----------+----------+
AAM is shown as a two byte encoding used to divide AL by 10, putting the quotient in AH, and the remainder in AL.
However, AAM is listed in the op code map as a single byte instruction. This leads one to wonder why a two-byte
opcode is listed in the single-byte opcode map. In reality, the second byte is an undocumented operand to AAM.
The operand is the divisor. In its documented incarnation, AAM is encoded as D4 0A. The operand 0A is the divisor.
This divisor can be changed to any value between 0 and FF. Using AAM in this manner is useful -- as it extends the
CPU instruction set to include a DIV IMM8 instruction that is not available from any other form of the DIV
instruction.
The extended form of the AAM instruction is also useful because it sets the flags register according to the results,
unlike the DIV or IDIV instruction. According to Intel documentation, SF, ZF, and PF flags are set according to the
result, while OF, AF, and CF are undefined. However, if AAM were used strictly as documented, then the Sign Flag (SF)
could not be set under any circumstances, since anything divided by 10 will leave a remainder between 0 and 9.
Obviously the remainder could never be between 128 and 255 (or -1 and -128 if you prefer) if used only as documented.
Since AAM divides an 8 bit number by another 8-bit number, a carry or overflow could never occur. Therefore CF and
OF always=0. Intel claims they are undefined, but my observations are consistent with my theory.
Contrary to documentation, AAM will generate exceptions in real mode, protected mode, and V86 mode. AAM can only
generate Exception 0 -- divide by 0. Finally, in the Pentium User's Manual, this heretofore undocumented form of
AMM is described. Intel says:
Note: imm8 has the value of the instruction's second byte. The second byte under normally assembly [sic] of
this instruction will be 0A, however, explicit modification of this byte will result in the operation described
above and may alter results.
This instruction exists in this form on all Intel x86 processors. See the file AAM.ASM for diagnostics source code
for this instruction.
See [AAM.ASM](AAM.ASM) for the source code mentioned above.