VRC6 CHR Bank Malformed?

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

puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

So now that I've got everything working, I'd like to discuss the best way to switch banks. "Best" meaning the method that uses the least amount of ROM and CPU cycles. This is what I've come up with so far and it does the job, but is there a better way?

Code: Select all

SwitchBankCHR:
	;this code will change all eight banks of CHR-ROM. 
	;input
	;A = the bank you want to switch to. Currently, anything other than 0 or 1 is an invalid input. Call this with the following:
	
	;lda bank_number
	;sta temp
	;jsr SwitchBankCHR
	
	
	
	txa
	pha
	tya
	pha
	
	
	lda currentBankCHR	;backup the current bank.
	sta prevBankCHR
	
	lda temp			;take the input and make it the new current bank.
	sta currentBankCHR
	tax					;transfer the bank number to X to use it as an index.
	
	ldy #0
	
;GOAL: Each bank follows this pattern: 00 01 02 03 04 05 06 07. The next bank is each of those numbers plus 8. For the number of banks we have we need to be able to quickly add or subtract 8 enough times to get to the desired bank. 

	lda chr_lookup,x	;this equals x times 8. We will use this as our base value.
	sta temp	
	
SwitchBankCHRloop:
	lda temp			;if our input equals 1 this should be 8 on the first iteration of the loop. Next is 9, then 10, and so on.
	sta softR0,y		;we initialized this to zero so next time it will be 1, then 2, and so on. These "soft" registers will be copied to the real ones during nmi
	
	inc temp
	iny
	cpy #$08
	bne SwitchBankCHRloop
	
	pla
	tay
	pla
	tax
	rts
	
chr_lookup:
	.db $00, $08, $10, $18, $20, $28, $30, $38
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: VRC6 CHR Bank Malformed?

Post by Controllerhead »

puppydrum64 wrote: Fri May 14, 2021 4:14 pm

Code: Select all

iny
cpy #$08
bne SwitchBankCHRloop
Generally best to load the highest value of a loop and decrement through it, as long as there are no good reasons not to. The Z (zero) and N (negative) flags are set through most operations, the exception being the storage ones.

Code: Select all

LDY #$07
myLoop:
  
  ; Do the loop stuff
  
  DEY
BPL myLoop  ; Break on $FF
Image
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

