VRC7 Help (Really more about mappers that support multiple banks in general)

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by tokumaru »

Your ROM can't be 3x 16KB because that adds up to a chip size that doesn't exist. Valid PRG-ROM sizes are 16KB, 32KB, 64KB, 128KB, and so on. This means that if you want to extend your ROM past 32KB, you need to add *two* 16KB banks so the total size is 64KB. If later you want more than 64KB, then you'll need *four* new banks to go all the way up to 128KB. Every time you need more space, you have to double the amount of ROM. This is also true for CHR-ROM.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by Pokun »

puppydrum64 wrote: Sat May 22, 2021 5:53 pm I asked a similar question in another thread and realized it was over ten years old so I basically created a zombie thread :oops:
I don't think this forum disallows necroposting, and digging up old discussions can be a good thing if done right. In my opinion necroposting is totally fine and better than creating a new thread if what you have to add is directly related to the discussion and you want to continue it. If it's only loosely connected you are better off creating a new thread, especially if you can't find the the thing you want to discuss anywhere else after searching for a bit. In this case it was a direct continuation of the discussion so it was fine IMHO.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by puppydrum64 »

tokumaru wrote: Sat May 22, 2021 6:52 pm Your ROM can't be 3x 16KB because that adds up to a chip size that doesn't exist. Valid PRG-ROM sizes are 16KB, 32KB, 64KB, 128KB, and so on. This means that if you want to extend your ROM past 32KB, you need to add *two* 16KB banks so the total size is 64KB. If later you want more than 64KB, then you'll need *four* new banks to go all the way up to 128KB. Every time you need more space, you have to double the amount of ROM. This is also true for CHR-ROM.
I guess I find the VRC6 in particular confusing because it has a switchable 16k bank, a switchable 8k bank, and a fixed 8k bank. Since this adds up to 32k I would need at a minimum 2 16k banks. That seems to make sense. What doesn't make sense is why my code that's anywhere except the fixed bank doesn't appear in MESEN's debugger. Nor does it seem that I can add more banks without making the rom impossible to load.

EDIT: Rereading my old thread "My cart runs differently in different emulators" showed me what was wrong with the code outside the fixed bank not loading:

"No. The program counter starts at the address that the NES's CPU loads from the cart.

In the same way that NMI jumps to the address at $FFFA, reset jumps to the address at $FFFC.

The VRC6 only guarantees that the last 8KB of the PRG ROM is accessible at addresses $E000-$FFFF, so your code must write to the VRC6 registers before you access anything at $8000-$DFFF.

(And your PRG ROM must be a power of 2 in size)"

I had accidentally had the bank register selecting bank 1 when there was no bank 1. That's what made my code in the 16k bank seem to vanish. But that shouldn't have anything to do with the apparent inability to add more banks. There must be an error with my formatting of the base and pad directives but I don't know what it is.

I tried doing the following and I can't get it to boot.

Code: Select all

PRG_COUNT 		= 4 ;1 = 16KB, 2 = 32KB
CHR_COUNT		= 4 ;1 = 8KB, 2 = 16KB, ETC
MAPPER_LOW		= $80
MAPPER_HIGH		= $10		;together these make mapper 24
FAMICOM_NES		= %0000
PLAY_CHOICE_TEN		= %0010
VS_SYSTEM		= %0001
MIRRORING 		= %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen
	.org $7FF0
;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------
	
	.db "NES", $1a ;identification of the iNES header
	.db PRG_COUNT ;number of 16KB PRG-ROM pages
	.db CHR_COUNT ;number of 8KB CHR-ROM pages
	.db MAPPER_LOW|MIRRORING ;mapper bottom 4 bits and mirroring
	.db MAPPER_HIGH|FAMICOM_NES	  ;mapper top 4 bits, NES
	.dsb 8, $00 ;clear the remaining bytes
	
	.pad $8000, $00
;BANK 0 16K
	.base $8000				;16k switchable 16k PRG-ROM bank
	
	
	; BANK CONTENTS GO HERE

	.pad  $C000

; BANK 1 16K
	.base $8000				;16k switchable 16k PRG-ROM bank
	
	
	; this breaks the game right now.

	.pad  $C000
	
;BANK 0 8K	
	.base $C000						;8K switchable PRG-ROM bank
	; BANK CONTENTS GO HERE
	include "CollisionMaps.asm"
	include "collision_Subroutines.asm"
	.pad  $E000

;BANK 1 8K

	.base $C000
	; this breaks the game right now.
	.pad  $E000
	
	
	.base $E000						;8K fixed PRG-ROM bank
	
Reset:
	...
	
	.org $fffa

	.dw NMI
	.dw Reset
	.dw IRQ
