asm/AJx64AssemblerTests.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Wed, 15 Jun 2016 23:46:29 +0100
changeset 23 d2d9a2d4d6bf
parent 4 f2d0d2859193
child 24 5aace704e3c8
permissions -rw-r--r--
Added README, licenses and copyright notices.

"{ Package: 'jv:dragonfly/asm' }"

"{ NameSpace: Smalltalk }"

AJx86AssemblerTests subclass:#AJx64AssemblerTests
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:'AJx86Registers'
	category:'AsmJit-Tests'
!


!AJx64AssemblerTests class methodsFor:'as yet unclassified'!

shouldInheritSelectors
    ^ true
! !

!AJx64AssemblerTests methodsFor:'tests'!

testAssembly0
    self assert: [ :a | a mov: 16rfeedface -> RAX ] 
        bytes: #[72 184 206 250 237 254 0 0 0 0]
!

testAssembly01
    self
        assert: [ :a | 
            self assert: (a reg: 8 size: 4) = R8D.	"mov    $0xfeedface,%r8d"
            a mov: 16rfeedface asUImm to: R8D ]
        bytes: #[65 184 206 250 237 254]
!

testAssembly1

    
    self 
        assert: [ :a|
            a 
                push: a RBP;
                mov: a RSP -> a RBP;
                mov: 1024 -> a RAX;
                mov: a RBP -> a RSP;
                pop: a RBP;
                ret.]	
        bytes: #[
            85 
            72 139 236 
            72	199	192	0	4	0	0
            72 139 229 
            93 
            195]
!

testAssembly2

    self 
        assert: [ :a|
        asm 
            push: a BP;
            mov: a SP -> a BP;
            mov: 16r400 -> a RAX;
            mov: a BP -> a SP;
            pop: a RSP;
            ret. ]
        bytes: #[
            102 85
            102 139 236 
            72	199	192 0 4 0 0
            102 139 229 
            92 
            195]
    
!

