SusiKette's project help thread

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

User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

SusiKette's project help thread

Post by SusiKette »

Instead of making new threads every time I have a problem or something to ask, I decided to make a one thread that I post whenever I'm in need of help.

After several years of not doing any NES stuff, I'm trying to remember things. I'm currently making the initialization code. The only functions if does is do the usual reset stuff, load the default palettes to the PPU and then turn rendering on. However, When I run the ROM, the backdrop color is desaturated red instead of black that I set it to. FCEUX's shows Pattern Tables, Palettes and Nametables as the default grey color when you open any of the PPU related tools. I have tried to debug this issue for quiet some time now and still can't find the cause for this. It's probably some really stupid mistake that I have just forgotten or something.

Bonus question:
How does NESASM3's bank logic work? I followed a tutorial that showed a code where the banks were assigned in a particular way. It didn't really explain the banks other than saying that everything is arranged in 8 Kb (or 16 Kb) sized banks. If I rearrange the banks to contain something different (regardles of using the correct .org $---- to set the proper address they need to be in ROM) the ROM just won't work. I'm assuming these banks are also used when doing bank switching, but for the sake of simplicity; how do they work on a ROM that has no mapper?

EDIT: The attachment has my project source code so far as well as a compiled ROM.
Attachments
main.zip
(7.5 KiB) Downloaded 208 times
Avatar is pixel art of Noah Prime from Astral Chain
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: SusiKette's project help thread

Post by Kasumi »

Banks really are just 8KB segments in NESASM.
.bank 0 is the first 8KB of your ROM.
.bank 1 is the second 8KB of your ROM.
etc.

The NES ROM format requires PRG banks to be first, CHR banks (if there are any) to be last. The 6502 requires vectors to be at $FFFA-$FFFF so it knows where to start running your code.

One thing that's a bit of a gotcha is that the ROM header (.inesprg) specifies the number of SIXTEEN KB PRG banks.
So if that value is 1, you need .bank 0 and .bank 1. .bank 2 would be your first CHR bank.
If that value is 2, you need .bank 0, .bank 1, .bank 2, and .bank 3. .bank 4 would be your first CHR bank.

If you are using no mapper, the order of your banks in the ROM file directly corresponds to their order in NES Memory. So the last PRG bank (either .bank 1 or .bank 3 depending on if you have one 16KB PRG bank for two) must have your vectors in its last six bytes. That's less about NESASM and more about NES.

So as far as bank organization, you could totally put .bank 1 as it is before .bank 0 as it is in your file and it'd work. But you're still beholden to the 6502 vector rules. You can't only have .org $FFFA with your vectors in bank 0 because then it doesn't end up in the right place in NES memory.


Edit: To further explain that... .org helps your assembler make references. Say there's a label and a jump to that label:

Code: Select all

.org $8000
label:
jmp label
That would get assembled as jmp $8000. And.

Code: Select all

.org $FFF0
label:
jmp label
That would get assembled as jmp $FFF0
But that's really it. You could do this:

Code: Select all

