kkzero

A curious little bunny

  • he/they, 20s

I like to create things.

Perhaps most known for romhacks: Game Boy colorizations, Samus Goes to the Fridge, etc.

I also do OC art from time to time.



Grab it at the usual place. Full changelog is as follows:

-Some core code rewritten, should solve the rare crashes/corruption.
	-Remember kids, don't do farcalls during interrupt handlers.
-Actually fixed the True Ending credits crash.
	-I didn't mean to lie last time, I'm sorry.
-Palette for Spark Kine's flashing changed.
-Palette for the Settings menu's button icons brightened just a touch.

Click read more for details about the code changes. It assumes familiarity with how programming for really retro hardware like the GB/GBC works.


In v1.0 and its hotfixes, the game had a rare chance of randomly crashing during gameplay, or causing visible corruption. The reason this happened was because I used a farcall in the Vblank and LCD interrupt handlers, most likely the former's fault.

A farcall, in general, is a procedure that calls code from another segment. In this case, the segments are the Game Boy's MBC ROM banks, and the farcall allows calling code in one bank in the swappable area from another such bank. In other words, no need to go back to Bank 0 and manually switch the bank.

Kirby's Dream Land 2 has two farcall functions, one that uses the di/ei instructions (00:05CF), and one that doesn't (00:05E5). The latter was definitely more suitable for the Vblank interrupt, but it still didn't prevent the issues. Depending on the precise timings, the interrupt farcall had the chance to overwrite the bank backup in HRAM, causing another farcall to return to the interrupt code's bank and cause issues.

Confession: I was well aware of this before v1.0. It was hard to fathom how I could fix it back then, though, because I wasn't certain I could find any extra space outside of any ROM bank beyond Bank 0 to squeeze my code into. As a result, I settled on a temporary fix with the following code:

SECTION "RST $38",ROM0[$0038]
	add sp, 2
	ret

Since the interrupt code's bank had freespace bytes of FF, equivalent to the rst 38 instruction, I figured I could just rely on that to make the issues mostly not happen. What this...fix...did was move the stack pointer up to the return point from before the last farcall's, skipping the intercepted farcall's business altogether, but also avoiding a crash or corruption...mostly.

You're probably baffled by just how lazy this seems. Well, maybe I was kind of lazy, but I'd say I was much more so really antsy. See, I was approaching the end of developing this hack during a rather tense time--my semester was coming to a close, and I was already procrastinating final projects and such for this hack that's been hanging over me for an even longer time. Rewriting such a big part of the code was out of the question for me, as it seemed.

Which brings us to how I fixed this for v1.1 recently--moving the interrupt code from the ROM banks to WRAM. I was able to find ample freespace in both the fixed WRAM bank and two in the swappable area, so after some trial-and-error in the rewriting, I got the Vblank and LCD interrupt edits reimplemented without relying on farcalls.

Soon after this, testing out the hack with its new tweaks, I ran into an issue I thought I'd fixed before--the rare crash in the True Ending credits.

Pain.

It wasn't a farcall from that area like I'd assumed. I did some debugging on the situation, taking over 10 minutes to reproduce the crash for the first time. I went to bed finding out that a value from one of the tile buffers was being popped off the stack and jumped to, but I didn't know why. I still don't. I just rewrote the credits attribute updater, the problematic code, to not have any push or pop instructions.

From v1.0b to v1.1, that code went from this:

Before
```
NewTrueEndingCreditsStuff:
	push de
	ld a, [bc]
	ldi [hl], a
	ld e, a
	inc bc
	ld a, [bc]
	ldi [hl], a
	sub $98
	ld d, a
	push hl
	ld hl, wTrueEndCreditsAttrs
	add hl, de
	ld a, d
	add $98
	ld d, a
	ld a, BANK(wTrueEndCreditsAttrs)
	ldh [rSVBK], a
	ld a, 1
	ldh [rVBK], a
	push bc
	ld b, h
	ld c, l
	;ld a, BANK(TrueEndCreditsAttrs)
	;call $05BF ;Bankswitch and copy
	;
	;ld a, BANK(RefreshTrueEndCreditsAttrs)
	;ld hl, RefreshTrueEndCreditsAttrs
	;call $05CF ;Bankswitch and call
	;
	call RefreshTrueEndCreditsAttr
	xor a
	ldh [rVBK], a
	ldh [rSVBK], a
	pop bc
	pop hl
	pop de
	ret
	;
RefreshTrueEndCreditsAttr:
	ld h, b
	ld l, c
	ld c, $20
.stat_check
	ldh a, [rSTAT]
	and a, 3
	cp a, 2
	jr nc, .stat_check
	ld a, 1
	ldh [rVBK], a
	ld a, BANK(wTrueEndCreditsAttrs)
	ldh [rSVBK], a
	ldi a, [hl]
	ld [de], a
	inc de
	dec c
	jr nz, .stat_check
	;
	ret
```

To this:

After
```
UpdateTrueEndCreditsAttrs:
	;ld bc, [$DF1E] ;Current credits scroll tile dest
	ld a, [$DF1F]
	sub $98
	ld b, a
	ld a, [$DF1E]
	ld c, a
	ld hl, wTrueEndCreditsAttrs
	add hl, bc
	ld a, b
	add $98
	ld b, a
	ld e, $20
.stat_check
	ldh a, [rSTAT]
	and a, 3
	cp a, 2
	jr nc, .stat_check
	ld a, 1
	ldh [rVBK], a
	ld a, BANK(wTrueEndCreditsAttrs)
	ldh [rSVBK], a
	ldi a, [hl]
	ld [bc], a
	inc bc
	dec e
	jr nz, .stat_check
	xor a
	ldh [rVBK], a
	ldh [rSVBK], a
	jp $4C0F ;Normal credits scene scroll tile updater
```

And after testing the credits with the new code 26 times without a crash, I declared the issue fixed and prepared the update.

Hopefully I do not have to go back and fix anything, and if I do, I hope it's more trivial then all this. This project took up a lot of my time, and I don't wanna spend even more of it on the same thing.


You must log in to comment.
Pinned Tags