It is currently Wed Nov 14, 2018 4:28 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 15 posts ] 
Author Message
PostPosted: Fri Jun 29, 2018 9:44 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
Image

Hello! I'm working on an Arkanoid Clone for NES. I need to understand what's the best way to change the bricks tile on the background after a collision. For now I'm loading the background on the screen in this way:

Code:
LoadPlayScreen:         ;load playfield screen
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address

  LDA #LOW( TileLevel_1 )
  STA ADDR_LOW
  LDA #HIGH( TileLevel_1 )
  STA ADDR_HIGH
 
  LDX #$04              ; Loop X 4 times
  LDY #$00              ; Loop Y 256 times
LoadBgLvlLoop:
  LDA [ ADDR_LOW ], y
  STA $2007
  INY
  BNE LoadBgLvlLoop
; Outer loop
  INC ADDR_HIGH            ; increment high byte of address backg to next 256 byte chunk
  DEX                      ; one chunk done so X = X - 1.
  BNE LoadBgLvlLoop        ; if X isn't zero, do again


I understand that the only way to communicate with the PPU is trough $2006/$2007, so, what's the best way to change a tile on a XY position that's already on the screen? The entire tile is on the VRAM right? So can I modify a tile and update the background?

Thanks!


Top
 Profile  
 
PostPosted: Fri Jun 29, 2018 10:10 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20764
Location: NE Indiana, USA (NTSC)
1. Calculate at what address to draw it: $2000 plus 32 times the vertical position of the tile (in 8-pixel units) plus the horizontal position of the tile (in 8-pixel units)
2. Calculate what tile to draw, in your case the empty background tile
3. Wait for vertical blanking
4. Write the high byte of the address to $2006: this is usually values $20 to $23
5. Write the low byte of the address to $2006
6. Write the new tile number to $2007

Are you looking for more detailed instruction about how to "Calculate at what address to draw it" in 6502 assembly language?


Top
 Profile  
 
PostPosted: Fri Jun 29, 2018 10:51 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
tepples wrote:
1. Calculate at what address to draw it: $2000 plus 32 times the vertical position of the tile (in 8-pixel units) plus the horizontal position of the tile (in 8-pixel units)
2. Calculate what tile to draw, in your case the empty background tile
3. Wait for vertical blanking
4. Write the high byte of the address to $2006: this is usually values $20 to $23
5. Write the low byte of the address to $2006
6. Write the new tile number to $2007

Are you looking for more detailed instruction about how to "Calculate at what address to draw it" in 6502 assembly language?



Why from $20 and $23? There is some example that I can read? Thanks! :)


Top
 Profile  
 
PostPosted: Fri Jun 29, 2018 12:07 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
PPU memory map.

0-1fff = tiles
2000-23ff = nametable 0
etc.

nametable is a silly word that means tile map, which tiles go where on the screen.

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


Top
 Profile  
 
PostPosted: Fri Jun 29, 2018 4:58 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10962
Location: Rio de Janeiro - Brazil
In your other thread I posted an example of how to access an individual tile in the level map. You can use that exact same code to calculate the address of a tile in VRAM, the only differences are that the base address should be the base address of the name table in VRAM (e.g. $2000) and the final result will have to be written to $2006, instead of being used as a pointer.

Note that the formula tepples posted (BASE + Y * 32 + X) takes X and Y in 8-pixel units, while the routine I posted takes them in pixel units. I wrote it that way because I assumed you'd be calculating those coordinates from sprite coordinates, which are in pixel units, so you save a little bit of CPU time by not shifting Y right 3 times just to shift it left 5 times... clearing the lower 3 bits and shifting it left twice (like in my routine) does the same job in less time.


Top
 Profile  
 
PostPosted: Sat Jun 30, 2018 3:24 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
Ok thanks, I can change the background without problem now. But now I've another couple of questions :oops: . And that's about the collision test. Now I've the entire background on VRAM, theoretically I can access to any position on VRAM by

