Collision detection problem with my project.

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

Code: Select all

LDA BALL_POS_X
  SEC
  SBC #$0F                ; subtrack 16 pixel to put x in the same x space of the tiles
  
  LSR A
	LSR A
	LSR A
	LSR A                   ; Divide by 16
  STA BALL_POS_X_ON_TILE

  LDA BALL_POS_Y
  SEC
  SBC #$0F

  LSR A
  LSR A
  LSR A
  LSR A                   ; Divide by 16
  STA BALL_POX_Y_ON_TILE
Now I've the position ( x, y ) of the ball on Tile. Now I need to understand how to take the tile from the X and Y position stored in $2007 with this method:

Code: Select all

bg_raw:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 ; 0 raw background
bg_lvl_top_corner:  
  .db $24,$2B,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$2B,$24,$24,$24,$24,$24,$24,$24 ; 1 top corner
bg_lvl_1_0:
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 2
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 3
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$1C,$1D,$1E,$0D,$12,$18 ; 4
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$15,$0E,$0A,$1F,$0E,$1C ; 5
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$02,$00,$01,$08 ; 6
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 7
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 8
  .db $24,$2B, $2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E,$2D,$2E, $2B,$24,$24,$24,$24,$24,$24,$24 ; 9

bg_lvl_1_1:
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 10
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 11
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 12
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 13
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 14
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 15
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 16
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 17

bg_lvl_1_2
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 18
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 19
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 20
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 21
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 22
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 23
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 24
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 25

bg_lvl_1_3
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 26
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 27
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 28
  .db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 29
User avatar
Punch
Posts: 365
Joined: Sat Feb 16, 2013 11:52 am

Re: Collision detection problem with my project.

Post by Punch »

Kikutano: Check collision.asm on my code if you're trying to figure out angular and subpixel movement, especially this section:

Code: Select all

;This table shows the amount of pixels
;that you should travel in the x/y directions
;in order to move with a certain speed (1-8)
;in 30, 45 or 60 degrees.
Vector_Angle_Table:
	.db 0, 1, 2, 3, 4, 5, 6, 6 ;30 degree X
	.db 0, 1, 1, 2, 2, 3, 3, 4 ;30 degree Y
	
	.db 0, 1, 2, 2, 3, 4, 4, 5 ;45 degree X
	.db 0, 1, 2, 2, 3, 4, 4, 5 ;45 degree Y
	
	.db 0, 1, 1, 2, 2, 3, 3, 4 ;60 degree X
	.db 0, 1, 2, 3, 4, 5, 6, 6 ;60 degree Y
	
;base 256 fractions to represent the angle as floating point
Vector_Fraction_Table:
	.db 223, 186, 153, 119, 84, 50, 23, 246 ;30 deg X
	.db 128, 0, 128, 0, 128, 0, 128, 0 ;30 deg Y
	.db 181, 106, 31, 212, 137, 62, 243, 168 ;45 deg X
	.db 181, 106, 31, 212, 137, 62, 243, 168 ;45 deg Y
	.db 128, 0, 128, 0, 128, 0, 128, 0 ;60 deg X
	.db 223, 186, 153, 119, 84, 50, 23, 246 ;60 deg Y
	
	
	
	
Ain't nothing as fast as reading fron a table... :lol: Not that it would be difficult to generate such a table, but to save you the trouble:

I, the author, release the code snippet above as Public Domain and forfeit my exclusive Author Intellectual Property Rights (so go ahead and copy it if you want without crediting me). :p
This is a block of text that can be added to posts you make. There is a 255 character limit.
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

I'm working on collision, and it start to works finally :D. All bricks are on background, the next step is to compute the bouncing and update the background. Programming for Nes is a pain in the ass, but hey, it's so funny! :D I will post here the collision detection code, I hope to upload on Github the entire project when I will finish it.

Image
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

Image

