Loading the next screen
Moderator: Moderators
Loading the next screen
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.
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.
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.
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.
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")
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.
P.S. If you don't get this note, let me know and I'll write you another.
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.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.
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.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).
Thanks for the replies
- commodorejohn
- Posts: 193
- Joined: Mon Sep 11, 2006 6:48 pm
- Location: Moose Lake, Minnesota
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.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.
Always hide unused sprites at the bottom of the screen, where it doesn't matter if they occupy the same scanlines.
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.
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.
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.
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.
P.S. If you don't get this note, let me know and I'll write you another.