Post

Switch case using EA in ROP - Ep.1

Basic introduction to switch case using EA

Notes: All addresses mentioned below is for verA


EA Branch Method

The EA Branch Method relies on the two-bytes traversal function using EA and ER2 by calling 0:9C20

.l_01C:
0:9C20H	9252			L ER2, [EA+]
0:9C22H	C903			BEQ .l_026
0:9C24H	F207			CMP ER2, ER0
0:9C26H	C8FC			BNE .l_01C
0:9C28H	E000			MOV ER0, #00H
.l_026:
0:9C2AH	F28E			POP PC

This function traverses the content at the EA address and checks if it matches the value in ER0. If it does, ER0 is set to 0, and the function executes a POP PC to return. If it reads 0000, it ends the function immediately (BEQ .l_026) by executing POP PC. Thus, the address stored in EA correspond to the memmory address of ER0 + 2 (because the EA+ instruction is executed)


Setup Steps for Branching

  1. Create a Jump Table
    • Create a case - value table where the first 2 bytes are the case, and the last 2 bytes are the value.
    • The last case must be set to 0000 to terminate the traversal, and serves as the default case.

    These tables can be stored anywhere in memory, but storing them after the program is recommended to avoid corruption from PUSH instructions. Before use, the tables should be copied to another location since the [EA+] instruction in 0:9C20 will alter them.

    Your table should looks like this

     01 10 80 E9     ; case 1001, return E980
     03 30 69 E9     ; case 3003, return E969
     00 00 E0 E9     ; default case, return E9E0
    
  2. Load the Value into ER0 and Traverse the Table After initializing EA to point to the table’s starting address, traverse the table using 0:9C20. After traversing, the value stored in EA is the pointer of the return value (since the case is exactly 1 word long, the current EA will perfectly aligned to the return value in the memory)

Before Calling 0:9C20 (Table traversal)

Before calling the table traversal function, we have to set EA to the starting address of the case table.

  1. Set EA using MOV by calling 1:7CA6 Use 1:7CA6 to set EA with ER14:

     f_17CA6:
     1:7CA6H	F0EA			LEA [ER14]
     1:7CA8H	9056			L QR0, [EA+]
     1:7CAAH	9852			L ER8, [EA+]
     1:7CACH	F0CA			LEA [ER12]
     1:7CAEH	9057			ST QR0, [EA+]
     1:7CB0H	9853			ST ER8, [EA+]
     1:7CB2H	FE1F			RT
    

    You could use POP XR12 to set both ER12 and ER14 at once (this is recommended). If ER12 = ER14, this sets EA to *[ER14] + 0xA without causing any memory corruption. Be noted that this function ends with RT so setlr is required.

    Alternatively, you can call 1:7CAC to directly set EA to *[ER12] + 0xA, but this will overwrite the 10 bytes after *[ER2]. Use it with caution.

  2. Set EA using POP by calling 1:0E80 Before calling 1:0E80, set R6 to 0x01 to prevent you from looping back to the beginning of the function. Note that this function ends with a POP XR4, so 4 bytes of space must be reserved.

     1:0E80H	F18E			POP EA
     1:0E82H	16FF			ADD R6, #-1H
     1:0E84H	C8F4			BNE .l_010
     1:0E86H	F42E			POP XR4
     1:0E88H	F28E			POP PC
    

    (Explanation: After the POP EA which pops the next 2 bytes after the call, the function subtracts R6 by 0x01, thus settings the Z flag. If R6 = 0x01, after subtracting, the Z flag will equals to 1, which avoids the BNE .l_010 which is triggered when Z = 0)


Padding values to EA

While there’s no direct ADD EA, ERn instruction in ROM, you can call from 1:973A to 1:9742 to achieve a 2 bytes pad per instruction.

1:973AH	9053			ST ER0, [EA+]
1:973CH	9253			ST ER2, [EA+]
1:973EH	9253			ST ER2, [EA+]
1:9740H	9253			ST ER2, [EA+]
1:9742H	9053			ST ER0, [EA+]
1:9744H	FE1F			RT

This function repeatedly executes [EA+], for each 9n53 it increases EA by 0x02, ultimately padding EA by 0x0A bytes. These set of instructions will allow you to adjust EA by a specific even value from 0x02 to 0x0A. Although the ST instruction alters the table, the content at EA address remains unaffected.

Alternatively, you can call from 1:C654 to 1:C662 to pad an odd number of bytes to EA

1:C654H	9D51			ST R13, [EA+]
1:C656H	9C51			ST R12, [EA+]
1:C658H	9B51			ST R11, [EA+]
1:C65AH	9A51			ST R10, [EA+]
1:C65CH	9951			ST R9, [EA+]
1:C65EH	9851			ST R8, [EA+]
1:C660H	9751			ST R7, [EA+]
1:C662H	9651			ST R6, [EA+]
1:C664H	F28E			POP PC

Reading Content at EA

Use 1:C64A to read the content at *[EA]:

1:C64AH	9652			L ER6, [EA+]
1:C64CH	9856			L QR8, [EA+]
1:C64EH	F00CD000		LEA D000H
.l_00C:
1:C652H	9E53			ST ER14, [EA+]
1:C654H	9D51			ST R13, [EA+]
1:C656H	9C51			ST R12, [EA+]
1:C658H	9B51			ST R11, [EA+]
1:C65AH	9A51			ST R10, [EA+]
1:C65CH	9951			ST R9, [EA+]
1:C65EH	9851			ST R8, [EA+]
1:C660H	9751			ST R7, [EA+]
1:C662H	9651			ST R6, [EA+]
1:C664H	F28E			POP PC

This function reads the content:

  • at EA, stores into ER6
  • at EA + 0x2, stores into ER8
  • at EA + 0x4, stores into ER10
  • at EA + 0x6, stores into ER12
  • at EA + 0x8, stores into ER14

From 1:C64E onwards, the LEA D000H instruction gets executed and the function executes the rest of instructions at D000H. Since our program typically doesn’t operate at D000H, it won’t be affected.


Assign EA to ER0

Although the ROM does not have the MOV ER0, EA command, we can call 2:08E8 to assign EA to ER0 through a pair of PUSH and POP.

2:08E8H	F1CE			PUSH EA
2:08EAH	F01E			POP ER0
2:08ECH	9209FFFF		ST R2, -1H[ER0]
2:08F0H	FE1F			RT

Afterward, the instruction ST R2, -1H[ER0] will alter the memory at [ER0] - 1, but the memory at [ER0] will remain unaffected.

This address is particularly useful in cases where multiple parameters need to be associated with a single keycode. If a keycode needs to link to several related parameters, additional corresponding parameter tables can be added after the main table.

Furthermore, this address can be called to backup the EA, since when you use 1:C64A to read the content at *[EA], EA is moved to D000, making it inaccessible for further reads. Once backed up, further reads can be performed using mentioned instructions.

Additionally, if EA needs to be compared with a specific value, 2:08E8 can be used to assign the value of EA to ER0, then ER0 can be used for comparison.


Similar Structures and Extended Applications

In the next post, I will discuss about 1:BCCC, which shares a similar structure. It sets EA to a fixed value, allowing you to verify whether EA has been altered. This can be applied in a pixel editor to check if the cursor position exceeds the screen boundaries.