;----------------------------------------------------------------
; CHR-ROM banks
;----------------------------------------------------------------

	.incbin "astrobirds_00.chr"
	.incbin "astrobirds_01.chr"
	.incbin "astrobirds_02.chr"
	.incbin "astrobirds_03.chr"

Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by Pokun »

You specify 4 x 16 = 64 kB PRG and 4 x 8 = 32 kB CHR in the header. But you only have (2 x 16) + (2 x 8) = 48 kB PRG ROM. You need one more 16 kB bank or two more 8 kB banks. You also call your four PRG banks 0 and 1 twice, but I guess that's a typo.

Also what is the size of those CHR files? The total size must be 32 kB as you specified in the header. If it's less you must pad to 32 kB or change the header.

Initializing all mapper registers (and all hardware in the NES) is another thing, but I guess the init code is just omitted.
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by Quietust »

Pokun wrote: Sun May 23, 2021 12:01 pm You also call your four PRG banks 0 and 1 twice, but I guess that's a typo.
Actually, they're labeled "BANK 0 16K"/"BANk 1 16K" and "BANK 0 8K"/"BANK 1 8K", which I suspect is part of a larger misunderstanding which I'm going to try and clear up.
puppydrum64 wrote: Sun May 23, 2021 10:54 am I guess I find the VRC6 in particular confusing because it has a switchable 16k bank, a switchable 8k bank, and a fixed 8k bank. Since this adds up to 32k I would need at a minimum 2 16k banks. That seems to make sense.
The VRC6 does not have separate "16K banks" and "8K banks" - it only has 8KB banks, and the "16KB bank" at $8000-$BFFF is merely two consecutive banks which are selected by a single register with the further restriction that the first 8KB bank must be an even one. That is, if you write "3" to $8000 (to map "16KB bank #3" to $8000-$BFFF), you are actually mapping "8KB bank #6" to $8000-$9FFF and "8KB bank #7" to $A000-$BFFF.

Similarly, it's also possible to map either "half" of a 16KB bank to $C000-$DFFF by using the appropriate bank number - as above, writing "6" or "7" to $C000 will map the first or second half (respectively) of "16KB bank #3" to $C000-$DFFF.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by puppydrum64 »

Quietust wrote: Sun May 23, 2021 1:05 pm
Pokun wrote: Sun May 23, 2021 12:01 pm You also call your four PRG banks 0 and 1 twice, but I guess that's a typo.
Actually, they're labeled "BANK 0 16K"/"BANk 1 16K" and "BANK 0 8K"/"BANK 1 8K", which I suspect is part of a larger misunderstanding which I'm going to try and clear up.
puppydrum64 wrote: Sun May 23, 2021 10:54 am I guess I find the VRC6 in particular confusing because it has a switchable 16k bank, a switchable 8k bank, and a fixed 8k bank. Since this adds up to 32k I would need at a minimum 2 16k banks. That seems to make sense.
The VRC6 does not have separate "16K banks" and "8K banks" - it only has 8KB banks, and the "16KB bank" at $8000-$BFFF is merely two consecutive banks which are selected by a single register with the further restriction that the first 8KB bank must be an even one. That is, if you write "3" to $8000 (to map "16KB bank #3" to $8000-$BFFF), you are actually mapping "8KB bank #6" to $8000-$9FFF and "8KB bank #7" to $A000-$BFFF.

Similarly, it's also possible to map either "half" of a 16KB bank to $C000-$DFFF by using the appropriate bank number - as above, writing "6" or "7" to $C000 will map the first or second half (respectively) of "16KB bank #3" to $C000-$DFFF.
Ohhh I see! That explains why Mesen showed $01 at A000 and why my code at $C000 was mirrored at $8000. Thank you! I really wish the wiki explained that :oops:
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by puppydrum64 »

So now with that new understanding in mind I'm a little bit more confused why this works:

Code: Select all

;BANK 0 16K
	.base $8000				;16k switchable 16k PRG-ROM bank
	
	
	; BANK CONTENTS GO HERE
	include "CollisionMaps.asm"
	include "collision_Subroutines.asm"
	.pad  $C000

	
;BANK 1 8k
	.base $C000						

	.pad  $E000


	
	
	.base $E000						;8K fixed PRG-ROM bank
If all the banks are really 8k and the "16k bank" is just two consecutive 8k banks in sequence when loaded at $8000, why does this setup of directives make the game boot where having ".base $8000 .pad $C000" doesn't?
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by puppydrum64 »

