It is currently Sun Jan 20, 2019 11:54 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Wed Dec 19, 2018 3:49 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Hello! So i am an amateur game programmer that started with C++ and SFML and now is trying to learn how to do games for the NES. Up until now i have been using the Nerdy Nights tutorials to learn how to do stuff and now was trying to do collision and platformer physics, the following is my code just to make the character go left or right.

Code:
.proc nmi_handler
   LDA #$00
   STA $2003

   LDA #$02
   STA $4014

   LDA #$01
   STA $4016
   LDA #$00
   STA $4016

;Reads every button press and do some shift magic to put everything in a variable(controle)
   LDX #$00
   LoopLendoControle:
      LDA $4016
      LSR A
      ROL controle

      INX
      CPX #$08
      BNE LoopLendoControle

;Checks if right button is pressed, if so move every sprite to the right
   LDA controle
   AND #%00000001
   BEQ fimDireita
      LDX #$00
      @LoopSprites:
        ;$0203 as it is the first x position for the sprites.
         INC $0203, x
         
         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   fimDireita:
;Checks if left button is pressed, if so move every sprite to the left
   LDA controle
   AND #%00000010
   BEQ fimEsquerda
      LDX #$00
      @LoopSprites:
         DEC $0203, x

         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   fimEsquerda:

   RTI
.endproc
.proc main
   LDX $2002
   LDX #$3f
   STX $2006
   LDX #$00
   STX $2006                   
               
   JSR CarregarPaleta
   JSR CarregarSprites
   JSR CarregarBackground

   LDA #%10010000
   STA $2000

   LDA #%00011110
   STA $2001
forever:
   JMP forever   
.endproc

It is working perfectly, but i have seen somewhere that it's a good thing to separate game logic from the NMI, so i moved my control handling code to after the forever label
Code:
.proc nmi_handler
   LDA #$00
   STA $2003

   LDA #$02
   STA $4014

;Here used to be the game logic, now it is after the 'forever' label.

   RTI
.endproc
.proc main
   LDX $2002
   LDX #$3f
   STX $2006
   LDX #$00
   STX $2006                   
               
   JSR CarregarPaleta
   JSR CarregarSprites
   JSR CarregarBackground

   LDA #%10010000
   STA $2000

   LDA #%00011110
   STA $2001
forever:
        ;Same code as before
   LDA #$01
   STA $4016
   LDA #$00
   STA $4016

   LDX #$00
   LoopLendoControle:
      LDA $4016
      LSR A
      ROL controle

      INX
      CPX #$08
      BNE LoopLendoControle

   LDA controle
   AND #%00000001
   BEQ fimDireita
      LDX #$00
      @LoopSprites:
         INC $0203, x
         
         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   fimDireita:
   LDA controle
   AND #%00000010
   BEQ fimEsquerda
      LDX #$00
      @LoopSprites:
         DEC $0203, x

         INX
         INX
         INX
         INX
         CPX #$10
         BNE @LoopSprites
   fimEsquerda:
   JMP forever   
.endproc

But then the changes brought with it two problems, the first one is that the sprites move way too fast(when i programmed games with SFML i used a delta time to move stuff according to the framerate, since there is no floating point numbers here i don't really know what to do) and the second is that it seems the control reading is bugged since the sprite starts moving without any input.
tl;dr: How to properly separate my game logic from the NMI?


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 4:22 pm 
Offline
User avatar

Joined: Wed Sep 07, 2005 9:55 am
Posts: 351
Location: Phoenix, AZ
For #1, you need to wait until the nmi handler returns before jumping back to "forever." As it is, the move code is being called multiple times a frame.

I set a frame finished flag at the end of the game logic, and loop until the nmi clears it. Then jump back to the start of the gameplay logic.

#2 might be related to not preserving your registers in your nmi handler (pha/pla...).

_________________
. That's just like, your opinion, man .


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 5:13 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
never-obsolete wrote:
For #1, you need to wait until the nmi handler returns before jumping back to "forever." As it is, the move code is being called multiple times a frame..

Just to be sure i understood you right, this is what you meant?
Code:
vblankwait:
      BIT $2002
      BPL vblankwait

If so, yay cause doing it before any game logic made the speed of light movement go away, thanks!

never-obsolete wrote:
#2 might be related to not preserving your registers in your nmi handler (pha/pla...)

Actually just doing the above fixed the bug, thanks for remembering me that the stack exists though kkkkk


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 5:22 pm 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3630
Location: Indianapolis
Polling $2002 sort-of works, but isn't 100% reliable. It will sometimes miss. It's best to do something like this:

Code:
wait:
 lda nmi_happened
 beq wait
 lda #0
 sta nmi_happened

...

nmi:
 inc nmi_happened


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 5:51 pm 
Offline

Joined: Tue Dec 18, 2018 9:48 pm
Posts: 13
Oh, okay then, changed the code to do it according to what you said, thanks everybody!


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 6:25 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2390
Location: DIGDUG
If your nmi code still does...

LDA #$00
STA $2003

LDA #$02
STA $4014

and you have a main thread going, you need to back up your A register, like this.

nmi:
pha ;push A on stack
LDA #$00
STA $2003

LDA #$02
STA $4014
pla ;pull a from stack
rti

because an NMI is an "interrupt", it could happen while the main thread is still thinking, and would return from the nmi interrupt with a changed A register.

If your NMI thread uses X or Y, they also need to be backed up and restored, like.

PHa
TXA
PHA
TYA
PHA

...code...

PLA
TAY
PLA
TAX
PLA
RTI

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 7:07 pm 
Offline
User avatar

Joined: Sun Apr 08, 2018 11:45 pm
Posts: 33
Location: Southern California
The minimal ca65 example demonstrates a good approach for this, including having the game logic write render commands to a buffer that the NMI then handles at the appropriate time.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group