Loading the next screen

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

Post Reply
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Loading the next screen

Post by Roth »

Hello all.

I've been trying to implement using the Start button to switch screens, to simulate going from a title screen, to the actual game area. I'm wondering if anyone knows exactly what would cause the second screen to just be a jumbled mess? Mind you that I use the same nametable to load the second screen. When I used the other to preload that second screen, it still gets jumbled after pressing Start. Here's the ROM if you would like to see what I mean:

http://roth.zhxhome.net/junk/NCAT.NES

If anyone could let me know what could be causing it, then I can check out my code and see if there's something similar to what is described. Thanks.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

The next screen seems like a lot of new data. Are you disabling rendering before drawing the second screen? If by any chance you are trying to draw it during rendering time, you're sure to get garbage on the screen.

As for the start button, you should do that trick that will only detect keys that have changed since the last read, because when you hold the start button down for a while, the next screen is reloaded an insane ammount of times. The trick goes something like this (supposing you use a byte to store the status of all 8 possible buttons): EOR the byte holding the old states with the one holding the new states. Whatever buttons changed will have 1's, the ones that remain the same will be 0's. AND that to the new byte, so that what CHANGED AND IS PRESSED NOW will be 1's, and everything else will be 0's. This way you'll only detect the moment when the button is pressed (transition), wich is the desired behaviour of the start button.
User avatar
Quietust
Posts: 1786
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Post by Quietust »

As for reloading an entire nametable, you pretty much have 2 options - either disable rendering and load it all at once, or load it a little bit at a time (during VBLANKs) into an alternate nametable and then switch it in when you're done.
From what I've seen, though, most games don't disable the screen to reload nametables - they'll turn the palette black and then queue up a screen's worth of data to be updated during VBLANKs.
You may find it useful to code a general-purpose I/O queue for dispatching VRAM updates during VBLANK and splitting them into sufficiently small chunks so as to avoid overflowing into rendering. I have some code that does exactly this (for Drip, an Amiga game I'm porting* to the NES) - if you're interested, I'll post it here (it's reasonably small, but fairly versatile).


(* not so much "porting", moreso "completely rewriting it from scratch to work in mostly the same way")
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Post by Roth »

tokumaru wrote:As for the start button, you should do that trick that will only detect keys that have changed since the last read, because when you hold the start button down for a while, the next screen is reloaded an insane ammount of times. The trick goes something like this (supposing you use a byte to store the status of all 8 possible buttons): EOR the byte holding the old states with the one holding the new states. Whatever buttons changed will have 1's, the ones that remain the same will be 0's. AND that to the new byte, so that what CHANGED AND IS PRESSED NOW will be 1's, and everything else will be 0's. This way you'll only detect the moment when the button is pressed (transition), wich is the desired behaviour of the start button.
Thanks for the tip on that. I noticed when I held down Start that it just stayed in place, then upon release loaded the garbage. I think I've seen some code like what you described before. I'll check out a few sources and see what's going on in them.

Queitust wrote:As for reloading an entire nametable, you pretty much have 2 options - either disable rendering and load it all at once, or load it a little bit at a time (during VBLANKs) into an alternate nametable and then switch it in when you're done.
From what I've seen, though, most games don't disable the screen to reload nametables - they'll turn the palette black and then queue up a screen's worth of data to be updated during VBLANKs.
You may find it useful to code a general-purpose I/O queue for dispatching VRAM updates during VBLANK and splitting them into sufficiently small chunks so as to avoid overflowing into rendering. I have some code that does exactly this (for Drip, an Amiga game I'm porting* to the NES) - if you're interested, I'll post it here (it's reasonably small, but fairly versatile).
I tried disabling, then running the code, then enabling. It did indeed draw the screen MUCH better, but the upper-left corner was still garbage. I'll play with the code a little more and see if I can get the code shorter, as it seems like I must be stepping on something I shouldn't have been... and sure, I'd like to see the code for that you have. It's always nice to see how someone implements something.

Thanks for the replies :)
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Top left corner being garbage means that you need to either turn sprite display off or put all the unused sprites offscreen (y attribute value between $EF and $FF).
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Post by Roth »

Tepples, you are certainly the man. I made a seperate subroutine for turning on the PPU, but without the sprites on... this made it work perfect :) Thanks. Now to get to the controller polling. Thanks again all.
User avatar
commodorejohn
Posts: 193
Joined: Mon Sep 11, 2006 6:48 pm
Location: Moose Lake, Minnesota

Post by commodorejohn »

Another option for garbage sprites is to turn on sprite clipping in the leftmost 8 pixels and then put all the sprites at X location 0.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

commodorejohn wrote:Another option for garbage sprites is to turn on sprite clipping in the leftmost 8 pixels and then put all the sprites at X location 0.
You won't get garbage sprites like this, sure, but remember that the NES can only display 8 sprites on a scanline. Those sprites hidden in the left 8 pixels still count for this limit, so you'd be lowering your displayed sprite count if you hid them like this. If many of them occupy the same scanlines, you may even be blanking the sprites for those entire scanlines, depending on sprite priorities.

Always hide unused sprites at the bottom of the screen, where it doesn't matter if they occupy the same scanlines.
User avatar
Memblers
Site Admin
Posts: 3901
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

