Completely misunderstood how banks and addresses work?

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
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Completely misunderstood how banks and addresses work?

Post by Skelpolu »

Hey, everyone

I've been following Bunnyboy's Nerdy Nights series for a couple of weeks now. Assembler used is NESASM3.
I was moving onto the sound-section of the series, lead by Metal Slime.
Now, in this tutorial, Metal Slime has added another 16KB of PRG code memory, adding up to a total of 32KB (max. without using mappers). So far so good.

However, when I add another 16KB of memory for the code to be stored in,

Code: Select all

  .inesprg 2   ; 2x 16KB PRG code
Then the code simply breaks entirely.
I've tried debugging it myself, and had this line of logic in my head:
Obviously, now that there's twice as much PRG code memory, the banks have shifted, too.
0-1 used to be for PRG code, bank 2 for graphics. Double the PRG banks, and we've got 0-3 for PRG code, and bank 4 for graphics.
And thus I've changed this line,

Code: Select all

  .bank 2 ;<---- this one
  .org $0000
  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1

  .bank 4 ;<---- to this one
  .org $0000
  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1
But it doesn't work, regardless. Could it be that the addresses shift by adding the 16 KB?
The same thing happens when applied to this example file.

I feel I might have mixed up a couple of things there.
TL;DR: Can someone help me figure out where my logic went wrong?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Completely misunderstood how banks and addresses work?

Post by tepples »

After you've expanded your project's PRG ROM to four 8192-byte banks, here are two things to try:
  • The reset and NMI vectors need to go at the end of bank 3, not bank 1.
  • Have you tried putting at least some data in all four banks (0-3)?
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

I've tried switching to bank 3 for the vector-configuration with no luck - I'm sure it's because I misunderstood what I was supposed to try out.
I'm nearly 100% this issue is a thing solely because I didn't quite understand how banks work.

For instance, I am more than confused as to why, in the Nerdy Nights series, they start bank 0 at $C000, instead of $8000, where PRG code starts.
Of course my code does that as well, but I have no idea why. To me it seems more of a hassle having it start so far out. I reckon putting each bank next to each other ($8000-A000, $A000-C000, etc.)

From Nerdy Nights alone I wasn't able to figure out exactly how it all works. Is there a good read on that, if possible in relation to 6502 NES assembly?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Completely misunderstood how banks and addresses work?

Post by tepples »

PRG banks for NROM-128, which has 16384 bytes of PRG ROM, are located at $C000 and $E000. They are also mirrored into $8000 and $A000, but their canonical locations are $C000 and $E000.

PRG banks for NROM-256, which has 32768 bytes of PRG ROM, are located at $8000, $A000, $C000, and $E000.

Could you attach your code so that others can look at it to help you determine what's going wrong?
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

Sure, I can. I just thought it wouldn't make much sense before, since it's so close to the original example by Bunnyboy.
After reading up on NROM again, I immediately realized you said is correct - when only 16 KB are set to be used as PRG code, it'll be mirrored to fill in the rest.

Anyhow, down below you can find the code, unaltered, working and ready for use of 16 KB, not 32 KB. When run, it should display the setup Bunnyboy made in this tutorial.
Attachments
problemzone.asm
(6.02 KiB) Downloaded 102 times
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Completely misunderstood how banks and addresses work?

Post by tokumaru »

Currently you have:

Code: Select all

  .bank 0
  ;(stuff mapped to CPU $C000-$DFFF)
  .bank 1
  ;(stuff mapped to CPU $E000-$FFFF)
  .bank 2
  ;(stuff mapped to PPU $0000-$1FFF)
To expand this to 32KB of PRG-ROM it has to become:

Code: Select all

  .bank 0
  ;(stuff mapped to CPU $8000-$9FFF)
  .bank 1
  ;(stuff mapped to CPU $A000-$BFFF)
  .bank 2
  ;(stuff mapped to CPU $C000-$DFFF)
  .bank 3
  ;(stuff mapped to CPU $E000-$FFFF)
  .bank 4
  ;(stuff mapped to PPU $0000-$1FFF)
Is this what you're doing?
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

Yes, I have. It still has the same issues as before.
In case I am doing it wrong, here is the code with all banks assigned.
Attachments
problemzone2.asm
(6.07 KiB) Downloaded 115 times
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Completely misunderstood how banks and addresses work?

Post by tokumaru »

Your vectors are in the wrong bank. You're trying to put stuff that goes at $FFFA in the bank that contains $A000-$BFFF.

