Addressing mode Program Counter Relative with Index support?
I'm trying to research a neogeo game. I using MAME to export the code. There is a piece of code that appears frequently in the game.
090714: D040 add.w D0, D0
090716: D040 add.w D0, D0
090718: 4EFB 0002 jmp ($2,PC,D0.w)
09071C: 6000 0046 bra $90764
090720: 6000 0068 bra $9078a
090724: 6000 0082 bra $907a8
090728: 6000 008C bra $907b6
09072C: 6000 00AE bra $907dc
This is a jumping table. My main focus is on ($2,PC,D0.w).This is a little complex addressing mode called Program Counter Relative with Index. His syntax is (d8,Dn,PC). It can be understood as jump to PCAddressNextOpcode+D0.w+d8. But after I compiled this code, its actual syntax is (label,pc,d0.w). I tried to compile this instruction on easy68k and got the same result. I'm new at disassembly. I would like to ask if the label method is standard of 68k compiler, or if it just lacks support for the d8 method?
I noticed that #20 mentioned the relevant issue. I'm also trying to use ghidra for research. But I still prefer the label method shown in the code exported by MAME.
The label method is standard: when relative to the program counter, displacements are absolute rather than relative, which allows code to be written like this...
move.w (Sign_Index,pc,d0.w),d1
...instead of this:
move.w (Sign_Index-*-2,pc,d0.w),d1
I cannot find any proof of relative program counter displacements being supported by 68k assemblers, so it appears that MAME is outputting invalid assembly.
I'm not quite sure what you mean. Also, there were some issues with my previous description. So I'd like to clarify it again.
00000000 303C 0004 11 MOVE #$4,D0
00000004 4EFB 000A 12 JMP $A(PC,D0)
00000008 4E71 13 NOP
0000000A 4E71 14 NOP
0000000C 4E71 15 NOP
0000000E 4E71 16 NOP
00000010 6000 0FEE 17 bra $1000
00000014 6000 1FEA 18 bra $2000
00000018 6000 2FE6 19 bra $3000
0000001C 6000 3FE2 20 bra $4000
code A
This code will jump to address $14 in MAME to execute the bra $2000 instruction. This is because offset + (PC + 2) + D0 ,which is $A + (4 + 2) + 4 = $14.
But after compiling the code, I discovered it compiles the JMP instruction as: (note the difference of the machine code)
00000004 4EFB 0004 12 JMP $A(PC,D0)
It will jump to $E, which is the fourth NOP. This is not correct.
The code must be corrected as follows to achieve the intended jump:
00000000 303C 0004 11 MOVE #$4,D0
00000004 4EFB 000A 12 JMP TBL(PC,D0)
00000008 4E71 13 NOP
0000000A 4E71 14 NOP
0000000C 4E71 15 NOP
0000000E 4E71 16 NOP
00000010 17 TBL:
00000010 6000 0FEE 18 bra $1000
00000014 6000 1FEA 19 bra $2000
00000018 6000 2FE6 20 bra $3000
0000001C 6000 3FE2 21 bra $4000
code B
The label TBL equals $10. This can be understood as label_address + D0, which is $10 + 4 = $14.
The compiler seems to have optimized this addressing mode. It modifies the number before the parentheses based on the location of the jmp instruction, leading to this result.
00000000 303C 0004 11 MOVE #$4,D0
00000004 41F8 000A 12 LEA $A,A0 ;Assign the JMP address + 2 to A0, making it function identically to the PC in the previous example.
00000008 4EF0 000A 13 JMP $A(A0,D0)
0000000C 4E71 14 NOP
0000000E 4E71 15 NOP
00000010 4E71 16 NOP
00000012 4E71 17 NOP
00000014 18 TBL:
00000014 6000 0FEA 19 bra $1000
00000018 6000 1FE6 20 bra $2000
0000001C 6000 2FE2 21 bra $3000
00000020 6000 3FDE 22 bra $4000
code C
Here, I change to Ax for jumping. I observed that the second word of JMP's machine code is identical to the offset, just like code A. It appears the compiler doesn’t optimize addressing modes when using Ax.
I tested various compilers and disassemblers.
The following tools followed the expression label_address + D0:
The following tools followed the expression offset + (PC + 2) + D0:
I found this video and this article. They seems to followed the expression offset + (PC + 2) + D0.
I'm confused. I don't know which one is correct. Or perhaps different tools follow different conventions.