#COMPILE EXE "test_addressing_modes.exe"
#DIM ALL
#TOOLS OFF
#OPTIMIZE OFF
#DEBUG ERROR ON

' ======================================================================
' test_addressing_modes.pb
' Comprehensive compile test for ALL x64 addressing modes in #ASM blocks.
' Each category is in a separate function.
' ======================================================================

' Global data for RIP-relative addressing
GLOBAL g_data AS LONG
GLOBAL g_data2 AS LONG

SUB setup()
    #ASM
        lea rax, [rsp - 0x200]
        and rax, -16
        mov QWORD PTR [rsp + 0x100], rax

        ' Load registers with known values for base/index
        mov rax, rsp
        mov rbx, rsp
        mov rcx, rsp
        mov rdx, rsp
        mov rsi, rsp
        mov rdi, rsp
        mov r8,  rsp
        mov r9,  rsp
        mov rbp, rsp
    #ENDASM
END SUB

' ==============================================================
' Mode 1: [rax] - Register Indirect
' Both source and destination variants
' ==============================================================
FUNCTION am_register_indirect() AS LONG
    setup
    #ASM
        ; Register indirect as source
        mov rbx, QWORD PTR [rax]
        mov rcx, DWORD PTR [rbx]
        mov rdx, WORD PTR [rcx]
        mov rsi, BYTE PTR [rdx]
        movsd xmm0, QWORD PTR [rax]

        ; Register indirect as destination
        mov QWORD PTR [rax], rbx
        mov DWORD PTR [rdi], ecx
        mov WORD PTR [rsi], dx
        mov BYTE PTR [rdi], al
        movsd QWORD PTR [rax], xmm0
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 2: [rax + 0x100] - Register + Displacement
' ==============================================================
FUNCTION am_reg_plus_disp() AS LONG
    setup
    #ASM
        ; Source
        mov rbx, QWORD PTR [rax + 0x100]
        mov rcx, DWORD PTR [rbx + 0x50]
        mov rdx, WORD PTR [rcx + 0x20]
        mov rsi, BYTE PTR [rdx + 0x10]
        movsd xmm0, QWORD PTR [rax + 0x80]

        ; Destination
        mov QWORD PTR [rax + 0x100], rbx
        mov DWORD PTR [rdi + 0x50], ecx
        mov WORD PTR [rsi + 0x20], dx
        mov BYTE PTR [rdi + 0x10], al
        movsd QWORD PTR [rax + 0x80], xmm0
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 3: [rax + rbx] - Base + Index
' ==============================================================
FUNCTION am_base_plus_index() AS LONG
    setup
    #ASM
        ; Source
        mov rcx, QWORD PTR [rax + rbx]
        mov rdx, DWORD PTR [rbx + rcx]
        mov rsi, WORD PTR [rcx + rdx]
        mov rdi, BYTE PTR [rdx + rsi]
        movsd xmm0, QWORD PTR [rax + rbx]

        ; Destination
        mov QWORD PTR [rax + rbx], rcx
        mov DWORD PTR [rbx + rcx], edx
        mov WORD PTR [rcx + rdx], si
        mov BYTE PTR [rdx + rsi], al
        movsd QWORD PTR [rax + rbx], xmm0
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 4: [rax + rbx*4] - Base + Index*Scale
' ==============================================================
FUNCTION am_base_plus_index_scale() AS LONG
    setup
    #ASM
        ; Source with scale factors 1, 2, 4, 8
        mov rcx, QWORD PTR [rax + rbx*1]
        mov rdx, DWORD PTR [rbx + rcx*2]
        mov rsi, WORD PTR [rcx + rdx*4]
        mov rdi, BYTE PTR [rdx + rsi*8]
        movsd xmm0, QWORD PTR [rax + rbx*8]

        ; Destination
        mov QWORD PTR [rax + rbx*1], rcx
        mov DWORD PTR [rbx + rcx*2], edx
        mov WORD PTR [rcx + rdx*4], si
        mov BYTE PTR [rdx + rsi*8], al
        movsd QWORD PTR [rax + rbx*4], xmm0
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 5: [rax + rbx*4 + 0x100] - Base + Index*Scale + Disp
' ==============================================================
FUNCTION am_full_sib() AS LONG
    setup
    #ASM
        ; Source
        mov rcx, QWORD PTR [rax + rbx*1 + 0x100]
        mov rdx, DWORD PTR [rbx + rcx*2 + 0x50]
        mov rsi, WORD PTR [rcx + rdx*4 + 0x20]
        mov rdi, BYTE PTR [rdx + rsi*8 + 0x10]
        movsd xmm0, QWORD PTR [rax + rbx*8 + 0x80]

        ; Destination
        mov QWORD PTR [rax + rbx*1 + 0x100], rcx
        mov DWORD PTR [rbx + rcx*2 + 0x50], edx
        mov WORD PTR [rcx + rdx*4 + 0x20], si
        mov BYTE PTR [rdx + rsi*8 + 0x10], al
        movsd QWORD PTR [rax + rbx*4 + 0x80], xmm0
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 6: [rsp + 0x100] - RSP-Relative
' ==============================================================
FUNCTION am_rsp_relative() AS LONG
    setup
    #ASM
        ; Source
        mov rax, QWORD PTR [rsp + 0x100]
        mov rbx, DWORD PTR [rsp + 0x50]
        mov rcx, WORD PTR [rsp + 0x20]
        mov rdx, BYTE PTR [rsp + 0x10]

        ; Destination
        mov QWORD PTR [rsp + 0x100], rax
        mov DWORD PTR [rsp + 0x50], ebx
        mov WORD PTR [rsp + 0x20], cx
        mov BYTE PTR [rsp + 0x10], dl
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 7: [rip + offset] - RIP-Relative (via global data label)
' ==============================================================
FUNCTION am_rip_relative() AS LONG
    setup
    #ASM
        ; RIP-relative source (via global symbol in PBXA64)
        mov rax, QWORD PTR [rip + g_data]
        mov eax, DWORD PTR [rip + g_data2]

        ; RIP-relative with explicit offset
        mov rax, QWORD PTR [rip + 0x100]

        ; RIP-relative with index register (no scale)
        mov rax, QWORD PTR [rip + rbx + g_data]

        ; RIP-relative with index*scale
        mov rax, QWORD PTR [rip + rcx*8 + g_data]
        mov rax, QWORD PTR [rip + rdx*4 + 0x80]
        mov rax, QWORD PTR [rip + r8*8 + g_data2]
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 8: [rbp] - RBP-Relative
' ==============================================================
FUNCTION am_rbp_relative() AS LONG
    setup
    #ASM
        ; Source
        mov rax, QWORD PTR [rbp]
        mov rbx, DWORD PTR [rbp]
        mov rcx, WORD PTR [rbp]
        mov rdx, BYTE PTR [rbp]

        ; Destination
        mov QWORD PTR [rbp], rax
        mov DWORD PTR [rbp], ebx
        mov WORD PTR [rbp], cx
        mov BYTE PTR [rbp], dl
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 9: [rbp - 8] - RBP-Relative with Negative Displacement
' ==============================================================
FUNCTION am_rbp_negative_disp() AS LONG
    setup
    #ASM
        ; Source
        mov rax, QWORD PTR [rbp - 8]
        mov rbx, DWORD PTR [rbp - 4]
        mov rcx, WORD PTR [rbp - 2]
        mov rdx, BYTE PTR [rbp - 1]

        ; Destination
        mov QWORD PTR [rbp - 8], rax
        mov DWORD PTR [rbp - 4], ebx
        mov WORD PTR [rbp - 2], cx
        mov BYTE PTR [rbp - 1], dl
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 10: Short-form displacement (8-bit vs 32-bit)
' ==============================================================
FUNCTION am_disp_sizes() AS LONG
    setup
    #ASM
        ; 8-bit displacement (1 byte in encoding)
        mov rax, QWORD PTR [rcx + 0x7F]

        ; 32-bit displacement (4 bytes in encoding)
        mov rax, QWORD PTR [rcx + 0x12345678]

        ; Zero displacement explicit
        mov rax, QWORD PTR [rcx + 0]
        mov QWORD PTR [rcx + 0], rax
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 11: Index-only (scale but no base)
' ==============================================================
FUNCTION am_index_only() AS LONG
    setup
    #ASM
        ; Note: [rax*4] requires a base register in x64 encoding
        ; So we use the form [rax + rbx*4] covered above

        ; rip-relative with index
        mov rax, QWORD PTR [rip + rbx*8 + 0x100]
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 12: All register combinations for SIB byte testing
' ==============================================================
FUNCTION am_sib_combinations() AS LONG
    setup
    #ASM
        ; Base=RSP, Index=RSP+scale (uses SIB with no index)
        mov rax, QWORD PTR [rsp + 0x100]
        mov rax, QWORD PTR [rsp + rbx]

        ; Base=RBP, Index used
        mov rax, QWORD PTR [rbp + rcx*4]
        mov rax, QWORD PTR [rbp + rbx*8 + 0x100]

        ; Base=R12-R15 (requires REX prefix)
        mov rax, QWORD PTR [r12 + 0x100]
        mov rax, QWORD PTR [r13 + r14*4]
        mov rax, QWORD PTR [r14 + r15*8 + 0x200]
        mov rax, QWORD PTR [r15 + 0x100]

        ; Index=R8-R15 (requires REX prefix)
        mov rax, QWORD PTR [rcx + r8*4]
        mov rax, QWORD PTR [rbx + r9*8]
        mov rax, QWORD PTR [rcx + r10*4 + 0x50]
        mov rax, QWORD PTR [rbx + r11*8 + 0x100]

        ; Both base and index are R8-R15
        mov rax, QWORD PTR [r8 + r9*4]
        mov rax, QWORD PTR [r10 + r11*8 + 0x100]
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' Mode 13: Segment override prefixes (fs:, gs:)
' ==============================================================
FUNCTION am_segment_override() AS LONG
    setup
    #ASM
        ; fs: segment override as source
        mov rax, QWORD PTR fs:[rcx]
        mov rax, fs:[rcx]

        ; fs: segment override as destination
        mov QWORD PTR fs:[rax], rbx

        ; gs: segment override as source
        mov rax, gs:[rcx]

        ; gs: segment override as destination
        mov gs:[rax], rbx

        ; fs: with displacement
        mov rax, QWORD PTR fs:[rcx + 0x100]

        ; gs: with index*scale + displacement
        mov rax, gs:[rcx + rdx*4 + 0x100]

        ; fs: with SSE instruction
        movsd xmm0, QWORD PTR fs:[rax]

        ; gs: with x87 instruction
        fld QWORD PTR gs:[rax]
    #ENDASM
    FUNCTION = 0
