I was wondering if you guys could give me some direction on updating name tables for the storyline that I want to be seen before the main title screen of the game is displayed. Can I populate the name table, wait for the user to press A (once they've read the information), turn off the PPU, update the name table with the new information, wait for the player to press A, etc,etc. Is that doable and a proper way of updating the name table? What do you guys suggest.
Thanks.
Personally I think the best way is when it runs on it's own, but the player can press A to skip the delays.
The way you want to do it sounds fine. I would add "wait for vblank" in there.
if user presses A:
wait for vblank
disable PPU
draw screen
wait for vblank
enable PPU
If you don't wait before turning it on, the screen will jump.
It's also kinda fun to have delays between writing the characters of the text.. I did that in an unreleased thing and it was pretty cool. I made a little table of subtly varying delays it goes through, so it looks kinda like someone is typing it. heheh
Fade-ins and fade-outs would be nice too... Combined with progressive drawing of the screen (something like Memblers said) it'll look damn good. You could, for example, fade-in each line of text at a time, from top to bottom (if it's just text). Then fade-out everything at once so you can fade-in the next screen the same way.
Memblers wrote:
If you don't wait before turning it on, the screen will jump.
When I wait for vblank before turning the screen back on the main window of nintendulator doesn't show my background. However, the name table screen, palette, and pattern tables all hold the correct information. Here is what I have ... probably did something stupid...
Code:
lda #$21
sta $2006
lda #$40
sta $2006
ldx #$00
scene_one_loop:
lda scene_one_data, X
sta $2007
inx
cpx #$0A
bne scene_one_loop
jsr wait_for_vblank
lda #%10001100
sta $2000
lda #%00011010
sta $2001
....
wait_for_vblank:
lda nmihit
beq wait_for_vblank
lda #$00
sta nmihit
rts
....
nmi:
lda #%10001100
sta $2000
lda #$01
sta nmihit
lda #$00
sta $2005
sta $2005
Quote:
It's also kinda fun to have delays between writing the characters of the text.. I did that in an unreleased thing and it was pretty cool. I made a little table of subtly varying delays it goes through, so it looks kinda like someone is typing it. heheh
Are you just counting frames for your delays between text?
tokumaru wrote:
Fade-ins and fade-outs would be nice too... Combined with progressive drawing of the screen (something like Memblers said) it'll look damn good. You could, for example, fade-in each line of text at a time, from top to bottom (if it's just text). Then fade-out everything at once so you can fade-in the next screen the same way.
Yeah fade-ins and fade-outs do sound really cool. I'm not sure where to start especially since I can't get my darn background to display properly
I don't think you should wait for VBlank and use NMI's. Maybe you should use the good old "wait for VBlank" in the intro, and use NMI's in the game itself. Of course you could use the NMI for everything, just jump to the desired routine (depending on what part of the game you are - title screen, intro, in game) in the begining of it (you can use a jump table for that).
As for the fade-ins and outs, I think you can mess with the high digit of each color. When the high digit is at it's maximum allowed value (3), colors are very bright, and when it's 0, the colors are very dark. For a fade-in from black, right after the black screen you can set the palette with the desired hues, but all with low brightness (high digit 0). Then, every few frames (or it'll be too fast, the NES does not have many colors), you increase the brightness of the colors until you reach the desired brightness of each one. Of course, some colors will get there first (the darkest ones), but I don't think that'll ruin the effect.
tokumaru wrote:
I don't think you should wait for VBlank and use NMI's.
It looks like he's doing it the right way (that is, he's not polling $2002). He's just waiting for an NMI to trip and set his flag in RAM -- nothing wrong with that.
Disch wrote:
It looks like he's doing it the right way (that is, he's not polling $2002). He's just waiting for an NMI to trip and set his flag in RAM -- nothing wrong with that.
I only polled $2002 @ startup (polled, cleared OAM, polled). Everything else after I used the flag in RAM.
Oh, yeah, sorry, I missed that. Yes, this is a perfectly valid way to wait for Vblank.
This is what I get from taking a quick scan over the code! Sorry lynxsolaris, you got it right. Have you found what the problem is yet?
tokumaru wrote:
Oh, yeah, sorry, I missed that. Yes, this is a perfectly valid way to wait for Vblank.
This is what I get from taking a quick scan over the code! Sorry lynxsolaris, you got it right. Have you found what the problem is yet?
Yes I did. Thanks for asking. However, using the code I posted above, my tiles show up @ the top of the screen (instead of $2140 which is where I'm setting my name table to). It acts as if I'm setting the NT to $2000. The funny thing is in nintendulator's name table viewer shows the tiles show up in the right position. Just not in the actual execution window. It's probably something I'm overlooking ... just good to have another pair of eyes to see it.
Thanks.
lynxsolaris wrote:
Are you just counting frames for your delays between text?
Yep.
Quote:
Yes I did. Thanks for asking. However, using the code I posted above, my tiles show up @ the top of the screen (instead of $2140 which is where I'm setting my name table to).
You need to reset the $2006 register after writing also. Set it to $0000 or $2000. Usually I do that in the NMI code, but then you need to be careful when doing VRAM updates outside of the NMI routine (disable NMI first, or have that part of NMI skipped, so it doesn't interrupt your writing).
Otherwise it draws the background starting at whatever address $2006 was left at.
Memblers wrote:
You need to reset the $2006 register after writing also. Set it to $0000 or $2000.
Isn't setting $2005 correctly (as he does in the NMI routine) enough?
Setting PPUSCROLL ($2005) and PPUCTRL ($2000) is enough, but you need to do it after drawing, at the end of your vblank code, just before you turn rendering back on in PPUMASK ($2001). Don't do it in your NMI handler unless your NMI handler is handling all the drawing.
tokumaru wrote:
Memblers wrote:
You need to reset the $2006 register after writing also. Set it to $0000 or $2000.
Isn't setting $2005 correctly (as he does in the NMI routine) enough?
Nope. IIRC, the vertical scroll value doesn't take effect until the 2nd $2006 write.
In programs that change vertical scrolling mid-screen you'll see writes like this (ignoring loads):
Code:
sta $2006
sta $2005
sta $2005
sta $2006
Yes, midscreen. I was absolute sure that during VBlank, the 2 writes to $2005 would do the job. I'm not saying they do, but I really thought they did.
I always thought so also, until I did the mid-screen stuff later. But I guess since I always had writes to $2006 in my NMI, I wouldn't have noticed it (or emulators back then would've displayed wrongly).
Strange... and loopy's doc would seem to confirm that the writes to $2005 are enough. Look:
Quote:
2000 write:
t:0000110000000000=d:00000011
2005 first write:
t:0000000000011111=d:11111000
x=d:00000111
2005 second write:
t:0000001111100000=d:11111000
t:0111000000000000=d:00000111
frame start (line 0) (if background and sprites are enabled):
v=t
The writes to $2005 plus the write to $2000 should be enough to set everything correctly, according to what happens when the frame starts. Unless loopy is wrong, but people have been using this document as some sort of bible for a while now...
Quote:
frame start (line 0) (if background and sprites are enabled):
v=t
Ah, I guess I didn't consider that part. You're right. $2006 still does need to be reset sometime, though (just after writing $2007 would be fine then).
$2006 doesn't need to be reset if you write $2000 and $2005+$2005 (during VBLANK, of course), since it affects the same internal register.
Game loop should look like this:
Code:
while (1) {
end of frame
NMI
turn off rendering in PPUMASK ($2001)
write to VRAM using PPUADDR ($2006) and PPUDATA ($2007)
set PPUCTRL ($2000) and PPUSCROLL ($2005)
turn on rendering in PPUMASK
start of frame
}
Make sure that the writes to PPUSCROLL and PPUCTRL happen
after the last access to PPUADDR or PPUDATA.
I forgot to set $2006 back to $0000 after drawing to the screen (as Memblers said). That corrected my issue. I decided to do the write right after the drawing and not in NMI so that I don't encounter any unforeseen (to me) issues with that write in the future.
EDIT: Also, as far as giving the delay between text goes, whats a good number of frames to wait before "typing" the next word?
Depends. Human speech runs at roughly 120 to 140 words per minute, or 26 to 30 frames per word. A word is about four to five letters plus space, or roughly 5 frames per character.
Watch TV with captions to get some ideas.
lynxsolaris wrote:
Also, as far as giving the delay between text goes, whats a good number of frames to wait before "typing" the next word?
You should experiment and find out. I guess it depends on dramatic effect, length, or whatever. You can estimate it by knowing that 60 frames is one second. Just don't do it like Ikari Warriors 2, heheh. That had to be the slowest intro ever.
If it helps any (when you need the same number in multiple places, it sure does), you can do stuff like this:
Code:
delay_time = 15
..
ldx #delay_time
Or if you're making an RPG or another game with a metric backsideload of text, put in user-selectable delay as in Actraiser for Super NES.
lynxsolaris wrote:
I forgot to set $2006 back to $0000 after drawing to the screen (as Memblers said). That corrected my issue.
Let me just ask you one more thing about this: were you using $2006 after you wrote 0's to $2005? The writes to $2005 (and to $2000) should be enough to keep the screen in the correct place, as long as you do them after you're done with $2006. I just wanna get this right... something is weird...
I usually write to $2000 last, after set two $2005 writes. Normally it wouldn't change anything if you do it the other way arround, but normally $2006 shouldn't be used in VBlank to set the scrolling, but only outside of VBlank, because all vertical scroll $2005 writes will be ignored, allowing you to scroll only horizontally.
As for text speed, I recommand it between 2 letters per frame and one letter for 3 frames, wich is the slowest you'd want to agrue. I hate too slow text !! I prefer when the text goes too fast, but let the user wait and press the 'A' button to say if he's finished reading. If you want an intro that doesn't need player's intevention, go for upload one letter each 2 frames or 3 frames. That should be slow enough for slow readers, but supportable for fast reader.
I now remember a game that have an awfully slow text intro : Final Fantasy Legend 3 for the Game Boy. Just Breed's intro was also a bit annoying I found.
Ok I'm having a problem getting my text delays to work properly. This is what I did:
Code:
wait_again:
lda text_flag
cmp #$99
bne wait_again
..
nmi:
..
inc text_flag
..
My wait_again label is in between text prints. It displays both(all i have right now) characters @ once. Also, I'm doing all my text displays in my name tables and not from OAM. Is that okay? Or do I just have this whole thing screwed up??
Thanks again!
The code snippet doesn't show enough to tell anything. I don't see it resetting the frame counter, or the VRAM buffering and writing.
It looks like you're making it get stuck in that loop. That's OK though if it has nothing to do besides wait, but normally I would branch past the "=99 frames" code so other stuff can be put in the loop if needed.
Writing to the background rather than using OAM is the natural thing to do. OAM would be easy to use though if you only want 8 chars per line (really limiting for text).
Memblers wrote:
The code snippet doesn't show enough to tell anything. I don't see it resetting the frame counter, or the VRAM buffering and writing.
Sorry. Here is more of the code:
Code:
.zp
text_flag = $00
...
reset:
sei
cld
...
ldx #$ff
txs
inx
stx $2000
stx $2001
stx text_flag
jsr wait_vblank
jsr wait_vblank
;load color palette first
lda #$3F
sta $2006
lda #$00
sta $2006
ldx #$00
loop_pal:
lda color_pal,X
sta $2007
inx
cpx #$10
bne loop_pal
; set the VRAM address
lda #$20
sta $2006
lda #$84
sta $2006
; text demo will display "A(0B) B(0C)"
lda #$0B
sta $2007
lda #$00
sta $2006
sta $2006
lda #%00001000
sta $2000
lda #%00011000
sta $2001
wait_again:
lda text_flag
cmp #$99
bne wait_again
lda #$00
sta text_flag
lda #$00
sta $2000
sta $2001
lda #$20
sta $2006
lda #$85
sta $2006
lda #$0C
sta $2007
lda #$00
sta $2006
sta $2006
lda #%00001000
sta $2000
lda #%00011000
sta $2001
text_end:
jmp text_end
wait_vblank:
bit $2002
bpl wait_vblank
rts
color_pal:
.db $0D,$30,$22,$16,$0D,$00,$10,$30,$0D,$00,$16,$30,$0D,$3C,$1B,$09
nmi:
lda #%10001000
sta $2000
inc text_flag
...
With this code, A displays but B never does....
Thanks for the help.
[/code]
Looks to me like the NMI code will never run. The only place it gets enabled is.. inside the NMI routine itself.
Memblers wrote:
Looks to me like the NMI code will never run. The only place it gets enabled is.. inside the NMI routine itself.
When I enable NMI the "A" just flashes and "B" never appears.
Sounds odd. I'm not sure what's going on then. Posting the new code (or editing the previous post with it) might shed some light (include the full NMI routine also).
Often a good thing to do in cases like this is step through the code with a debugger and see what happens there.
Memblers wrote:
Sounds odd. I'm not sure what's going on then. Posting the new code (or editing the previous post with it) might shed some light (include the full NMI routine also).
Memblers,
Here is the code in its entirety:
Code:
.inesprg 1
.ineschr 1
.inesmap 0
.inesmir 1
.zp
text_flag = $00
.bss
.code
.org $8000
.bank 0
reset:
sei
cld
ldx #$40
stx $4017
ldx #$ff
txs
inx
stx $2000
stx $2001
ldx #$99
stx text_flag
jsr wait_vblank
jsr wait_vblank
;load color palette first
lda #$3F
sta $2006
lda #$00
sta $2006
ldx #$00
loop_pal:
lda color_pal,X
sta $2007
inx
cpx #$10
bne loop_pal
lda #$20
sta $2006
lda #$84
sta $2006
; text demo will display "A(0B) B(0C)"
lda #$0B
sta $2007
lda #$00
sta $2006
sta $2006
lda #%10001000
sta $2000
lda #%00011000
sta $2001
wait_again:
lda text_flag
cmp #$00
bne wait_again
lda #$99
sta text_flag
lda #$00
sta $2000
sta $2001
lda #$20
sta $2006
lda #$85
sta $2006
lda #$0C
sta $2007
lda #$00
sta $2006
sta $2006
lda #%10001000
sta $2000
lda #%00011000
sta $2001
text_end:
jmp text_end
wait_vblank:
bit $2002
bpl wait_vblank
rts
color_pal:
.db $0D,$30,$22,$16,$0D,$00,$10,$30,$0D,$00,$16,$30,$0D,$3C,$1B,$09
nmi:
lda #%10010000
sta $2000
dec text_flag
int:
rti
.bank 1
.org $fffa
.dw reset,nmi,int
.bank 2
.org $0000
.incbin "text.chr"
Thanks for your help.
Your NMI routine modify the content of the accumulator. You should save at least your accumulator on the stack and I'd reccomand to save X and Y too, in case you modify the NMI routine to use code with the use of X and Y registers, then restore all these register at the end of the NMI routine, in order to not have the accumulator randomly changed in the main code by the interrupt, that can happen ANYTIME you enabled it.
The cmp #$00 instruction is useless, because when loading the value in the accumulator, the Z flag is automatically set if the value loaded is zero, so you can just delete this line and this won't affect anything, saving you two bytes.
I have no idea why you write twice to $2006 after writing one name table byte, you'd better write twice to $2005 in order to proprely reset the scrolling registers.
Also, I'd put a value lower than $99 in your wait timer, because $99 is several second long and you would want less time as far as I understand.
I'd also do a decent programm that writes the content of a text buffer to the screen with VBlank interval and a decent programm that fill the buffer with $0b, $0c, $00 (for example, assuming that $00 marks the end) and call for the first routine. This would structure your code in a decent way and make your life easier.
Bregalad wrote:
The cmp #$00 instruction is useless, because when loading the value in the accumulator, the Z flag is automatically set if the value loaded is zero, so you can just delete this line and this won't affect anything, saving you two bytes.
Yeah, that was dumb of me.
Quote:
I have no idea why you write twice to $2006 after writing one name table byte, you'd better write twice to $2005 in order to proprely reset the scrolling registers.
I've been doing this because its the only way I could get my character to show up @ all..... I
MUST be doing something wrong. $2005x2 writes aren't working for me. With
just writes to $2005 my "A" (or "B") isn't displayed.
Quote:
Also, I'd put a value lower than $99 in your wait timer, because $99 is several second long and you would want less time as far as I understand.
I put it that high just so I'd actually see it happen. See the code I posted in this thread is just test code. I wouldn't actually do it exactly like that in my real code. But my problem here is that "B" is never displayed. It's like I never get past the
wait_again loop.
Quote:
I'd also do a decent programm that writes the content of a text buffer to the screen with VBlank interval and a decent programm that fill the buffer with $0b, $0c, $00 (for example, assuming that $00 marks the end) and call for the first routine. This would structure your code in a decent way and make your life easier.
Yeah, again, just test code
Quote:
Your NMI routine modify the content of the accumulator. You should save at least your accumulator on the stack and I'd reccomand to save X and Y too, in case you modify the NMI routine to use code with the use of X and Y registers, then restore all these register at the end of the NMI routine, in order to not have the accumulator randomly changed in the main code by the interrupt, that can happen ANYTIME you enabled it.
Do you think this is why I'm having problems?
I'm not sure, because when NMI is SUPPOSED to hit, you're just looping if a is nonzero, and if A contain the value written to $2000 instead, the branch happen anyways, so I'm unsure if your problems are caused by this or not, but anyway you must save all register you modify in your NMI routine, just because it doesn't always hit when it is supposed too, and that shouldn't crash your programm.
I don't see any other error, but maybe by doing a more decent structured programm that doesn't write the hard-wired letter "A" and "B", but that can write any message and another that order it to write "AB", then you must have less problems figuring your bugs. I'd highly reccomand directly tring to code the routine you want to be coded, even for test purposes.
Oh by the way you souldn't use $0d as black, but $0e or $0f because $0d will output noncorrect color voltage to the TV. This shouldn't cause problems with emulators, tough.
Finally, you should "bit $2002" in your NMI, to clear the VBlank flag, to avoid another NMI to be triggered as soon as the first one exits.
Bregalad wrote:
Finally, you should "bit $2002" in your NMI, to clear the VBlank flag, to avoid another NMI to be triggered as soon as the first one exits.
That is incorrect - the
only thing that would cause a repeat NMI in such a circumstance is if you were to clear and then re-set the NMI enable flag in $2000. Remember, NMIs are
edge-triggered, unlike IRQs (which are level-triggered).
Quote:
Remember, NMIs are edge-triggered, unlike IRQs (which are level-triggered).
For some reasong I was under the beliving of the other way arround.
So yeah, I still have the habbit to use $2002 at the begining of VBlank, but this may not be necessary.
I would still recommend doing the "bit $2002" in NMI. If nothing else it will reset the $2005/$2006 toggle and clear the VBlank flag so that if you do happen to disable and re-enable NMIs in the same VBlank, you won't run into a problem.
Quietust wrote:
That is incorrect - the only thing that would cause a repeat NMI in such a circumstance is if you were to clear and then re-set the NMI enable flag in $2000. Remember, NMIs are edge-triggered, unlike IRQs (which are level-triggered).
Disch wrote:
I would still recommend doing the "bit $2002" in NMI. If nothing else it will reset the $2005/$2006 toggle and clear the VBlank flag so that if you do happen to disable and re-enable NMIs in the same VBlank, you won't run into a problem.
so bit $2002 in NMI or no?
Bregalad wrote:
you must save all register you modify in your NMI routine, just because it doesn't always hit when it is supposed too, and that shouldn't crash your programm
I haven't done much with pushing or pulling off the stack yet. If I save my values in NMI do I need to restore them once out of NMI?
Does anyone else have any suggestions for me? Thanks for the help thus far.
lynxsolaris wrote:
so bit $2002 in NMI or no?
You have nothing to lose by doing it other than the 4 cycles it takes. And while it isn't necessarily manditory, it sure can't hurt, and possibly will prevent future potential problems.
So yes, do it.
Q was only saying that the NMI won't reoccur on RTI if you don't do it -- he never said it shouldn't be done. (He was merely making a technical correction -- not really offering advice).
lynxsolaris wrote:
I haven't done much with pushing or pulling off the stack yet. If I save my values in NMI do I need to restore them once out of NMI?
Well if you don't restore them, what's the point of saving them? :P
The reason A/X/Y registers are often backed up and restored in NMI/IRQ routines is because they are not automatically backed up by the interrupt. If the interrupt cut into code that was executing -- that code would could get REALLY screwed up if A/X/Y suddenly changed on it.
For example, consider the following:
Code:
something_the_program_is_doing:
LDA #$05
CLC
ADC #$05
STA somewhere
RTS
nmi_routine:
LDA #$00
RTI
'somewhere' would be expected to have $0A written to it ($05 + $05). But what would happen if the NMI tripped right before that CLC? The executed code would flow something like the following:
Code:
LDA #$05
; **nmi occurs here, go to nmi vector**
LDA #$00
RTI ; return to previous routine
CLC
ADC #$05 ; 0 + 5 = $05, NOT $0A like it should be
STA somewhere ; somewhere is officially screwed
If such an interruption occurs during program critical calculations, it's very possible for such an occurance to completely crash and burn the program. However if you save and restore A/X/Y in interrupts, such problems are avoided because none of the registers change and the code being interrupted has no idea its been interrupted (a good thing).
Though now... you're typically enabling NMIs and just waiting around for them to happen because your program isn't complicated. So you probably don't NEED to backup your registers -- however it's one of those things like the $2002 thing. It can't hurt, and it costs nothing aside from a handful of CPU cycles.
So yeah... do it. Every NMI, every IRQ. Backup and restore A/X/Y.
lynxsolaris wrote:
I haven't done much with pushing or pulling off the stack yet. If I save my values in NMI do I need to restore them once out of NMI?
Just a quick note here: you have to restore them
before leaving the NMI, like, the last thing before the RTI. The way you said it looks like you want to restore them
after leaving the NMI, wich you can't, because you don't know where you'll be after returning.
tokumaru wrote:
lynxsolaris wrote:
I haven't done much with pushing or pulling off the stack yet. If I save my values in NMI do I need to restore them once out of NMI?
Just a quick note here: you have to restore them
before leaving the NMI, like, the last thing before the RTI. The way you said it looks like you want to restore them
after leaving the NMI, wich you can't, because you don't know where you'll be after returning.
Nah, I figured that much out on my own
As you just said, there isn't any way to tell where in the code NMI will occur. I was hoping that some of the tips above would help with my issue. Unfortunately, it didn't.
Ok, I've changed some of the code around. Here is the updated code:
Code:
.zp
text_flag = $00
.bss
.code
.org $8000
.bank 0
reset:
sei
cld
ldx #$40
stx $4017
ldx #$ff
txs
inx
stx $2000
stx $2001
ldx #$02
stx text_flag
lda #$3F
sta $2006
lda #$00
sta $2006
ldy #$00
loop_pal:
lda pal_colors,Y
sta $2007
iny
cpy #$10
bne loop_pal
lda #$21
sta $2006
lda #$84
sta $2006
lda #$0B
sta $2007
lda #$00
sta $2006
sta $2006
lda #%10001000
sta $2000
lda #%00011000
sta $2001
wait_again:
lda text_flag
bne wait_again
lda #$00
sta $2000
sta $2001
lda #$21
sta $2006
lda #$85
sta $2006
lda #$0C
sta $2007
lda #$00
sta $2006
sta $2006
ldx #%10001000
stx $2000
ldx #%00011000
stx $2001
nmi:
pha
txs
tya
pha
sec
lda text_flag
sbc #$01
sta text_flag
lda #$00
sta $2005
sta $2005
lda #%10001000
sta $2000
bit $2002
pla
tay
tsx
pla
rti
int:
rti
pal_colors:
.db $0E,$30,$22,$16,$0E,$00,$10,$30,$0E,$00,$16,$30,$0E,$3C,$1B,$09
.bank 1
.org $fffa
.dw reset,nmi,int
.bank 2
.org $0000
.incbin "text.chr"
"A" still shows and "B" does not. This is what appears in the debug window of nintendulator:
Code:
Invalid opcode $FF (ISB) encountered at $0000
Invalid opcode $03 (SLO) encountered at $0103
Invalid opcode $03 (SLO) encountered at $0903
and when the code is running (according to CPU Trace) it seems to just be sitting in
Code:
wait_again:
lda text_flag
bne wait_again
It seems to be that something is wrong in my NMI and my subtraction of the text_flag var.
Any ideas?
lynxsolaris wrote:
It seems to be that something is wrong in my NMI and my subtraction of the text_flag var.
Any ideas?
Yes.
Code:
nmi:
pha
txs
tya
pha
sec
lda text_flag
sbc #$01
sta text_flag
lda #$00
sta $2005
sta $2005
lda #%10001000
sta $2000
bit $2002
pla
tay
tsx
pla
rti
You do NOT want to be doing txs/tsx in your save/restore registers code - doing so modifies the
stack pointer, making it very easy to corrupt the interrupt's return address if you're not very careful (which, in this case, you are not).
Your NMI routine should read as follows:
Code:
nmi:
pha
txa
pha
tya
pha
(stuff)
pla
tay
pla
tax
pla
rti
Also, why did your remove your two wait for VBlank at startup ? There really was nothing wrong with it, but it is wrong that your removed it.
You must do your bit $2002 at the start of the NMI to reset the $2005/$2006 flip-flop.
As Quitetust stated, this is not a good idea to save X on the stack POINTER because this will effectivly save the X register, but will totally corrupt the stack, so Y will backup anywhere in the stack in function of the old value of X, and restore correctly, but the A register and the return adress WON'T return correctly, causing your programm to crash.
I think you misunderstood txs and tsx as being phx and plx, wich just doesn't exist.
On a side not, you should do a routine that fills at least one whole Name Table and Attribute Table (PPU $2000-$23ff) with known values. Typically, $00 is your "blank" tile, you should fill it wihth $00, using color $00 for the whole screen. Most emulators will do it automatically, but NOT the real hardware. Remember that is not because you didn't write the number of a tile in your programm that it will be blank on the screen.
Now you shouldn't have that much problems from here. If it still doesn't work, use FCE Ultra to trace your code.
I ran my code through nintendulator's and FCEU's debuggers. It seems that when the program reaches the first write to $2001 it jumps back up to $8000.
Code:
$8045:A9 00 LDA #$00
$8047:8D 06 20 STA $2006 = #$84
$804A:8D 06 20 STA $2006 = #$84
$804D:A9 88 LDA #$88
$804F:8D 00 20 STA $2000 = #$00
$8052:A9 18 LDA #$18 <-- Step stops here then just back to
sei @ $8000
$8054:8D 01 20 STA $2001 = #$00
$8057:AD 00 00 LDA $0000 = #$01
$805A:D0 FB BNE $8057
So I'm assuming thats why my "A" blinks because its hitting my wait for vblank routine (which I did put back in) over and over.... Anyone tell me why its jumping back up to $8000?
Thanks.
It jumps back to the reset address? Are your vectors set up correctly at the end of the ROM?
tokumaru wrote:
It jumps back to the reset address? Are your vectors set up correctly at the end of the ROM?
Yes, sir. Here is my setup:
Code:
.bank 1
.org $fffa
.dw reset,nmi,int
I think it should be:
Code:
.dw nmi,reset,int
EDIT: This would explain the invalid opcodes (execution starts in NMI, so there is no *valid* return address when you return), and the fact that as soon as NMI's are turned on it jumps to RESET.
tokumaru wrote:
I think it should be:
Code:
.dw nmi,reset,int
DOH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! That's it! I can't believe I overlooked that. Thanks tokumaru!
Yeah... we never look at the simple stuff! =)
Yeah. This, apparently, was also the reason I had to set $2006 back to zero to get my characters in place. Once I corrected my vectors the $2005 write was enough. Makes sense.... now (been nice if it would've clicked earlier).
It's good that the NMI/RESET vector confusion was cleared up. I want to point out something a little more subtle though (which may not be a problem now, but may raise its ugly head in the future) :
lynxsolaris wrote:
Code:
$8045:A9 00 LDA #$00
$8047:8D 06 20 STA $2006 = #$84
$804A:8D 06 20 STA $2006 = #$84
$804D:A9 88 LDA #$88
$804F:8D 00 20 STA $2000 = #$00
$8052:A9 18 LDA #$18 <-- Step stops here then just back to
sei @ $8000
$8054:8D 01 20 STA $2001 = #$00
$8057:AD 00 00 LDA $0000 = #$01
$805A:D0 FB BNE $8057
The reason that code was jumping to the NMI vector before (which happened to actually be the RESET vector) was because you're raising $2000.7 (enabling NMIs) while the VBlank flag ($2002.7) is still set. Unless you
want NMIs to occur this way... which... typically you wouldn't... you should make sure that the VBlank flag is clear by reading $2002.
This is why I was still recommending you read $2002 right away in your NMI routine (
before you do any writes to $2000) -- to clear the flag as soon as it is set, so that unwanted additional NMIs won't trip.
Disch wrote:
This is why I was still recommending you read $2002 right away in your NMI routine (before you do any writes to $2000) -- to clear the flag as soon as it is set, so that unwanted additional NMIs won't trip.
Done. I've assimilated this into my code.
I have one more question. I've decided to place an "image" above the text to go along with the story. Each time a character appears on the screen the image "flickers". What can I do to stop the image from flickering?
You'd better tell us how you render your image and your text to have us be able to help you.
If you just do one $2007 write during VBlank to write text, and be sure to setup proper scroll value before the end of VBlank, this should be OK. In that case you should do something wrong with scrolling or BG enabling via $2001.
Else, if you try to change something mid-frame between your image and your text, this could cause many different problems.
Sorry for such a late response. Because this is my test code, all I've done is basically added the image above the write of the text .... like so:
Code:
lda #$21
sta $2006
lda #$28
sta $2006
ldx #$00
scene_one_loop:
lda scene_one_data,X
sta $2007
inx
cpx #$FF
bne scene_one_loop
bit $2002
lda #%10001000
sta $2000
lda #%00011000
sta $2001
;;;;; not sure if I need something here ....
ldx #$00
ldy #$62
loop_text:
lda #$00
sta $2000
sta $2001
lda #$22
sta $2006
sty $2006
lda scene_one_text,X
sta $2007
inx
iny
bit $2002
lda #%10001000
sta $2000
lda #%00011000
sta $2001
wait_again:
lda text_flag
bne wait_again
lda #$06
sta text_flag
cpx #$55
bne loop_text
The image and the text flicker ... but when its just the text it seems to be fine .... Any pointers would be great! Thanks.