1

Any writes I do to cartridge RAM on my test game assembled with RGBDS are silently ignored. I wanted to move the stack pointer to cartridge RAM to have more room in high ram to use as a scratchpad, but the game won't recognize that the cartridge ram is available even after enabling it. (My routines for detecting the type of game boy and initializing cartridge ram are identical between my game assembled with RGBDS and my game assembled with VASM, yet only the one asssembled in VASM seems to work with regards to cartridge RAM. I double-checked to make sure that both ROMs use the same cartridge header, and reviewed the code and found it to be the same on both.

Code (RGBDS syntax):

;both games call this before doing "LD SP,$BFFF" but it only works on the second one!
DetectHardware::
    cp $11          
                    
    ld a,0          
    jr nz,.skip
    push af
    push bc         ;B contains the GBA/no GBA state at boot.
        call EnableFastCPU  ;speed up the cpu for gbc and gba
    pop bc
    pop af
    
    inc a           ;ld a,1
    dec b           ;if B was 1, it now equals zero.
    jr nz,.skip ;B wasn't 1.
    add $FE         ;ld a,255
.skip:
    LD [isGameBoyColor],a   ; store hardware type.
    xor a       ;LD A,0
    jr GBCart_RamBank


GBCart_RamBank::            ;Turn on ram bank 0-3 
    push af
        ld b,$0A
        call GBCart_RamBankAlt      ;IF YOU CALL GBCART_RAMBANKOFF THE RAM IS DISABLED INSTEAD.
    pop af
    ld [$4000],a            ;SELECT CARTRIDGE RAM BANK NUMBER 0-3
    ret 
GBCart_RamBankOff::         ;Turn off ram bank (NOT EXECUTED AT STARTUP)
    ld b,$00
GBCart_RamBankAlt:
    ld a,$01
    ld [$6000],a            ;WRITE 1 TO [$6000] TO TELL THE GAME BOY CPU THE CARTRIDGE HAS RAM
    ld a,b
    ld [$0000],a            ;LD [$0000],$0A ENABLES THE EXTRA RAM BANK
                            ;LD [$0000],$00 DISABLES IT
    ret

