MASM - Macro variable?

414 views Asked by At

This is my first question here and I hope that you're able to help me! I'm currently working on a GameBoy Emulator and want to write it in MASM, for handling the CPU instructions, I want to create an array with variables to make it easier for me to access.

Here is an example what I want to achieve:

assume esi:ptr CPU_CORE
REGISTER_A equ (([esi].registers.AF AND 0F0h) SAR 3h)
REGISTER_B equ (([esi].registers.BC AND 0F0h) SAR 3h)
REGISTER_C equ (([esi].registers.BC AND 0Fh))
PARAM_TABLE [Type?] REGISTER_A
            [Type?] REGISTER_B
            [Type?] REGISTER_C
assume esi:ptr NOTHING

and if I want to grab a value from the PARAM_TABLE it should work like this:

lea esi, PARAM_TABLE
mov ecx, 1h ; just as example for accessing REGISTER_B
mov eax, [esi+ecx*[TYPE?]]
;EAX now contains the value from the hi-byte of the BC register, so: (([esi].registers.BC AND 0F0h) SAR 3h)

So basically my idea is to create variables like REGISTER_A to make it easier to access. I hope you understand me. Maybe it would be possible to do this with macros?

1

There are 1 answers

0
BolterVi On

So I answer my own question after tying out some things. Indeed you can create constants using registers like:

REGISTER_A equ [esi+CPU_CORE.registers.A]

but you can only load it like:

mov eax, REGISTER_A ;EAX will now contain the value of the register A.

Then i've figure out that you can just directly access the offsets of a structure (i don't know why i never have used it before.)

REGISTER_A equ CPU_CORE.registers.A

Since this works i've created a structure called PARAM which looks like:

PARAM struct
  pointer DWORD  ? ;Register pointer
  flags   BYTE   ? ;BIT 0: Content Flag, BIT 1: 8 or 16 bit
  desc    DWORD  ? ;Description of the parameter
PARAM ends

And from that i've just created a parameter list for the LD R,R opcode. which looks like:

PARAM_LIST_R    PARAM       <CPU_CORE.registers.B, 0, _stro('B')>
                PARAM       <CPU_CORE.registers._C, 0, _stro('C')>
                PARAM       <CPU_CORE.registers.D, 0, _stro('D')>
                PARAM       <CPU_CORE.registers.E, 0, _stro('E')>
                PARAM       <CPU_CORE.registers.H, 0, _stro('H')>
                PARAM       <CPU_CORE.registers.L, 0, _stro('L')>
                PARAM       <CPU_CORE.registers.H, 1, _stro('(HL)')>
                PARAM       <CPU_CORE.registers.A, 0, _stro('A')>

and a function to read from the register

LoadParam       proc        cpu_core:DWORD, param:DWORD
    LOCAL value:DWORD
    pushad
    mov esi, cpu_core
    mov edi, param
    assume esi:ptr CPU_CORE
    assume edi:ptr PARAM
    add esi, [edi].pointer
    movzx ebx, [edi].flags
    bt ebx, 0
    test ebx, ebx
    jnc @NO_CONTENT
    movzx edx, word ptr ds:[esi]
    ;-- ADDING CPU_READ FUNCTION --;
    jmp @DONE
@NO_CONTENT:
    bt ebx, 1
    jc @GET16Bit
    movzx eax, byte ptr ds:[esi]
    jmp @DONE
@GET16Bit:
    movzx eax, word ptr ds:[esi]
@DONE:
    mov value, eax
    assume esi:ptr NOTHING
    assume edi:ptr NOTHING
    popad
    mov eax, value
    ret
LoadParam endp

This function loads the CPU_CORE into ESI and the PARAM into EDI, the pointer of the PARAM gets added to the CPU_CORE. After that the flags are getting tested, if BIT 0 is set it reads from the CPU memory (as example: (HL)), if it's not set it will just grab the value from the register. The BIT 1 is for the size if it's set the function will read a 16-bit register (BC, DE, HL) or an 8-bit register (B, C, D, E, H, L, A).

I hope you can understand what I've wrote. If you have any questions feel free to ask, this is just by far the "easiest" way for me to solve my problem.

If you're wondering why I've created the table: You can "decode" the opcodes for further information google "z80 decoding".