Ahh, I see! That lets you avoid having to do a comparison. (Funnily enough the Nerdy Nights tutorial uses CMP/CPX/CPY #$00) in a few places which to my knowledge is never needed.)

EDIT: In this case I don't think that's worth it. I'm using y for both the loop counter and as an index to ensure I write to the correct part of zero page RAM, since both happen to increment in the same way. I'd have to make everything backwards and my first attempt caused it to fail so I'm just going to leave it as is. This is what I think I would have to change to get it to work again but I must have done something wrong:

Code: Select all

SwitchBankCHR:
	;this code will change all eight banks of CHR-ROM. 
	;input
	;A = the bank you want to switch to. Currently, anything other than 0 or 1 is an invalid input.
	
	txa
	pha
	tya
	pha
	
	
	lda currentBankCHR	;backup the current bank.
	sta prevBankCHR
	
	lda temp			;take the input and make it the new current bank.
	sta currentBankCHR
	tax					;transfer the bank number to X to use it as an index.
	
	ldy #7
	
;GOAL: Each bank follows this pattern: 00 01 02 03 04 05 06 07. The next bank is each of those numbers plus 8. For the number of banks we have we need to be able to quickly add or subtract 8 enough times to get to the desired bank. 

	lda chr_lookup,x	;this equals x times 8. We will use this as our base value.
	sta temp	
	
SwitchBankCHRloop:
	lda temp			;if our input equals 1 this should be 8 on the first iteration of the loop. Next is 9, then 10, and so on.
	sta softR0,y		;we initialized this to zero so next time it will be 1, then 2, and so on.
	
	dec temp
	dey
	bpl SwitchBankCHRloop
	
	pla
	tay
	pla
	tax
	rts
	
chr_lookup:
	.db $07, $0f, $17, $1f, $27, $27, $37, $3f
Somehow this only affected R0 and not the other banks.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: VRC6 CHR Bank Malformed?

Post by Pokun »

puppydrum64 wrote: Wed May 12, 2021 3:46 pm I don't think padding is technically necessary either, but NESASM tends to fill empty space with #$ff.
Padding is necessary to get the right file size, so NESASM is doing it right. Some assemblers pads with 0 instead of $FF.

puppydrum64 wrote: Fri May 14, 2021 4:42 pm Ahh, I see! That lets you avoid having to do a comparison. (Funnily enough the Nerdy Nights tutorial uses CMP/CPX/CPY #$00) in a few places which to my knowledge is never needed.)
The comparison instructions are not needed before a branch in a loop if you used increment or decrement type of instructions and just want to compare the loop counter with 0 (since INC/DEC sets the zero flag properly). I think Nerdy Nights uses CMP even when it's not needed just to avoid confusing beginners. There is no need to learn to optimize the code that soon.

CMP is needed if you want to compare the accumulator with a specific value other than 0.
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: VRC6 CHR Bank Malformed?

Post by unregistered »

Pokun wrote: Sat May 15, 2021 3:46 pm CMP is needed if you want to compare the accumulator with a specific value other than 0.
I have to disagree with this bc any increment/decrement instruction can adjust both the zero AND negative flag. So, if you were wanting to run a loop #$80 times, you could:

Code: Select all

ldx #$00
- inx 
bpl - ;that will loop #$80 times bc when 
;x reaches #$80 the negative flag will be
;set and the loop will end

;it starts x at 00 so 1+#$7F == #$80 times :)
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

unregistered wrote: Sat May 15, 2021 4:07 pm
Pokun wrote: Sat May 15, 2021 3:46 pm CMP is needed if you want to compare the accumulator with a specific value other than 0.
I have to disagree with this bc any increment/decrement instruction can adjust both the zero AND negative flag. So, if you were wanting to run a loop #$80 times, you could:

Code: Select all

ldx #$00
- inx 
bpl - ;that will loop #$80 times bc when 
;x reaches #$80 the negative flag will be
;set and the loop will end

;it starts x at 00 so 1+#$7F == #$80 times :)
Clever! It would seem that there are only a few numbers that can set flags like this. I'm guessing those would be #$00, #$FF, #$80, and #$7F. I've never used Branch if Overflow set so I couldn't tell you about that one.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: VRC6 CHR Bank Malformed?

Post by Pokun »

Yeah nice trick, though there are still other situations than loops when you may want check if a variable is a certain non-zero value, so of course CMP isn't a redundant instruction. Like checking a signature in backup RAM.

The Overflow flag is the most seldom used flag in 6502 (arithmetic overflow is handled automatically in math operations so V is normally not needed). It becomes useful when you need to do comparisons with signed numbers.
CMP is really the same as SEC+SBC as far as flags are concerned (subtractions always results in 0 if minuend and subtrahend is equal so that sets the Zero flag), but CMP doesn't save the result of the subtraction in the accumulator unlike SBC.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: VRC6 CHR Bank Malformed?

Post by tokumaru »

Counting down in loops is indeed a common trick to avoid an explicit compare at the end, but if you absolutely need to count up, you can adjust indices and addresses so the count ends at $80, allowing you to use the N flag to break out of the loop. For example, this code:

Code: Select all

  ldx #$00
loop:
  lda buffer, x
  sta $2007
  inx
  cpx #$20
  bne loop
Can be rewritten like this:

Code: Select all

  ldx #($80-$20)
loop:
  lda buffer-($80-$20), x
  sta $2007
  inx
  bpl loop
Depending on where "buffer" is in memory, this could end up resulting in the lda being 1 cycle slower due to page-crossing, but since the compare instruction takes 2 cycles, this will still be faster. To prevent any page-crossing, just make sure that the array is located in the upper half of its memory page (i.e. > $XX7F).
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

Controllerhead wrote: Fri May 14, 2021 4:32 pm
puppydrum64 wrote: Fri May 14, 2021 4:14 pm

Code: Select all

iny
cpy #$08
bne SwitchBankCHRloop
Generally best to load the highest value of a loop and decrement through it, as long as there are no good reasons not to. The Z (zero) and N (negative) flags are set through most operations, the exception being the storage ones.

Code: Select all

LDY #$07
myLoop:
  
  ; Do the loop stuff
  
  DEY
BPL myLoop  ; Break on $FF
Actually, now that I think about it, let's consider the following:

Code: Select all

LDY #$07
myLoop:

; do loop stuff

DEY
BNE myLoop
And compare it with this

(Assume Y is already zero)

Code: Select all

myLoop:

;do loop stuff

INY
CPY #$07
BNE myLoop
When y (or x if we're using x) is already zero it seems to make no difference since both LDY and CPY take 2 CPU cycles to complete.
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: VRC6 CHR Bank Malformed?

Post by Controllerhead »

puppydrum64 wrote: Sun May 16, 2021 2:01 pm When y (or x if we're using x) is already zero it seems to make no difference since both LDY and CPY take 2 CPU cycles to complete.
Multiple times?
Image
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

Good point
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: VRC6 CHR Bank Malformed?

Post by Controllerhead »

puppydrum64 wrote: Sun May 16, 2021 2:14 pm Good point
I generally like to design my loops to come to a happy medium between instruction space and efficiency. Consider this full attribute table updater here. I make 8 passes with 8 stores per pass and have the data arranged so i can directly address each part on each pass.

Code: Select all

LDX set_bufferAttribute1 ;  7 if updating, 0 if not.
BEQ +
  LDA #$27
  STA PPU_ADDR
  LDA #$C0
  STA PPU_ADDR

  -
    LDA attribute1_Buffer, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$08, x 
    STA PPU_DATA
			
    LDA attribute1_Buffer+$10, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$18, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$20, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$28, x 
    STA PPU_DATA
			
    LDA attribute1_Buffer+$30, x
    STA PPU_DATA

    LDA attribute1_Buffer+$38, x
    STA PPU_DATA		
			
    DEX
  BPL -
+
If i were to loop it 64 times, that would take many more cycles. If i wrote 64 straight loading and storing instructions, that would take much more space. I like to find a nice balance.
We are in the realm of personal opinion of course.
Image
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: VRC6 CHR Bank Malformed?

Post by puppydrum64 »

Controllerhead wrote: Sun May 16, 2021 2:25 pm
puppydrum64 wrote: Sun May 16, 2021 2:14 pm Good point
I generally like to design my loops to come to a happy medium between instruction space and efficiency. Consider this full attribute table updater here. I make 8 passes with 8 stores per pass and have the data arranged so i can directly address each part on each pass.

Code: Select all

LDX set_bufferAttribute1 ;  7 if updating, 0 if not.
BEQ +
  LDA #$27
  STA PPU_ADDR
  LDA #$C0
  STA PPU_ADDR

  -
    LDA attribute1_Buffer, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$08, x 
    STA PPU_DATA
			
    LDA attribute1_Buffer+$10, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$18, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$20, x
    STA PPU_DATA
			
    LDA attribute1_Buffer+$28, x 
    STA PPU_DATA
			
    LDA attribute1_Buffer+$30, x
    STA PPU_DATA

    LDA attribute1_Buffer+$38, x
    STA PPU_DATA		
			
    DEX
  BPL -
+
If i were to loop it 64 times, that would take many more cycles. If i wrote 64 straight loading and storing instructions, that would take much more space. I like to find a nice balance.
We are in the realm of personal opinion of course.
That's quite clever. Could I use that in my code?
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: VRC6 CHR Bank Malformed?

Post by Controllerhead »

puppydrum64 wrote: Sun May 16, 2021 7:13 pm Could I use that?
Sure! You'll have to allocate and load the buffer correctly. I should warn you that in its current state it would be awkward to work with manually, and this isn't something that i do: it's part of an engine with an accompanying editor. I just wanted to post a code snippet that demonstrated my thought process.
Image
Post Reply