pcjs/pubs/docs/x86/ops/AAM/AAM.ASM

218 lines
10 KiB
NASM

.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