Nerdy Nights basics: role of the "bank" code

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
ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Nerdy Nights basics: role of the "bank" code

Post by ludoVIC » Thu Jan 21, 2021 3:19 am

I am studying the NN basic tutorials and, despite a basic fair understanding, I'd like to go e little bit more into details.
Let's consider the following general code:

Code: Select all

 
  .inesprg 1
  .ineschr 1
  .inesmap 0
  .inesmir 1

  .bank 0
  .org $C000
	; do something
Forever:
  JMP Forever

  .bank 1
  .org $FFFA
	; set the nes vectors

  .bank 2
  .org $0000
  .incbin "mario.chr"
  
It should be the simplest code structure for beginners, am I right?
If "yes", there is a point that I do not yet understand. The instructions are meant to run sequentially.
In particular, we start from bank 0, we do something, and we finally go into the infinite jmp loop.
Therefore, what's the point of defining bank 1 and bank 2? When precisely is the code there executed?
Any feedback is appreciated!

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

Re: Nerdy Nights basics: role of the "bank" code

Post by tokumaru » Thu Jan 21, 2021 4:36 am

NESASM is a little quirky in that it requires these .bank directives to control the sizes of the ROMs it generates. Just think of each NESASM "bank" as a chunk of 8KB, and use as many banks as you need to make your ROM as big as you need it to be. In this example, banks 0 and 1 form the 16KB of PRG-ROM (the minimum that the iNES format can represent) and bank 2 is for holding 8KB of CHR-ROM. Once you go past 32KB of PRG and 8KB of CHR, you need to start using a mapper in order to to access the extra memory.

EDIT: regarding how the code in the extra banks is executed: you can JMP (jump) or JSR (jump to subroutine) between banks 0 and 1 freely (and also 2 and 3 if you have 32KB of PRG), and the PC will also seamlessly cross from one bank into the other if you let it reach the boundary (e.g. $DFFF->$E000). CHR banks will never get executed, because they're connected to the PPU, not the CPU.

ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Re: Nerdy Nights basics: role of the "bank" code

Post by ludoVIC » Thu Jan 21, 2021 4:57 am

Thank you for the explanation. If I may, I'd like to ask for a couple of clarifications more.

Each bank has a maximum capacity of 8 KB. But, how precisely is this value "filled"?
Do code instructions occupy memory in the bank?

In other words, if I understood correctly, the code itself is structured in pieces, each capable of running a limited about of instructions.
It is possible to jump between these pieces, but only when they are properly arranged in the memory (as the example you mentioned).
Finally, some banks are not meant to me executed, but rather just to set some "system variable", as the CHR banks.

Is my reasoning right?

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

Re: Nerdy Nights basics: role of the "bank" code

Post by tokumaru » Thu Jan 21, 2021 5:09 am

ludoVIC wrote:
Thu Jan 21, 2021 4:57 am
Each bank has a maximum capacity of 8 KB.
Yes. Keep in mind that this is a NESASM thing though, it's not a universal NES or 6502 thing. Other 6502 assemblers can have variable bank sizes, or not even have the concept of banks at all. This is just the solution NESASM uses to control the size of the binaries it generates.
But, how precisely is this value "filled"?
Do code instructions occupy memory in the bank?
All the code/data you write and/or include after the .bank directive gets put in that bank, starting from the lowest address and going up. If it doesn't fill the whole 8KB, the assembler will pad the remaining space (I think!).
In other words, if I understood correctly, the code itself is structured in pieces, each capable of running a limited about of instructions.
It is possible to jump between these pieces, but only when they are properly arranged in the memory (as the example you mentioned).
NESASM kinda forces you to break things into 8KB chunks, but that's quite a lot of space to fill when you're making a simple game. Most 6502 programs are built from subroutines that are much smaller than that, so you can easily distribute them across the PRG banks in the way that's most convenient for you. If you're not using mappers, it hardly matters where things go, since everything is accessible at all times.
Finally, some banks are not meant to me executed, but rather just to set some "system variable", as the CHR banks.
It has nothing to do with "system variables". CHR-ROM occupies space, and the only way to get space in NESASM is to start a new bank.

ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Re: Nerdy Nights basics: role of the "bank" code

Post by ludoVIC » Thu Jan 21, 2021 6:26 am

Fantastic, wonderful explanation!
Yes, and so this problem has been solved, too. Thank you very much :beer:

User avatar
Quietust
Posts: 1687
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Nerdy Nights basics: role of the "bank" code

Post by Quietust » Thu Jan 21, 2021 7:36 am

tokumaru wrote:
Thu Jan 21, 2021 4:36 am
the PC will also seamlessly cross from one bank into the other if you let it reach the boundary (e.g. $DFFF->$E000).
My recollection was that NESASM did not allow that, that trying to put more than 8KB in a single bank would either throw an error or silently discard everything past the end of the bank (though if you were really careful, you could put a .bank directive in the middle of a routine, possibly padded with NOPs, and get it to work correctly).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Re: Nerdy Nights basics: role of the "bank" code

Post by ludoVIC » Thu Jan 21, 2021 9:30 am

Very good to know, thanks.
To be sure, I will always be careful of not exceeding the 8KB dimension.

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

Re: Nerdy Nights basics: role of the "bank" code

Post by tokumaru » Thu Jan 21, 2021 9:44 am

Quietust wrote:
Thu Jan 21, 2021 7:36 am
My recollection was that NESASM did not allow that
Oh, I meant that the CPU will seamlessly cross from one bank to the other, not the assembler. Sorry for not making that clear.
(though if you were really careful, you could put a .bank directive in the middle of a routine, possibly padded with NOPs, and get it to work correctly).
Yeah, that's what you'd have to do in NESASM to get the CPU to just "flow" into the next bank. That's pretty weird though, so don't do that.

Post Reply