.bank 0
.org $8000
label:
jmp label
.bank 1
.org $8000
label2:
jmp label2
Even though bank 1 is .org'd to $8000, that's actually not where the code will end up in memory. (Assuming no mapper.) Both jmp label2 and jmp label would go to label. (Because in a mapperless ROM, the first 8KB of your ROM file will get mapped to $8000. Bank 1 would get mapped to $A000... So label2 is REALLY $A000 and should have been .org'd that way so those jmps would make sense. That's similar to why a .org $FFFA wouldn't work in bank 0 (in a mapper less ROM.) It still doesn't end up in $FFFA in NES memory.

And you might wonder why the assembler can't do it automatically... and the reason is because mappers can sort of put any part of the ROM anywhere. Bank 0 and bank 1 could BOTH be designed to be at $8000. (Though not at the same time)

Code: Select all

ldx #$7F;Why $7F and not $FF?
stx stack_ptr
txs
You've got some subtle problems. This never writes $FC to $0200:

Code: Select all

MemoryClear:
 ldx #$FF
 lda #$FC

MemLoop1:
 sta $0200,x
 dex
 cpx #$00
 bne MemLoop1
At MemLoop1, imagine X is 1. sta $0200,x will make $0201 equal to $FC. Dex. X is 0. 0 is equal to 0, so we don't branch again. So an sta $0200,x where x was zero never happens.
You (usually) don't need to do a comparison with zero, because the zero flag is set/cleared by most instructions.

This would do the same thing (in this context):

Code: Select all

MemoryClear:
 ldx #$FF
 lda #$FC

MemLoop1:
 sta $0200,x
 dex
 bne MemLoop1
But that still missed zero. Instead, start with zero and count up. Getting back to zero will still break the loop, but that's fine since you did zero at the start before any conditional branch check:

Code: Select all

MemoryClear:
 ldx #$0
 lda #$FC

MemLoop1:
 sta $0200,x
 inx
 bne MemLoop1
The same problems are in your second clear memory loop. Here's both with that change:

Code: Select all

MemoryClear:
 ldx #0
 lda #$FC
MemLoop1:
 sta $0200,x
 inx
 bne MemLoop1
;We know X is zero, because the branch didn't happen.
;So no need to reload it
lda #$00;For the astute, txa would also work. Since, again, we know X is zero.
MemLoop2:
 sta $00,x
 sta $0100,x
 sta $0300,x
 sta $0400,x
 sta $0500,x
 sta $0600,x
 sta $0700,x
 inx
 bne MemLoop2
Your NMI will break your game because it doesn't restore the registers from the stack correctly.

Code: Select all

NMI:
 pha;Pushes A to the stack: Current Stack: A
 txa
 pha;Pushes X to the stack: Current Stack: AX
 tya
 pha;Pushes Y to the stack: Current Stack: AXY
;...
 SkipNMI:
 pla;Get the old value for Y into A ;Current stack: AX
 tya;Transfer Y to A... now the old value for Y is completely gone.
 pla;Get the old value for X into A ; Currently stack: A
 txa;Transfer X to A... now the old value for X is completely gone.
 pla
You want:

Code: Select all

 SkipNMI:
 pla
 tay;So that the old value for Y is back in Y when we return
 pla
 tax;So that the old value for X is back in X when we return
 pla
The way you're writing to PPU memory is not correct.

Code: Select all

lda HIGH(PPU_PAL);First, you need the # here. lda HIGH(PPU_PAL) will make
;A the value stored in RAM location $3F rather than the constant #$3F
;As opposed to lda #HIGH(PPU_PAL) which would make A equal to #$3F

;You write the high byte to PPU_ADDR
 sta PPU_ADDR;But you also need to write the low byte and you don't.
;X and Y should be switched here. ,x is used as an offset in bg_pal,x, so X is the one you should load
;with update_pal_start
 ldy update_pal_start
 ldx #$00

UpdatePalLoop:
 lda bg_pal1,x
 sta PPU_DATA,y;$2007 (PPU_DATA) is the register that writes a byte to the PPU. Not $2008. Not $2009.
;If you write to $2007,y and Y is not zero, you're usually not actually writing a byte
 iny
 inx
 cpx update_pal_size
 bne UpdatePalLoop

NoPalUpdate:
 rts
So the fixes for all that look like:

Code: Select all

UpdatePal:
 lda update_pal
 beq NoPalUpdate
  bit $2002;Makes it so our next write PPU_ADDR will write the high byte
 lda #HIGH(PPU_PAL)
 sta PPU_ADDR
 lda #LOW(PPU_PAL)
 sta PPU_ADDR
 ldx update_pal_start
; ldy #$00;No need for this

UpdatePalLoop:
 lda bg_pal1,x
 sta PPU_DATA
;iny;No need for this
 inx
 cpx update_pal_size
 bne UpdatePalLoop

NoPalUpdate:
 rts
If you do all these fixes, your ROM will work. But the palette will still not be black.

Code: Select all

DefaultPal:
 .db $00,$30,$21,$02
 .db $00,$30,$21,$02
 .db $00,$30,$21,$02
 .db $00,$30,$21,$02

 .db $00,$30,$21,$02
 .db $00,$30,$21,$02
 .db $00,$30,$21,$02
 .db $00,$30,$21,$02
$00 is not black. Try $0F.

Edit: Oh! I was wondering how sprites were moving on the screen when there was seemingly no code to make them do anything.

lda #$02 is different than lda $02.

Code: Select all

;If #$FF is stored at $02
lda $02;A = $FF
lda #$02;A = $02
So this code...

Code: Select all

lda $00
 sta OAM_ADDR
 lda $02
 sta OAM_DMA
Draws sprites using RAM at the zero page when the value at $02 is zero. You want:

Code: Select all

lda #$00
 sta OAM_ADDR
 lda #$02
 sta OAM_DMA
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: SusiKette's project help thread

Post by thefox »

Could you please consider changing your signature? It's really distracting.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: SusiKette's project help thread

Post by SusiKette »

Kasumi wrote:Banks really are just 8KB segments in NESASM... *cut*
Thanks for taking the time to make such a long post :)
Everything is working as it should for now.