Because of the vectors (and hardwired banks in case there's bankswitching), when expanding ROMs we often add new banks to the beginning, not the end.
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

And with that, you've solved a lot of questions I had starting with Assembly and the 2A03.

Code: Select all

  .bank 3
  .org $FFFA     ;first of the three vectors starts here
  .dw NMI        ;when an NMI happens (once per frame if enabled) the
                   ;processor will jump to the label NMI:
  .dw RESET      ;when the processor first turns on or is reset, it will jump
                   ;to the label RESET:
  .dw 0          ;external interrupt IRQ is not used in this tutorial

  .bank 2
  .org $C000

  .bank 3
  .org $E000
That fixed it in a pinch, indeed!

Since I've had trouble with this before, I hope you don't mind if I ask for some verification on this before I call it "understood":

Code: Select all

  .org
This directive simply points at the specified address, correct? If so, why would I do that, since most of the stuff I can do is done by directing, for example, the opcode LDA to a specific address anyway.
As mentioned in this thread, and I thought that wasnt the case before, the banks have pre-defined locations in memory. ".org" doesn't define their starting point (what I originally thought).

So in other words:
.bank only switches the 8kb bank I've selected. .org only points to a specific memory-address for further (for me unknown) processing.
Meaning, that the code above does absolutely nothing, other than switching through the banks and pointing at their respective starting-memory.
It isn't necessary to go through them all once, then? I could have 4 banks but only need 3 without having to ever switch to the 4th?

Yeah I tried getting the gist of it before, so I'm glad I am asking here, despite feeling pretty dumb now, heh.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Completely misunderstood how banks and addresses work?

Post by tokumaru »

Skelpolu wrote:

Code: Select all

  .org
This directive simply points at the specified address, correct?
Actually, it tells the assembler where in the address space of the target machine the code that follows will be mapped. The assembler needs this in order to calculate the addresses of labels, variables, and such. It doesn't CAUSE code to be mapped at the specified address though - the console and the mapper decide that, you're merely informing the assembler where the code will be.

Anyway, the first .org simply lets the assembler know where the code that follow will be mapped, but subsequent .org commands will pad the ROM from the current address until the specified address so that whatever comes next is guaranteed to be at that address. This is what happens with the vectors, for example.
As mentioned in this thread, and I thought that wasnt the case before, the banks have pre-defined locations in memory. ".org" doesn't define their starting point (what I originally thought).
I'm not very familiar with NESASM, but it's possible that you don't need an .org at the beginning of every bank (maybe the program counter automatically rolls over to the next bank, IDK, you have to try), but you definitely need the first one, or the assembler won't be able to translate labels and such into addresses.
.bank only switches the 8kb bank I've selected.
.bank is a stupid directive that needlessly complicates things​, but there's no way around it if you're using NESASM. NESASM forces you to create ROMs composed of 8KB banks regardless of the mapper you're using, if any, so you must explicitly insert these "breaks" every 8KB.
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

Thanks for the detailed reply, Tokumaru. :)
I get the gist of things much more now.

So wait, you're saying that NESASM is the sole reason we need to split the memory into 8KB segments?
I actually thought that the 2A03 internally works like this to begin with. If it doesn't, or if there's a more convenient, well supported assembler, which one would you recommend?
Heard about CC65 and ASM6, but it's hard for me to decide on one of them, especially since the Nerdy Nights tutorials use the NESASM syntax and I'm afraid I won't keep up with it.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Completely misunderstood how banks and addresses work?

Post by tepples »

Skelpolu wrote:So wait, you're saying that NESASM is the sole reason we need to split the memory into 8KB segments?
If you're doing NROM, this is true. It's also true of CNROM, BNROM, AOROM, GNROM, Color Dreams, UNROM, MMC1, Action 53, and other relatively simple mappers. But MMC2, MIMIC-1, MMC3, FME-7, RAMBO-1, VRC2, VRC4, VRC7, and several other mappers use the same 8 KiB bank size as NESASM.
I actually thought that the 2A03 internally works like this to begin with.
NESASM is ultimately based on an assembler that targeted the PC Engine console, which was called TurboGrafx-16 outside Asia. The TG16's CPU is a 65C02 with a few more instructions and an integrated memory controller that divides the address space into eight 8192-byte windows. (A "bank" is an area of memory of a particular size, and a "window" is a piece of address space that can be mapped to a particular bank.)

PRG ROM windows on the NES, by contrast, are implemented by mapper circuitry inside the cartridge. The vast majority of NES games use PRG ROM windows 8 KiB, 16 KiB, or 32 KiB in size, and some mappers (VRC6 and MMC5) even support a mix of two different window sizes. From the point of view of an NES programmer, the 8 KiB bank size of NESASM is because that's the smallest window size of any common NES mapper. You can have your assembler's banks being the same size as the mapper's windows or smaller but not bigger. A mapper's windows are as big as one, two, or four NESASM banks.
If it doesn't, or if there's a more convenient, well supported assembler, which one would you recommend?
For simplicity comparable to that of NESASM, use ASM6. For flexibility particular with larger projects, use ca65.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Completely misunderstood how banks and addresses work?

Post by tokumaru »

Yeah, the 8KB Bankai things os exclusive to NESASM. I always suggest ASM6 for beginners and ca65 for more experienced coders. These are the main points where ASM6 differs from NESASM:

-It uses < and > instead of LOW() and HIGH();
-it uses () instead of [] for indirection;
-It uses ENUM for declaring variables instead of RSSET;
-It doesn't create an NES header automatically, so you either have to create one yourself using DB statements or using a macro pack;
-It doesn't have any banking bullshit - if you're not using bankswitching at all, just use ORG, if you do have multiple banks, use BASE to rollback the PC whenever a new bank starts;

That's all I can think of right now. If you keep these things in mind, it's possible to use ASM6 and still file the Nerdy Nights tutorials. The code itself is largely the same.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: Completely misunderstood how banks and addresses work?

Post by thefox »

It could be a good exercise to adapt the NESASM syntax to ASM6 syntax as you go. If it feels too difficult, some people have already adapted the code for ASM6, you can use it as a reference: https://forums.nesdev.com/viewtopic.php?f=10&t=12097.

ca65 adaptation here: https://bitbucket.org/ddribin/nerdy-nights/src. (There might be others.)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Skelpolu
Posts: 7
Joined: Tue Apr 11, 2017 7:28 am

Re: Completely misunderstood how banks and addresses work?

Post by Skelpolu »

At this point, it seems like a reasonable choice to jump straight to CA65 for multiple reasons.
One being that I eventually want to go deeper into 2A03 programming and proper optimizing the code, which, if I understood this correctly, is not quite as possible with NESASM.
Then, of course, it's easier to stick with an assembler that suits my needs from the start rather than having to switch to another assembler once I need to.

Anyway, thanks to everybody who's replied. I'll definitely look into it more now, but I guess the problem I had is resolved. :)
Post Reply