Source Equates and Scoped Replacements - HoraceAndTheSpider/Bloodwych-68k GitHub Wiki

Source equates and scoped replacements

Reverse-engineered source sometimes gives an immediate constant the appearance of an address. This happens when the original instruction contains a long numeric value which coincides with an address in the program. The distinction matters: a pointer identifies where something is stored, whereas a count remains the same number even if the program is moved.

The SPS 439 disassembly provides a confirmed example:

move.l  #adrL_0186A0,d1    ;223C000186A0

In this disk-reading routine, $000186A0 is a countdown value used while waiting for disk DMA completion. It is not a pointer to the data later labelled adrL_0186A0. The clearer representation is:

DiskReadTimeoutCount: equ $000186A0

move.l  #DiskReadTimeoutCount,d1    ;223C000186A0

The opcode remains 223C000186A0, so the correction improves the source without changing the executable.

Why replacements must be scoped

Many small constants have several unrelated meanings. For example, #$40 might represent character form $40 (Zendik) in a character-selection routine, but the same literal can be a count, flag, colour or size elsewhere.

A constant must therefore never be replaced globally merely because its value matches. Its meaning comes from the instructions which use it. In the disk example, the value is counted down while waiting; it is not used to read from memory at that address.

The relevant routine labels, complete instruction and encoded bytes provide a way to check each use. The instruction using the named constant should assemble to exactly the same bytes as the original.

Constants and addresses

An EQU can also give a name to a real address within a larger block:

PictureOffsets: equ PictureData+$20

This illustrative alias moves with PictureData. By contrast, DiskReadTimeoutCount is a fixed count and must not change when source or data moves. Both are written with EQU, but they express different things.

This is particularly important when a block is represented by INCBIN. Code may still need to refer to a table inside that block. A base label plus a named offset keeps that reference meaningful without inserting a label inside the included file.