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

puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

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

Post by puppydrum64 »

I'm a bit confused from reading the wiki about how advanced mappers work. The pages on the NESdev wiki seem geared towards people who understand how mappers work in general (which isn't me for sure :mrgreen: ) but I thought it would be more straightforward than this. The question I'm having this time is with Konami's VRC7 chip used in Lagrange Point and Tiny Toon Adventures 2 (aka Mapper 85). Here's my NESASM3 compatible source code:

Code: Select all

  .inesprg 2   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 85   ; mapper 85 = Konami VRC7
  .inesmir 1   ; background mirroring
  
  .include "NESmacros.asm"
  
BUTTON_A EQU $80
BUTTON_B EQU $40
BUTTON_SELECT EQU $20
BUTTON_START EQU $10
BUTTON_UP	EQU $08
BUTTON_DOWN EQU $04
BUTTON_LEFT EQU $02
BUTTON_RIGHT EQU $01

SPRITE_YPOS EQU $0200
SPRITE_TILENO EQU $0201
SPRITE_ATTR	EQU $0202
SPRITE_XPOS EQU $0203

IRQ_LATCH EQU $E010
IRQ_CTRL EQU $F000
IRQ_ACK	EQU $F010

  .rsset $0000
;ZERO PAGE RAM

joypad1 .rs 1           ;button states for the current frame
joypad1_old .rs 1       ;last frame's button states
joypad1_pressed .rs 1   ;current frame's off_to_on transitions
sleeping .rs 1          ;main program sets this and waits for the NMI to clear it.  Ensures the main program is run only once per frame.
                        ;   for more information, see Disch's document: http://nesdevhandbook.googlepages.com/theframe.html
needdraw .rs 1          ;drawing flag.
dbuffer_index .rs 1     ;current position in the drawing buffer
temp_dbuffer_index .rs 1
dbuffer_index_4x .rs 1
ptr1 .rs 2              ;a pointer
soft2001 .rs 1			;$2001 (PPUMASK) buffer. Unlike PPUMASK can load this into a register.
tempX .rs 1
tempY .rs 1
playerPosX .rs 1
playerPosY .rs 1


;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;

    
  .bank 0
  .org $6000	;8 KB PRG-RAM bank, fixed
  
  .bank 1
  .org $8000	;8 KB switchable PRG-ROM bank
  
  .bank 2
  .org $A000	;8 KB switchable PRG-ROM bank
  
  .bank 3
  .org $C000	;8 KB switchable PRG-ROM bank
  
  .bank 4
  .org $E000	;8 KB fixed PRG-ROM bank
  
  .org $E011	;placed after $e010 to avoid corruption of IRQ controls mapped to $E008 and $E010
nmihandler:
	pha
	txa
	pha
	tya
	pha
	
	;your code goes here
	
	pla
	tay
	pla
	tax
	pla
	rti
	
irqhandler:
	rti
	
reset:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0300, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0200, x    ;move all sprites off screen
  INX
  BNE clrmem
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2

finalprep:
  cli 				;enable IRQs
  
  LDA #%10000000   ; enable NMI, sprites from Pattern Table 0
  STA $2000

  LDA #%00010100   ; enable sprites
  STA $2001
  
  .org $FFFA
	dw nmihandler
	dw reset
	dw irqhandler
  
  .bank 5		;ppu CHR-ROM bank 0
  .org $0000
  incbin "RawNES.RAW"
  .bank 6		;ppu CHR-ROM bank 1
  .org $0400
  
  .bank 7		;ppu CHR-ROM bank 2
  .org $0800	
  
  .bank 8		;ppu CHR-ROM bank 3
  .org $0C00
  
  .bank 9		;ppu CHR-ROM bank 4
  .org $1000
  
  .bank 10		;ppu CHR-ROM bank 5
  .org $1400
  
  .bank 11		;ppu CHR-ROM bank 6
  .org $1800
  
  .bank 12		;ppu CHR-ROM bank 7
  .org $1c00
I used this page on the NESdev wiki https://wiki.nesdev.com/w/index.php/VRC7 to figure out where to set up the banks and the org directives. The first CHR-ROM bank contains this little guy courtesy of Keith from Chibiakumas.com. His dev kit has this image converted to a RAW bitmap file that can be imported into the NES ROM.

