« on: April 07, 2016, 04:26:48 pm »
Conducting a disassembly of FF Legend with my new tool, CdisZ80.
As I conduct the disassembly, I will make hypotheses about the code and report on what actually goes on. Commentary is welcome.
We will see how predictable game code is or isn't.
To begin with, we have the first span of code which runs from bank 0, $0224 (as pointed to at $100) to $034C (unconditional jump to $7D50).
My hypotheses about this span:
- memory is cleared
- generic jump pointers (if any) are established in RAM
- the title screen is loaded via a series of ROM reads/OAM writes
- the music playback routine is called
- the title menu input loop is either called or executed explicitly
- nothing else happens
OK so first thing is they knock out the Nintendo logo by resetting the LCD, then they do the memory clear stuff, setting A to 0 ("xor A" does this) and writing it to the hardware registers space ($FF##).
ROM0:0224 F3 di
ROM0:0225 31 00 D0 ld sp,D000
ROM0:0228 3E 80 ld a,80
ROM0:022A E0 40 ld (ff00+40),a
ROM0:022C AF xor a
ROM0:022D E0 0F ld (ff00+0F),a
ROM0:022F E0 FF ld (ff00+FF),a
ROM0:0231 E0 41 ld (ff00+41),a
ROM0:0233 2E 24 ld l,24
ROM0:0235 22 ldi (hl),a
ROM0:0236 22 ldi (hl),a
ROM0:0237 22 ldi (hl),a
ROM0:0238 E0 47 ld (ff00+47),a
ROM0:023A E0 48 ld (ff00+48),a
ROM0:023C E0 49 ld (ff00+49),a
ROM0:023E E0 43 ld (ff00+43),a
ROM0:0240 E0 42 ld (ff00+42),a
Then the first interesting thing that happens is a compare between $1B and the value at $C425 (that is, 15).
ROM0:0242 06 01 ld b,01
ROM0:0244 3E 1B ld a,1B
ROM0:0246 21 25 C4 ld hl,C425
ROM0:0249 E5 push hl
ROM0:024A BE cp (hl)
ROM0:024B 23 inc hl
ROM0:024C 20 06 jr nz,0254
Any purpose for this? I NOP'd it and it was business as usual.
Following this is a call to $82, which is apparently a memory fill routine that writes 0 to 255 to RAM over and over again.
ROM0:0256 21 00 C0 ld hl,C000
ROM0:0259 01 00 0F ld bc,0F00
ROM0:025C CD 82 00 call 0082
ROM0:0084 5F ld e,a
ROM0:0085 73 ld (hl),e
ROM0:0086 23 inc hl
ROM0:0087 0B dec bc
ROM0:0088 79 ld a,c
ROM0:0089 B0 or b
ROM0:008A 20 F9 jr nz,0085
In a compiler, it would look like this on the first run of the loop:
BC = $F00
[HL($C000)] = A(0)
BC($F00)-- ; then C = $FF due to wrap around
A = C($FF)
loop until A($FF) | B($E) = 0
In this situation, 256 bytes are written across RAM 16 times, for a total of 3840 bytes. But looking at the RAM before and after (BGB isn't tracing through the sub), it seems to have zeroed the whole range. Afterward, $D000 and onward is zeroed via $82 to the tune of $1000 bytes (256 more than at $C000). Not exactly sure what's going on, but I guess this is the RAM clear function.
Following this is another clear function, this one for $FF80. The docs say that this is "high RAM", the only memory that can be accessed during screen draw. For this reason the V-BLANK routine has to be here. Strangely enough it is not standardized (like a BIOS), but instead has to be copied here directly from the cartridge. The next 20 or so instructions deal with with the peculiars of reading/writing these routines to memory.
I hypothesize that these zero page routines are standard issue stuff in Square Gameboy games. They probably have a header file that is first in line in the assembler, or maybe it's even hard-coded into the assembler itself. I theorize thus because people are loathe to reinvent the wheel and I can't imagine a game where they wouldn't start off this way (not to mention these clear routines are pretty generic and, being in zero page, they are obviously -the- clear routines.
Following this, it meanders on a little longer before coming to this:
ROM0:028C 21 00 C3 ld hl,C300As you can see, it's a loop that copies reads bytes from $FF00 to C300 thru C380. It's not a subcall so BGB wants me to hold down the F3 key until I've traced it all $80 times (that's 128). That I can tell, $FF00 is a "divider" register which has no purpose but to increase its value 16k times a second. I -think- it's a random number generator (or a potential component of one), but it can also be used as a delay.
ROM0:028F 06 80 ld b,80
ROM0:0291 F0 04 ld a,(ff00+04)
ROM0:0293 22 ldi (hl),a
ROM0:0294 05 dec b
ROM0:0295 20 FA jr nz,0291
Following this, more V-BLANK prep. The docs say these writes can't all be done at once so that may figure into the delay, maybe? Then more v-blank fun, including a "disable interrupt" call so that more prep can be set in motion. Once things come back to earth, we start moving into the prep for the VRAM writes.
The VRAM write process is laborious (with a number of odd features like jumps to jumps that suggest they had some sort of automated production system for this thing) and actually makes use of two routines for no explicable reason. A lot of blank space is written as the font is loaded in one line of one character at a time. I doubt they would have tried to get away with this on the NES, because they waste a ton of time (not that it really mattered... I mean, do you notice any perceptable delay between the "Nintendo" blank and the title?). It took me a decent amount of time to trace that damn loop... no fun.
After the VRAM is written in, the background (that is, the title) is loaded and the game proceeds to the input wait loop. HALT is made use of, ostensibly to save power. I haven't been able to pick out the music routine yet but I figure it must be set to one of the timers. I'll let you know when I have something.
I went in game. The shops use a similar method as the title... whenever the hand cursor is present, HALT is used to suspend the action until either an "ON JOYPAD" event or the music (or V-BLANK) gets the CPU going again. It seems like HALT is a standard method of obtaining input, and most of the action in the game is driven by interrupts which invoke method depending on the circumstances. Coming from the world of PC programming where you pretty much poll repeatedly for the input while things happen around you, this is something of a surprise.
Oh, and I had to refer back to my original dump to make it into town: blotting out that routine at the start killed the game right about a second after I entered.
The town code comes down to a series of routines in a loop:
ROM0:1243 CD 7D 12 call 127D
ROM0:1246 CD 26 1E call 1E26
ROM0:1249 CD 9D 20 call 209D
ROM0:124C CD 73 21 call 2173
ROM0:124F CD 66 24 call 2466
ROM0:1252 CD E0 16 call 16E0
ROM0:1255 CD CC 22 call 22CC
ROM0:1258 18 E9 jr 1243
So far, there have been no bank switches. The map/town code and title/character creator use the same bank (0). This suggests that the game's code is actually rather tiny. We'll see if the battle code uses a different bank.
The sprites seem to be moving somewhere inside call $127D.
The overworld map uses the same code as the towns. In battle though, the upper bank is switched to 6.
As hypothesized, the battle menu uses the same basic algorithm as the title:
ROM6:5EF6 CD 9B 01 call 019B
ROM6:5EF9 CB 7F bit 7,a
ROM6:5EFB C4 13 5F call nz,5F13
ROM6:5EFE CB 77 bit 6,a
ROM6:5F00 C4 22 5F call nz,5F22
ROM6:5F03 CB 47 bit 0,a
ROM6:5F05 20 05 jr nz,5F0C
ROM6:5F07 CD A4 01 call 01A4
ROM6:5F0A 18 EA jr 5EF6
ROM6:5F0C F0 92 ld a,(ff00+92)
ROM6:5F0E FE 0E cp a,0E
ROM6:5F10 C8 ret z
At this point, I'm going to stall the disassembly. Most of the code is bit manipulations... these are very difficult to follow without some sort of feedback, which BGB doesn't offer. I've still not located the music code... I would have already found it if there was some kind of "limited view" feature in the emulator which only shows me the addresses of opcodes already processed. Also there is no distinguishment at all between CHR ROM and actual code (particularly on the GB)... it's dumped all the same as bogus ASM. It's categorically unreasonable to expect new comers to try to cope with these conditions... the field will remain handicapped until the tools are improved.