Hello World

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
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

Okay, so I've meed those changes, and have this now:

Code: Select all

; Inits everything.
.proc reset
	; Clears the flags.
	clc
	sei
	cld
	clv
	
	; Sets the stack pointer.
	ldx #$FF
	txs
	
	; Waits for the PPU to warm up.
:	lda $2002 
	bpl :-
	
	; Inits the PPU.
	inx
	stx PPU_CONTROL
	stx PPU_MASK
	dex
	
	jmp main
.endproc
However, my code still gets stuck in the NMI handler. I don't even understand how it's supposed to escape it. It continually compares nmis to AC, neither of which are programmed to change in the loop.

Code: Select all

	lda nmis
:	cmp nmis
	beq :-
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

You need to actually enable the NMI.

Read this: http://wiki.nesdev.com/w/index.php/PPU_registers

Code: Select all

   inx
   stx PPU_CONTROL
   stx PPU_MASK 
What you did there is write 0 to both of those registers. Which means no NMI interrupt happens at the start of each frame. (Because bit 7 of $2000 is clear)

So after your setup code is done, set bit 7 of $2000. What this causes to start happening is an NMI "interrupt". An interrupt actually interrupts whatever your program was doing before the interrupt, and starts running something else. When that "something else" is done, it returns to where it was before the interrupt took place.

This is how nmis will change, even though your current loop does nothing to change it.

So while your main loop is doing this:

Code: Select all

   lda nmis
:   cmp nmis
   beq :-
Eventually, a new frame will start. When that happens, your code will be interrupted and jmp to your NMI routine. Your NMI routine will change the variable nmis, and then return allowing your main loop to continue down.

I don't know how to set the NMI vectors with your current setup, but you need to put the address for the NMI routine directly before the address of your reset vector.

Edit: Here is a quick NMI to get you started that should at least get you out of that loop:

Code: Select all

nmi:
     pha;The interrupt might have happened when you were doing something important
     tya;So we save the registers to the stack
     pha;And restore them later so that when it
     txa;Returns, the same values will be in them.
     pha

     inc nmis

     pla;Restoring the registers
     tax;From the stack.
     pla
     tay
     pla

     rti;Return from interrupt
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

Okay, so like this?:

Code: Select all

; Inits everything.
.proc reset
	; Clears the flags.
	clc
	sei
	cld
	clv
	
	; Sets the stack pointer.
	ldx #$FF
	txs
	
	; Waits for the PPU to warm up.
:	lda $2002 
	bpl :-
	
	; Inits the PPU.
	ldx #%10000000
	stx PPU_CONTROL
	
	ldx #%00011000
	stx PPU_MASK
	
	; Sets the stack pointer again.
	ldx #$FF
	
	jmp main
.endproc
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

You still want 0 written to both of those PPU registers while the PPU is still warming up. Also, you should wait two frames for the PPU to warm up, not just one.

More like this?

Code: Select all

; Inits everything.
.proc reset
   ; Clears the flags.
   ;clc;This isn't needed in your setup code. Its state being unknown
   ;Doesn't affect how your code will work in the very beginning
   sei;
   cld;We only do this because NES doesn't actually have decimal mode. 
   ;clv;No need to do this either.
   
   ; Sets the stack pointer.
   ldx #$FF
   txs
   inx
   stx PPU_CONTROL
   stx PPU_MASK
   
   ; Waits for the PPU to warm up
:   lda $2002
   bpl :-;Frame 1

   ; Waits for the PPU to warm up.
:   lda $2002
   bpl :-;Frame 2
   
   ; Inits the PPU.
   ldx #%10000000
   stx PPU_CONTROL
   
   ldx #%00011000
   stx PPU_MASK
     
   jmp main
.endproc
And you also need to put the NMI routine I posted in your code.

I don't use what you're using, but it looks like the way to do it is this (from source code Tepples recommended looking at earlier in the topic):

Code: Select all


.segment "VECTORS"
  .addr nmi, reset, irq
Then:

Code: Select all

.proc irq;Sure, here's an IRQ too for good measure
  rti
.endproc
.proc nmi
     pha;The interrupt might have happened when you were doing something important
     tya;So we save the registers to the stack
     pha;And restore them later so that when it
     txa;Returns, the same values will be in them.
     pha

     inc nmis

     pla;Restoring the registers
     tax;From the stack.
     pla
     tay
     pla

     rti;Return from interrupt 

.endproc
One other thing:

Code: Select all

; Sets the stack pointer again.
   ldx #$FF 
That doesn't do anything with the stack pointer. Not that it matters. You already set the stack pointer at the beginning of your program. Once you transfer a number from X to the stack pointer (they are not the same thing) with TXS, you can use X for whatever you like. If you're not going to use the #$FF in X you loaded right then for something, there's no need to do it.

In short:

1. Changing X won't change the stack pointer unless you use TXS.
2. You don't need to change the stack pointer again since you set it up already.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero, I must say it looks like you are just guessing and hoping for things to somehow work out, instead of actually understanding what you're doing.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

