Skip navigation
Welcome, Guest! Please Login or Join

Loading...

Nerdy Nights week 7 subroutines, game layout, starting Pong

Jun 8, 2008 at 1:40:31 AM
bunnyboy (81)
avatar
(Funktastic B) < Bowser >
Posts: 7477 - Joined: 02/28/2007
California
Profile
Previous Week - Backgrounds

This Week:  Most of this lesson is about how to organize and structure your game.  Subroutines and game states help arrange the code for easier reading and reuse of code.  


Variables

As covered in week 1, variables are data stored in RAM that you can change any time.  The sprite data in RAM is all variables.  You will need more variables for keeping track of things like the score in the game.  To do that you first need to tell NESASM where in RAM to put the variable.  This is done using the .rsset and .rs directives.  First .rsset is used to set the starting address of the variable.  Then .rs is used to reserve space.  Usually just 1 byte is reserved, but you can have as much as you want.  Each time you do a .rs the address gets incremented so you don't need to do .rsset again.

  .rsset $0000    ;put variables starting at 0

  score1   .rs 1  ;put score for player 1 at $0000

  score2   .rs 1  ;put score for player 2 at $0001

  buttons1 .rs 1  ;put controller data for player 1 at $0002

  buttons2 .rs 1  ;put controller data for player 2 at $0003


Once you set the address for the variable, you do not need to know the address anymore.  You can just reference it using the variable name you created.  You can insert more variables above the current ones and the assembler will automatically recalculate the addresses.


Constants

Constants are numbers that you do not change.  They are just used to make your code easier to read.  In Pong an example of a constant would be the position of the outer walls.  You will need to compare the ball position to the walls to make the ball bounce, but the walls do not change so they are good constants.  Doing a compare to LEFTWALL is easier to read and understand than a comparison to $F6.

To declare constants you use the = sign:

  RIGHTWALL      = $02 ; when ball reaches one of these, do something

  TOPWALL        = $20

  BOTTOMWALL     = $D8

  LEFTWALL       = $F6


The assembler will then do a find/replace when building your code.


Subroutines

As your program gets larger, you will want subroutines for organization and to reuse code.  Instead of progressing linearly down your code, a subroutine is a block of code located somewhere else that you jump to, then return from.  The subroutine can be called at any time, and used as many times as you want.  Here is what some code looks like without subroutines:

RESET:

  SEI          ; disable IRQs

  CLD          ; disable decimal mode


vblankwait1:       ; First wait for vblank to make sure PPU is ready

  BIT $2002

  BPL vblankwait1


clrmem:

  LDA #$FE

  STA $0200, x

  INX

  BNE clrmem

 

vblankwait2:      ; Second wait for vblank, PPU is ready after this

  BIT $2002

  BPL vblankwait2


Notice that the vblankwait is done twice, so it is a good choice to turn into a subroutine.  First the vblankwait code is moved outside the normal linear flow:

vblankwait:      ; wait for vblank

  BIT $2002

  BPL vblankwait


RESET:

  SEI          ; disable IRQs

  CLD          ; disable decimal mode


clrmem:

  LDA #$FE

  STA $0200, x

  INX

  BNE clrmem


Then that code needs to be called, so the JSR (Jump to SubRoutine) instruction is where the vblankwait code used to be:

RESET:

  SEI          ; disable IRQs

  CLD          ; disable decimal mode


  JSR vblankwait  ;;jump to vblank wait #1


clrmem:

  LDA #$FE

  STA $0200, x

  INX

  BNE clrmem


  JSR vblankwait  ;; jump to vblank wait again


And then when the subroutine has finished, it needs to return back to the spot it was called from.  This is done with the RTS (ReTurn from Subroutine) instruction.  The RTS will jump back to the next instruction after the JSR:

     vblankwait:      ; wait for vblank  <--------

       BIT $2002                                  \

       BPL vblankwait                              |

 ----- RTS                                         |

/                                                  |

|    RESET:                                        |

|      SEI          ; disable IRQs                 |