Code:
LDA $2002
  LDA #$20
  STA $2006
  LDA #$01
  STA $2006
  LDX $2007
  LDX $2007


So by reading $2007 I can now witch tile is in $2001. Modify and store it again, but I read in another thread that this is slow. So, I want to understand if can I copy the ENTIRE ( for now ) background tile in RAM and work on it for the collision detections. I tried to do the same thing that I do for copying the background on VRAM, but for some reason this doesn't work.

Code:
LDA #LOW( TileLevel_1 )
  STA ADDR_LOW
  LDA #HIGH( TileLevel_1 )
  STA ADDR_HIGH
 
  LDX #$04              ; Loop X 4 times
  LDY #$00              ; Loop Y 256 times
LoadBgLvlLoop:
  LDA [ ADDR_LOW ], y
 
  STA $0400, y
 
  INY
  BNE LoadBgLvlLoop
; Outer loop
  INC ADDR_HIGH            ; increment high byte of address backg to next 256 byte chunk
  DEX                      ; one chunk done so X = X - 1.
  BNE LoadBgLvlLoop        ; if X isn't zero, do again


I'm storing from $0400. But when I try to read, after the loading on RAM, at $0401 I can't find my tile. Probably I'm doing something wrong. Any suggest? What's the best way to that?

Thanks a lot! You are saving my life in this code adventure :)


Top
 Profile  
 
PostPosted: Sat Jun 30, 2018 5:43 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2329
Location: DIGDUG
STA $0400, y can only reach from $400-4ff. 256 bytes. So, on the second pass of the larger loop, you're overwriting $400-4ff multiple times.

You seem to be moving 4x256=1024 bytes.

You need a second pointer to sta.

LDA #0
STA ADDR_LOW2
LDa #4
STA ADDR_HIGH2 ; ptr now points to $400
...
...
LDA [ADDR_LOW], y
STA [ADDR_LOW2], y
...
...
;and increase both high bytes
INC ADDR_HIGH
INC ADDR_HIGH2

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


Top
 Profile  
 
PostPosted: Sat Jun 30, 2018 6:36 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
dougeff wrote:
STA $0400, y can only reach from $400-4ff. 256 bytes. So, on the second pass of the larger loop, you're overwriting $400-4ff multiple times.

You seem to be moving 4x256=1024 bytes.

You need a second pointer to sta.

LDA #0
STA ADDR_LOW2
LDa #4
STA ADDR_HIGH2 ; ptr now points to $400
...
...
LDA [ADDR_LOW], y
STA [ADDR_LOW2], y
...
...
;and increase both high bytes
INC ADDR_HIGH
INC ADDR_HIGH2


It works! :D Thanks a lot!


Top
 Profile  
 
PostPosted: Sat Jun 30, 2018 7:46 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
Code:
UpdateBGScreen:
 
  LDA TILE_COLLISION_NUM
  CMP #$00
  BEQ _NoCollision

  LDA $2000
  STA VRAM_ADDRESS_COLLISION
  LDA VRAM_ADDRESS_COLLISION
  CLC
  ADC TILE_COLLISION_NUM
  STA VRAM_ADDRESS_COLLISION

  LDA LOW( VRAM_ADDRESS_COLLISION )
  STA ADDR_LOW
  LDA HIGH( VRAM_ADDRESS_COLLISION )
  STA ADDR_HIGH

  LDA $2002
  LDA ADDR_HIGH
  STA $2006
  LDA ADDR_LOW
  STA $2006

  LDA #$30
  STA $2007

  LDA #$00
  STA TILE_COLLISION_NUM

_NoCollision:
 
  RTS


When I catch a collision, I save the tile Number in TILE_COLLISION_NUM, so when I call UpdateBGScreen I check if there is some collision to update the BG. The code above translate the tile number in the RAM in a VRAM Address by adding $2000+tile_number ( VRAM and RAM are now 1:1 ) but for some reason, only some bricks are update, other bricks are remain the same, but when the brick is changed, is changed the right brick in the right position. I'm doing some mistake, I've no problem with the formula (BASE + Y * 32 + X), I want to know how to write the correct address format.