Image

Now that being said, I have a few questions.

1. Did I set up my banks and header correctly?
2. I opened my ROM using YY-CHR to view the game's graphics. I would expect to only see the graphic mentioned above. It is there, but not where I thought it should be. YY-CHR says the graphic is at address $A000, despite it being stored in the PPU's bank 0. Below is a link to a screenshot of YY-CHR with my test ROM loaded.
3. A lot of these mappers seem to have audio registers and IRQ handlers in places that are inconvenient. For example the address $9000 in this mapper is used to select which 8 KB PRG ROM bank is loaded into the area from $C000 to $DFFF. Suppose I had some code that started at $8000 and it took up $1200 bytes, would whatever instruction that happens to be at address $9000 corrupt the bank loaded into $C000? If so, what's the best way to prevent "clobbering" these registers?
https://ibb.co/YbFr8TD
Pokun
Posts: 2675
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 »

2. I'm not familar with how YY-CHR gives things addresses, but it sounds like it's using CHR-RAM and the graphics are stored in an arbitrary location in the PRG-ROM. That's normal if you use CHR-RAM. I'm not sure though as it says CHR-ROM on a tab. Maybe if you could upload the CHR file from Chibiakuma?

3. That's not a problem as that is a ROM area. Writing to that area won't mess with the ROM since it's read-only, so the mapper puts its write-only I/O registers in the ROM area. The CPU executing instructions in that area isn't a problem either, the registers are only affected by writes addressed to the area (like STA $9000), not if the actual instruction is fetched and executed from there by the CPU.
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 »

Pokun wrote: Sun May 09, 2021 11:19 am 2. I'm not familar with how YY-CHR gives things addresses, but it sounds like it's using CHR-RAM and the graphics are stored in an arbitrary location in the PRG-ROM. That's normal if you use CHR-RAM. I'm not sure though as it says CHR-ROM on a tab. Maybe if you could upload the CHR file from Chibiakuma?

3. That's not a problem as that is a ROM area. Writing to that area won't mess with the ROM since it's read-only, so the mapper puts its write-only I/O registers in the ROM area. The CPU executing instructions in that area isn't a problem either, the registers are only affected by writes addressed to the area (like STA $9000), not if the actual instruction is fetched and executed from there by the CPU.
Is this the same reason that LDA $2001 doesn't work?

Also with regards to the CHR file, I don't have one. This was a .RAW file created using Chibiakumas' AkuSprite Editor program. Unfortunately I can't find a way to load it into NES Screen Tool. I tried using the mario.chr file from Nerdy Nights' tutorial package and the same thing happens, it's stored in address 00A000, even though my directives should have put it in the first CHR-ROM bank
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 »

puppydrum64 wrote: Sun May 09, 2021 1:28 pmIs this the same reason that LDA $2001 doesn't work?
These are similar situations, yes. For every memory access made, the system has to decide which device is going to respond to it. To make this decision, some of the address bits are used (this process is called "address decoding"), and the signal that indicates whether the memory access is a read or a write can also be taken into account. The mapper uses this signal to differentiate between the CPU simply reading instructions from the ROM and the game code writing to one of the mapper registers.

As for the PPU registers, some of them are read-only, while others are write-only, so if you access those addresses using the incorrect operation, you'll simply not trigger a response from the PPU, it will just ignore the operation.
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: Sun May 09, 2021 4:28 pm
puppydrum64 wrote: Sun May 09, 2021 1:28 pmIs this the same reason that LDA $2001 doesn't work?
These are similar situations, yes. For every memory access made, the system has to decide which device is going to respond to it. To make this decision, some of the address bits are used (this process is called "address decoding"), and the signal that indicates whether the memory access is a read or a write can also be taken into account. The mapper uses this signal to differentiate between the CPU simply reading instructions from the ROM and the game code writing to one of the mapper registers.

As for the PPU registers, some of them are read-only, while others are write-only, so if you access those addresses using the incorrect operation, you'll simply not trigger a response from the PPU, it will just ignore the operation.
So addresses like $2001 are WOM (Write-only memory) :lol:
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 »