|      CLD          ; disable decimal mode         |

|                                                  |

|      JSR vblankwait  ;;jump to vblank wait #1 --/

|

\--> clrmem:

      LDA #$FE

      STA $0200, x

      INX

      BNE clrmem


      JSR vblankwait  ;; jump to vblank wait again, returns here



Better Controller Reading

Now that you can set up subroutines, you can do much better controller reading.  Previously the controller was read as it was processed.  With multiple game states, that would mean many copies of the same controller reading code.  This is replaced with one controller reading subroutine that saves the button data into a variable.  That variable can then be checked in many places without having to read the whole controller again.


ReadController:

  LDA #$01

  STA $4016

  LDA #$00

  STA $4016

  LDX #$08

ReadControllerLoop:

  LDA $4016

  LSR A           ; bit0 -> Carry

  ROL buttons     ; bit0 <- Carry

  DEX

  BNE ReadControllerLoop

  RTS



This code uses two new instructions.  The first is LSR (Logical Shift Right).  This takes each bit in A and shifts them over 1 position to the right.  Bit 7 is filled with a 0, and bit 0 is shifted into the Carry flag.

bit number      7 6 5 4 3 2 1 0  carry

original data   1 0 0 1 1 0 1 1  0

                \ \ \ \ \ \ \  \

                 \ \ \ \ \ \ \  \ 

shifted data    0 1 0 0 1 1 0 1  1


Each bit position is a power of 2, so LSR is the same thing as divide by 2.

The next new instruction is ROL (ROtate Left) which is the opposite of LSR.  Each bit is shifted to the left by one position.  The Carry flag is put into bit 0.  This is the same as a multiply by 2.

These instructions are used together in a clever way for controller reading.  When each button is read, the button data is in bit 0.  Doing the LSR puts the button data into Carry.  Then the ROL shifts the previous button data over and puts Carry back to bit 0.  The following diagram shows the values of Accumulator and buttons data at each step of reading the controller:

                      Accumulator                               buttons data

bit:             7  6  5  4  3  2  1  0  Carry           7  6  5  4  3  2  1  0  Carry 

read button A    0  0  0  0  0  0  0  A  0               0  0  0  0  0  0  0  0  0

LSR A            0  0  0  0  0  0  0  0  A               0  0  0  0  0  0  0  0  A

ROL buttons      0  0  0  0  0  0  0  0  A               0  0  0  0  0  0  0  A  0

 

read button B    0  0  0  0  0  0  0  B  0               0  0  0  0  0  0  0  A  0

LSR A            0  0  0  0  0  0  0  0  B               0  0  0  0  0  0  0  A  B

ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  0  0  A  B  0

 

read button SEL  0  0  0  0  0  0  0 SEL 0               0  0  0  0  0  0  0  A  0

LSR A            0  0  0  0  0  0  0  0 SEL              0  0  0  0  0  0  0  A SEL

ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  0  A  B SEL 0


read button STA  0  0  0  0  0  0  0 STA 0               0  0  0  0  0  0  0  A  0

LSR A            0  0  0  0  0  0  0  0 STA              0  0  0  0  0  0  0  A STA

ROL buttons      0  0  0  0  0  0  0  0  0               0  0  0  0  A  B SEL STA 0



The loop continues for a total of 8 times to read all buttons.  When it is done there is one button in each bit:

bit:       7     6     5     4     3     2     1     0

button:    A     B   select start  up   down  left right


If the bit is 1, that button is pressed.


Game Layout

The Pong game engine will use the typical simple NES game layout.  First all the initialization is done.  This includes clearing out RAM, setting up the PPU, and loading in the title screen graphics.  Then it enters an infinite loop, waiting for the NMI to happen.  When the NMI hits the PPU is ready to accept all graphics updates.  There is a short time to do these so code like sprite DMA is done first.  When all graphics are done the actual game engine starts.  The controllers are read, then game processing is done.  The sprite position is updated in RAM, but does not get updated until the next NMI.  Once the game engine has finished it goes back to the infinite loop.


