It is currently Tue Jun 27, 2017 12:08 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 15 posts ] 
Author Message
PostPosted: Tue Apr 11, 2017 8:07 am 
Offline

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


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 8:47 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18525
Location: NE Indiana, USA (NTSC)
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)?


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 10:47 am 
Offline

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


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 10:57 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18525
Location: NE Indiana, USA (NTSC)
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?


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 11:19 am 
Offline

Joined: Tue Apr 11, 2017 7:28 am
Posts: 7
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 12 times
Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 11:27 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
Currently you have:
Code:
  .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:
  .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?


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 12:03 pm 
Offline

Joined: Tue Apr 11, 2017 7:28 am
Posts: 7
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 13 times
Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 12:18 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 2:46 pm 
Offline

Joined: Tue Apr 11, 2017 7:28 am
Posts: 7
And with that, you've solved a lot of questions I had starting with Assembly and the 2A03.

Code:
  .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:
  .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.


Top
 Profile  
 
PostPosted: Tue Apr 11, 2017 3:19 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
Skelpolu wrote:
Code:
  .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.

Quote:
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.

Quote:
.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.


Top
 Profile  
 
PostPosted: Wed Apr 12, 2017 2:34 pm 
Offline

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


Top
 Profile  
 
PostPosted: Wed Apr 12, 2017 3:14 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18525
Location: NE Indiana, USA (NTSC)
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.

Quote:
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.

Quote:
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.


Top
 Profile  
 
PostPosted: Wed Apr 12, 2017 3:24 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9757
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Apr 13, 2017 5:42 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2839
Location: Tampere, Finland
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: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Thu Apr 13, 2017 2:28 pm 
Offline

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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


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