puppydrum64 wrote: Sun May 09, 2021 6:31 pmSo addresses like $2001 are WOM (Write-only memory) :lol:
In a sense, yes, because the bits you write there do get stored inside the PPU somewhere, but it's such a small amount of non-general-purpose memory that we don't really call it that.
Pokun
Posts: 2675
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 »

Hardware I/O mapped into address space like this is sometimes also called MMIO (Memory Mapped Input-Output), since it's mapped into memory space although it isn't really memory that you access (but as Tokumaru said, the PPU has memory internally for storing various things as an indirect effect of accessing various PPU I/O). Some people prefer to use the term "address space" instead of "memory space" since you don't only map memory (ROM and RAM) to it, but other hardware as well.

Some CPUs like the Zilog Z80 has specific pins for mapping hardware I/O to, and they are accessed using special instructions (called IN and OUT in Z80 assembly). This frees up more address space for ROM and RAM (like for the 6502, the address space for the Z80 is 64 kB large). The 6502 doesn't have that though, so all hardware I/O must be mapped to the address space and be accessed by normal LDA and STA instructions.


puppydrum64 wrote: Sun May 09, 2021 1:28 pm Also with regards to the CHR file, I don't have one. This was a .RAW file created using Chibiakumas' AkuSprite Editor program. Unfortunately I can't find a way to load it into NES Screen Tool. I tried using the mario.chr file from Nerdy Nights' tutorial package and the same thing happens, it's stored in address 00A000, even though my directives should have put it in the first CHR-ROM bank
What size is that RAW file?
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 »

Pokun wrote: Mon May 10, 2021 3:59 am Hardware I/O mapped into address space like this is sometimes also called MMIO (Memory Mapped Input-Output), since it's mapped into memory space although it isn't really memory that you access (but as Tokumaru said, the PPU has memory internally for storing various things as an indirect effect of accessing various PPU I/O). Some people prefer to use the term "address space" instead of "memory space" since you don't only map memory (ROM and RAM) to it, but other hardware as well.

Some CPUs like the Zilog Z80 has specific pins for mapping hardware I/O to, and they are accessed using special instructions (called IN and OUT in Z80 assembly). This frees up more address space for ROM and RAM (like for the 6502, the address space for the Z80 is 64 kB large). The 6502 doesn't have that though, so all hardware I/O must be mapped to the address space and be accessed by normal LDA and STA instructions.


puppydrum64 wrote: Sun May 09, 2021 1:28 pm Also with regards to the CHR file, I don't have one. This was a .RAW file created using Chibiakumas' AkuSprite Editor program. Unfortunately I can't find a way to load it into NES Screen Tool. I tried using the mario.chr file from Nerdy Nights' tutorial package and the same thing happens, it's stored in address 00A000, even though my directives should have put it in the first CHR-ROM bank
What size is that RAW file?
The RAW file is 576 bytes. The mario.chr file that came with Nerdy Nights tutorial is 8 KB
Pokun
Posts: 2675
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 »

The mario.chr file is a full dump of the CHR-ROM from SMB1, thus it's 8 kB, and that's what you use with NES Screen Tool. You can also import an indexed BMP as CHR and the save it as a CHR file. You can use one of those free ones on the forum to use as a template for drawing your own patterns, as they are already indexed BMP as NES Screen Tool requires. I think it saves one pattern table as 4 kB CHR files by default, so you need two files for the full CHR-ROM in that case.

You include the CHR using incbin "mario.chr" (or whatever your CHR file is named) after the vectors.

Code: Select all

;Header:
	db "NES",$1a			;ID
	db $01				;PRG-ROM banks (16 kB each)
	db $01				;CHR-ROM banks (8 kB each)
	;...

;PRG-ROM:
	;...

	org $FFFA			;vectors
	dw nmihandler			;FFFA - Non-maskable interrupt handler
	dw RESET			;FFFC - Entry point
	dw irqhandler			;FFFE - IRQ handler
	
CHR-ROM:
	incbin "sp.chr"			;pattern table for sprites (4 kB)
	incbin "bg.chr"			;pattern table for BG tiles (4 kB)
This is for the standard 8 kB CHR-ROM. The CHR files must be in the same folder as the source file.
Edit: Forgot to actually change the header. Fixed now.