Init Code -> Infinite Loop -> NMI -> Graphics Updates -> Read Buttons -> Game Engine --\

                   ^                                                                    |

                    \------------------------------------------------------------------/


Game State

The use of a "game state" variable is a common way to arrange code.  The game state just specifies what code should be run in the game engine each frame.  If the game is in the title screen state, then none of the ball movement code needs to be run.  A flow chart can be created that includes what each state should do, and the next state that should be set when it is done.  For Pong there are just 3 basic states.


 ->Title State              /--> Playing State            /-->  Game Over State

/  wait for start button --/     move ball               /      wait for start button -\

|                                move paddles           |                               \

|                                check for collisions   /                               |

|                                check for score = 15 -/                                |

 \                                                                                     /

  \-----------------------------------------------------------------------------------/ 



The next step is to add much more detail to each state to figure out exactly what is needed.  These layouts are done before any significant coding starts.  Some of the game engine like the second player and the score will be added later.  Without the score there is no way to get to the Game Over State yet.


Title State:

  if start button pressed

    turn screen off

    load game screen

    set paddle/ball position

    go to Playing State

    turn screen on



Playing State:

  move ball

    if ball moving right

      add ball speed x to ball position x

      if ball x > right wall

        bounce, ball now moving left


    if ball moving left

      subtract ball speed x from ball position x

      if ball x < left wall

        bounce, ball now moving right


    if ball moving up

     subtract ball speed y from ball position y

      if ball y < top wall

        bounce, ball now moving down


    if ball moving down

      add ball speed y to ball position y

       if ball y > bottom wall

         bounce, ball now moving up


  if up button pressed

    if paddle top > top wall

      move paddle top and bottom up


  if down button pressed

    if paddle bottom < bottom wall

      move paddle top and bottom down

 

  if ball x < paddle1x

    if ball y > paddle y top

      if ball y < paddle y bottom

        bounce, ball now moving left



Game Over State:

  if start button pressed

    turn screen off

    load title screen

    go to Title State

    turn screen on

 




Putting It All Together

Download and unzip the pong1.zip sample files.  The playing game state and ball movement code is in the pong1.asm file. Make sure that file, mario.chr, and pong1.bat is in the same folder as NESASM3, then double click on pong1.bat. That will run NESASM3 and should produce pong1.nes. Run that NES file in FCEUXD SP to see the ball moving!

Other code segments have been set up but not yet completed.  See how many of those you can program yourself.  The main parts missing are the paddle movements and paddle/ball collisions.  You can also add the intro state and the intro screen, and the playing screen using the background information from the previous week.




Edited: 05/15/2010 at 02:38 PM by bunnyboy

Jun 9, 2008 at 12:56:11 AM
Rachel (10)
avatar
(Player of Games, Killer of Threads) < Eggplant Wizard >
Posts: 334 - Joined: 05/24/2008
Texas
Profile
I've really been looking forward to this post. Awesome <3! I'll be looking over it this week. Thank you x100, bunnyboy.

-------------------------
Resident collector of and expert on vintage girly games and consoles--especially rare stuff! 

Currently playing: Miitomo (iOS), Yoshi's Woolly World (WiiU)

Jun 9, 2008 at 6:36:21 AM
Rachel (10)
avatar
(Player of Games, Killer of Threads) < Eggplant Wizard >
Posts: 334 - Joined: 05/24/2008
Texas
Profile
I have a question about this portion of the tut. I wonder if someone wouldn't mind answering it.

Originally posted by: bunnyboy

Notice that the vblankwait is done twice, so it is a good choice to turn into a subroutine. First the vblankwait code is moved outside the normal linear flow:

vblankwait: ; wait for vblank

BIT $2002

BPL vblankwait


RESET:

SEI ; disable IRQs

CLD ; disable decimal mode


clrmem:

LDA #$FE

STA $0200, x

INX

BNE clrmem


I'm wondering why the vblankwait, or, in general, a subroutine, has to be moved outside the "normal linear flow." Why can't you put the code in the first place you want the vblank wait to run and then JSR to it every time after? Also, how would you know when something is sufficiently out of the "normal linear flow?"
Thanks!
R