Hello! Some progress here and new doubts! The collision detection works great with background. But I've a couple of question about the way to address the nametables where I want to compute the collision.

I load the background in this way:

Code: Select all

LDX #$00 
LoadBackgroundLVLLoop: 
  LDA bg_lvl_1_0, x      
  STA $2007               
  INX                     
  CPX #$00                
  BNE LoadBackgroundLVLLoop
  
  LDX #$00
LoadBackgroundLVLLoop1: 
  LDA bg_lvl_1_1, x      
  STA $2007               
  INX                     
  CPX #$00                
  BNE LoadBackgroundLVLLoop1

  LDX #$00
LoadBackgroundLVLLoop2: 
  LDA bg_lvl_1_2, x      
  STA $2007               
  INX                     
  CPX #$00                
  BNE LoadBackgroundLVLLoop2

  LDX #$00
LoadBackgroundLVLLoop3: 
  LDA bg_lvl_1_3, x      
  STA $2007               
  INX                     
  CPX #$80                
  BNE LoadBackgroundLVLLoop3
With 4 loop because I can't count over 256, so now I'm calculating the collision only on

Code: Select all

bg_lvl_1_0
in this way:

Code: Select all

PerformBallColWithBrickLeft:
  LDX BALL_POS_X_ON_TILE
  LDA bg_lvl_1_0, x
  CMP #$2D
  BEQ PerformBallCollisionWithBrick
So, there is a way to switch dynamically

Code: Select all

bg_lvl_1_0
with, for example,

Code: Select all

bg_lvl_1_1
? I need this not only to compute the collision on the portion of the background, but also to load a new background for a new level.

Can I do something like this? :

Code: Select all

LDA bg_lvl_1_0
  STA NAMETABLE_TO_LOAD

  LDX #$00 
LoadBackgroundLVLLoop: 
  LDA NAMETABLE_TO_LOAD, x      
  STA $2007               
  INX                     
  CPX #$00                
  BNE LoadBackgroundLVLLoop
So I can change only NAMETABLE_TO_LOAD? Thanks a lot!
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

So your problem it's basically that you don't know how to have dynamic access to more than 256 bytes,is that it? What you need to do is use a pointer. A pointer is a pair of consecutive memory positions in ZP, that together form a 16-bit address, which's able to point anywhere in the 6502's 16-bit address space.

Say you have #$00 stored at $0040, and #$84 stored at $0041. The instruction LDA ($40), Y will load the value at address $8400+Y. This is similar to the regular indexed addressing mode you've been using, except that the base address isn't fixed, it's stored in RAM and you can change it. This way you can update the address in RAM when the index (Y) goes over 255, so you can access tables longer than 256 bytes.
Last edited by tokumaru on Mon Jun 25, 2018 10:47 am, edited 1 time in total.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Collision detection problem with my project.

Post by dougeff »

missing parentheses.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

tokumaru wrote:So your problem it's basically that you don't know how to have dynamic access to more than 256 bytes,is that it? What you need to do is use a pointer. A pointer is a pair of consecutive memory positions in ZP, that together form a 16-bit address, which's able to point anywhere in the 6502's 16-bit address space.
Ok thanks! But I want to know if how can I do something like this:

LDA bg_lvl_1_0
STA NAMETABLE_TO_LOAD

LDX #$00
LoadBackgroundLVLLoop:
LDA NAMETABLE_TO_LOAD, x
STA $2007
INX
CPX #$00
BNE LoadBackgroundLVLLoop


where bg_lvl1_1_0 is the nametable that I want to draw:

bg_lvl_1_0:
.db $24,$2B, $27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27,$27, $2B,$24,$24,$24,$24,$24,$24,$24 ; 2
....


So there is a way to use NAMETABLE_TO_LOAD as variable to it can point to bg_lvl_1_0 or another level for example bg_lvl_2_0? So I can compute the collision on the current Level?

