This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

BNROM cutting off at $C000

BNROM cutting off at $C000
by on (#202413)
I'm trying to set up my ca65/cc65 config and nes header for a BNROM mapper, using 4 32K banks. But it seems that once the game is loaded, and I switch to my first bank, it only loads the first half of the bank ($8000-$C000) and leaves the rest empty ($C000-$FFFA). I'm clearly doing something wrong in the configuration, but it's not obvious to me what that is. Maybe one of you more experienced folks could point me in the right direction?

My ca65 config looks like:
Code:
MEMORY {
  ZP:     start = $10, size = $f0, type = rw;
  HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00;
  RAM:    start = $0300, size = $0500, type = rw;
  PRG0:    start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF;
  PRG1:    start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF;
  PRG2:    start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF;
  PRG3:    start = $8000, size = $8000, type = ro, file = %O, fill=yes, fillval=$FF;
}

SEGMENTS {

  INESHDR:   load = HEADER, type = ro, align = $10;
  ZEROPAGE:  load = ZP, type = zp;
  VRAMBUFF:  load = RAM, type = bss, define = yes, align = $100;
  OAMSHADOW: load = RAM, type = bss, define = yes, align = $100;
  BSS:       load = RAM, type = bss, define = yes, align = $100;

  INIT0:     load = PRG0, type = ro, start = $8040;
  STARTUP:   load = PRG0, type = ro, align = $100;
  LOWCODE:   load = PRG0, type = ro, align = $100, optional = yes;
  CODE:      load = PRG0, type = ro, align = $100;
  RODATA:    load = PRG0, type = ro, align = $100;
  VECTORS0:  load = PRG0, type = ro, start = $FFFA;

  INIT1:     load = PRG1, type = ro, start = $8040;
  CODE1:     load = PRG1, type = ro, align = $100;
  RODATA1:   load = PRG1, type = ro, align = $100;
  VECTORS1:  load = PRG1, type = ro, start = $FFFA;

  INIT2:     load = PRG2, type = ro, start = $8040;
  CODE2:     load = PRG2, type = ro, align = $100;
  RODATA2:   load = PRG2, type = ro, align = $100;
  VECTORS2:  load = PRG2, type = ro, start = $FFFA;

  INIT3:     load = PRG3, type = ro, start = $8040;
  CODE3:     load = PRG3, type = ro, align = $100;
  RODATA3:   load = PRG3, type = ro, align = $100;
  VECTORS3:  load = PRG3, type = ro, start = $FFFA;

}


FILES {
  %O: format = bin;
}



And my iNes header looks like:

Code:
.segment "INESHDR"
  .byt "NES",$1A  ; magic signature
  .byt 8          ; PRG ROM size in 16384 byte units
  .byt 0          ; CHR ROM size in 8192 byte units
  .byt $20       ; mirroring type and mapper number lower nibble
  .byt $02        ; mapper number upper nibble


When I inspect the file with a hex editor, it appears that all the data from my RODATA segment is in there. But when I start the game, and immediately write 0 to $8000 (to make sure I'm in the first bank), everything from $C000 to $F000 is empty. (my RODATA segment gets cut off halfway through, as it overlaps that point).

Code:
.segment "INIT0"

reset_handler:
  lda #0      ;jump to first bank
  sta $8000
  jmp start

.segment "INIT1"

  lda #0      ;jump to first bank
  sta $8000
  jmp start

.segment "INIT2"

  lda #0      ;jump to first bank
  sta $8000
  jmp start

.segment "INIT3"

  lda #0      ;jump to first bank
  sta $8000
  jmp start


.segment "VECTORS0"
.addr nmi_handler, reset_handler, irq_handler

.segment "VECTORS1"
.addr nmi_handler, reset_handler, irq_handler

.segment "VECTORS2"
.addr nmi_handler, reset_handler, irq_handler

.segment "VECTORS3"
.addr nmi_handler, reset_handler, irq_handler





Is there something I'm doing wrong in the header or the bankswitching that's causing it to only load half my bank? Thanks, all.
Re: BNROM cutting off at $C000
by on (#202414)
One thing to keep in mind is that BNROM suffers from bus conflicts, so depending on the value at $8000 (it should be $00 if you're trying to switch to bank $00), a bank other than the one you want could be selected. Not sure if this could have something to do with your problem.
Re: BNROM cutting off at $C000
by on (#202415)
Oh crap, your header is wrong. The upper nibble of the mapper number is in the wrong position, so this is probably being interpreted as mapper #2, which uses 16KB banks.
Re: BNROM cutting off at $C000
by on (#202422)
tokumaru wrote:
Oh crap, your header is wrong. The upper nibble of the mapper number is in the wrong position, so this is probably being interpreted as mapper #2, which uses 16KB banks.


Ah, yup. Thank you, I somehow kept missing that. (I even compared the hex dump of the header with a known good BNROM header and somehow missed it there also)

Which makes sense, as I went to sleep stewing over this issue, I got convinced that it was switching in 16k chunks.
Re: BNROM cutting off at $C000
by on (#202442)
gauauu wrote:
tokumaru wrote:
Oh crap, your header is wrong. The upper nibble of the mapper number is in the wrong position, so this is probably being interpreted as mapper #2, which uses 16KB banks.


Ah, yup. Thank you, I somehow kept missing that. (I even compared the hex dump of the header with a known good BNROM header and somehow missed it there also)

Which makes sense, as I went to sleep stewing over this issue, I got convinced that it was switching in 16k chunks.

Protip: To avoid such errors in the future, replace your header with something like this:
Code:
INES_NUM_16K_PRG_BANKS = 8
INES_NUM_8K_CHR_BANKS = 0
INES_MAPPER_NUMBER = 34
INES_MIRRORING = 0

.segment "HEADER"
    .byte "NES", $1A
    .byte INES_NUM_16K_PRG_BANKS
    .byte INES_NUM_8K_CHR_BANKS
    .byte ( INES_MAPPER_NUMBER & $F ) << 4 | INES_MIRRORING
    .byte INES_MAPPER_NUMBER & $F0

Might as well take advantage of the assembler's expression evaluation abilities.

There's also this: https://wiki.nesdev.com/w/index.php/NES ... r_for_ca65 (instead of the examples there, personally, I would replace 131072 with 128*1024, 524288 with 512*1024, and so on to make it easier to read).
Re: BNROM cutting off at $C000
by on (#202446)
Yeah, best let the assembler shuffle all the bits around for you than doing it in your head each time. I use macros to create my headers.