At long last I finally got bankswitching to work! Thank you all for your help. I'll go ahead and share what I've learned for anyone who has the same question later. Just to clarify this is for VRC6 and I'm using ASM6 so you might need to change your directive syntax accordingly if you're using a different assembler.

Code: Select all

;----------------------------------------------------------------
; constants
;----------------------------------------------------------------

;HEADER CONSTANTS

PRG_COUNT 		= 4 ;1 = 16KB, 2 = 32KB, etc. This number should equal exactly half your total number of banks. Both should be even.
CHR_COUNT		= 4 ;1 = 8KB, 2 = 16KB, etc. This must equal the number of .chr files at the end of your code.
MAPPER_LOW		= $80
MAPPER_HIGH		= $10		;together these make mapper 24
FAMICOM_NES		= %0000		
PLAY_CHOICE_TEN	= %0010
VS_SYSTEM		= %0001
MIRRORING 		= %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen


;----------------------------------------------------------------
; variables
;----------------------------------------------------------------
	enum $0000	;ZERO PAGE RAM
	
	;zero page ram goes here
	.ende
;----------------------------------------------------------------


	;NOTE: you can also split the variable declarations into individual pages, like this:


	
	;.enum $0400
	;.ende
	
	;.enum $0600
	;.ende
	
	
	.org $7FF0
;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------
	
	.db "NES", $1a ;identification of the iNES header
	.db PRG_COUNT ;number of 16KB PRG-ROM pages
	.db CHR_COUNT ;number of 8KB CHR-ROM pages
	.db MAPPER_LOW|MIRRORING ;mapper bottom 4 bits and mirroring
	.db MAPPER_HIGH|FAMICOM_NES	  ;mapper top 4 bits, NES
	.dsb 8, $00 ;clear the remaining bytes
	
	.pad $8000, $00
;----------------------------------------------------------------
; MACRO LISTS
;----------------------------------------------------------------

	;MACROS GO HERE

;----------------------------------------------------------------
; program bank(s)
;----------------------------------------------------------------

;BANK 0
	.base $8000				
	
	; BANK CONTENTS GO HERE

	.pad  $A000
	
;BANK 1
	.base $A000
	
	;BANK CONTENTS GO HERE
	
	.pad  $C000

	
;BANK 2
	.base $C000	
						
	; BANK CONTENTS GO HERE

	.pad  $E000
	
;BANK 3
	.base $8000
	; BANK CONTENTS GO HERE

	.pad  $A000

;BANK 4
	.base $A000
	; BANK CONTENTS GO HERE

	.pad $C000
;BANK 5
	.base $C000
	; BANK CONTENTS GO HERE

	.pad $E000
	
;BANK 6
	.base $8000
	.; BANK CONTENTS GO HERE

	.pad $A000
;FIXED BANK 7
	.base $E000						;8K fixed PRG-ROM bank
	
	
	
;------------------------------------------------------------
; Reset and Initialization Code
;------------------------------------------------------------
Reset:

;YOUR RESET CODE GOES HERE


;This part sets up your CHR-ROM like a typical NES game
	LDA #$00
	STA $D000
	LDA #$01
	STA $D001
	LDA #$02
	STA $D002
	LDA #$03
	STA $D003
	LDA #$04
	STA $E000
	LDA #$05
	STA $E001
	LDA #$06
	STA #$E002
	LDA #$07
	STA #$E003
	
;SET UP DEFAULT SWITCHABLE BANKS - IT'S A GOOD IDEA TO HAVE SHADOW REGISTERS FOR THE CURRENT BANK AND THE PREVIOUS BANK
	LDA #$00
	STA $8000		;SETS $8000-$A000 TO BANK 0 AND $A000-$C000 TO BANK 1. BANK NUMBERS WRITTEN TO THIS ADDRESS
				;ARE AUTOMATICALLY DOUBLED.
	LDA #$02
	STA $C000		;SETS $C000-$E000 TO BANK 2 (NO AUTOMATIC DOUBLING)
	
	

;ADD THIS PART TO ENABLE IRQs
	LDA #$FF
	STA $F000
	
	
	CLI	

;------------------------------------------------------------
; main
;------------------------------------------------------------


MainGameLoop:

	;-----------------------------------------------------
	;WRITING #%00000010 TO $F001 TRIGGERS AN IRQ
	LDA #%00000010
	STA $F001
	;-----------------------------------------------------
	
	JMP MainGameLoop
	
;------------------------------------------------------------
; interrupt handlers
;------------------------------------------------------------
NMI:

    PHA     ;save registers
    TXA
    PHA
    TYA
    PHA
    
    ;NMI ROUTINE GOES HERE

    PLA     ;restore registers
    TAY
    PLA
    TAX
    PLA
    
    RTI