-------------------------
Resident collector of and expert on vintage girly games and consoles--especially rare stuff! 

Currently playing: Miitomo (iOS), Yoshi's Woolly World (WiiU)


Edited: 06/09/2008 at 06:39 AM by Rachel

Jun 9, 2008 at 7:17:19 AM
Roth (66)
avatar
(Rob Bryant) < Lolo Lord >
Posts: 1742 - Joined: 09/14/2006
Illinois
Profile
If you make that a subroutine where it first appears, here's what it will look like:

vblankwait: ; wait for vblank
BIT $2002
BPL vblankwait
RTS

RESET:
SEI ; disable IRQs
CLD ; disable decimal mode

See that RTS in there after the test for branch? That's the ReTurn from Subroutine(RTS) that was mentioned by bunnyboy. If you first encounter that without having a Jump to SubRoutine (JSR), what will happen? Who knows! There is nowhere for the routine that was supposedly called to return to.

The RTS will get hit because the code goes in order. If you stick a subroutine out of the regular linear order (i.e. somewhere after your main game loop), that portion of code will be jumped to, code executed, then when it hits RTS, return to where it left off in the main loop of the program.

I'm really bad at explaining things. I hope you understand what I mean. RTS will get hit because of the linear layout of the language, but the results will be nothing like what you want if there was no Jump to SubRoutine beforehand.

-------------------------
http://slydogstudios.org...


Edited: 06/09/2008 at 07:18 AM by Roth

Jun 9, 2008 at 7:25:25 AM
Rachel (10)
avatar
(Player of Games, Killer of Threads) < Eggplant Wizard >
Posts: 334 - Joined: 05/24/2008
Texas
Profile
Yup, makes sense. For some reason I had it in my head that if there were an RTS with no JSR before it that it would just kind of ignore the RTS... not sure why I thought that...

-------------------------
Resident collector of and expert on vintage girly games and consoles--especially rare stuff! 

Currently playing: Miitomo (iOS), Yoshi's Woolly World (WiiU)

Jun 9, 2008 at 7:54:50 AM
Zzap (47)
avatar
(James ) < King Solomon >
Posts: 3298 - Joined: 05/01/2007
Australia
Profile
Technically, the JSR will push the program counter (the pointer to where the program is currently running) onto the stack before moving the program counter to the subroutine location, and the RTS pops it back off the stack. So an RTS without a JSR would try to pop a number off the stack and start running from there (probably result in a crash).

-------------------------

Chunkout for iPhone, iPad and iTouch out now!
Chunkout Games: FaceBook | Web

Jun 24, 2008 at 1:34:49 PM
Rachel (10)
avatar
(Player of Games, Killer of Threads) < Eggplant Wizard >
Posts: 334 - Joined: 05/24/2008
Texas
Profile
(Sorry to make this the "Rachel's questions" thread...)
It looks like all of this game is done within the NMI, correct? Couldn't you "run out of room," so to speak, within the NMI time? It seems like if you had lots of graphics and/or a number of jumps from one game state to another before the completion of any game state that you would run out of NMI time.
I hope that question makes sense. Even if it doesn't, asking nonsense questions is still helpful in my learning . Thanks in advance!

-------------------------
Resident collector of and expert on vintage girly games and consoles--especially rare stuff! 

Currently playing: Miitomo (iOS), Yoshi's Woolly World (WiiU)

Jun 24, 2008 at 1:49:23 PM
bunnyboy (81)
avatar
(Funktastic B) < Bowser >
Posts: 7477 - Joined: 02/28/2007
California
Profile
Your NMI just has to finish (RTI) before the next NMI triggers, one full frame later.

What you have to worry about is running out of vblank time, which is why all the graphics updates are done at the beginning of NMI. Once vblank is done you can't do more graphics updates, but you can still run the rest of your engine. If you see a game slow down during high action, that is usually the engine time going too long so the graphics updates for that frame arent done.

