It is currently Thu May 24, 2018 5:13 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Apr 17, 2018 2:17 am 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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 39 times
Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 2:58 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
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:
.org $8000
label:
jmp label

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

That would get assembled as jmp $FFF0
But that's really it. You could do this:
Code:
.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:
ldx #$7F;Why $7F and not $FF?
stx stack_ptr
txs


You've got some subtle problems. This never writes $FC to $0200:
Code:
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:
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:
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:
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:
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:
 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:
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:
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:
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:
;If #$FF is stored at $02
lda $02;A = $FF
lda #$02;A = $02

So this code...
Code:
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:
lda #$00
 sta OAM_ADDR
 lda #$02
 sta OAM_DMA

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 7:12 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3066
Location: Tampere, Finland
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


Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 8:07 am 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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:
 .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?


Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 8:15 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20064
Location: NE Indiana, USA (NTSC)
SusiKette wrote:
1. If I have, lets say a 64kb chr file and I put it like this:
Code:
 .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.


Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 12:17 pm 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7432
Location: Chexbres, VD, Switzerland
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.


Top
 Profile  
 
PostPosted: Tue Apr 17, 2018 12:35 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6291
Location: Canada
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


Top
 Profile  
 
PostPosted: Wed Apr 18, 2018 8:24 am 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 871
Location: cypress, texas
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


Top
 Profile  
 
PostPosted: Thu Apr 19, 2018 11:05 pm 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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.

Top
 Profile  
 
PostPosted: Thu Apr 19, 2018 11:31 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
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:
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:
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.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Thu Apr 19, 2018 11:51 pm 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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.


Top
 Profile  
 
PostPosted: Fri Apr 20, 2018 12:23 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
Subtracting from the original values for both fade in and fade out is an excellent idea.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Fri Apr 20, 2018 5:42 am 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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 34 times
Top
 Profile  
 
PostPosted: Fri Apr 20, 2018 10:08 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6291
Location: Canada
$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


Top
 Profile  
 
PostPosted: Mon Apr 23, 2018 2:49 am 
Offline
User avatar

Joined: Fri Mar 16, 2018 1:52 pm
Posts: 29
Location: Finland
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?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next

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