IRQ:
	PHA
	TXA
	PHA
	TYA
	PHA
	
	;IRQ ROUTINE GOES HERE
	
	STA $F002		;without this the IRQ will never end. What's in the accumulator at this line doesn't matter.
	
	PLA
	TAY
	PLA
	TAX
	PLA
	RTI

;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------

	.org $fffa

	.dw NMI
	.dw Reset
	.dw IRQ

;----------------------------------------------------------------
; CHR-ROM banks
;----------------------------------------------------------------

	.incbin "astrobirds_00.chr"		;USE THE NAME OF YOUR CHR FILE, NOT MINE!  :P 
	.incbin "astrobirds_01.chr"
	.incbin "astrobirds_02.chr"
	.incbin "astrobirds_03.chr"
EDIT: Minor changes for clarification.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by unregistered »

Congratulations! 🎉🎊

Maybe, if you want to save reset space, you could do this instead:

Code: Select all

ldx #03
- txa
  eor #00000100b ;<invert 4-value bit in accumulator
  sta $E000, x
  eor #00000100b
  sta $D000, x
  dex ;2-ff
  bpl -
^that’s 16 bytes compared to your *meant* 40 bytes

Note: Also, it’s around 85 cycles compared with your *meant* 56 cycles, but size of code matters in reset. :)


Oooh, this only works if the ROM addresses don’t need to be written to consecutively. Aaand, interested, please read and understand how the code works instead of just copy pasting. You’ll improve your coding-self. :)


EDIT: ^Our code is untested; but, it seems to be logically valid.

FINAL-EDIT: ^code attempts to do this:
puppydrum64 wrote: Tue May 25, 2021 12:45 pm

Code: Select all

	LDA #$00
	STA $D000
	LDA #$01
	STA $D001
	LDA #$02
	STA $D002
	LDA #$03
	STA $D003
	LDA #$04
	STA $E000
	LDA #$05
	STA $E001
	LDA #$06
	STA #$E002
	LDA #$07
	STA #$E003
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by unregistered »

Sigh, the code can probably be reduced even more:

Code: Select all

ldx #03
- txa
  sta $D000, x
  eor #00000100b ;<invert 4-value bit in accumulator
  sta $E000, x
  dex ;2-ff
  bpl -
If that does work, it would be only 14 bytes and around 77 cycles.


If we do $D000 store first, the values are already correct; then the txa at the top eliminates the need to invert accumulator back to normal bc it’s already normal (due to txa). :)
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by puppydrum64 »

It's interesting how it's actually quicker to load everything backwards due to the fewer commands required to check if a number equals zero or is greater than $80. I'm sure that wasn't an intentional design of the 6502. I wrote my template the way I did for simplicity's sake because although slower it is much easier to understand the intent of the code. Loading the banks the way I did matches how CHR-ROM works in most cartridges that don't have CHR bankswitching. That way the user has some easy-to-understand groundwork to start from. I feel that the documentation on VRC6 is currently very poor for the beginner programmer.

EDIT: My actual code uses shadow regs for D000-D003 & E000-E003. I never directly write to the actual registers, only the shadow registers and then copy to the real ones every NMI. I tried making your compact loop work but unfortunately STY addr,x isn't allowed by the 6502.

I've taken a bit of a break from the NES for now, I find it frustrating to work with. I guess the old phrase "Genesis does what Nintendon't" really was true after all. (Well technically I'm working with NEOGEO now but they use the same processor.)
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: VRC7 Help (Really more about mappers that support multiple banks in general)

Post by unregistered »

puppydrum64 wrote: Sat Jun 19, 2021 8:41 am I tried making your compact loop work but unfortunately STY addr,x isn't allowed by the 6502.

I've taken a bit of a break from the NES for now, I find it frustrating to work with. I guess the old phrase "Genesis does what Nintendon't" really was true after all. (Well technically I'm working with NEOGEO now but they use the same processor.)
Hmmm… my compact loop also wouldn’t work with sty addr,x bc eor only works on the accumulator. You’d have to tax or tay and that adds more cycles so the loop would no longer be compact, imo. :)

In my experience, a needed sty addr,x can be replaced if you just spend a bit of effort using the registers different. Usually it works out. 👍

Enjoy the neogeo! :)


EDIT:
puppydrum64 wrote: Sat Jun 19, 2021 8:41 am I wrote my template the way I did for simplicity's sake because although slower it is much easier to understand the intent of the code.
Totally understand; however, your code is actually faster than my loop. Just your code’s massive size is unwelcome in the midst of reset, bc that pushes your main loop’s start further into your fixed bank; at least, that’s how large code in my reset affected me.
Post Reply