Jun 24, 2008 at 2:01:37 PM
Paul (224)
avatar
(Paul ) < Bonk >
Posts: 17040 - Joined: 07/02/2007
Tennessee
Profile
Rachel,

I don't think anyone minds all of the questions, I'm sure Bunnyboy doesn't mind, at least you have intelligent questions about NES programming, I couldn't even get past week 2 without being confused.

Jun 24, 2008 at 2:35:52 PM
Sivak (40)
avatar
(Sivak -) < Kraid Killer >
Posts: 2344 - Joined: 05/04/2007
Ohio
Profile
Yeah, you need to do graphical updates quickly in one frame of NMI, but you also need to do other things as well. I recall running into problems with Pillars. I used a bad multiplication routine that worked, but whenever huge numbers were in the mix, it would cause the game to freeze. I made a much better routine that only needs do do a loop 8 times.

Another similar thing was when you get 3 in a row, it redraws the entire background of the play area, but the process is done across 4 frames. That routine was very hard to get right.

-------------------------
My website: Here

Battle Kid 2 demo videos: Playlist
Battle Kid demo videos: Playlist

Check out my current: Want list
Check out my current: Extras list

Jun 28, 2008 at 3:21:25 PM
Rachel (10)
avatar
(Player of Games, Killer of Threads) < Eggplant Wizard >
Posts: 334 - Joined: 05/24/2008
Texas
Profile
Yeah, I was wondering about huge mid-game background redraws. I'm not sure how that works just yet.
Of course, instead of working through tutorials like a good girl should, I'm trying to simultaneously learn from them AND create the game I want to make. (I can't help myself.)

-------------------------
Resident collector of and expert on vintage girly games and consoles--especially rare stuff! 

Currently playing: Miitomo (iOS), Yoshi's Woolly World (WiiU)

Jun 28, 2008 at 4:08:40 PM
EVIL OVERLORD
Dain (223)
avatar
(Dain Anderson) < Founder >
Posts: 12092 - Joined: 08/14/2006
North Carolina
Profile
Originally posted by: Rachel

Yeah, I was wondering about huge mid-game background redraws. I'm not sure how that works just yet.
Of course, instead of working through tutorials like a good girl should, I'm trying to simultaneously learn from them AND create the game I want to make. (I can't help myself.)

We call that being an over-achiever


