My first attempt

Discussion of programming and development for the original Game Boy and Game Boy Color.
Post Reply
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

My first attempt

Post by tepples »

So I finally got around to trying RGBDS, ending up with a port of my NES sprite demo after six hours. It runs in mGBA 0.7-5026-1a6b47a2 and on my GBC with an EverDrive GB X5. With my luck, there are probably a bunch of 6502 cargo cult practices that made their way in, much like the blatant lack of optimization that code written by 68000-to-6502 converts often shows.

Things I could do from here:
  1. Make the code less "Ho-ly-shit" in its (lack of) organization
  2. Test on NovaSquirrel's Super Game Boy to make sure I'm not hitting any mono bugs that were fixed in color
  3. Register on some other Game Boy dev forum and ask those more experienced with LR35902 for a code review. Is gbdev.gg8.se worthwhile, or is there better?
  4. Write a preprocessor to allow local non-label symbols, anonymous local symbols, and sane indentation

(Heh-heh, "lame boy".)
Attachments
hello-1.png
hello-1.png (430 Bytes) Viewed 9665 times
hello-0.01.zip
(12.27 KiB) Downloaded 660 times
adam_smasher
Posts: 271
Joined: Sun Mar 27, 2011 10:49 am
Location: Victoria, BC

Re: My first attempt

Post by adam_smasher »

I had a quick look. Nothing too egregious, but some thoughts:
  • Rather than hardcoding variable addresses, you may want to use BSS or HRAM SECTIONs
  • Using fixed SECTIONs to place code/data at addresses you care about is almost always cleaner than manually writing out garbage to fill space
  • You really ought to use the HALT instruction and wait for the vblank interrupt instead of busy looping in your main loop; this puts the CPU into low-power mode and saves battery
  • I don't know anything about needing to "release the controller", as you do on lines 344-346. That might be on me, though! Do you have a documentation source for that?
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: My first attempt

Post by koitsu »

adam_smasher wrote:You really ought to use the HALT instruction and wait for the vblank interrupt instead of busy looping in your main loop; this puts the CPU into low-power mode and saves battery
For tepples' benefit: this is conceptually like wai on 65816, which I believe you're familiar with. That said (and note: I do not know Z80), there do seem to be quirks you need to know of with HALT. The .txt file mentioned there does cover something that would terrify me had I not known about it in advance, if applicable:
WARNING: The instruction immediately following the HALT instruction is "skipped" when interrupts are disabled (DI) on the GB,GBP, and SGB. As a result, always put a NOP after the HALT instruction. This instruction skipping doesn't occur when interrupts are enabled (EI). This "skipping" does not seem to occur on the GameBoy Color even in regular GB mode. ($143=$00)
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: My first attempt

Post by tepples »

Acknowledged about SECTIONs in RAM, SECTIONs in ROM, and interrupts. But the distinction in RGBDS between "labels" and "symbols", with only labels eligible to be .local, was kicking my behind at the time.
adam_smasher wrote:You really ought to use the HALT instruction and wait for the vblank interrupt instead of busy looping in your main loop; this puts the CPU into low-power mode and saves battery
Agreed in principle. Are there examples?
adam_smasher wrote:I don't know anything about needing to "release the controller", as you do on lines 344-346. That might be on me, though! Do you have a documentation source for that?
Search "GameBoy Programming Info" by Marat Fayzullin for "Set 1s at both P14 and P15 lines". Keeping the key matrix deselected while not reading could be a power saving method. Or it could just be cargo cult programming based on incomplete knowledge of hardware behavior, especially given the state the scene was in when Marat was most active.

koitsu is referring to WAI on 65816 and some 65C02 variants, and SWI 2 on GBA. These not only save battery on hardware but also improve emulation speed especially on a slow or mobile device. It's just a matter of sitting down and troubleshooting the IRQ edge cases, such as possibly not firing at all while rendering is disabled. And even though I use mGBA most of the time, beware in #gbdev on EFnet has convinced me to occasionally run it in (proprietary) bgb with "reality" and all "break on" enabled. (Sorry calima.)

As for the HALT "delay slot" quirk: RGBDS is encoding HALT as 76 00, a 2-byte instruction with the second being $00 (NOP). This reminded me of how the disassembler in the Apple II Monitor ROM since roughly IIGS ROM 01 began to treat BRK as the 2-byte instruction that it is.
adam_smasher
Posts: 271
Joined: Sun Mar 27, 2011 10:49 am
Location: Victoria, BC

Re: My first attempt

Post by adam_smasher »

Here's some relevant snippets from my project:

Code: Select all

SECTION "Boot", HOME[$0100]
		NOP
		JP Start

SECTION "Start", HOME[$0150]
Start:	DI				; disable interrupts
			LD HL, $E000			; init stack from $E000 down
			LD SP, HL
			CALL Init

;; Init calls InitInterrupts