I previously used to just set all their tiles #s to a blank tile. It probably is better to set the Y positions below the screen though, that possibly would've hidden a (fairly rare) sprite display bug that shows up in Munchie Attack.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Yes, left clipping isn't the way to go, especially that in some case you'd want to not have the clipping enabled.
Putting all sprite vertical range of $f0 or above will NEVER have them to be even considered by the PPU, making them disabled. All other tricks may hide the sprites to the user, but those are still tricks and technically, the sprites will be displayed.
Useless, lumbering half-wits don't scare us.
User avatar
Quietust
Posts: 1786
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Post by Quietust »

A ways up, I said I would post my "VRAM update queue" code. I suppose I can do that now.

One limitation is that it does not support "vertical" writing (i.e. using the "increment VRAM address by 32" option), but that's mainly because I don't use it.

Code: Select all

;Variables - these ones are best located in zeropage
ioptr:		.res 2
addxy:		.res 1

splitiobank:	.res 1
splitiolen:	.res 2
splitiofrom:	.res 2
splitioto:	.res 2
splitiocur:	.res 2

io_nextbank:	.res 1
io_nextlen:	.res 1
io_nextfrom:	.res 2
io_nextto:	.res 2

;More variables - these can go elsewhere in RAM
io_bank:	.res 32
io_len:		.res 32
io_from:	.res 64
io_to:		.res 64
io_reqi:	.res 1
io_reqo:	.res 1

splitio_tmp:	.res 1

;Macros

.define	PLO(ptr) ptr+0
.define	PHI(ptr) ptr+1

.macro	clr_pointer	dest
	LDA #$00
	STA dest+0
	STA dest+1
.endmacro

.macro	copy_pointer	src, dest
	LDA PLO src
	STA PLO dest
	LDA PHI src
	STA PHI dest
.endmacro

.macro	load_pointer_x	src, dest
	LDA PLO src,X
	STA PLO dest
	LDA PHI src,X
	STA PHI dest
.endmacro

.macro	save_pointer_x	src, dest
	LDA PLO src
	STA PLO dest,X
	LDA PHI src
	STA PHI dest,X
.endmacro

;Code

;*** Waits for a bulk I/O request to clear.
.proc waitio
	PHA
:	LDA io_reqi
	CMP io_reqo
	BNE :-
	PLA
	RTS
.endproc

;*** Queues a bulk VRAM update - as big as you want. This is the main routine you use.
;splitiofrom - where you want to copy memory from
;splitiolen - the length of the block you want to copy
;splitiobank - PRG bank in which the data resides (optional)
;splitioto - the VRAM address you want to copy the data to
.proc splitio
	clr_pointer splitiocur
:	SEC
	LDA PLO splitiolen
	SBC PLO splitiocur
	STA splitio_tmp
	LDA PHI splitiolen
	SBC PHI splitiocur
	BNE :+
	LDA splitio_tmp
	CMP #$40
	BCC :++
:	LDA #$40
:	STA io_nextlen
	LDA splitiobank
	STA io_nextbank
	copy_pointer splitiofrom, io_nextfrom
	copy_pointer splitioto, io_nextto
	JSR iorequest

	LDA #$40
	LDX PHI splitiofrom
	LDY PLO splitiofrom
	JSR add_xy_a
	STX PHI splitiofrom
	STY PLO splitiofrom

	LDX PHI splitioto
	LDY PLO splitioto
	JSR add_xy_a
	STX PHI splitioto
	STY PLO splitioto

	LDX PHI splitiocur
	LDY PLO splitiocur
	JSR add_xy_a
	STX PHI splitiocur
	STY PLO splitiocur

	LDA PHI splitiocur
	CMP PHI splitiolen
	BCC :---
	LDA PLO splitiocur
	CMP PLO splitiolen
	BCC :---
	RTS

add_xy_a:
	STA addxy
	TYA
	CLC
	ADC addxy
	TAY
	TXA
	ADC #$00
	TAX
	LDA addxy
	RTS
.endproc

;*** Resets the I/O queue, in case you need to do something like that
.proc ioreset
	LDA #$00
	STA io_reqi
	STA io_reqo
	RTS
.endproc

;*** Used by splitio, queues a single 64-byte VRAM transfer
.proc iorequest
	PHA
	TXA
	PHA
	LDX io_reqi
	LDA io_nextlen
	STA io_len,X
	LDA io_nextbank
	STA io_bank,X
	TXA
	ASL A
	TAX
	save_pointer_x io_nextfrom, io_from
	save_pointer_x io_nextto, io_to
	INX
	INX
	TXA
	LSR A
	AND #$1F
	STA io_reqi
	PLA
	TAX
	PLA
	RTS
.endproc

;*** Called during VBLANK as part of your NMI routine, dispatches one VRAM transfer
.proc iodispatch
	LDY io_reqo
	CPY io_reqi
	BEQ :++
	TYA
	ASL A
	TAX
	load_pointer_x io_from, ioptr
	LDA PHI io_to,X
	STA $2006
	LDA PLO io_to,X
	STA $2006
	LDA io_bank,Y
	STA MAP_PRG      ;this is your mapper's PRG banking register
	LDA io_len,Y
	TAX
	LDY #$00
:	LDA (ioptr),Y
	STA $2007
	INY
	DEX
	BNE :-
	LDY io_reqo
	INY
	TYA
	AND #$1F
	STA io_reqo
:	RTS
.endproc
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
Post Reply