I have no idea what that Chibiakuma RAW thing is. If it doesn't load as CHR in NES Screen Tool it might not be a CHR file, or maybe it requires a full 4 kB pattern table.
Last edited by Pokun on Mon May 10, 2021 2:07 pm, edited 2 times in total.
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, you said your assembler wouldn’t accept “.org $10000”; that makes perfect sense. But, if you want to use CHR files less than 4kb, maybe you don’t want to waste the small amount of wasted space at the end of a not-fully-used CHR file, then:

Say your zero.chr CHR file is not 4kb (4096 bytes large); rather, it’s 4032 bytes large bc it doesn’t include four unused tiles.
Then, in asm6 I’d:

Code: Select all

incbin “zero.chr” ;banks 0-7
;fill the rest of the 4K block with zeros
align $1000
incbin “one.chr” ;banks 8-15
align $1000
In asm6’s README.txt:
ALIGN

Fill memory from the current address to an N byte boundary.
A fill value may also be specified.

ALIGN 256,$EA
(In my example above, $1000 is easier for me to write than 4096 to specify the 4kb boundary.)

I’m sure your assembler has some sort of align. And, if not, this may also work in asm6:

Code: Select all

incbin “zero.chr” ;banks 0-7
;skip to the next 4K block
pad $+64 ;4032+64=4096
incbin “one.chr” ;banks 8-15
pad fills memory with 0s up until it reaches the specified address. $ stands for the current address. At least this way you are not specifying $10000… so that’s why this MAY also work in asm6. Sry, it’s untested.
Pokun
Posts: 2675
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 »

Align exists in both VASM and CA65 as well, but I think NESASM doesn't have it and requires some creativity to replicate it.
I honestly just fill the empty space with empty patterns or cross marks to fill out the CHR table, but this is a typical situation when align is very useful.
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 »

Pokun wrote: Mon May 10, 2021 2:18 pm Align exists in both VASM and CA65 as well, but I think NESASM doesn't have it and requires some creativity to replicate it.
I honestly just fill the empty space with empty patterns or cross marks to fill out the CHR table, but this is a typical situation when align is very useful.
I created a file called pad256.asm that just contains ".db $00" 256 times. Then I made a second file called "pad8k.asm" that says ".include pad256.asm" 32 times
Pokun
Posts: 2675
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 »

This is not tested but I think you can just do .ds ($1000 - *) & $1000 in NESASM. If it works you could also wrap it into a macro.
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 »

