Ecco II Disassembly: Random Bits and Bobs

If you're working on a project that involves hacking the games, or making a new Ecco game, post it here!
Post Reply
User avatar
Moduvator
Site Admin
Posts: 55
Joined: 16 Dec 2025, 22:10
Location: Atlantis
Contact:

Ecco II Disassembly: Random Bits and Bobs

Post by Moduvator »

Johnny did a marvelous job reverse engineering some of the Ecco aspects. The problem was that there was never much of it documented and with Dark Sea long gone there isn't really a good reliable source of information for the others to pick up.

Since I am following up on some of the sections of Tides of Time, such as password system and related elements, I will be documenting my findings here.
The posts will not be following any structure in particular at the moment and I will merely be posting things as I run into and disassemble them.

This first message in the topic can serve as an index and I will likely put a table of contents here.
User avatar
Moduvator
Site Admin
Posts: 55
Joined: 16 Dec 2025, 22:10
Location: Atlantis
Contact:

Re: Ecco II Disassembly: Random Bits and Bobs

Post by Moduvator »

First random bit of Ecco II code analysis.

The Asterite.
Curiously enough, he is actually stored as two halves inside two 32-bit variables at RAM addresses 0xD434⁩⁩ and 0xD438⁩⁩. Every globe is represented by a single bit in those: e.g., 0x0F is 4, 0xFF is 8, etc., up to 32 in each helix.

Code: Select all

// Global variables to store Asterite's status
// ⁨⁨RAM addresses 0xD434⁩⁩ and ⁨⁨0xD438
uint32_t gAsteriteGlobesLeft, gAsteriteGlobesRight;
image.png
It is possible by manipulating those variables to spawn an "asymmetrical" Asterite, as well as making "holes" in him: the game does not mind this. However, none of the functions that I have met so far appear to be using this and in fact the one responsible for counting globe pairs will return -1 if pairs mismatch. It's possible that they were intending to manipulate individual globes, such as in Ecco 1, but feature ended up never used.

Storing data like this is very inefficient otherwise, but they can get away with this since that is really only used when saving the game in a password and restoring it from that.
The following is a reconstruction of the function at ROM offset 0xB0BA4 that counts the globes:

Code: Select all

int countAsteriteGlobePairs(void) {
    int retval = -1;
    uint32_t MASK = 0x80000000;
    int16_t globeCount = 0;

    if (gAsteriteGlobesLeft == gAsteriteGlobesRight) {
        for (uint16_t i = 32; i > 0; i--) {
            if ((gAsteriteGlobesLeft & MASK) != 0) {
                globeCount++;
            }
            MASK = MASK >> 1;
        }
        retval = globeCount;
    }
    return retval;
}
Function to populate Asterite at ROM offset 0xB0BD8:

Code: Select all

void setAsteriteGlobes(uint8_t numPairs) {
	uint8_t i;
	uint32_t MASK = 1;
	gAsteriteGlobesLeft = 0;
	
	for(i = numPairs; i > 0; i--) {
		gAsteriteGlobesLeft |= MASK;
		MASK = MASK << 1;
	}
	
	gAsteriteGlobesRight = gAsteriteGlobesLeft;
	return;
}
Following that is a function at ROM offset 0xB0BFC that initializes Asterite, setting both globe variables to 1, but it also takes care to fill 196 bytes at 0xD35E with zeroes. It actually makes more sense to post the raw disassembly of it for now since it's very short:

Code: Select all

clearAsteriteGlobes:
    moveq      #0x0,D1
    lea        (DAT_ffffd35e).w,A1
    movea.w    #0x31,A0
    moveq      #0x0,D0
.loop:
    move.l     D0,(A1)+
    addq.w     #0x1,D1w
    cmp.w      A0w,D1w
    blt.b      .loop
    moveq      #0x1,D0
    move.l     D0,(gAsteriteGlobesLeft).w
    move.l     D0,(gAsteriteGlobesRight).w
    rts
Essentially a memset() and two assignments, really. I have not yet worked out what said location does exactly but so far my hunch is that the actual globe objects are to be stored there for animating them. This array is referenced in a lot of functions:

Code: Select all

fClearAsteriteGlobes:000b0bfe(*), 
fClearAsteriteGlobes:000b0c08(W), 
FUN_000b1536:000b1542(*), 
FUN_000b1536:000b154e(*), 
FUN_000b1554:000b1562(*), 
FUN_000b1554:000b156e(*), 
FUN_000b1574:000b1580(*), 
FUN_000b1574:000b158c(*), 
FUN_000b1f60:000b20f8(*), 
FUN_000b2208:000b2260(*)
Post Reply