clownassembler icon indicating copy to clipboard operation
clownassembler copied to clipboard

Addressing mode Program Counter Relative with Index support?

Open czxinc opened this issue 6 months ago • 1 comments

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.

czxinc avatar Oct 15 '25 22:10 czxinc

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.

Clownacy avatar Nov 09 '25 12:11 Clownacy

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.

czxinc avatar Nov 30 '25 19:11 czxinc