I got CHR banks to work, (I'm using ASM6 now but they still work in NESASM3 just the same) but I can't seem to get PRG-ROM banks to work right. I asked a similar question in another thread and realized it was over ten years old so I basically created a zombie thread :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 »

puppydrum64 wrote: Sat May 22, 2021 5:53 pm I got CHR banks to work, (I'm using ASM6 now but they still work in NESASM3 just the same) but I can't seem to get PRG-ROM banks to work right. I asked a similar question in another thread and realized it was over ten years old so I basically created a zombie thread :oops:
Here's my source code if anyone wants to have a look. I commented out the extra banks for now.

Code: Select all

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

;HEADER CONSTANTS

PRG_COUNT 		= 2 ;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

; GAME CONSTANTS
TOTAL_MAX_OBJECTS = 16

BUTTON_A 		= $80
BUTTON_B 		= $40
BUTTON_SELECT 	= $20
BUTTON_START 	= $10
BUTTON_UP		= $08
BUTTON_DOWN 	= $04
BUTTON_LEFT 	= $02
BUTTON_RIGHT 	= $01

PLAY_AREA_TOP	= $24
PLAY_AREA_BOT	= $E8
PLAY_AREA_LEFT	= $08
PLAY_AREA_RIGHT	= $F8

;MEMORY MAPPED ADDRESSES - STANDARD NES - COMMON TO ALL GAMES
PPUCTRL				equ $2000	;VPHB SINN
PPUMASK				equ $2001	;BGdb bMmG
PPUSTATUS			equ $2002	;VSO- ----
OAMADDR				equ $2003
OAMDATA				equ $2004
PPUSCROLL			equ $2005
PPUADDR				equ $2006
PPUDATA				equ $2007
OAMDMA				equ $4014
APUCTRL				equ $4015
CONTROLLER1			equ $4016
CONTROLLER2			equ $4017
APUIRQ				equ $4017

;KONAMI VRC6 MEMORY MAPPED LOCATIONS

;sound ports are defined in sound engine

PPU_BANKING_STYLE 	equ $B003

R0 EQU $D000
R1 EQU $D001
R2 EQU $D002
R3 EQU $D003
R4 EQU $E000
R5 EQU $E001
R6 EQU $E002
R7 EQU $E003

IRQ_LATCH	equ $F000
IRQ_CTRL	equ $F001
IRQ_ACK		equ $F002
;----------------------------------------------------------------
; variables
;----------------------------------------------------------------
	enum $0000	;ZERO PAGE RAM
vblanked 		.dsb 1	;vBlank timer
soft2000 		.dsb 1	;PPUCTRL buffer
soft2001		.dsb 1	;PPUMASK buffer
bankingstyle	.dsb 1	;PPU_BANKING_STYLE buffer
temp			.dsb 1	;DO NOT USE DURING NMI OR IRQ
joypad2			.dsb 1
joypad1 		.dsb 1  ;button states for the current frame
joypad2_old		.dsb 1
joypad1_old 	.dsb 1  ;last frame's button states

joypad2_pressed	.dsb 1
joypad1_pressed .dsb 1   ;current frame's off_to_on transitions
joypad2_held	.dsb 1
joypad1_held	.dsb 1	;which buttons are held down for multiple frames.
joypad2_release	.dsb 1
joypad1_release .dsb 1	;current frame's on_to_off transitions
joypad3			.dsb 1	;temp variable for Famicom expansion support
joypad4			.dsb 1	;see above
parallaxtimer	.dsb 1	;used for faux parallax scrolling fx
pointerLo		.dsb 1	;a pointer for lookups of backgrounds, attributes, etc.

pointerHi		.dsb 1
scroll			.dsb 1	;PPUSCROLL buffer
lockUserInput	.dsb 1	;is the game paused or no? (0=No, Anything Else = Yes)
temp16Lo		.dsb 1	;a pointer for lookups of backgrounds, attributes, etc.
temp16Hi		.dsb 1
currentBank16k	.dsb 1	;currently active 16k PRG-ROM bank from $8000 to $BFFF
currentBank8k	.dsb 1	;currently active 8k  PRG-ROM bank from $C000 to $DFFF
prevBank16k		.dsb 1	;most recent 16k PRG-ROM bank for bank switching

prevBank8k		.dsb 1	;most recent 8k  PRG-ROM bank for bank switching
softR0			.dsb 1	;buffer for $D000 (R0)
softR1			.dsb 1	;buffer for $D001
softR2			.dsb 1	;buffer for $D002
softR3			.dsb 1	;buffer for $D003
softR4			.dsb 1	;buffer for $E000
softR5			.dsb 1	;buffer for $E001
softR6			.dsb 1	;buffer for $E002

softR7			.dsb 1	;buffer for $E003
gameState		.dsb 1	;0 = Main Game, 1 = Title Screen, 2 = Level Select, etc
tempX			.dsb 1
tempY			.dsb 1
looptempX		.dsb 1
looptempY		.dsb 1

;REQUIRED BY SOUND ENGINE
; var_Temp:				.dsb 1						; Temporary 8-bit
; var_Temp2:				.dsb 1
; var_Temp3:				.dsb 1
; var_Temp4:				.dsb 1
; var_Temp16:				.dsb 2						; Temporary 16-bit
; var_Temp_Pointer:		.dsb 2						; Temporary
; var_Temp_Pointer2:		.dsb 2
; var_Temp_Pattern:		.dsb 2						; Pattern address (temporary)
; var_Note_Table:			.dsb 2

; ACC:					.dsb 2						; Used by division routine
; AUX:					.dsb 2
; EXT:					.dsb 2
	.ende
;----------------------------------------------------------------


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

	;.enum $0100
	;.ende

	;.enum $0200
	;.ende
	.enum $0400
OBJECT_XPOS			.dsb TOTAL_MAX_OBJECTS			
OBJECT_YPOS			.dsb TOTAL_MAX_OBJECTS
OBJECT_STATUS 		.dsb TOTAL_MAX_OBJECTS	
	;#%ABCDEFGH
;A = ACTIVE								E =
;B = FLAGGED FOR DESTRUCTION			F =
;C = 									G =
;D =									H =
OBJECT_ID			.dsb TOTAL_MAX_OBJECTS
OBJECT_PROPERTIES	.dsb TOTAL_MAX_OBJECTS		
;#%ABCDEFGH
;A = OBEY GRAVITY			E = DRAW BEHIND BKGD
;B = BOSS MINION			F = 
;C = INDESTRUCTIBLE			G =
;D = IGNORE COLLISION		H =
							
							
OBJECT_SIZE			.dsb TOTAL_MAX_OBJECTS ;	Total number of sprites needed to display the object.
OBJECT_HEALTH		.dsb TOTAL_MAX_OBJECTS ;
OBJECT_BASE_SPRITE	.dsb TOTAL_MAX_OBJECTS ;	The tile ID for the top left sprite of that object.
OBJECT_PALETTE		.dsb TOTAL_MAX_OBJECTS ; 	pre-buffer for OAM attribute data
OBJECT_SLOTS		.dsb TOTAL_MAX_OBJECTS ;	0 if empty, otherwise that slot is occupied.
OBJECT_CURRENT_TILE	.dsb TOTAL_MAX_OBJECTS ;    which metatile the object occupies.
	.ende
	
	enum $0600
	
	PLAYER_1_LIVES	.dsb 1
	PLAYER_2_LIVES	.dsb 1
	PLAYER_1_SCORE	.dsb 5
	PLAYER_2_SCORE	.dsb 5
	BOSS_HEALTH		.dsb 1
	BOSSTIMER_LO	.dsb 1
	BOSSTIMER_HI	.dsb 1
	
	.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
;----------------------------------------------------------------
	.include "NESmacros.asm"		;Basic macros that work on every NES cartridge.
	
	.include "VRC6macros.asm"		;Macros designed to work with the Konami VRC6's features.

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

;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"
	.pad  $E000

;BANK 1 8K

	;.base $C000
	; this breaks the game right now.
	;.pad  $E000
	
	
	.base $E000						;8K fixed PRG-ROM bank
	
	
	
;------------------------------------------------------------
; Reset and Initialization Code
;------------------------------------------------------------
Reset:
	SEI					;ignore interrupts for the reset
	LDA #$00			; load the number 00
	STA $2000			; disables NMI
	STA $2001			; disables rendering
	STA $4010			; disables DMC IRQ 
	STA $4015			; disables APU sound
	LDA #$40			; Loads the number 64
	STA $4017			; disables the APU IRQ
	CLD					; disables decimal mode 
	LDX	#$FF		
	TXS					; initializes the STAck
	
; wait for first vblank
	JSR vBlankWait
	
	
; clear ram
	LDX #$00
clrMemLoop:
	LDA #$00
	STA $0000,x
	STA $0100,x 
					;; skip 200, this is where the sprites are drawn
					;; we'll set them to $FE to draw them off screen
	STA $0300,x
	STA $0400,x
	STA $0500,x
	STA $0600,x
	STA $0700,x
	LDA #$FE		; instead of zero, write #$FE to 0200,x
	STA $0200,x		; to place sprites off screen
	INX
	BNE clrMemLoop
	
; Second vblank. PPU should be ready after this.
	JSR vBlankWait
	
; Initialize PRG-ROM banks
	LDA $01					;bank 0
	STA currentBank16k
	STA prevBank16k
	STA $8000 				;16k PRG ROM BANK

	STA currentBank8k
	STA prevBank8k
	STA $C000				;8K PRG ROM BANK

	LDA #$20
	STA PPU_BANKING_STYLE	;set BIT 5 disabling PPU mirroring
	
	; ldx #$00				(optimization: x equals zero at this point.)
loop_initCHRROM:
	TXA
	STA softR0,x			;store 0 to R0, 1 to R1, etc.
	INX
	CPX #$08
	BNE loop_initCHRROM
	JSR PrepCHRROM

	LDA #$00
	STA softR5

	JSR LoadSprites
	
	LDA #%00010000
	STA $2000			  ;DEBUG: Allows background tiles to be displayed in PPU VIEWER as they are drawn.
						  
						  
; LOAD PALETTES

	LDA #<(Palette_00)
	STA pointerLo
	LDA #>(Palette_00)
	STA pointerHi
	JSR LoadPalettes

; Draw Starting Screen
	
	LDA #<(background)
	STA pointerLo
	LDA #>(background)
	STA pointerHi

	LDX #$00 ;nametable $2000

	JSR LoadBackground


	LDA #<(background)
	STA pointerLo
	LDA #>(background)
	STA pointerHi
	
	LDX #$01 ;nametable $2400
	
	JSR LoadBackground
	
	
; Place Attribute Data

	LDA #<(attribute)
	STA pointerLo
	LDA #>(attribute)
	STA pointerHi
	
	LDX #$00 ;nametable $2000
	
	JSR LoadAttribute
	
	LDX #$01 ;nametable $2400
	
	JSR LoadAttribute
	
	JSR LoadStatusBar
	


	
;10 Enable NMI
	LDA #%10010000	; turn on NMI, set sprites $0000, bkgd to $1000
	STA $2000
	STA soft2000
	
	LDA #%00011110
	STA soft2001
	STA $2001
	
	LDA #$00
	STA $2005
	STA $2005
;END OF RESET ROUTINE


;------------------------------------------------------------
; main
;------------------------------------------------------------
INIT:

;Prep Player 1 Object
LDX #$00		;done here for later convenience during MainGameLoop.
LDA #$80
STA OBJECT_XPOS
STA OBJECT_YPOS
STA OBJECT_SLOTS
STA OBJECT_STATUS







MainGameLoop:
	JSR read_joypad				;uses x, callee preserved
	JSR handle_input			;uses y, no preservation
	JSR generateOAM				;uses x and y, callee preserved
	JSR calculateCurrentTile	;uses x as input but does not overwrite x.
	JSR waitframe				;uses A, callee preserved
	
	
	
	
	JMP MainGameLoop
	
	include "Subroutines.asm"
	include "VRC6_Subroutines.asm"
	
;------------------------------------------------------------
; interrupt handlers
;------------------------------------------------------------
NMI:

	PHA     ;save registers
    TXA
    PHA
    TYA
    PHA
	
	
	

	
PerformOAMDMA:
	LDA #$00
	STA $2003  ; set the low byte (00) of the RAM address
	LDA #$02
	STA $4014  ; set the high byte (02) of the RAM address, Start the transfer

UpdatePPU:

	
;BEGINNING OF SPRITE ZERO HIT CODE
						 ; do scroll the screen
	LDA #$00
	STA $2006       	 ; clean up PPU address registers
	STA $2006

	LDA #$00        	 ; start with no scroll for status bar
	STA $2005
	STA $2005
  
WaitNotSprite0:
	lda $2002
	and #%01000000
	bne WaitNotSprite0   ; wait until sprite 0 not hit

WaitSprite0:
	lda $2002
	and #%01000000
	beq WaitSprite0      ; wait until sprite 0 is hit
	
	
	ldx #$55			 ; chosen to minimize graphical glitches
WaitScanline:
	dex
	bne WaitScanline
	
;END OF SPRITE ZERO HIT

	LDA lockUserInput
	BNE dontScroll
	
HandleParallax:
	inc parallaxtimer
	lda parallaxtimer
	AND #%00000001
	BNE nowScroll		;skip parallax this frame

	lda softR5
	BPL dontWrap
	lda #$1F
	sta softR5
	jmp nowScroll
dontWrap:
	dec softR5
	
nowScroll:
	inc scroll	; soft PPUSCROLL buffer
	
dontScroll:


	JSR PrepCHRROM
	

	
	
	
	LDA scroll
	STA $2005
	LDA #$00
	STA $2005
	
	LDA soft2001
	STA $2001
	
;;;;;;; do non-ppu stuff last
	LDA #$00
	STA vblanked
	


	
doHandleMusic:

	
skipMusicUpdate:
	
	
	
	PLA     ;restore registers
    TAY
    PLA
    TAX
    PLA
	rti

IRQ:

	rti


	include "controllers.asm"
	include "Graphics_Data.asm"
	include "object_LUT.asm"
;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------

	.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"
Post Reply