END FUNCTION

' ==============================================================
' MAIN
' ==============================================================
FUNCTION PBMAIN() AS LONG
    LOCAL result AS LONG
    LOCAL failures AS LONG
    result = 0

    result = am_register_indirect()
    IF result <> 0 THEN INCR failures

    result = am_reg_plus_disp()
    IF result <> 0 THEN INCR failures

    result = am_base_plus_index()
    IF result <> 0 THEN INCR failures

    result = am_base_plus_index_scale()
    IF result <> 0 THEN INCR failures

    result = am_full_sib()
    IF result <> 0 THEN INCR failures

    result = am_rsp_relative()
    IF result <> 0 THEN INCR failures

    result = am_rip_relative()
    IF result <> 0 THEN INCR failures

    result = am_rbp_relative()
    IF result <> 0 THEN INCR failures

    result = am_rbp_negative_disp()
    IF result <> 0 THEN INCR failures

    result = am_disp_sizes()
    IF result <> 0 THEN INCR failures

    result = am_index_only()
    IF result <> 0 THEN INCR failures

    result = am_sib_combinations()
    IF result <> 0 THEN INCR failures

    result = am_segment_override()
    IF result <> 0 THEN INCR failures

    IF failures = 0 THEN
        PRINT "ALL ADDRESSING MODE TESTS PASSED"
        FUNCTION = 0
    ELSE
        PRINT "SOME ADDRESSING MODE TESTS FAILED: "; failures
        FUNCTION = 1
    END IF
END FUNCTION