Thanks! :)


Top
 Profile  
 
PostPosted: Sat Jun 30, 2018 9:24 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10962
Location: Rio de Janeiro - Brazil
It's true that read-modify-write operations on VRAM are usually considered too slow, specially if the "modify" step consists in something complex like collision logic, but in this specific case, you only need to check one or two tiles, so the 20 scanlines of vblank time are way more than enough to do it.

It's cool that you chose to go with a RAM mirror of the map though, because that allows for better separation of the game logic and graphics rendering, so it scales better once you move on to more complex games.


Top
 Profile  
 
PostPosted: Thu Jul 05, 2018 12:13 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
Hello again! Some progress here, now I can destroy my bricks without problem. But only until the 255 tile :P. Here is my problem. I've stored on RAM a 1:1 map of the background for now, I compute the Tile number where the collision occurs and I store it on BALL_POS_X_ON_TILE. But BALL_POS_X_ON_TILE can count from 0 to 255, so, if I have a collision on the 300 tile, in BALL_POS_X_ON_TILE probably I've something wrong ( 45 maybe? ).

The code above show how i compute the Tile position and I load the tile on ram by

LDX BALL_POS_X_ON_TILE
LDA $0400, x

But, if the collision is on tile 300 ( 1AF ) how can I load the 300° tile on RAM if BALL_POS_X_ON_TILE can hold only 0 - 255 values? Thanks! :)

Code:

LDA BALL_POS_X_ON_TILE        ; we need this for collision detection
  STA BALL_POS_X_ON_TILE_PREV
  LDA BALL_POS_Y_ON_TILE
  STA BALL_POS_Y_ON_TILE_PREV

  LDA #$00
  STA BALL_POS_X_ON_TILE
  STA BALL_POS_Y_ON_TILE

  LDA BALL_POS_X
  LSR A
   LSR A
   LSR A                    ; Divide by 8
  STA BALL_POS_X_ON_TILE

  LDA BALL_POS_Y         
 
  LSR A
  LSR A
  LSR A                   ; Divide by 8
  STA BALL_POS_Y_ON_TILE

_ContinueCheckColl:
  LDX #$00
ComputeXYOnTile:
  LDA BALL_POS_X_ON_TILE
  CLC
  ADC #$20
  STA BALL_POS_X_ON_TILE
  INX
  CPX BALL_POS_Y_ON_TILE
  BNE ComputeXYOnTile

  ;Verify the collision with the left part of the brick
PerformBallColWithBrickLeft:
  LDX BALL_POS_X_ON_TILE
  LDA $0400, x
  CMP #$2D
  BEQ PerformBallCollisionWithBrick


Top
 Profile  
 
PostPosted: Thu Jul 05, 2018 1:15 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10962
Location: Rio de Janeiro - Brazil
Like I said before: use a 16-bit pointer instead of an 8-bit index. Calculate the tile offset (TileY * 32 + TileX) and then add the base address ($0400, is it?). This will give you a pointer you can use to access any part of the map.


Top
 Profile  
 
PostPosted: Thu Jul 05, 2018 8:24 pm 
Offline

Joined: Wed May 19, 2010 6:12 pm
Posts: 2763
tokumaru wrote:
Like I said before: use a 16-bit pointer instead of an 8-bit index. Calculate the tile offset (TileY * 32 + TileX) and then add the base address ($0400, is it?). This will give you a pointer you can use to access any part of the map.


You can do the TileX amount with indexing.


Top
 Profile  
 
PostPosted: Thu Jul 05, 2018 10:22 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10962
Location: Rio de Janeiro - Brazil
Yup. The Pointer can be just $0400 + TileY * 32, while index register Y holds TileX.


Top
 Profile  
 
PostPosted: Sat Jul 07, 2018 3:45 am 
Offline

Joined: Sat May 26, 2018 6:14 am
Posts: 49
Location: Italy
Ok, I'm working on it. Thanks a lot! :)


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 6 guests


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