Need some direction with multiple backgrounds

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
log in
Posts: 72
Joined: Tue Jun 24, 2008 1:06 pm
Location: neverland

Need some direction with multiple backgrounds

Post by log in »

Hi guys,

I picked up nes programming again.
A long time ago i did the nerdy night tutorial and made a 1 screen pong game.

Now i want to ad multiple screens.
So i had a idea in my head for 2 screens. Push start screen and actual playing screen.

With the nerdy nights stuff ( mario.chr, and background.bat and NESASM3 )
I created this idea that i have not tested yet.

- load title screen
- program start button so that if pressed the screen is turned of and the game screen is loaded.

But i really need to learn how to programm more nes screens. I think this is done by adding more chr. But i'm not sure.
In other words i need more information about adding more backgrounds and switching between them.
I searched the forum but i can't find it . So any help would be welcome.
im a newbie,lets see how far i can get
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Need some direction with multiple backgrounds

Post by tepples »

If the screens share the same background tile set, you can just disable rendering (LDA #$00 STA $2001), load in the new data through $2006 and $2007, and then reenable rendering (LDA #$1E STA $2001)

If they have different tile sets, you have three choices:
  1. Stick with NROM (no mapper) but use $2000 to switch the background tile start address between PPU $0000 and $1000.
  2. Use a CHR ROM mapper to switch a different tile set into $2000.
  3. Use a CHR RAM board and load a new tile set into PPU $0000-$1FFF.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

log in wrote:I think this is done by adding more chr.
Not necessarily. You can have thousands of screens sharing the same 256 (or 512) tiles. Look at SMB or any other NROM game.

The basic idea behind multiple screens is that each screen (I like the names "mode" and "state" better, since one of these parts can have more than 1 screen because of scrolling and such) is its own isolated program. Each program mode should have its own initialization code, main loop, and even a PPU update code, if you choose not to make a generic vblank handler that serves all program modes.
In other words i need more information about adding more backgrounds and switching between them.
You can keep the basic structure you have from Nerdy Nights, but you have add different initialization code for each mode and select the proper logic block and PPU update block depending on the current program mode.

The system initialization can be left intact, but you should put a label marking the start of the code that sets up your very first screen, so you can jump to it whenever you want to go back to that mode. Then you can do the same for how many more program modes you want. Then, when the NMI hits, you have to jump to the proper handler for the current program mode, instead of always running the same handler. It goes something like this (BTW, this is not NESASM syntax... I believe you have to use [] in the indirect JMP and LOW()/HIGH() to get the individual bytes of an address):

Code: Select all

	;(REGULAR INITIALIZATION GOES HERE)

;****************************************************************

TitleScreenInitialize:
	;(TURN NMIs OFF)
	;(INITIALIZE VARIABLES, BACKGROUND, ETC. FOR THE TITLE SCREEN)
	lda #<TitleScreenNMI
	sta ProgramModeNMI+0
	lda #>TitleScreenNMI
	sta ProgramModeNMI+1
	;(TURN NMIs ON)
TitleScreenInfiniteLoop:
	jmp TitleScreenInfiniteLoop

TitleScreenNMI:
	;(UPDATE THE PPU FOR THE TITLE SCREEN)
	;(RUN LOGIC FOR THE TITLE SCREEN)
	rti

;****************************************************************

MainGameInitialize:
	;(TURN NMIs OFF)
	;(INITIALIZE VARIABLES, BACKGROUND, ETC. FOR THE MAIN GAME)
	lda #<MainGameNMI
	sta ProgramModeNMI+0
	lda #>MainGameNMI
	sta ProgramModeNMI+1
	;(TURN NMIs ON)
MainGameInfiniteLoop:
	jmp MainGameInfiniteLoop

MainGameNMI:
	;(UPDATE THE PPU FOR THE MAIN GAME)
	;(RUN LOGIC FOR THE MAIN GAME)
	rti

;****************************************************************

GameOverInitialize:
	;(TURN NMIs OFF)
	;(INITIALIZE VARIABLES, BACKGROUND, ETC. FOR THE GAME OVER SCREEN)
	lda #<GameOverNMI
	sta ProgramModeNMI+0
	lda #>GameOverNMI
	sta ProgramModeNMI+1
	;(TURN NMIs ON)
GameOverInfiniteLoop:
	jmp GameOverInfiniteLoop

GameOverNMI:
	;(UPDATE THE PPU FOR THE GAME OVER SCREEN)
	;(RUN LOGIC FOR THE GAME OVER SCREEN)
	rti

;****************************************************************

NMI:
	jmp (ProgramModeNMI)
With this, you can JMP from mode to mode whenever necessary. For example, if during the main game logic you detect that the player was hit and he lost all of his lives, you can just jmp GameOverInitialize to switch from the main game to the game over screen. The same goes for the transition between the title screen and the main game, only you JMP when start is pressed.

This is just one possible solution (the simplest one I can think of based on what I know from the Nerdy Nights tutorials), but there are literally hundreds of ways of doing this. When you get more acquainted with NES development you will understand the pros and cons of the various methods and you will certainly tweak the code to whatever works best for your game. You could for example use generic PPU update code for all program modes, and only then JMP to the different frame logic handlers, that could save some ROM space.
User avatar
Sogona
Posts: 186
Joined: Thu Jul 23, 2015 7:54 pm
Location: USA
Contact:

Re: Need some direction with multiple backgrounds

Post by Sogona »

If you dont want the screen to scroll and just have a new screen be loaded when something exits the screen (Like in battle kid), it's actually very simple. Just have an address table of screens

Code: Select all

Screen0:
  ;nametable for the first screen goes here

Screen1:
  ;nametable for second screen goes here

Screens:
  .dw Screen0,Screen1
Then have what's called a pointer, which is basically just a variable that takes up 2 bytes. The only restriction is that it needs to be in zero page.

Then have a variable that keeps track of which screen you're in, and when your player exits the boundaries of one screen, you can load the correct next screen based on where he or she left.

Note that in order to draw an entire new screen, the ppu needs to be turned off. This is as simple as writing #%00000110 to $2001, and seting a variable NMI_enabled to 0 (At the start of NMI, just RTI immediately if its at 0.)

Then this code will load the correct screen

Code: Select all

lda screen_number
asl ;multiply by 2 to use as an index to a table of words
tax
lda Screens,x ;get the LOW byte of the address of your nametable
sta screen_pointer
lda Screens+1,x ;get the HIGH byte of the nametable address
sta screen_pointer+1
;tell ppu its time to draw
lda $2002
lda #$20
sta $2006
lda #$00
sta $2006
ldx #$04  ;gotta copy 256 × 4 tiles
ldy #$00
@loop:
lda (screen_pointer),y ;This combines the 2 bytes in the pointer to make a new address. The value of whatever address that was is then loaded into the accumulator. (You have to use y)
sta $2007
iny
bne @loop
inc screen_pointer+1 ;load the next 256 bytes
dex
bne @loop
After that, just add your attributes, set NMI_enabled back to 1, turn on the PPU again by writing #%00011110 to $2001 (You should do this in the NMI), and you should be all set.

If you do want to do scrolling, I haven't tried to tackle that yet so I can't really help you there. But it's a LOT more complicated. Not trying to steer you the wrong way though. :)

I typed this all out on a phone, so I appologize if there are any typos.
Last edited by Sogona on Wed Sep 16, 2015 7:09 pm, edited 1 time in total.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

Yeah, when we talk about "multiple screens" we can actually mean 2 things... I described how to handle different types of screens, while Sogona explained how to have multiple screens of the same type. You'll often have to combine both things, by using pointers to load data for a particular screen in the initialization phase of a particular screen type. The details will obviously vary depending on the type of game you're making.
User avatar
log in
Posts: 72
Joined: Tue Jun 24, 2008 1:06 pm
Location: neverland

Re: Need some direction with multiple backgrounds

Post by log in »

Thanks for all the replies guys.

I Came up with this idea ( that's not tested yet !!!! ) And i haven't read the last 2 posts !!

* Inesheader
* .rsset With the variables TITLESCREEN HI+LOW and MAINGAME HI+LOW
* bank 0 + reset
* vblank 1 / clrmem/ vblank 2
* load palettes + loop

^ regulair stuff except the 4 variables


LDA $2002
LDA #$20
STA $2006 ; write the high byte of $2000 address
LDA #$00
STA $2006 ; write the low byte of $2000 address

LDA #low(background)
STA TITLESCREENLow
LDA #high(background)
STA TITLESCREENHigh

LDX #$04 ; Loop X 4 times
LDY #$00 ; Loop Y 256 times


LoadBackgroundsLoop:

LDA [TITLESCREENLow],y
STA $2007
INY
BNE LoadBackgroundsLoop


; Outer loop


INC TITLESCREENHigh ; increment high byte of address backg to next 256 byte chunk
DEX ; one chunk done so X = X - 1.
BNE LoadBackgroundsLoop ; if X isn't zero, do again


LDA $2002 ; Reset Scroll
LDA #$00
STA $2005
STA $2005
STA $2006
STA $2006
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LatchController:
LDA #$01
STA $4016
LDA #$00
STA $4016 ; tell both the controllers to latch buttons


ReadStartButton:
LDA $4016 ; player 1 - A
AND #%00000001 ; only look at bit 0
BEQ ReadStartButtonDone ; branch to ReadStatButtonDone if button is NOT pressed (0)
; add instructions here to do something when button IS pressed (1)
LDX #$00
STX $2000 ; disable NMI
STX $2001 ; disable rendering

JMP Maingame ; jumps to maingame

ReadStartButtonDone: ; handling this button is done



LDA #%10010000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
STA $2000

LDA #%00011110 ; enable sprites, enable background, no clipping on left side
STA $2001

Forever:
JMP Forever ;jump back to Forever, infinite loop, waiting for NMI ;titlescreen will keep looping untill start is pressed !?

NMI:
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

MAINGAME:


LDA $2002
LDA #$20
STA $2006 ; write the high byte of $2000 address
LDA #$00
STA $2006 ; write the low byte of $2000 address

LDA #low(background)
STA MAINGAMELow
LDA #high(background)
STA MAINGAMEHigh

rest of the code .........
im a newbie,lets see how far i can get
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Need some direction with multiple backgrounds

Post by dougeff »

I see you're taking the "all in the NMI" approach, yet i see no button reads in the NMI, and so this will almost certainly never read the Start button.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

That will not work (EDIT: I'm talking about log in's code here), because you're reading the controller only once, during the initialization of the title screen. You need to read it every frame, either inside the forever loop or in the NMI handler (which fits the Nerdy Nights way better), places where the frame logic can be. Apparently you created different initialization steps for the different program modes (title screen, main game), but you didn't do anything about the logic or vblank handling (besides moving the title screen frame logic to its initialization code, which is wrong).

The vblank handling can be unified for all program modes if you want, if you use generic VRAM update lists and the like. The frame logic however is different for each mode (in the title screen, the logic is to read the controller and change modes once start is pressed, in the main game, the logic is to move the game objects, check for collisions, etc.), and you must run the appropriate logic block for the current program mode every frame. My suggestion was to set up a pointer to the respective frame logic code in the initialization code of each program mode, and JMP to the the address indicated by that pointer every frame, after the common PPU updates are done.
Last edited by tokumaru on Fri Sep 18, 2015 2:17 pm, edited 1 time in total.
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Need some direction with multiple backgrounds

Post by dougeff »

tokumaru, that second paragraph isn't 100% clear. Maybe you could write some pseudo-code to better demonstrate what you mean?
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

dougeff wrote:tokumaru, that second paragraph isn't 100% clear. Maybe you could write some pseudo-code to better demonstrate what you mean?
Just to clarify, when I said "That will not work" I was referring to log in's post, not yours. :) Sorry for not making that clear.

What I just described is exactly what's in the code I posted before though. The Initialization code of each program mode sets up a pointer to the vblank handler and frame logic of that program mode, and the NMI handler simply JMPs to that address. Of course you could do some tasks that are common to all program modes before JMPing to the custom NMI code if you wanted to, like a generic vblank handler. In that case, the custom code would be just the frame logic.
User avatar
log in
Posts: 72
Joined: Tue Jun 24, 2008 1:06 pm
Location: neverland

Re: Need some direction with multiple backgrounds

Post by log in »

Thanks again guys.
Seems like i got a couple of nmi methods mixed up.
So i'm going with the nerdy nights set up that looks like what tokumaru told.

variables :gamestate / backgound high+low
constands : statetitle / stateplaying /stategameover

;;; regulair setup

after the palettesloop

; get starting gamestate
LDA satetitle
STA gamestate


2000 and 2001 set up
JMP Forever
NMI

ppu cleanup 2000, 2001 and 2005

read controller

game engine:

Titlestate
code comes here

playingstate
code comes here

gameoverstate
code comes here

RTI


I think this will work and btw this is a rough code.
The only thing that i can't figure out is the background.

do i really have to start that backgroundloop every time ? And how does the code know what is background 1 and 2.
Another question i have is how many backgrounds can my code handle ? The rows of .db $24 that is.
im a newbie,lets see how far i can get
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Need some direction with multiple backgrounds

Post by dougeff »

how does the code know what is background 1 and 2.
You give each data set a label. The label translates to a starting address. You can then have a list of pointers to the background labels. Then you would have a variable that keeps track of the current game state (title, game, end). So you would index with this variable to the list of pointers, and write them to a zero page # and #+1, then you use that as an indirect index to the data set. (#), y.

The 'load background' routine itself would be a generic one that can load every background, and called when needed.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

Now you got the right idea.
log in wrote:do i really have to start that backgroundloop every time ?
Do you mean having multiple loops for drawing the background because you have different backgrounds? No, you can have a single subroutine to do this, and index the different backgrounds, like dougeff suggested. Then you can just JSR to this routine whenever you need, passing the index of the background you want to draw as a parameter.
And how does the code know what is background 1 and 2.
Indexing something means giving it a number. When we're talking about blocks of data that can be read, that means building a table containing the addresses of all the different blocks of data. Like this:

Code: Select all

BackgroundPointer:
.dw TitleScreenBackground ;0
.dw GameOverBackground ;1
.dw GameplayCaveBackground ;2
.dw GameplaySnowBackground ;3
.dw GameplayWaterBackground ;4
(...)
Then you can simply do this when you need to draw the cave background:

Code: Select all

lda #2 ;index of the cave background
jsr DrawBackground
The subroutine that draws backgrounds will then look something like this:

Code: Select all

DrawBackground:
asl ;multiply the index by 2 because each address is 2 bytes
tax
lda BackgroundPointers+0, x ;copy the pointer to zero page
sta TempPointer+0
lda BackgroundPointers+1, x
sta TempPointer+0
lda #$20 ;set the destination address
sta $2006
lda #$00
sta $2006
ldx #$04 ;copy 4 x 256 = 1024 bytes
ldy #$00
CopyByte:
lda (TempPointer), y
sta $2007
iny
bne CopyByte
inc TempPointer+1
dex
bne CopyByte
rti ;return
Another question i have is how many backgrounds can my code handle ? The rows of .db $24 that is.
A full NT + AT is 1024 bytes, or 1KB. If you store this data raw, without any compression, an NROM (mapper 0) cartridge, which has at most 32KB of PRG-ROM, would be able to store 32 screens, if you didn't have anything else in the ROM (which is obviously not the case). Yes, raw backgrounds need a lot of space, which is why people normally resort to compression.

To compress backgrounds, you have to create a tool (or use someone else's) to proccess the data and include the resulting binary file in your program, instead of using .db statements. Alternatively, you could use a simple compression format like RLE or meta tiles, that are simple enough to still be encoded by hand, even though that's a bit tedious, and keep using .db. Then you need to modify the DrawScreen function to decompress the data while copying it to VRAM.
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Need some direction with multiple backgrounds

Post by dougeff »

Double check your code, tokumaru.

One of those "sta TempPointer+0" looks wrong.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Need some direction with multiple backgrounds

Post by tokumaru »

Oh, your right. I'll just pretend it was intentional... you know, to prevent mindless copying & pasting. :mrgreen:

BTW, I like to use "+0" like this when working with 16-bit variables, even though it doesn't do anything, just to make it clear that it's a 2-byte variable. This is particularly helpful when you manipulate only the lower byte, for whatever reason.
Post Reply