You still want 0 written to both of those PPU registers while the PPU is still warming up.
But I thought I was writing to them after the PPU has already warmed up.
Also, you should wait two frames for the PPU to warm up, not just one.
Oops, it's fixed now.
And you also need to put the NMI routine I posted in your code.
I don't understand yours. You push a bunch of stuff onto the stack only to pop it off in the very same routine after only incrementing nmis.
That doesn't do anything with the stack pointer. Not that it matters. You already set the stack pointer at the beginning of your program. Once you transfer a number from X to the stack pointer (they are not the same thing) with TXS, you can use X for whatever you like. If you're not going to use the #$FF in X you loaded right then for something, there's no need to do it.
I know. My math routines use a stack based on the zeropage with x as the index register, as its stack pointer. That's what I meant by "stack pointer" here.
FinalZero, I must say it looks like you are just guessing and hoping for things to somehow work out, instead of actually understanding what you're doing.
That's sort of how I feel. I *think* I understand it, and then later I find out that I was wrong. Anyways, I've certainly never programmed something where I have to take care of things like vblanks and interrupts before. I admit, lots of it seems cryptic. I apologize if it seems like I'm wasting your time.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:
And you also need to put the NMI routine I posted in your code.
I don't understand yours. You push a bunch of stuff onto the stack only to pop it off in the very same routine after only incrementing nmis.
Hail John Frum, who brings the cargo.

There are three structures for a game loop:
  1. NMI handler just increments nmis. Main loop waits for increment, updates VRAM, updates sound, then runs one frame of game logic.
  2. NMI handler updates VRAM and then updates sound, then increments nmis. Main loop waits for increment then runs one frame of game logic.
  3. NMI handler updates VRAM, updates sound, then runs one frame of game logic.
B and C type NMI handlers generally require pushing all registers and pulling them at the end. Some people are so used to B or C type NMI handlers that they end up typing out the pushes by habit and forgetting that they're not strictly needed for A type NMI handlers. Forgive them.
FinalZero, I must say it looks like you are just guessing and hoping for things to somehow work out, instead of actually understanding what you're doing.
That's sort of how I feel. I *think* I understand it, and then later I find out that I was wrong. Anyways, I've certainly never programmed something where I have to take care of things like vblanks and interrupts before. I admit, lots of it seems cryptic. I apologize if it seems like I'm wasting your time.
You could try a new thread about starting from my project template and hacking around with it.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:But I thought I was writing to them after the PPU has already warmed up.
It goes like this: before the warm up, write 0 to both PPU registers (the purpose here is to "reset" the PPU... kinda), and after the warm up you write the actual configuration you're gonna use (this includes enabling NMIs).
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

FinalZero wrote:
You still want 0 written to both of those PPU registers while the PPU is still warming up.
But I thought I was writing to them after the PPU has already warmed up.
I apologize for my mistake. I always thought those registers started with an unknown state, which isn't true. Even if they did start at something unknown, writes to them are also totally ignored for the first few frames.

What tokumaru posted is what I was describing, but the wiki says you don't need to.

I'll probably still do it to be safe, because I am paranoid.
I don't understand yours. You push a bunch of stuff onto the stack only to pop it off in the very same routine after only incrementing nmis.
Exactly. I explained why it does that in the comments, but Tepples makes a good point. The NMI I posted currently doesn't affect the status registers, but the idea would be that you add on to it once you're passed this hurdle.

Here's a real world example of why the pushing and popping is done.

Imagine this code is in your main routine:

Code: Select all

   lda #$FF
   ldx #$00
loop:
   sta $0200,x
   inx
   inx
   inx
   inx
;*
   bne loop
;We can continue only when X is exactly 0. I've used code like to remove all sprites from the screen (by putting their y position below the screen).
Now imagine this is your nmi routine. It interrupts at some point during the loop. Say... where that * is.

Code: Select all

nmi:
   inc nmis
   lda #$20;This line will cause a #$20 to be written instead of #$FF
   sta $2006
   ldx #$03;This line will cause an infinite loop.
   sta $2006
  
   ldy #$00
   sta $2007
;
   rti
When that code returns you're in an infinite loop, because the nmi changed X to something that makes completing the loop impossible.

Can (4*Z+3)%256 ever equal 0?
(Z = the number of times we have looped)

No. But (4*Z+0)%256 can be 0 which would allow your main loop to continue.

We push the registers to the stack in the NMI because it can interrupt our code at ANY time. After they're pushed, we can safely change them in the NMI, and restore them when we're done for when we return.

The inc nmis part might make sense now too. Because the opposite happens than this example. The nmi not changing that variable KEEPS us in an infinite loop when it should break us out of one.

Is that clear? If not I'll try a different approach. Tepples also did an explanation of game loops where you can avoid pushing and pulling if you like.
I know. My math routines use a stack based on the zeropage with x as the index register, as its stack pointer. That's what I meant by "stack pointer" here.
My bad. I'm a new guy in this thread, so I might have missed where that was mentioned. I'm just trying to make sure you're not doing anything you don't understand.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Kasumi wrote:What tokumaru posted is what I was describing, but the wiki says you don't need to.
Perhaps you need to on the Famicom, which doesn't fully reset the PPU when the player presses reset.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

When coding in assembly I like to assume that everything is in an unknown state on power up. Better safe then sorry.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Then, I was right the first time! Like I said, I'd do it anyway.

But now a question for my own curiosity: Are writes to $2000 still ignored for a few frames after a famicom reset? If so, even if $2000 and $2001 were set to something potentially harmful, you still couldn't do anything about it before the PPU was warmed up anyway.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Post by thefox »

Kasumi wrote:But now a question for my own curiosity: Are writes to $2000 still ignored for a few frames after a famicom reset? If so, even if $2000 and $2001 were set to something potentially harmful, you still couldn't do anything about it before the PPU was warmed up anyway.
Going out on a limb here, but I don't think so. As far as I understand, the reset line of the PPU simply isn't connected to the PPU on the Famicom, so when you push the reset button, PPU keeps running like nothing happened.
Post Reply