PerformBallColWithBrickLeft:
LDX BALL_POS_X_ON_TILE
LDA bg_lvl_1_0, x ( LDA NAMETABLE_TO_LOAD, x )
CMP #$2D
BEQ PerformBallCollisionWithBrick


In short words, I need to change and draw the level dynamically and compute the collision using the background information of the level. Maybe it's the wrong way, I don't know, this is my first time on a Nes and on ASM! Thanks a lot! :oops:
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

dougeff wrote:missing parentheses.
Oh, sorry about that. Yeah, it's supposed to be LDA ($40), Y in my example above. Or LDA [$40], Y if you're using NESASM.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

The straightforward way to do what you want is to have a list of pointers to the name tables you have, like this:

Code: Select all

Level0Data:
.db $24, $2B, $27, $27, (...)

Level1Data:
.db $20, $4B, $28, $28, (...)

Level1Data:
.db $34, $40, $16, $4B, (...)

LevelPointers:
.dw Level0Data, Level1Data, Level2Data
Then you can copy any of the pointers to ZP and access the data via indirect addressing. Here's an example routine to copy a name table to VRAM:

Code: Select all

;call this subroutine with the map index in register A
CopyBackground:

  ;multiply the index by 2, since each pointer is 2 bytes, and put it in an index register
  asl
  tay

  ;copy the pointer to zero page
  lda LevelPointers+0, y
  sta Pointer+0
  lda LevelPointers+1, y
  sta Pointer+1

  ;copy 1024 bytes from the location indicated by the pointer to VRAM
  ldx #$04
  ldy #$00
CopyByte:
  lda (Pointer), y
  sta $2007
  iny
  bne CopyByte ;go copy another byte if less than 256 have been copied
  inc Pointer+1 ;advance the pointer by 256 positions
  dex
  bne CopyByte ;go copy 256 more bytes if less than 1024 have been copied

  rts
You can use the same pointers for collision purposes, you just need to fetch the base address from the table of level pointers, add the offset of the tile you want, and then use the result as a pointer to read the tile. Something like this:

Code: Select all

;X coordinate in register A, Y coordinate in register X
GetTile:

  ;divide the X coordinate by 8
  lsr
  lsr
  lsr
  tay

  ;simulate a division by 8 followed by a multiplication by 32 of the Y coordinate
  lda #$00
  sta Pointer+1
  txa
  and #%11111000
  asl
  rol Pointer+1
  asl
  rol Pointer+1

  ;add the result to the base address of the NT
  adc CurrentLevelPointer+0
  sta Pointer+0
  lda Pointer+1
  adc CurrentLevelPointer+1
  sta Pointer+1

  ;get the tile
  lda (Pointer), y

  rts
This subroutine expects coordinates in pixel units, since that's what you'll probably have. It also assumes you have cached the current level pointer, so you don't have to read it off the table every time you need it.
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

Thanks a lot! I will try now! :beer:
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

Even though I made an example routine that reads individual tiles from the level maps in RAM, I don't think you'll be doing that in the game, right? I mean, since the blocks are breakable, it makes no sense to read the original state of the map from ROM.

What's your approach, then? Do you keep a copy of the level in RAM, using half of the total RAM to make the level destructible? Or do you read map data from VRAM during vblank? Either way, you still have to calculate the tile's offset using the offset = TileY * 32 + TileX formula.
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

tokumaru wrote:Even though I made an example routine that reads individual tiles from the level maps in RAM, I don't think you'll be doing that in the game, right? I mean, since the blocks are breakable, it makes no sense to read the original state of the map from ROM.
I don't know yet, probably I will copy the level in RAM. Btw, I'm trying to run your code, but the result is as you can see in the screen.

Code: Select all