The same code (VASM syntax): The only difference is that this version runs in-line (The stack pointer was initialized to FFFF before executing this routine in both cases, so that shouldn't matter.)

DetectHardware:
    cp &11          
                    
    ld a,0          
    jr nz,isntGBA
    push af
    push bc         ;B contains the GBA/no GBA state at boot.
        call EnableFastCPU  ;speed up the cpu for gbc and gba
    pop bc
    pop af
    
    inc a           ;ld a,1
    dec b           ;if B was 1, it now equals zero.
    jr nz,isntGBA   ;B wasn't 1.
    add &FE         ;ld a,255
isntGBA:
    LD (isGameBoyColor),a   ; store hardware type.
    xor a       ;LD A,0
    call GBCart_RamBank

; above code runs in-line, execution will set SP to $BFFF after this and continue initializing the game. The code below is somewhere else that the program counter won't reach without a jump or call.

GBCart_RamBank:         ;Turn on ram bank 0-3 
;THIS EXTRA RAM IS LOCATED FROM &A000-&BFFF
;USAGE: LD A,#DESIRED RAM BANK
;       CALL GBCART_RAMBANK
    push af
        ld b,&0a
        call GBCart_RamBankAlt      ;IF YOU CALL GBCART_RAMBANKOFF THE RAM IS DISABLED INSTEAD.
    pop af
    ld (&4000),a            ;SELECT CARTRIDGE RAM BANK NUMBER 0-3
    ; THIS RAM IS BATTERY BACKED, AND IF THE GAME IS SHUT OFF
    ;   WHILE LOADED THE RAM CAN BE CORRUPTED.
    ;   IF YOU'RE JUST USING IT AS A SCRATCHPAD IT DOESN'T MATTER!
    ret 
GBCart_RamBankOff:          ;Turn off ram bank
    ld b,0
GBCart_RamBankAlt:
    ld a,&01
    ld (&6000),a            ;WRITE 1 TO (&6000) TO TELL THE GAME BOY CPU THE CARTRIDGE HAS EXTRA RAM
    ld a,b
    ld (&0000),a            ;LD (&0000),&0A ENABLES THE EXTRA RAM BANK
                            ;LD (&0000),&00 DISABLES IT
    ret

I can tell the RGBDS-assembled ROM isn't writing to cartridge ram because doing a function call after relocating the stack pointer to $BFFF doesn't put the return address into cartridge ram like it should. Then when it tries to return, execution returns to $FFFF and eventually crashes.

I seriously have no idea why this isn't working anymore. Just because I switched to a new assembler shouldn't change how bankswitching works (I believe I translated the syntax correctly).

4
  • 3
    “I had made an earlier question and canceled it because I found the answer.” In future, it would be better if you answered your own question instead. Then, if future people have that problem, it will just show up in internet search results. (That's why we have an “answer my own question” tick box in the “ask question” menu.)
    – wizzwizz4
    Nov 27, 2021 at 22:03
  • Oh, so that's how you do it! My bad. I was wondering where the game boy logo goes when the game begins, as it turns out, all Game Boy games have the Nintendo logo stored in the tile patterns, however the tile map (0x9800) will show blank tiles on Game Boy Color games only, where original Game Boy games show the logo on screen until the screen is cleared by the game itself Nov 28, 2021 at 1:42
  • For the future, could you perhaps phrase your question titles differently? Titles that shoehorn tags in brackets or separated by a colon are pretty lousy. If you need to include some context in the title, try to phrase it naturally as part of the sentence. (In this case, I think the toolchain name makes it unnecessary to disambiguate the context further) Nov 28, 2021 at 6:42
  • @user3840170 Somebody edited one of my older questions and put "(Game Boy)" in the title so I thought it was something you're supposed to do Dec 5, 2021 at 23:31

1 Answer 1

1

Your two listings aren't the same. Your RGBDS code uses jr GBCart_RamBank (a jump) and your VASM code uses call GBCart_RamBank (a subroutine call).

One or both of your listings is wrong, depending on if you want DetectHardware: to jump into GBCart_RamBank:, call it, or move into it as the PC increases.


Here's a more general troubleshooting guide I wrote before I noticed the above mistake. :)

This looks like you're writing for a cartridge with an MBC1 mapper.

I'd recommend getting this working with one RAM bank first before trying it with multiple banks (as you seem to be doing by setting $6000 to $01). Also, the routine you've written will only work if the SP is set to internal RAM. You cannot call or return from subroutines and you must disable interrupts while your stack's RAM is not paged in. This means that this routine only makes sense to use during initial boot-up before whenever you set SP to cartridge RAM.

First thing is to make sure your cartridge header is correct during building and your emulator is respecting it.

  • The 'cartridge type' should be set to $02 or $03 for MBC1 + RAM + (BATTERY?).
  • The 'RAM size' should be set to $02 for 8 KBytes of RAM. (Or $01 if you want your cartridge RAM to only be 2KBytes at $A000-$A7FF.)
  • Your emulator should not override the header. Some emulators let you do this to enable a specific MBC or Super Game Boy support when the ROMs header is wrong.

Then the start-up code I would use for a simple 32KBytes ROM + 8 KBytes RAM MBC1 cart would be something like:

  • Power on. The entry point assumes nothing about the system state, except that the first ROM bank $0000-$3FFF is available, and CGB/SGB detection bits.
  • Disable interrupts.
  • Perform CGB/SGB detection.
  • Set SP to $E000. (End of internal RAM. Only if you use subroutines instead of macros for the following steps.)
  • Set all GB video/joypad/serial/timer registers to safe, clean values.
  • Clear all RAM, High RAM and VRAM. (Except CGB/SGB detection flags. Note that this will invalidate the stack again.)
  • Set all MBC1 registers to known values explicitly:
    • $0000 = $0A enable cartridge RAM.
    • $2000 = $01 map ROM bank 1 into $4000-$7FFF.
    • $4000 = $00 set upper bits of ROM bank number to zero.
    • $6000 = $00 8KByte RAM, 2Mbyte ROM mode.
  • Clear the cartridge RAM.
  • Set SP to $C000. The stack is now pointing at 8Kbytes of cartridge RAM.
  • Proceed to the game. :)

Your external RAM stack should be available now. Keep in mind that this stack will 'disappear' if the current RAM bank number changes.

P.S. What you've written here isn't accurate:

;WRITE 1 TO (&6000) TO TELL THE GAME BOY CPU THE CARTRIDGE HAS EXTRA RAM

When you write to $6000 (or any address lower than $8000) you're telling the cartridge that you want to use a specific type of addressing. The Game Boy itself has no record of what type of cartridge is installed or what modes the mapper has set. The GB's board is set up so that read or write accesses in the ranges $0000-$7FFF and $A000-$BFFF expect a response from the cartridge slot.

9
  • 1
    I thought I explained this earlier but maybe I didn't: The RGBDS version calls the "DetectHardware" routine, which jumps to the bank switch, borrowing the bank switch's ret to return, while the VASM version runs the "DetectHardware" routine in-line, which is why it CALLs the bank switch instead. I don't see why it would make a difference however. Nor do I understand why the code works fine on VASM. But I'll try your method and see what happens, after which I'll re-implement the ability to take bank numbers as an argument for the bank switch function. Nov 28, 2021 at 5:05
  • I see. I didn't understand what youd written.
    – knol
    Nov 28, 2021 at 5:15
  • As it turns out this had also helped me solve another problem - my syntax for rsset was incorrect and my user ram labels were interpreted as writes to 0000 instead of C000. Nov 28, 2021 at 5:33
  • While setting the MBC1 registers as you stated in your answer worked on RGBDS, trying to re-implement the code shown in the original post still breaks it on RGBDS only. I'm still at a loss as to why it only seems to work on VASM. Nov 28, 2021 at 6:00
  • 3
    @puppydrum64: don't you use a version control system for your sources?
    – chthon
    Nov 28, 2021 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.