InitInterrupts:	LD A, %00000001			; enable VBlank
						LDH [$FF], A
						XOR A				; clear pending IRQs
						LDH [$0F], A
						EI
						RET

SECTION "VBlank", HOME[$40]
VBlank:		JP VBlank2 

;; VBlank2 is my actual handler
SECTION "IRQHandlers", HOME
VBlank2:	PUSH AF
				PUSH BC
				PUSH DE
				PUSH HL
				CALL $FF80				; sprite DMA
				CALL UpdateVRAM
				LD A, 1
				LD [VBlankFlag], A
				POP HL
				POP DE
				POP BC
				POP AF
				RETI

GameLoop:	CALL WaitForVBlank
;; followed by game logic...

WaitForVBlank:
  LD HL, VBlankFlag
  XOR A
  LD [HL], A
.loop
  HALT
  CP [HL]
  JR Z, .loop
  LD [HL], A
  RET
Hopefully that helps.
tobiasvl
Posts: 2
Joined: Sat Jan 20, 2018 10:59 am

Re: My first attempt

Post by tobiasvl »

tepples wrote:Register on some other Game Boy dev forum and ask those more experienced with LR35902 for a code review. Is gbdev.gg8.se worthwhile, or is there better?
That's the place to go. It's not too active, but the regulars will notice and reply to your post. There's also a Discord if you want to chat, it's linked at https://avivace.github.io/awesome-gbdev
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: My first attempt

Post by tepples »

Right now, the main sticking point before I release my improvements so far is how to allocate local variables without using EQU. One solution suggested by someone in the parallel topic on gbdev.gg8.se involves putting all subroutines' sets of local variables into a UNION. But in a project larger than one file, that ends up separating the context where local variables are declared from the context where they are used, which in my opinion reduces maintainability.

After spending a while trying to work around the artificial distinction in RGBDS between labels and equates, I came up with a solution with one EQU for the locals area, a matching SECTION, and then RSSET/RB in each subroutine. In global.inc:

Code: Select all

hLocals EQU $FF80
locals_size EQU 16
In init.s, where I keep hardware init code:

Code: Select all

section "hram_locals", HRAM[hLocals]
  ds locals_size
In each subroutine that uses more local variables than will fit in BCDEHL:

Code: Select all

draw_metasprite::
  RSSET hLocals
Lbasex rb 1
Lbasey rb 1
Lwidth rb 1
Lheight rb 1
  ; Code goes here
  ret

mul8::
  RSSET hLocals
Lfactor1 rb 1
Lfactor2 rb 1
Lproduct rw 1
  ; Code goes here
  ret
Is that acceptable? Or is there still too much EQU?
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: My first attempt

Post by Oziphantom »

I typically just make

ZPTemp1
ZPTemp2
ZPTemp3
....
Pointer1
Pointer2
...

then I use those labels in the code and comment their actual intended purpose on the function. This way when I modify code to call another function I can quickly see if they clash/trash each other variables.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: My first attempt

Post by tepples »

Here's what I came up with so far.

Changes:
  • Split into separate files paralleling those of nrom-template
  • Use RLA SBC A sign extension idiom (suggested by ISSOtm)
  • Remove unnecessary and broken OAM clearing
  • Add optional zealous memory clearing to satisfy BGB exceptions
  • Add doc comments to most subroutines
  • lcd_*: Don't push HL so much
  • Put global variables and the DMA routine's run address under SECTION control
  • Try RSSET/RB to allocate local variables, nearly but not fully eliminating EQU allocation
  • Display frame count and initial A, B contents at top
  • Change frame count in vblank IRQ, and HALT until it changes
  • Explicitly mark MMIO port and local variable accesses as LDH because RGBDS lacks a counterpart to ca65 .importzp. What is this, NESASM? Ref
  • Add indentation correction to the build process
  • Change padding value to $FF
  • makefile: Add BGB as secondary emulator for "make debug"
Attachments
demoopt.gif
demoopt.gif (11.98 KiB) Viewed 9156 times
hello-0.02.zip
(20.62 KiB) Downloaded 473 times
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: My first attempt

Post by tepples »

Slowly but surely, I'm coming to terms with this hardware.

0.03 (2018-05-12)
  • Switch to de facto standard hardware.inc (requested by gbdev Discord members)
  • makefile: Use target name for rgbfix instead of hardcoding hello.gb
  • Hide BG and sprites before game loop to avoid sprite garbage (reported by nitro2k01)
  • Use palette registers instead of LCDC to hide BG and sprites for GBC friendliness (requested by ISSOtm)
  • Fade out Nintendo logo while detecting Super Game Boy on DMG
  • Add GBC palette approximating that used by nrom-template on NES
Attachments
hello-0.03opt.gif
hello-0.03opt.gif (38.34 KiB) Viewed 8761 times
hello-0.03.zip
(29.47 KiB) Downloaded 455 times
Post Reply