Jun 28, 2008 at 4:30:40 PM
bunnyboy (81)
avatar
(Funktastic B) < Bowser >
Posts: 7477 - Joined: 02/28/2007
California
Profile
Huge mid game redraws are either done with the screen turned off (fade out, then back in), or are done off screen then scrolled on screen (won't be covered for a long time). Without doing more tricks there isn't enough time to draw more than ~3 rows or columns per frame. If you are updating non contiguous tiles then you can't do that much because resetting the ppu address sucks up time.

Jul 15, 2008 at 5:47:29 PM
Kizul Emeraldfire (0)
avatar
(Kizul Emeraldfire) < Tourian Tourist >
Posts: 42 - Joined: 06/30/2008
United States
Profile
Sooo... when's the next week coming up? <.

-------------------------
 

Jul 20, 2008 at 11:59:57 AM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
I think the lesson is missing some stuff at the end ?

-------------------------
 

Jul 21, 2008 at 7:47:24 AM
dutch (0)
This user has been banned -- click for more information.
(bert n) < Meka Chicken >
Posts: 589 - Joined: 10/02/2007
Netherlands
Profile
There is a lot of stuff missing!

What i did is this: comparing the controller/background asmfile with the pong asm file and check for differences. And try to make sense of it.

From the top only the ines header is the same,then you get
* rsset/gamestate.rs 1/and state
> i believe this is the setup for variables(things that move and change) and constants (thing that don't change

Then you get the usual setup like reset,vblank,clrmem etc...

Then you get ball stats and stateplaying
> i believe this is a setup just like the palette/sprites/background/attribute needed for the ball movement( because i coundn't change the ball sprite)
I did change the code a bit for paddle sprites (pic added)you could also ad background because thats missing.

> JSR drawscore for adding score points?
> cleaun up? clean up your mess

> Gameengine >engine that needs to be added for tittle/game over and playing screen

>engineplaying here is a lot of information
i think this is what the ball(mario's backhead) makes it moving all over screen

BUT.........this is all guessing i have no idea if this is correct or how it works
Just have to waith for bunny lessons

http://img329.imageshack.us/img32...

-------------------------

LOOKING FOR GARAGE CART,pm me with buy or trading offer


Aug 21, 2009 at 11:14:57 AM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
So I made myself a nice big ball sprite with YY-Chr and have been able to make it bounce around the screen. 
My code works but before I continue I would like to check if it is good (or if someone has a better way).
here's the code I made to move my ball around:



UpdateSprites:
  LDA bally  ;;update all ball sprite info
  STA $0200  ; store Y position in Y value sprite 1
  STA $0204  ; store Y position in Y value sprite 2
  CLC ; clear carry
  ADC #$08 ; add 8 to move the sprite
  STA $0208 ; store (Y positon+8) in Y value sprite 3
  STA $020C ; store (Y positon+8) in Y value sprite 4
 
  LDA #$00 ; load 00 in A
  STA $0201 ; Store tile number 00 as first sprite 
  LDA #$01 ; load 01 in A
  STA $0205 ; store tile number 01 as second sprite
  LDA #$10 ; load 10 in A
  STA $0209 ; store tile number 10 as third sprite
  LDA #$11 ; load 11 in A
  STA $020D ; store tile number 01 as fourth sprite
 
  LDA #$00 ; attributes
  STA $0202 
  STA $0206 
  STA $020A
  STA $020E

  LDA ballx ; 
  STA $0203  ; store X position in X value sprite 1
  STA $020B  ; store X position in X value sprite 3
  CLC
  ADC #$08  
  STA $0207  ; store (X position+8) in X value sprite 2
  STA $020F  ; store (X position+8) in X value sprite 4





-------------------------
 


Edited: 08/21/2009 at 11:17 AM by KennyB

Aug 21, 2009 at 12:47:28 PM
bunnyboy (81)
avatar
(Funktastic B) < Bowser >
Posts: 7477 - Joined: 02/28/2007
California
Profile
Looks good. If it won't be changing, you do not need to update the tile number and attributes every time. The old value will still be there. It isn't bad, just uses up cpu time that you may need in advanced games.

Aug 22, 2009 at 8:13:42 AM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
Originally posted by: bunnyboy

Looks good. If it won't be changing, you do not need to update the tile number and attributes every time. The old value will still be there. It isn't bad, just uses up cpu time that you may need in advanced games.


I removed the updating for the tile number and the attributes, but now my sprite isn't the one I wanted.

The tile number in the sprites .db is set to the one I want. Do I need to change something else ? 

-------------------------
 

Aug 22, 2009 at 8:50:53 AM
bunnyboy (81)
avatar
(Funktastic B) < Bowser >
Posts: 7477 - Joined: 02/28/2007
California
Profile
You need to set it at least once, just not every time you want the ball to move.

Aug 22, 2009 at 9:13:24 AM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
Originally posted by: bunnyboy

You need to set it at least once, just not every time you want the ball to move.


Indeed, It works ! Thx 

-------------------------
 

Aug 23, 2009 at 2:59:11 PM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
New problem 

Can anyone put me in the right direction for changing a background tile according to a value from the game (like the score)

I programmed my ball to have various speeds (1 to 6) by using the UP button (I don't have paddles 
Now I wanted to display the current level of speed. 

I did the next: 

-  load the ball speed 
- compare with 6 (is the max speed)
- not equal: skip (BNE)
- if equal ==> store tile XX in a variable (speedlevel)
- .....
-...... do the same thing for values 5 till 1
-......
-......
- and I added  "speedlevel" to my .db for the background 

Offcourse, this didn't work  
Where I should see the speed level (numbers 1 to 6) , I get the tile $0F. 
This is the adress where my variable "speedlevel" is written. 
Is it possible what I'm trying to do ? 
Or is there a better way to change 1 tile of my background ? 








-------------------------
 

Aug 23, 2009 at 4:14:28 PM
KHAN Games (88)
avatar
(Kevin Hanley) < Master Higgins >
Posts: 7798 - Joined: 06/21/2007
Florida
Profile
You have to change background tiles in nmi. Instead of trying to write directly to the background, instead set a flag and then look for that flag in nmi. Then make the background change if that flag is set. It will look for the flag every frame.

Aug 30, 2009 at 10:41:43 AM
KennyB (14)
avatar
(No Go ogle result) < Eggplant Wizard >
Posts: 466 - Joined: 09/06/2006
Belgium
Profile
Originally posted by: mewithoutYou52

You have to change background tiles in nmi. Instead of trying to write directly to the background, instead set a flag and then look for that flag in nmi. Then make the background change if that flag is set. It will look for the flag every frame.


Do you mean something like this ?  Or am I completely missing the point ? The "Drawscore" get's called when the NMI happens. 


It semi-works now. However, before I change the speed (up or down), it displays 1. 
After I change the speed, it shoves the "1" one place to the right and displays the actual speed (1 to 6).
But I cant seem to find the solution for this.



Also, would it be better if I removed the highlighted pieces of codes (in the pastebin) and add a Jump after a compare (if the compare = 1) ? 
In the below example, first it would load A with the ball speed. If it's not equal to six, it will jump to the next value (NotSix). A is still loaded with the ball speed, so there is no need to load it again ? 

If the value is equal to six, the Jump is then used to skip unnecessary code and to prevent that the tile number stored in A get's compared with other speeds (5-1)

So you would get: 

Drawscore:
  • LDA ballspeedx 
    •   CMP #$06            
    •   BNE NotSix         
    •   LDA #$42             
    •   STA $2007        
    •   JMP DrawScoreDone ;if the speed of the ball is six => no need to do 5-1, jump to bottom
  • NotSix:                 
    •   LDA ballspeedx     ; this rule gets deleted, because if it's not 6, no other value was stored in A
    •   CMP #$05
    •   BNE NotFive
    •   LDA #$51
    •   STA $2007
    •  JMP DrawScoreDone
  • NotFive:
    .....
  • .....
DrawScoreDone:


EDIT: Problem Solved 

I also posted my problem over at nesdev and they found the bug in my code:

If the speed of my ball was anything else then 1, it would load that tile (for that speed) and write it to #$2353 using $2007
Because I didn't have a CMP and BNE for the speed 1, it would load the tile for number 1, and do another write at $2007 causing the tile for "1" getting stored into #$2354.

Adding a CMP and BNE solved my problem. However they also suggested a better code to use, which I have implemented (8 lines of code instead of my 38 )

Thx Kevin and Brian  for you're aid ! 


-------------------------
 


Edited: 08/31/2009 at 11:23 AM by KennyB

Dec 17, 2009 at 12:41:51 PM
themaincamarojoe (0)
avatar
(Joe Jackson) < Cherub >
Posts: 8 - Joined: 12/17/2009
California
Profile

How do you add the paddles?  In previous post we used a loop to add more than one sprite:

SpriteLoop:
 LDA Sprites, x
 STA $0200, x
 INX
 CPX #$10
 BNE SpriteLoop

However, in this one asm file, we used this way:

UpdateSprites:
 LDA bally
 STA $0200
 
 LDA #$75  ;i changed the the tile from 00 to 75 which is a ball looking tile
 STA $0201
 
 LDA #$00
 STA $0202
 
 LDA ballx
 STA $0203
 
 RTS

When having more than one sprite, they were written to $0200.  But when this one, and the first tutorial that uses sprite, when write to $0201.  I guessed that any extra ones I would write to the next location, being $0204, but that did not work.  Any ideas?

By the way, I'm not new to programming (but i'm not good at it), but I am new to nintentdo programming and the 6502.  I do know a little bit about Intel assembly.