improvement random item generation code (BGR / PPU ADDR)

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

Post Reply
sdm
Posts: 306
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

improvement random item generation code (BGR / PPU ADDR)

Post by sdm » Tue Sep 03, 2019 11:32 am

I wrote a simple code that generates items on the screen in the form of background elements. It works, but I would like to do better. I have a problem generating the HI address using a similar table that I used to generate the LO. Instead, it generates HI by comparing RNG values every 64 and then increasing HI by one.

Code: Select all

;################################################################################

PlaceRandomItem:

	LDA PlaceRandomItem_Flags	;bit 0 code control (ora bit0 = run code, and = lock)
	AND #%00000001
	BEQ PlaceRandomItem_no

	LDY RNG				;generate PPU LO
	LDA TablePPU_LO,y
	STA PPU_LO_Temp

	JSR TablePPU_HI			;jump generate PPU HI

	LDA PlaceRandomItem_Flags	;bit 1 blocks writing to PPU when RNG is above 240 (ora bit1 = run code)
	AND #%00000010
	BEQ PlaceRandomItem_no

	LDA PPU_HI_Temp
	STA $2006
	LDA PPU_LO_Temp
	STA $2006

	LDA ItemTile1of4
	STA $2007
	LDA ItemTile2of4
	STA $2007

	LDA PPU_HI_Temp
	STA $2006
	LDA PPU_LO_Temp
	CLC
	ADC #32
	STA $2006

	LDA ItemTile3of4
	STA $2007
	LDA ItemTile4of4
	STA $2007

	LDA PlaceRandomItem_Flags	;lock the code when done
	AND #%11111100
	STA PlaceRandomItem_Flags

PlaceRandomItem_no:
	RTS

;################################################################################

TablePPU_LO:

	.db $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12,$14,$16,$18,$1A,$1C,$1E
	.db $40,$42,$44,$46,$48,$4A,$4C,$4E,$50,$52,$54,$56,$58,$5A,$5C,$5E
	.db $80,$82,$84,$86,$88,$8A,$8C,$8E,$90,$92,$94,$96,$98,$9A,$9C,$9E
	.db $C0,$C2,$C4,$C6,$C8,$CA,$CC,$CE,$D0,$D2,$D4,$D6,$D8,$DA,$DC,$DE
	.db $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12,$14,$16,$18,$1A,$1C,$1E
	.db $40,$42,$44,$46,$48,$4A,$4C,$4E,$50,$52,$54,$56,$58,$5A,$5C,$5E
	.db $80,$82,$84,$86,$88,$8A,$8C,$8E,$90,$92,$94,$96,$98,$9A,$9C,$9E
	.db $C0,$C2,$C4,$C6,$C8,$CA,$CC,$CE,$D0,$D2,$D4,$D6,$D8,$DA,$DC,$DE
	.db $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12,$14,$16,$18,$1A,$1C,$1E
	.db $40,$42,$44,$46,$48,$4A,$4C,$4E,$50,$52,$54,$56,$58,$5A,$5C,$5E
	.db $80,$82,$84,$86,$88,$8A,$8C,$8E,$90,$92,$94,$96,$98,$9A,$9C,$9E
	.db $C0,$C2,$C4,$C6,$C8,$CA,$CC,$CE,$D0,$D2,$D4,$D6,$D8,$DA,$DC,$DE
	.db $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12,$14,$16,$18,$1A,$1C,$1E
	.db $40,$42,$44,$46,$48,$4A,$4C,$4E,$50,$52,$54,$56,$58,$5A,$5C,$5E
	.db $80,$82,$84,$86,$88,$8A,$8C,$8E,$90,$92,$94,$96,$98,$9A,$9C,$9E
	.db $C0,$C2,$C4,$C6,$C8,$CA,$CC,$CE,$D0,$D2,$D4,$D6,$D8,$DA,$DC,$DE

;################################################################################

TablePPU_HI:                               ;not used

	.db $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
	.db $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
	.db $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
	.db $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20
	.db $21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21
	.db $21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21
	.db $21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21
	.db $21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21
	.db $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
	.db $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
	.db $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
	.db $22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22,$22
	.db $23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23
	.db $23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23
	.db $23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23
	.db $23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23,$23	;attr table, don't generate

;################################################################################

TablePPU_HI:              ;used

	LDA RNG
	CMP #00
	BCC TablePPU_HI_Skip0

	LDA #$20
	STA PPU_HI_Temp

TablePPU_HI_Skip0:

	LDA RNG
	CMP #64
	BCC TablePPU_HI_Skip1

	LDA #$21
	STA PPU_HI_Temp