testAssembly3

    " instructions without operands.

    (AJInstructionDescription instructions select: [:each | each group = #emit]) keys asSortedCollection
     "
    | str |
    str :=
#(
#cbw 16r66 16r98
#cdq 16r99
#cdqe 16r48 16r98
#clc 16rF8
#cld 16rFC
#cmc 16rF5 
#cpuid 16r0F 16rA2
#cqo  16r48	16r99 "64 bit "
#cwd 16r66 16r99
#cwde 16r98
"#daa 16r27 32 bit"
"#das 16r2F 32 bit"
#emms 16r0F 16r77
#f2xm1 16rD9 16rF0
#fabs  16rD9 16rE1
#fchs 16rD9 16rE0
#fclex 16r9B 16rDB 16rE2
#fcompp 16rDE 16rD9
#fcos 16rD9 16rFF
#fdecstp 16rD9 16rF6
#fincstp 16rD9 16rF7
#finit 16r9B 16rDB 16rE3
#fld1 16rD9 16rE8
#fldl2e 16rD9 16rEA
#fldl2t 16rD9 16rE9
#fldlg2 16rD9 16rEC
#fldln2 16rD9 16rED
#fldpi 16rD9 16rEB
#fldz 16rD9 16rEE
#fnclex 16rDB 16rE2
#fninit 16rDB 16rE3
#fnop 16rD9 16rD0
#fpatan 16rD9 16rF3
#fprem 16rD9 16rF8
#fprem1 16rD9 16rF5
#fptan 16rD9 16rF2
#frndint 16rD9 16rFC
#fscale 16rD9 16rFD
#fsin 16rD9 16rFE
#fsincos 16rD9 16rFB
#fsqrt 16rD9 16rFA
#ftst 16rD9 16rE4
#fucompp 16rDA 16rE9
#fwait 16r9B
#fxam 16rD9 16rE5
#fxtract  16rD9 16rF4
#fyl2x  16rD9 16rF1
#fyl2xp1 16rD9 16rF9
#int3 16rCC
#leave  16rC9
#lfence 16r0F 16rAE 16rE8
#lock 16rF0 "prefix"
#mfence 16r0F 16rAE 16rF0
#monitor 16r0F 16r01 16rC8
#mwait 16r0F 16r01 16rC9
#nop 16r90
#pause 16rF3 16r90
"#popad 16r61 32 bit"
#popfd 16r9D
#popfq 16r48 16r9D   "- 64 bit "
"#pushad 16r60 32 bit"
#pushf 16r66 16r9C
"#pushfd 16r9C 32 bit"
#pushfq 16r9c" -64 bit"
#rdtsc 16r0F 16r31  
#rdtscp 16r0F 16r01 16rF9
#sahf 16r9E
#sfence 16r0F 16rAE 16rF8
#stc 16rF9
#std 16rFD
#ud2 16r0F 16r0B
#std 16rFD "dummy"
) readStream.

[ str atEnd ] whileFalse: [
    | instr tst bytes |
    instr := str next.
    tst := OrderedCollection new.
    [ str peek isInteger ] whileTrue: [ tst add: str next  ].

    asm reset noStackFrame.
    asm perform: instr.
    bytes := asm bytes.
    self assert: (bytes = tst asByteArray ) description: instr, ' failed. expected ', tst asByteArray printString, ' but got ', bytes asByteArray printString.
].
!

testAssemblyImmAddr
    "This is not supported in 64-bit mode -- the ModRM value for this results in RIP-relative addressing."

    self skipIf: true description: 'This is not supported in 64-bit mode -- the ModRM value for this results in RIP-relative addressing.'.
    super testAssemblyImmAddr

    "Modified: / 21-12-2015 / 09:47:24 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testAssemblyMemBase

    self 
        assert: [ :a | a mov: a RAX ptr to: a EAX ]
        bytes: #[ 16r8B 2r00000000 ].
        
    self 
        assert: [ :a | a mov: a RSP ptr to: a EAX]
        bytes: #[ 16r8B 16r04 16r24 ].
    self 
        assert: [ :a | a mov: a RBP ptr to: a EAX ]
        bytes: #[ 16r8B 16r45 16r00 ].
!

testAssemblyMemBaseDisp
    asm
        mov: RAX ptr + 1 -> EAX;
        mov: RBX ptr + ECX -> EAX.
    self assert: asm bytes = #(16r8B 16r40 16r01 16r8B 16r04 16r0B) asByteArray
!

testAssemblyMemBaseDisp2
    asm
        mov: RAX ptr - 1 -> EAX;
        mov: (RBX ptr + ECX) * 2 - 5 -> EAX.
    self assert: asm bytes = #(16r8B 16r40 16rFF 16r8B 16r44 16r4B 16rFB) asByteArray
!

testAssemblyMemBytes
    asm
        mov: (RSI ptr + ECX size: 1) -> BL;
        mov: BL -> (RSI ptr + ECX size: 1).
    self assert: asm bytes = #(16r8A 16r1C 16r0E 16r88 16r1C 16r0E) asByteArray
!

testBitTest
    "8 Bit ====================================================="
    self asmShould: [ :a| a bt: a R8B with: 16r1. ] raise: Error.
    
    "16 bit ====================================================="
    "lower bank 16bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a AX with: 16r01 ]
        bytes: #[ "16bit mode" 16r66  "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].

    "upper bank 16bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a R8W with: 16r01 ]
        bytes: #[ "16bit mode" 16r66 "REX" 2r01000001 "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].
        
    "32 bit ====================================================="
    "lower bank 32bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a EAX with: 16r01 ]
        bytes: #[ "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].
    
    "upper bank 32bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a R8D with: 16r01 ]
        bytes: #[ "REX" 2r01000001 "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].
    
    "64 bit ====================================================="
    "lower bank 32bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a RAX with: 16r01 ]
        bytes: #[ "REX" 2r01001000 "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].
    
    "upper bank 32bit register opcode + ModR/M"
    self 
        assert: [ :a| a bt: a R8 with: 16r01 ]
        bytes: #[ "REX" 2r01001001 "OP" 16r0f 16rba "ModRM" 2r11100000 "immediate" 16r01].
!

testByteRegs4through7
    "Test valid uses of byte registers SPL BPL SIL DIL, only available in 64-bit mode, and when using a REX prefix.
    Can't be used in the same instruction with AH, CH, DH, or BH -- this is tested in testHighByteRegistersInvalid."

    | byteRegs op2codes opBothCodes mixedWidthOpCodes byteRMOperands wideRegisters |
    "byteRegs -- register -> contribution to ModRM byte when used as the reg operand"
    byteRegs := {(SPL -> 16r20).
    (BPL -> 16r28).
    (SIL -> 16r30).
    (DIL -> 16r38)}.	"opBothCodes -- #selector -> #(opcode when byteReg second arg, opcode when byteReg first arg)"
    opBothCodes := {(#adc:with: -> #(16r10 16r12)).
    (#add:with: -> #(16r00 16r02)).
    (#mov:with: -> #(16r88 16r8A)).
    (#cmp:with: -> #(16r38 16r3A)).
    (#or:with: -> #(16r08 16r0A)).
    (#sbb:with: -> #(16r18 16r1A)).
    (#sub:with -> #(16r28 16r2A)).
    (#xor:with: -> #(16r30 16r32))}.	"op2Codes -- #selector -> multiByteBytecode. ByteReg is always the second arg"
    op2codes := {(#cmpxchg:with: -> #[16r0F 16rB0]).
    (#test:with: -> #[16r84]).
    (#xadd:with: -> #[16r0F 16rC0])	"xchg is not actually supported at this time (#xchg:with: -> #[16r86])"}.	"mixedWidthOpCodes -- #selector -> multiByteBytecode. ByteReg is always the second arg"
    mixedWidthOpCodes := {(#movsx:with: -> #[16r0F 16rBE]).
    (#movzx:with: -> #[16r0F 16rB6])}.	"**** Handle #crc32:with: separately due to its legacy prefix ****"	"wideRegisters -- register -> #[REX prefix, contribution to ModRM byte when used as r/m operand]"
    wideRegisters := {(EAX -> #[16r40 16rC0]).
    (RAX -> #[16r48 16rC0]).
    (R8D -> #[16r44 16rC0]).
    (R8 -> #[16r4C 16rC0])}.	"byteRMOperands -- operand -> #(REX prefix, #[modRMContribution, SIB and displacement bytes if any])"
    byteRMOperands := {(SPL -> #(16r40 #[16rC4])).
    (BPL -> #(16r40 #[16rC5])).
    (SIL -> #(16r40 #[16rC6])).
    (DIL -> #(16r40 #[16rC7])).
    (R8B -> #(16r41 #[16rC0])).
    (AL -> #(16r40 #[16rC0])).
    (R8 ptr -> #(16r41 #[16r00])).
    (RAX ptr -> #(16r40 #[16r00])).
    (R8 ptr + 16r12 -> #(16r41 #[16r40 16r12])).
    (RAX ptr + 16r12 -> #(16r40 #[16r40 16r12])).
    (R8 ptr + 16r1234 -> #(16r41 #[16r80 16r34 16r12 16r00 16r00])).
    (RAX ptr + 16r1234 -> #(16r40 #[16r80 16r34 16r12 16r00 16r00])).
    ((RAX ptr + R8) * 2 -> #(16r42 #[16r04 16r40])).
    ((RAX ptr + RAX) * 2 -> #(16r40 #[16r04 16r40])).
    ((RAX ptr + R8) * 4 + 16r12 -> #(16r42 #[16r44 16r80 16r12])).
    ((RAX ptr + RAX) * 4 + 16r12 -> #(16r40 #[16r44 16r80 16r12])).
    ((RAX ptr + R8) * 8 + 16r1234 -> #(16r42 #[16r84 16rC0 16r34 16r12 16r00 16r00])).
    ((RAX ptr + RAX) * 8 + 16r1234 -> #(16r40 #[16r84 16rC0 16r34 16r12 16r00 16r00]))}.
    byteRegs
        do: [ :reg | 
            byteRMOperands
                do: [ :rm | 
                    opBothCodes
                        do: [ :opcode | 
                            | opcodeByte op1 op2 |
                            op1 := reg key.
                            op2 := rm key.
                            opcodeByte := opcode value last.
                            self
                                assert: [ :a | a perform: opcode key with: op1 with: op2 ]
                                bytes:
                                    (ByteArray with: rm value first with: opcodeByte with: reg value | rm value last first) , rm value last allButFirst	"REX"	"ModRM"	"SIB and displacement"	"Need to add the necessary data to allow testing the reverse order of operands." ].
                    op2codes
                        do: [ :opcode | 
                            self
                                assert: [ :a | a perform: opcode key with: rm key with: reg key ]
                                bytes:
                                    ((ByteArray with: rm value first) , opcode value copyWith: reg value | rm value last first) , rm value last allButFirst	"REX"	"ModRM"	"SIB and displacement" ] ].
            mixedWidthOpCodes
                do: [ :opcode | 
                    wideRegisters
                        do: [ :rm | 
                            self
                                assert: [ :a | a perform: opcode key with: rm key with: reg key ]
                                bytes: ((ByteArray with: rm value first) , opcode value copyWith: reg value >> 3 | rm value last)	"REX"	"ModRM"	"SIB and displacement" ] ] ]
!

testCall
    "relative calls ==================================================================="
    "8bit offset"
    self assert: [:a | a call: 16r12 ] bytes: #[ 16rE8 16r12 0 0 0].
    "16bit offset"
    self assert: [:a | a call: 16r1234 ] bytes: #[ 16rE8 16r34 16r12 0 0 ].
    "32bit offset"
    self assert: [:a | a call: 16r12345678 ] bytes: #[ 16rE8 16r78 16r56 16r34 16r12 ].
    
    "indirect calls ==================================================================="
    "lower bank register"
    self assert: [:a | a call: asm RAX ] bytes: #[ 16rFF 2r11010000 ].
    self assert: [:a | a call: asm RDI ] bytes: #[ 16rFF 2r11010111 ].
    
    "upper bank register (require REX prefix)"
    self assert: [:a | a call: asm R8 ] bytes: #[ 2r01001001 16rFF 2r11010000 ].
    self assert: [:a | a call: asm R15 ] bytes: #[ 2r01001001 16rFF 2r11010111 ].
    
    "double indirect calls (with ModR/M) =============================================="
    "mod = 2r00"
    "lower bank register"
    self assert: [:a | a call: a RAX ptr ] bytes: #[ 16rFF 2r00010000 ].
    self assert: [:a | a call: a RDI ptr ] bytes: #[ 16rFF 2r00010111 ].
    
    "upper bank register (require REX prefix)"
    self assert: [:a | a call: a R8 ptr ] bytes: #[ 2r01000001 16rFF 2r00010000 ].
    self assert: [:a | a call: a R15 ptr ] bytes: #[ 2r01000001 16rFF 2r00010111 ].
    
    "double indirect calls with offsets =============================================="
    "mod = 2r01 hence with a folllwing 8bit offset"
    "lower bank register"
    self assert: [:a | a call: a RAX ptr + 8 ] bytes: #[ 16rFF 2r01010000 8].
    self assert: [:a | a call: a RDI ptr + 8 ] bytes: #[ 16rFF 2r01010111 8].
    
    "upper bank register (require REX prefix)"
    self assert: [:a | a call: a R8 ptr + 8] bytes: #[ 2r01000001 16rFF 2r01010000 8].
    self assert: [:a | a call: a R15 ptr + 8] bytes: #[ 2r01000001 16rFF 2r01010111 8].
    
    "double indirect calls with offsets =============================================="
    "mod = 2r10 hence with a following 32bit offset"
    "lower bank register"
    self assert: [:a | a call: a RAX ptr + 16r12345678 ] bytes: #[ 16rFF 2r10010000 16r78 16r56 16r34 16r12].
    self assert: [:a | a call: a RDI ptr + 16r12345678 ] bytes: #[ 16rFF 2r10010111 16r78 16r56 16r34 16r12].
    
    "upper bank register (require REX prefix)"
    self assert: [:a | a call: a R8 ptr + 16r12345678] bytes: #[ 2r01000001 16rFF 2r10010000 16r78 16r56 16r34 16r12].
    self assert: [:a | a call: a R15 ptr + 16r12345678] bytes: #[ 2r01000001 16rFF 2r10010111 16r78 16r56 16r34 16r12].
!

testCallInvalid
    "on 64 bit ..
    - only 32bit relative offset are allowed
    - only 64bit registers for indirect addresses"

    "relative calls with 64bit addresses are not supported"

    self asmShould: [ :a | a call: 16r123456789ABCDEF ] raise: Error.
    AJx86Registers generalPurpose
        do: [ :register | 
            register is64
                ifFalse: [ self asmShould: [ :a | a call: register ] raise: Error ]
                ifTrue: [ self deny: (self bytes: [ :a | a call: register ]) isEmpty ] ]
!

testHighByteRegistersInvalid
    "Can't access AH, BH, CH, DH if a REX byte is required.
    This test attempts to test every instruction supported by AsmJit that can access an 8-bit general-purpose register AND also require a REX prefix."

    | legacyHRegs op2codes opBothCodes mixedWidthOpCodes byteOperandsRequiringRex wideRegistersRequiringRex |
    legacyHRegs := {AH.
    CH.
    DH.
    BH}.
    opBothCodes := #(#adc:with: #add:with: #mov:to: #cmp:with: #or:with: #sbb:with: #sub:with #xchg:with: #xor:with:).
    op2codes := #(#cmpxchg:with: #test:with: #xadd:with:).
    mixedWidthOpCodes := #(#crc32:with: #movsx:to: #movzx:to:).
    wideRegistersRequiringRex := {RAX.
    R8D}.	"RAX requires REX.W, R8D requires REX.R or REX.B"
    byteOperandsRequiringRex := {SPL.
    BPL.
    SIL.
    DIL.
    R8B.
    (R8 ptr).
    (R8 ptr + 16r12).
    (R8 ptr + 16r1234).
    ((RAX ptr + R8) * 2).
    ((RAX ptr + R8) * 4 + 16r12).
    ((RAX ptr + R8) * 8 + 16r1234)}.
    legacyHRegs
        do: [ :hreg | 
            byteOperandsRequiringRex
                do: [ :operand | 
                    opBothCodes
                        do: [ :opcode | 
                            self asmShould: [ :a | a perform: opcode with: hreg with: operand ] raise: Error.
                            self asmShould: [ :a | a perform: opcode with: operand with: hreg ] raise: Error ].
                    op2codes do: [ :opcode | self asmShould: [ :a | a perform: opcode with: operand with: hreg ] raise: Error ] ].
            mixedWidthOpCodes
                do: [ :opcode | 
                    wideRegistersRequiringRex
                        do: [ :wideReg | self asmShould: [ :a | a perform: opcode with: wideReg with: hreg ] raise: Error ] ] ]
!

testImmLabels
    "test immediates with labels"

    | code pos |
    asm
        mov: RAX ptr -> EAX;
        mov: (16rFFFFFFFF asUImm label: (asm labelNamed: #foo)) to: EAX.
    code := asm generatedCode.
    pos := code offsetAt: #foo.
    self assert: (code bytes at: pos + 1) = 255.
    self assert: (code bytes at: pos + 2) = 255.
    self assert: (code bytes at: pos + 3) = 255.
    self assert: (code bytes at: pos + 4) = 255
!

testIndexScales
    self
        assert: [ :a | a mov: RAX -> ((RCX ptr + RDX) * 1) ] bytes: #[16r48 16r89 16r04 16r11];
        assert: [ :a | a mov: RAX -> ((RCX ptr + RDX) * 2) ] bytes: #[16r48 16r89 16r04 16r51];
        assert: [ :a | a mov: RAX -> ((RCX ptr + RDX) * 4) ] bytes: #[16r48 16r89 16r04 16r91];
        assert: [ :a | a mov: RAX -> ((RCX ptr + RDX) * 8) ] bytes: #[16r48 16r89 16r04 16rD1].
    self
        assert: [ :a | a mov: (RCX ptr + RDX) * 1 -> RAX ] bytes: #[16r48 16r8B 16r04 16r11];
        assert: [ :a | a mov: (RCX ptr + RDX) * 2 -> RAX ] bytes: #[16r48 16r8B 16r04 16r51];
        assert: [ :a | a mov: (RCX ptr + RDX) * 4 -> RAX ] bytes: #[16r48 16r8B 16r04 16r91];
        assert: [ :a | a mov: (RCX ptr + RDX) * 8 -> RAX ] bytes: #[16r48 16r8B 16r04 16rD1]
!

testInvalidTest
    "In 64-bit mode, r/m8 cannot be encoded to access the following byte registers if an REX prefix is used: AH, BH, CH, DH."

    {AH.
    CH.
    DH.
    BH}
        do: [ :reg | 
            self deny: (self bytes: [ :a | a test: reg with: AL ]) isEmpty.
            self deny: (self bytes: [ :a | a test: AL with: reg ]) isEmpty.
            self deny: (self bytes: [ :a | a test: reg with: 16r12 ]) isEmpty.	"with an upper bank byte register => requires REX prefix"
            self asmShould: [ :a | a test: reg with: R8B ] raise: Error.
            self asmShould: [ :a | a test: R8B with: reg ] raise: Error.	"with a 64bit register requring again an REX prefix"
            self asmShould: [ :a | a test: reg with: RAX ] raise: Error.
            self asmShould: [ :a | a test: RAX with: reg ] raise: Error ]
!

testJumps

    self 
        assert: [:a|
            a 
                label: #label1;
                nop;
                nop;
                nop;
                jz: #label1.
        ] bytes: #[144 144 144 16r74 251 "-5 asByte"].
    
    
    
    asm 
        reset; noStackFrame;
        label: #label1.
        126 timesRepeat: [ asm nop ].
        asm jz: #label1.
    self assert: (asm bytes size = 128).


    self 
        assert: [:a |
            a
                reset; noStackFrame;
                label: #label1;
                nop;
                nop;
                nop;
                jmp: #label1.
        ] bytes: #[144 144 144 235 251 ].
    
    self 
        assert: [:a |
            a 
                reset; noStackFrame;
                jmp: #label1;
                label: #label1.
        ] bytes: #[ 16rEB 0 ].
        
!

testMovHighIndexRegister
    "Mov that use r8-r15 as an index register, therefore requiring REX.X"

    self
        assert: [ :a | a mov: RAX -> ((RCX ptr + R14) * 1) ] bytes: #[16r4A 16r89 16r04 16r31];
        assert: [ :a | a mov: (RCX ptr + R14) * 1 -> RAX ] bytes: #[16r4A 16r8B 16r04 16r31]
!

testMovImmediate
    "8bit immediate to 8bit register"
    self 
        assert: [:a | a mov: 16r12 to: a AL ] 
        bytes: #[16rB0     16r12].
        
    "16bit immediate to 16bit register (requires 16bit fallback prefix)"
    self 
        assert: [:a | a mov: 16r1234 to: a AX ] 
        bytes: #[16r66 16rB8     16r34 16r12].
    
    "32bit immediate to 32bit register"
    self 
        assert: [:a | a mov: 16r12345678 to: a EAX ] 
        bytes: #[16rB8     16r78 16r56 16r34 16r12].
    
    "64bit immediate to 64bit register (requires REX prefix)"
    self 
        assert: [:a | a mov: 16r123456789ABCDEF0 to: a RAX ] 
        bytes: #[2r01001000 16rB8    16rF0 16rDE 16rBC 16r9A 16r78 16r56 16r34 16r12].
        
    "32bit immediate sign-extended to 64bit register (REX prefix)"
    self
        assert: [:a | a mov: 16r12345678 to: a RAX]
        bytes: #[ 2r01001000 16rc7 "ModR/M"16rc0    16r78 16r56 16r34 16r12 ]
!

testMovMemory
    "mov memory to 8bit register =========================="
    self 
        assert: [:a | a mov: a RCX ptr to: a AL ] 
        bytes: #[16r8A 16r00000001 "ModR/M"].
    
!

testMovZX
    "byte to word ========================================"
    "lower bank 8bit to lower bank 16bit"
    self 
        assert: [:a | a movzx: a AL to: a AX ]
        bytes: #[102 15 182 192 ].
    "lower bank 8bit to upper bank 16bit"
    self 
        assert: [:a | a movzx: a AL to: a R8W ]
        bytes: #[102 68 15 182 192].
    "upper bank 8bit to lower bank 16bit"
    self 
        assert: [:a | a movzx: a R8B to: a AX ]
        bytes: #[102 65 15 182 192].
    "upper bank 8bit to upper bank 16bit"
    self 
        assert: [:a | a movzx: a R8B to: a R8W ]
        bytes: #[102 69 15 182 192].
    
    "byte to doubleword ================================"
    "lower bank 8bit to lower bank 32bit"
    self 
        assert: [:a | a movzx: a AL to: a EAX ]
        bytes: #[15 182 192 ].
    "lower bank 8bit to upper bank 32bit"
    self 
        assert: [:a | a movzx: a AL to: a R8D ]
        bytes: #[68 15 182 192].
    "upper bank 8bit to lower bank 32bit"
    self 
        assert: [:a | a movzx: a R8B to: a EAX ]
        bytes: #[65 15 182 192].
    "upper bank 8bit to upper bank 32bit"
    self 
        assert: [:a | a movzx: a R8B to: a R8D ]
        bytes: #[69 15 182 192].
    
    "byte to quadword ==================="
    "lower bank 8bit to lower bank 64bit"
    self 
        assert: [:a | a movzx: a AL to: a RAX ]
        bytes: #[72 15 182 192 ].
    "lower bank 8bit to upper bank 64bit"
    self 
        assert: [:a | a movzx: a AL to: a R8 ]
        bytes: #[76 15 182 192].
    "upper bank 8bit to lower bank 64bit"
    self 
        assert: [:a | a movzx: a R8B to: a RAX ]
        bytes: #[73 15 182 192 ].
    "upper bank 8bit to upper bank 64bit"
    self 
        assert: [:a | a movzx: a R8B to: a R8 ]
        bytes: #[77 15 182 192].
    
    "word to quadword ==================="
    "lower bank 16bit to lower bank 64bit"
    self 
        assert: [:a | a movzx: a AX to: a RAX ]
        bytes: #[72 15 183 192].
    "lower bank 16bit to upper bank 64bit"
    self 
        assert: [:a | a movzx: a AX to: a R8 ]
        bytes: #[76 15 183 192].
    "upper bank 16bit to lower bank 64bit"
    self 
        assert: [:a| a movzx: a R8W to: a RAX ]
        bytes: #[73 15 183 192].
    "upper bank 16bit to upper bank 64bit"
    self 
        assert: [:a | a movzx: a R8W to: a R8 ]
        bytes: #[77 15 183 192].
!

testMovZxSxInvalid
    {AH.
    CH.
    DH.
    BH}
        do: [ :reg | 
            self deny: (self bytes: [ :a | a movzx: reg to: a EAX ]) isEmpty.
            self asmShould: [ :a | a movzx: reg to: a RAX ] raise: Error ]
!

testMul
    "8bit unsigned multiplication =================================="
    "lower bank register: AX := AL * CL"
    self 
        assert: [ :a | a mul: a CL]
        bytes: #[ 16rF6 "ModR/M" 2r11100001 ].
    "upper bank register needs an REX prefix: AX := AL * R8B"
    self 
        assert: [ :a | a mul: a R9B]
        bytes: #[ 2r01000001 16rF6 2r11100001].
        
    "16bit unsigned multiplication =================================="	
    "DX:AX := AX * CX"
    self 
        assert: [ :a | a mul: a CX]
        bytes: #[ "16bit fallback" 16r66 16rF7 2r11100001].
    "32bit unsigned multiplication =================================="	
    "EDX:EAX := EAX * ECX"
    self 
        assert: [ :a | a mul: a ECX]
        bytes: #[ 16rF7 2r11100001 ].
    "64bit unsigned multiplication =================================="	
    "RDX:RAX := RAX * RCX"
    self 
        assert: [ :a| a mul: a RCX]
        bytes: #[ 2r01001000 16rF7 2r11100001].
!

testNeg
    "8bit ======================================================"
    self 
        assert: [ :a | a neg: a AL]
        bytes: #[ 16rF6 "ModR/M" 2r11011000 ].
    "8bit upper bank with REX"
    self 
        assert: [ :a | a neg: a R8B]
        bytes: #[ 2r01000001 16rF6 "ModR/M" 2r11011000 ].
    
    "16bit with fallback ======================================="
    self 
        assert: [ :a | a neg: a AX]
        bytes: #[ 16r66 16rF7"ModR/M" 2r11011000 ].
    "16bit upper bank with REX"
    self 
        assert: [ :a | a neg: a R8W]
        bytes: #[ 16r66 2r01000001 16rF7"ModR/M" 2r11011000 ].
    "word 16bit IP relative "
    self 
        assert: [ :a | a neg: a IP ptr16 + 16r12345678]
        bytes: #[16r66 16rF7 "ModR/M"2r00011101 16r78 16r56 16r34 16r12].
        
    "32bit ===================================================="
    self 
        assert: [ :a | a neg: a EAX]
        bytes: #[ 16rF7"ModR/M" 2r11011000 ].
    "32bit upper bank with REX"
    self 
        assert: [ :a | a neg: a R8D]
        bytes: #[ 2r01000001 16rF7"ModR/M" 2r11011000 ].
    "negate double word 32bit EIP relative "
    self 
        assert: [ :a | a neg: a EIP ptr32 + 16r12345678]
        bytes: #[16rF7 "ModR/M"2r00011101 16r78 16r56 16r34 16r12].
        
    "64bit with REX =========================================="
    self 
        assert: [ :a | a neg: a RAX]
        bytes: #[ 2r01001000 16rF7 "ModR/M"2r11011000 ].
    "64bit upper bank"
    self 
        assert: [ :a | a neg: a R8]
        bytes: #[ 2r01001001 16rF7 "ModR/M"2r11011000 ].
    "negate quadword 64bit RIP relative "
    self 
        assert: [ :a | a neg: a RIP ptr64 + 16r12345678]
        bytes: #["REX"2r01001000 16rF7 "ModR/M"2r00011101 16r78 16r56 16r34 16r12].
        
!

testPop
    "lower bank 64bit register"		
    self assert: [:a | a pop: a RSP ]
        bytes: #[ 16r5c ].
!

testPush
    "lower bank 64bit register"		
    self assert: [:a | a push: a RSP ]
        bytes: #[ 16r54 "16r50 + RSP index" ].
!

testSyscall
    self assert: [ :a | a syscall ] bytes: #[16r0F 16r05]
!

testTest
    "8bit operand  and lower bank 8bit register"		
    self assert: [:a | a test: a CL with: 16r12 ]
        bytes: #[246	 193 16r12].
    "8bit operand  and uppe bank 8bit register"	
    self assert: [:a | a test: a R8B with: 16r12]
        bytes: #[2r01000001 2r11110110  2r11000000  16r12].
    
    "16bit operand  and lower bank 16bit register"		
    self assert: [:a | a test: a CX with: 16r1234]
        bytes: #[102 247 193   16r34 16r12].
    "16bit operand  and uppe bank 16bit register"	
    self assert: [:a | a test: a R8W with: 16r1234]
        bytes: #[102 65 247 192   16r34 16r12].
    
    "32bit operand  and lower bank 32bit register"		
    self assert: [:a | a test: a ECX with: 16r12345678]
        bytes: #[247 193   16r78 16r56 16r34 16r12].
    "32bit operand  and uppe bank 32bit register"	
    self assert: [:a | a test: a R8D with: 16r12345678]
        bytes: #[65 247 192   16r78 16r56 16r34 16r12].
    
    "32bit operand  and lower bank 64bit register"		
    self assert: [:a| a test: a RCX with: 16r12345678]
        bytes: #[72 247 193   16r78 16r56 16r34 16r12].
    "32bit operand  and uppe bank 64bit register"	
    self assert: [:a| a test: a R8 with: 16r12345678]
        bytes: #[73 247 192	  16r78 16r56 16r34 16r12].
    
    
    
!

testXor
    "8bit register xor 8bit immediate =================================="		
    "lower bank 8bit register opcode + ModR/M"
    self 
        assert: [ :a | a xor: a CL with: 16r12]
        bytes: #[ 16r80 2r11110001    16r12].
        
    "upper bank 8bit register requiring REX"
    self 
        assert: [ :a | a xor: a R8B with: 16r12]
        bytes: #[2r01000001 16r80 2r11110000    16r12].
        
    "16bit register xor 8bit immediate =================================="
    "lower bank 16bit register"
    self 
        assert: [ :a | a xor: a CX with: 16r1234]
        bytes: #[16r66 16r81 2r11110001    16r34 16r12].
    "upper bank 16bit"
    self 
        assert: [ :a | a xor: a R8W with: 16r1234]
        bytes: #[16r66 2r01000001 16r81 2r11110000 16r34 16r12].
        
    "32bit register ====================================================="
    "lower bank 32bit register"
    self 
        assert: [ :a | a xor: a ECX with: 16r12345678]
        bytes: #[16r81 2r11110001    16r78 16r56 16r34 16r12].
        
    "upper bank register requiring REX prefix"
    self 
        assert: [ :a | a xor: a R8D with: 16r12345678]
        bytes: #[2r01000001 16r81 2r11110000     16r78 16r56 16r34 16r12]
    
!

testXorFastCode
    self  "shortcut for AL + 8bit immedidate"
        assert: [ :a | a xor: a AL with: 16r12]
        bytes: #[ 16r34    16r12].
        
    self  "shortcut for AX + 16bit immedidate"
        assert: [ :a | a xor: a AX with: 16r1234]
        bytes: #[ 16r66 16r35    16r34 16r12].
        
    self  "shortcut for EAX + 16bit immedidate"
        assert: [ :a | a xor: a EAX with: 16r12345678]
        bytes: #[ 16r35    16r78 16r56 16r34 16r12].
        
    self  "shortcut for RAX + 32bit immedidate"
        assert: [ :a | a xor: a RAX with: 16r12345678]
        bytes: #[ 2r01001000 16r35    16r78 16r56 16r34 16r12].
!

testXorInvalid
    "xor registers with non-matching sizes"

    self asmShould: [ :a | a xor: AL to: RAX ] raise: Error.
    self asmShould: [ :a | a xor: RAX to: AL ] raise: Error.
    self asmShould: [ :a | a xor: R8B to: RAX ] raise: Error.
    self asmShould: [ :a | a xor: RAX to: R8B ] raise: Error.	"in 64bit mode AH CH DH and BH cannot be encoded when an REX prefix is present"
    {AH.
    CH.
    DH.
    BH} do: [ :reg | self asmShould: [ :a | a xor: reg to: a R8B ] raise: Error ]
! !

!AJx64AssemblerTests methodsFor:'utility'!

newAssembler 
    ^ AJx64Assembler new
        noStackFrame;
        yourself
! !

!AJx64AssemblerTests class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
! !