Now for the next set of questions:

1. If I have, lets say a 64kb chr file and I put it like this:

Code: Select all

 .bank 0
 .incbin "64kb_file.chr"
Can NESASM divide the file to multiple banks as needed or do I have to split the file into smaller chunks and do it manually?

2. Is it common for games to update palettes and bank registers every frame (even if it's not needed)? I noticed this oddity when I was looking through a particular game's NMI.

3. How to set sprites to use second the pattern table in 8x16 mode? According to the wiki, the bit that controls which pattern table sprites use is ignored in 8x16 mode, yet I've seen games use the second pattern table in this mode.

4. Is it possible to split the stack into several smaller segment by changing the stack pointer around the "stack page" and not affect the contents of another segment?
Avatar is pixel art of Noah Prime from Astral Chain
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SusiKette's project help thread

Post by tepples »

SusiKette wrote:1. If I have, lets say a 64kb chr file and I put it like this:

Code: Select all

 .bank 0
 .incbin "64kb_file.chr"
Can NESASM divide the file to multiple banks as needed or do I have to split the file into smaller chunks and do it manually?
It might depend on which version of NESASM you use.

Aside: If you're putting CHR data in bank 0, that'll work only in a CHR RAM situation. Banks 2 and 3 of Haunted: Halloween '85 are all sprite tiles, for instance.
SusiKette wrote:2. Is it common for games to update palettes and bank registers every frame (even if it's not needed)? I noticed this oddity when I was looking through a particular game's NMI.
Some games don't come close to using all the time in vertical blanking for video memory updates. This allows them to have more of a "fixed function" VRAM update routine that makes all updates that might be needed without having to check a bunch of enable flags every time.
SusiKette wrote:3. How to set sprites to use second the pattern table in 8x16 mode? According to the wiki, the bit that controls which pattern table sprites use is ignored in 8x16 mode, yet I've seen games use the second pattern table in this mode.
Set bit 0 of the tile index (byte 4n+1 of OAM) to true (1). For example, tile $68 draws a sprite using tiles at $0680 and $0690, and $69 instead uses $1680 and $1690.
SusiKette wrote:4. Is it possible to split the stack into several smaller segment by changing the stack pointer around the "stack page" and not affect the contents of another segment?
Yes. The Popslide VRAM updater, for instance, uses this to switch between a program's main stack at $01C0-$01FF and an update buffer at $0108-$01AF.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: SusiKette's project help thread

Post by Bregalad »

thefox wrote:Could you please consider changing your signature? It's really distracting.
I added it to my adblock list, personally, and it works wonders.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: SusiKette's project help thread

Post by rainwarrior »

I don't know what was in SusiKette's signature (it seems to have been removed?) but this board has user preferences that let you turn off signatures entirely:
https://forums.nesdev.com/ucp.php?i=prefs&mode=view
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: SusiKette's project help thread

Post by unregistered »

rainwarrior wrote:I don't know what was in SusiKette's signature (it seems to have been removed?)
It was a very large (very tall) graphic; SusiKette must have removed it after thefox's request. : )
tepples wrote:
SusiKette wrote:4. Is it possible to split the stack into several smaller segment by changing the stack pointer around the "stack page" and not affect the contents of another segment?
Yes. The Popslide VRAM updater, for instance, uses this to switch between a program's main stack at $01C0-$01FF and an update buffer at $0108-$01AF.
That's really cool - and fantastic to learn! :mrgreen: :) Thank you SusiKette for being brilliant and asking that and thank you tepples for answering. :D
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: SusiKette's project help thread

Post by SusiKette »

Now that I have the palette update working when using stack, I guess I should do a palette fader next. I probably need some simple example to help me getting started and then possibly edit to fit my needs better.

Edit: A couple of ways that came in mind for this:
1. Subtract #$10 from the color (or #$20 if original color is#$3x). If result sets the negative flag, then color is set to #$0F. Fading in might be a bit harder since the fader would have to know what color to fade to.

2. I guess a bit easier, but more space consuming would be to have pre-made fade palettes and use indices to figure which one to use based on the "fade step".
Last edited by SusiKette on Thu Apr 19, 2018 11:34 pm, edited 1 time in total.
Avatar is pixel art of Noah Prime from Astral Chain
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: SusiKette's project help thread

Post by Kasumi »

Here's the NES palette with numbers scribbled all over it:
Image
The $3X row is lightest, $2X is a little darker, $1X is darker still, $0X is darkest. If you need to go darker than $0X, you can only go to black.

So to make the palette darker in a simple way, all you need to do is subtract $10 from every color each frame. If a color is already in the $0X row, subtracting $10 will clear the carry. And then you store black instead.

Code: Select all

ldx #15;Last color in the palette.
palettefadeloop:
lda palette,x;Load
sec
sbc #$10
bcs storedarker;It the subtract didn't cross zero, store the result
lda #$0F;Else load black
storedarker:
sta palette,x
dex
bpl palettefadeloop
One Caveat! $0D is a color to avoid using, since some TVs don't like it. So you want a special case to avoid it appearing. The column is affects is $XD. You still want $3D to go to $2D, but you want $2D to go straight to black.

Code: Select all

ldx #15;Last color in the palette.
palettefadeloop:
lda palette,x;Load
cmp #$2D
beq fadetoblack
sec
sbc #$10
bcs storedarker;It the subtract didn't cross zero, store the result
fadetoblack:
lda #$0F;Else load black
storedarker:
sta palette,x
dex
bpl palettefadeloop
Fade ins are a touch more complicated because every index has a different end point, but give it a shot based on this.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: SusiKette's project help thread

Post by SusiKette »

You were pretty fast with replying. I was in the middle of editing my post when you posted :p

Fading in might work if you either store the target palette to RAM or use indices based on where the target palette is located.

One method of implementing the fader could be by having it as a part of the palette buffer subroutine. If a flag for it is set, a counter will count every frame from set value (fade speed) to zero. Then a palette update would be requested and the fade step counter would be incremented or decremented based on fade direction. The fade step variable is used to calculate how much to subtract from the original color and then push it to the stack.
Avatar is pixel art of Noah Prime from Astral Chain
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: SusiKette's project help thread

Post by Kasumi »

Subtracting from the original values for both fade in and fade out is an excellent idea.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: SusiKette's project help thread

Post by SusiKette »

The palette fading itself seems to work perfectly, although for some reason during this process the screen is shaking. It might be some weird thing with the PPU that I'm not aware of since the variables I'm loading to the $2005 register stay at #$00 the entire time. I'm once again including both source code and compiled ROM.

EDIT: The nametable viewer does show the scroll position jumping around, but I'm still not sure what could cause this
Attachments
main.zip
(10.01 KiB) Downloaded 202 times
Avatar is pixel art of Noah Prime from Astral Chain
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: SusiKette's project help thread

Post by rainwarrior »

$2006 and $2005 are shared access to the same internal register, one makes it easy for addressing (for data upload) and one makes it easy for scrolling.

If you write $2006 to update the palettes, the scroll position is clobbered, so you need to write the scroll to $2005 before the end of vblank to restore the scroll position.

So, in the NMI when updating the graphics data, always set the scroll position last.

More info here: https://wiki.nesdev.com/w/index.php/PPU_scrolling
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: SusiKette's project help thread

Post by SusiKette »

Now that you mention it I remember that begin the case. I guess I forgot it during the BRK I had from asm.

Next up would be background update. I want to try to make the actual code on my own at first, but I need a "hint" to help a bit. So my question is:
What lookup tables do I need (aside the compressed level data)? Scrolling is vertical (I'm making a shoot em up).
I was planning on compressing the levels into 16x16 metatiles, then to rows on metatiles, and then to screens. Would the compressing to rows be useful if you keep decoding time and the additional compression rate in mind?
Avatar is pixel art of Noah Prime from Astral Chain
Post Reply