TablePPU_HI_Skip1:

	LDA RNG
	CMP #128
	BCC TablePPU_HI_Skip2

	LDA #$22
	STA PPU_HI_Temp

TablePPU_HI_Skip2:

	LDA RNG
	CMP #192
	BCC TablePPU_HI_Skip3

	LDA #$23
	STA PPU_HI_Temp

TablePPU_HI_Skip3:

	LDA RNG
	CMP #240
	BCC TablePPU_HI_Skip4

	LDA #$00
	STA PPU_HI_Temp
	STA PPU_LO_Temp

	LDA PlaceRandomItem_Flags	;blocks writing to PPU when RNG is above 240
	AND #%11111101
	STA PlaceRandomItem_Flags

TablePPU_HI_Skip4:
	RTS
Attachments
demoRNGitem.nes
A/B button generate item on screen
(128.02 KiB) Downloaded 129 times

User avatar
NOOPr
Posts: 68
Joined: Tue Feb 27, 2018 10:41 am
Location: Brazil
Contact:

Re: improvement random item generation code (BGR / PPU ADDR)

Post by NOOPr » Tue Sep 03, 2019 12:15 pm

I think you don't need those tables:

Code: Select all

	jsr rng ; rng is a proc that generates a random number from 0 to 255 in A
	and #3	; A have a value from 0 to 3
	clc
	adc #$20	; now A have $20 to $23
	sta PPUADDR	; set HI byte PPU address
	
	jsr rng		; generate another random byte
	cmp #240
	bcc set_hi
	sbc #240	; A have 0 to 239
set_hi:
	sta PPUADDR	; set LO byte PPU address
EDIT: fixed the low byte calculation

sdm
Posts: 306
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: improvement random item generation code (BGR / PPU ADDR)

Post by sdm » Wed Sep 04, 2019 12:41 am

Thanks, in fact, incomparably smaller code. The only problem is that all addresses of 8x8 BGR tiles are randomly drawn, while 16x16 blocks should be. In addition, the values enter the attribute tables. After all, I would also like to see how to change the original code and use the "TablePPU_HI" table, which I had a problem with - curiosity, for learning to analyze the code.

Code: Select all

;################################################################################

PlaceRandomItem2:

	LDA PlaceRandomItem2_Flags
	AND #%00000001
	BEQ PlaceRandomItem2_no

	LDA RNG     	; rng is a proc that generates a random number from 0 to 255 in A
	AND #3		; A have a value from 0 to 3
	CLC
	ADC #$20	; now A have $20 to $23
	STA PPU_HI_Temp	; set HI byte PPU address
   
	LDA RNG   	; generate another random byte
	CMP #240
	BCC set_hi
	SBC #240	; A have 0 to 239
set_hi:
	STA PPU_LO_Temp	; set LO byte PPU address

;-------

	LDA PPU_HI_Temp
	STA $2006
	LDA PPU_LO_Temp
	STA $2006

	LDA ItemTile1of4
	STA $2007
	LDA ItemTile2of4
	STA $2007

	LDA PPU_HI_Temp
	STA $2006
	LDA PPU_LO_Temp
	CLC
	ADC #32
	STA $2006

	LDA ItemTile3of4
	STA $2007
	LDA ItemTile4of4
	STA $2007

	LDA PlaceRandomItem2_Flags
	AND #%11111110
	STA PlaceRandomItem2_Flags

PlaceRandomItem2_no:
	RTS
Attachments
demoRNGitem_2.nes
A/B - old code, STA/SEL - NOOPr code
(128.02 KiB) Downloaded 128 times

User avatar
NOOPr
Posts: 68
Joined: Tue Feb 27, 2018 10:41 am
Location: Brazil
Contact:

Re: improvement random item generation code (BGR / PPU ADDR)

Post by NOOPr » Wed Sep 04, 2019 5:49 am

sdm wrote:The only problem is that all addresses of 8x8 BGR tiles are randomly drawn, while 16x16 blocks should be
Sorry, I missed that.
Just replace

Code: Select all

   jsr rng      ; generate another random byte
   cmp #240
by

Code: Select all

   jsr rng      ; generate another random byte
   asl a        ; an even low byte
   cmp #240

User avatar
tokumaru
Posts: 11646
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: improvement random item generation code (BGR / PPU ADDR)

Post by tokumaru » Wed Sep 04, 2019 8:34 am

Since there are only 16x15=240 16x16-pixel blocks in one screen, a single pseudo-random number provides enough bits to select one of them. You just need to convert this: YYYYXXXX (random byte) into this: 001000YY YY0XXXX0 (NT address). Here's one way to do it:

Code: Select all

	;get random coordinates and make sure they're in range
	jsr rng
	cmp #$f0
	bcc savecoords
	sbc #$f0
savecoords:
	sta xcoord ;save %YYYYXXXX
	and #%11110000 ;keep only %YYYY0000
	sta ycoord

	;calculate the high byte of the address
	lda #%00001000 ;$20 >> 2, ready to be shifted left 2x
	asl ycoord
	rol
	asl ycoord
	rol ;A = %001000YY, ycoord = YY000000
	sta nthigh

	;calculate the low byte of the address
	lda xcoord
	and #00001111 ;keep only %0000XXXX
	asl ;A = %000XXXX0
	ora ycoord ;A = %YY0XXXX0
	sta ntlow
If by any chance you need the attribute address as well (i.e. you need to change the block's colors), the original random number is still available in the xcoord variable, so you can use it to convert this: YYYYXXXX (random byte) into this: 00100011 11YYYXXX (AT address) + 000000YX (index of mask to clear and set attribute quadrants). Here's an idea:

Code: Select all

	;set the high byte of the address
	lda #%00100011
	sta athigh
	
	;put all the bits in place
	lda xcoord
	lsr ;A = %0YYYYXXX, C = lowest X bit
	sta xcoord
	and #%01111000 ;keep only %0YYYY000
	adc #%00000011 ;A = %0YYYYX** (lowest X bit in bit 2)
	lsr ;A = %00YYYYX*
	sta ycoord
	lsr ;A = %000YYYYX
	and #%00000011 ;keep only %000000YX
	sta quadrant ;index of the attribute quadrant
	
	;form the low byte of the address
	lda xcoord
	and #%00000111 ;keep only %00000XXX
	sta xcoord
	lda ycoord
	and #%00111000 ;keep only %00YYY000
	ora xcoord ;A = %00YYYXXX
	ora #%11000000 ;A = %11YYYXXX
	sta atlow
In order to update the attribute byte, you have to read it from the attribute table, clear the space for the new bits, set the new bits, and finally write the byte back. Something like this:

Code: Select all

	;prapare the new attribute bits
	ldx quadrant
	lda attribute
	and quadrantkeep, x
	sta attribute

	;get the attribute byte from VRAM
	lda athigh
	sta $2006
	lda atlow
	sta $2006
	lda $2007
	lda $2007

	;put the new bits in
	and quadrantclear, x
	ora attribute

	;write the byte back to VRAM
	ldx athigh
	stx $2006
	ldx atlow
	stx $2006
	sta $2007
The tables to keep and clear attribute bits for each quadrant are these:

Code: Select all

quadrantkeep:
	.db %00000011
	.db %00001100
	.db %00110000
	.db %11000000

quadrantclear:
	.db %11111100
	.db %11110011
	.db %11001111
	.db %00111111
It's worth noting that since you're using random coordinates, you don't necessarily have to respect the bit layouts I've used here (for random positions, it shouldn't matter which bits go where as long as all target spaces are filled), but I think it's a good idea to code it that way because then you can reuse the exact same logic in case you want to update blocks in known positions as well.

BTW, I haven't tested any of this code, so I may have made mistakes.

sdm
Posts: 306
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: improvement random item generation code (BGR / PPU ADDR)

Post by sdm » Wed Sep 04, 2019 11:30 am

Thanks Tokumaru for the extended code, I will check.
NOOPr wrote:
sdm wrote:The only problem is that all addresses of 8x8 BGR tiles are randomly drawn, while 16x16 blocks should be
Sorry, I missed that.
Just replace

Code: Select all

   jsr rng      ; generate another random byte
   cmp #240
by

Code: Select all

   jsr rng      ; generate another random byte
   asl a        ; an even low byte
   cmp #240
Unfortunately, it doesn't work. :/ (run demo3.nes and press STA/SEL to run code [ A/B old code])
Attachments
demo3.nes
(128.02 KiB) Downloaded 121 times

User avatar
tokumaru
Posts: 11646
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: improvement random item generation code (BGR / PPU ADDR)

Post by tokumaru » Wed Sep 04, 2019 12:13 pm

Using even addresses wouldn't align them to the 16x16-pixel grid anyway, because that'd only affect the horizontal alignment. You should probably use an and #%11011110 instead of that asl a, so that both the X and Y coordinates are made even.

sdm
Posts: 306
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: improvement random item generation code (BGR / PPU ADDR)

Post by sdm » Thu Sep 05, 2019 12:15 pm

I used your code. I just don't know if I understood correctly.

A / B button run code, D-PAD changes the palette. More precisely UP - lda 00000000 sta attribute, DOWN - lda 00000001 sta attribute, LEFT - lda 00000010 sta attribute, RIGHT - lda 00000011 sta attribute (demo4.nes)

Works, but the colors don't always change.

Code: Select all

;################################################################################

PlaceRandomItem3_no:
	RTS
PlaceRandomItem3:		;in NMI

	LDA PlaceRandomItem3_Flags
	AND #%00000001
	BEQ PlaceRandomItem3_no

	LDA RNG_Byte
	CMP #$F0
	BCC savecoords
	SBC #$F0

savecoords:

	STA xcoord		;save %YYYYXXXX
	AND #%11110000		;keep only %YYYY0000
	STA ycoord

				;calculate the high byte of the address

	LDA #%00001000		;$20 >> 2, ready to be shifted left 2x
	ASL ycoord
	ROL A
	ASL ycoord
	ROL A			;A = %001000YY, ycoord = YY000000
	STA nthigh

				;calculate the low byte of the address
	LDA xcoord
	AND #%00001111		;keep only %0000XXXX
	ASL A			;A = %000XXXX0
	ORA ycoord		;A = %YY0XXXX0
	STA ntlow

;-------

	LDA nthigh
	STA $2006
	LDA ntlow
	STA $2006

	LDA tile1
	STA $2007
	LDA tile2
	STA $2007

	LDA nthigh
	STA $2006
	LDA ntlow
	CLC
	ADC #32
	STA $2006

	LDA tile3
	STA $2007
	LDA tile4
	STA $2007

;-------

	lda #%00100011
	sta athigh
				;put all the bits in place
	lda xcoord
	lsr A			;A = %0YYYYXXX, C = lowest X bit
	sta xcoord
	and #%01111000		;keep only %0YYYY000
	adc #%00000011		;A = %0YYYYX** (lowest X bit in bit 2)
	lsr A			;A = %00YYYYX*
	sta ycoord
	lsr A			;A = %000YYYYX
	and #%00000011		;keep only %000000YX
	sta quadrant		;index of the attribute quadrant

				;form the low byte of the address
	lda xcoord
	and #%00000111		;keep only %00000XXX
	sta xcoord
	lda ycoord
	and #%00111000		;keep only %00YYY000
	ora xcoord		;A = %00YYYXXX
	ora #%11000000		;A = %11YYYXXX
	sta atlow

	ldx quadrant
	lda attribute
	and quadrantkeep,x
	sta attribute
				;get the attribute byte from VRAM
	lda athigh
	sta $2006
	lda atlow
	sta $2006
	lda $2007
	lda $2007
				;put the new bits in
	and quadrantclear,x
	ora attribute
				;write the byte back to VRAM
	ldx athigh
	stx $2006
	ldx atlow
	stx $2006
	sta $2007

	LDA PlaceRandomItem3_Flags		;lock code
	AND #%11111100
	STA PlaceRandomItem3_Flags

	RTS
EDIT:
I chang D-PAD (UDLR) writing to "attribute" , I changed to:
UP - lda 00000000 sta attribute
DOWN lda 01010101, LEFT - 10101010, RIGHT 11111111.
(demo4b.nes).

Now as we hold down one of the directions (+A/B button) the palette are changing.
Attachments
demo4b.nes
(128.02 KiB) Downloaded 124 times
demo4.nes
(128.02 KiB) Downloaded 128 times

User avatar
tokumaru
Posts: 11646
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: improvement random item generation code (BGR / PPU ADDR)

Post by tokumaru » Thu Sep 05, 2019 1:01 pm

sdm wrote:EDIT:
I chang D-PAD (UDLR) writing to "attribute" , I changed to:
UP - lda 00000000 sta attribute
DOWN lda 01010101, LEFT - 10101010, RIGHT 11111111.
Yup, the 2 attribute bits are supposed to be repeated 4 times in the source byte, so that any of the pairs can be selected by masking out the others. You can do it differently and shift a single pair of bits depending on the quadrant, but I prefer this way.

One important thing to keep in mind is that normally you'd calculate all the data and addresses beforehand, during render time, and then during vblank you'd only write the data to VRAM, since vblank time is a scarce resource. Unless you don't need that much VRAM bandwidth. Just pay attention to how much of your vblank time is being used so you don't go over the edge.

sdm
Posts: 306
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: improvement random item generation code (BGR / PPU ADDR)

Post by sdm » Fri Sep 06, 2019 12:14 am

Thanks. Yes, I know that. Of course, the code itself will be outside VBLANK, and only the transfer of value to $ 2007 through $ 2006 in NMI time.
In this case, everything was in NMI because of the test code.

Post Reply