;multiply the index by 2, since each pointer is 2 bytes, and put it in an index register
  asl A
  tay

  ;copy the pointer to zero page
  lda LevelPointers+0, y
  sta NAMETABLE_TO_LOAD+0
  lda LevelPointers+1, y
  sta NAMETABLE_TO_LOAD+1

  ;copy 1024 bytes from the location indicated by the pointer to VRAM
  ldx #$04
  ldy #$00
CopyByte:
  lda (NAMETABLE_TO_LOAD), y
  sta $2007
  iny
  bne CopyByte ;go copy another byte if less than 256 have been copied
  inc NAMETABLE_TO_LOAD+1 ;advance the pointer by 256 positions
  dex
  bne CopyByte ;go copy 256 more bytes if less than 1024 have been copied
For some reason the tile pointed is always "0" any idea? Thanks!
Attachments
Schermata 2018-06-26 alle 18.13.23.png
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

There's nothing wrong in the code that I can see... It could be something in the variable declarations or somewhere else. Have you extended NAMETABLE_TO_LOAD to 16 bits, now that it has to hold an address?
User avatar
kikutano
Posts: 115
Joined: Sat May 26, 2018 6:14 am
Location: Italy

Re: Collision detection problem with my project.

Post by kikutano »

This is my variables declarations:

Code: Select all

BALL_POS_X              .rs 1 ;$0000
BALL_POS_Y              .rs 1 ;$0001
BALL_DIRECTION_X        .rs 1
BALL_DIRECTION_Y        .rs 1
BALL_IS_DIR_X_POSITIVE  .rs 1
BALL_IS_DIR_Y_POSITIVE  .rs 1
PADDLE_RIGHT_X_POSITION .rs 1
PADDLE_LEFT_X_POSITION  .rs 1
BRICK_Y_MAX             .rs 1
BALL_IS_COLL_WITH_BRICK .rs 1
BALL_POS_X_ON_TILE      .rs 1
BALL_POS_Y_ON_TILE      .rs 1
BALL_POS_X_ON_TILE_PREV .rs 1
BALL_POS_Y_ON_TILE_PREV .rs 1
BALL_POS_NEXT_TILE      .rs 1
NAMETABLE_TO_LOAD       .rs 2
The sourcecode complete is here: https://pastebin.com/689fHti0

Btw, I've another question, just to understand how Assembly works. In this example code:

Code: Select all

LDX #$00
  LDA LevelPointers, x
  STA NAMETABLE_TO_LOAD 

  LDX #$00 
LoadBackgroundLVLLoop: 
  LDA (NAMETABLE_TO_LOAD), x      
  STA $2007               
  INX                     
  CPX #$00                
  BNE LoadBackgroundLVLLoop
NAMETABLE_TO_LOAD should point to first elemento of LevelPointers right? So when i do LDA (NAMETABLE_TO_LOAD), x and increase the x by INX it should point to the next element of the NAMETABLE? Right?

I know I can't works on the ROM memory to change the tile and hide the bricks on the screen, but I'm learning step by step so any suggests is welcome, even some article to read about! :P
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Collision detection problem with my project.

Post by tokumaru »

kikutano wrote:NAMETABLE_TO_LOAD should point to first elemento of LevelPointers right? So when i do LDA (NAMETABLE_TO_LOAD), x and increase the x by INX it should point to the next element of the NAMETABLE? Right?
Well, each entry in the table is 2 bytes (i.e. a 16-bit address), but the 6502 can only load/store one byte at a time. This means that index 0 is the low byte of the first element, index 1 is the high byte of the first element, index 2 is the low byte of the second element, and so on. This is why my example code multiplies the level index by 2. I also used a "trick" to read from the table... I could've used INX/INY to reach the high byte of the address, but I can just as well leave the index untouched and read from a base address 1 byte after the start of the table (i.e. LevelPointers+1), so final address read is the same, and I saved 2 cycles.

Code: Select all

LDA (NAMETABLE_TO_LOAD), x
This addressing mode doesn't work with X, only with Y, so plan your register use accordingly.
Post Reply