Banks and zero page using wla-dx

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
rchoudhary
Posts: 24
Joined: Wed Apr 10, 2019 4:24 pm

Banks and zero page using wla-dx

Post by rchoudhary » Tue May 07, 2019 10:46 pm

So a bit ago I asked how to show some graphics on the screen and managed to get it to work! :)

Now I wanted to add some sound! I arranged a song using SNES GSS and created the .spc file for it.

Now I tried reading online about how to load SPC programs and run them, and the documentation that I managed to find was... lacking (so to speak)

However I did manage to find a SNES program that plays SPC files. It was written with the wla-dx assembler in mind. Porting it to fit into my ca65 project seemed impossible; there was a lot of code!

Instead, I had the bright idea that rather than try to figure out how to port and incorporate that code into my program, it'd be a hell of a lot easier to port and incorporate my meager program into that.

To begin, I tweaked the spc player a bit and got it to play my .spc without showing anything on the screen. So far so good.

Now the next logical step to me would be to just include my binaries (palette data, map data, and tile data) in the ROM.

There was already some code in the spcplayer's main .asm file that included the needed data from the .spc file:

Code: Select all

.bank 1 slot 0

 ; SPC-700 register values
.org $0000
            .incbin     SPC_FILE skip $00025 read $0008

; DSP register values
.org $4000
.incbin     SPC_FILE skip $10100 read $0080

 ; The actual 64k SPC RAM dump

.bank 2

.section "musicDataLow"
.incbin     SPC_FILE skip $0100 read $8000
.ends

.bank 3

.section "musicDataHigh"
.incbin     SPC_FILE skip $8100 read $8000
.ends
That looked good to me. However I had an additional 32K of data to add in along with a single zero byte to use for DMA purposes. Clearly banks 1-3 are full, and bank 0 is for code. So I thought, "Ok, I'll use banks 4 and 5". Thus I added these lines of code:

Code: Select all

.bank 4

palette0:   .incbin     "img/frame0.pal" FSIZE palettelen
palette1:   .incbin     "img/frame1.pal"

frame0map:  .incbin     "img/frame0.map" FSIZE frame0maplen
frame0chr:  .incbin     "img/frame0.chr" FSIZE frame0chrlen

.bank 5

frame1map:  .incbin     "img/frame1.map" FSIZE frame1maplen
frame1chr:  .incbin     "img/frame1.chr" FSIZE frame1chrlen

zerobyte:   .db         $00

.define counter_limit   50


Well the assembler did NOT like that at all...

Code: Select all

project $ wla-65816 -o main.o main.asm
main.asm:185: DIRECTIVE_ERROR: ROM banks == 4, selected bank 4.
main.asm:185: ERROR: Couldn't parse ".bank".
project $ 
Are banks 4 and 5 not a thing? I read the wikibooks article on memory mapping and it says that the SNES has 256 banks and that ROMs can have up to 4 megabytes of data. To be honest though, the article is pretty sparse and I have no idea what the hell anything is at all! There's this table that I have no idea what it's saying:

Image

That table honestly goes in one ear and out the other...

Furthermore, in my code I need to use some variables, i.e. some scratch space. So in my original project which used ca65, I had the following:

Code: Select all

.segment "ZEROPAGE"

bgstatus:   .res    1
counter:    .res    1

init_bg = %00000001
bgmask  = %00000011
The thing is, wla-dx has no "zero page" directive. But I also realized that while I understand the definition of a zero page (the page that is at address $0000), I don't understand why variables need to go there only...

So here are my questions:
  • 1. What in the hell is a bank and why is it important?
    2. Why does the assembler not like more than 3 banks?
    3. Why do variables need to go in the zero page? What's important about the zero page?
    4. How do I define the zero page using wla-dx?
I'm totally cool with just being handed some reading materials if they clearly explain what is going on. The problem is I'm stuck between small, vague snippets of text here and there, and the full-on SNES development manual. Neither is great, and so here I am (or I should say, here I have been lol)



UPDATE:

In case it's relevant / anyone wants it, I've attached a zip file containing my entire project (or more accurately, my cannibalized version of spcplayer)
Attachments
snes-project.zip
(88.77 KiB) Downloaded 203 times

93143
Posts: 1104
Joined: Fri Jul 04, 2014 9:31 pm

Re: Banks and zero page using wla-dx

Post by 93143 » Tue May 07, 2019 11:53 pm

rchoudhary wrote:Are banks 4 and 5 not a thing?
Not in your project they're not. You told the assembler to make four banks, and they start at bank 0. So banks 4 and 5 are the fifth and sixth banks of those four. If you want more banks, you have to tell the assembler you want a bigger ROM.

More generally, since you seem confused about this:

The SNES has a 24-bit main address bus. This means it can directly address up to 16 MB, but that includes ROM, RAM, MMIO registers, unused address spaces that return garbage, etc., and the biggest practical ROM is just shy of 12 MB. However, the SNES only has an 8-bit data bus, which means that when an instruction includes an address, it has to be loaded in 8-bit chunks. And being a 65xx processor, it was designed to operate at pretty close to the maximum speed allowed by the memory, so the data bus is the bottleneck.

Therefore, in order to increase processing speed, not all instructions use 24-bit addressing. Every 8 bits you can shave off the instruction results in a one-cycle speedup of that instruction (broadly speaking; there are exceptions) and a one-byte saving in code size (universally, no exceptions). So 16-bit and even 8-bit addressing are used extensively in SNES code.

The 6502 only had 16-bit and 8-bit addressing. An 8-bit address was simply used as is with no offset, which resulted in faster access to the first 256 bytes in the 64 KB memory space. From this we get the term "zero page". Variables do not have to go in zero page on a 6502, but your code will be faster if you use zero page wisely. It will also be smaller, which is much more important on a 6502 than on a 65816.

The 65816 has a more flexible design. First, the zero page has been made movable. There is a register inside the CPU that stores a 16-bit offset to add to any 8-bit address encountered in the code, resulting in the potential for fast, compact access to any 256-byte window within the first 64 KB of address space. This is called "direct page", and ca65 doesn't know about it. You can change it yourself in code using the tcd or pld instructions, but make sure you don't get confused about where it is at any given moment, particularly in subroutines and interrupts.

Second, there are two 8-bit registers inside the CPU called the program bank and data bank registers. The data bank register is used with 16-bit addressing: when a 16-bit address is encountered, a 24-bit address is constructed using the data bank register as the top byte. This results in the address space being effectively divided into 64 KB chunks called banks. The program bank register is the top byte of the program counter and is also used with 16-bit addressing; 16-bit jumps must happen within the current program bank, and execution and branches wrap at bank boundaries. (Note that the data bank register does not operate on 8-bit direct page instructions, only on 16-bit or "absolute" instructions; direct page is limited to bank $00.)

An explicit 24-bit address is considered "long" and is only used if you really need to access something outside the current bank, either to read or write outside the current data bank or to use code blocks and subroutines outside the current program bank.

...

So that's why banks and zero page (or on the SNES, direct page) are a thing. It's to avoid the need to use full-length addresses, which would bloat and slow down code.

...

NB: ROM "banks" are not the same thing as SNES "banks". ROM can be in 32 KB or 64 KB "banks" (LoROM and HiROM, in the traditional nomenclature), which are then mapped into the SNES memory map, and typically mirrored so they appear in more than one place. You will only ever get one ROM bank to one SNES bank, but some SNES banks have other stuff in them besides ROM, which gets in the way a little and makes the remapping necessary. The first and third quarters of the SNES address space, banks $00-$3F and $80-$BF, have a system area in the bottom 32 KB of each bank, so only the top 32 KB of the associated ROM bank (which in the case of a 32 KB ROM bank is the whole thing) is visible in those areas. The second and fourth quarters of the address space have no system area and can be mapped as 100% ROM, except for banks $7E and $7F which are hardwired as the console's WRAM.

This is why the location of the vector table in ROM is different in LoROM and HiROM - a 32 KB LoROM bank appears in its entirety in the top half of a 64 KB SNES bank, so any address in the ROM is higher by $8000 when it appears on the SNES main bus. The vector table is always in the same place from the perspective of the S-CPU.

For HiROM the ROM banks are 64 KB, so the 16-bit addresses are the same between the ROM and the SNES, but the bottom half of the ROM banks are invisible in banks $00-$3F and $80-$BF on the SNES; full access is only possible in banks $40-$7D and $C0-$FF. Due to mirroring, this does not mean that the bottom half of bank $00 in ROM is inaccessible; you simply have to go to (usually) bank $40 or bank $C0 to get it.

There have been some good diagrams produced recently that somewhat illustrate this. They're two-dimensional, with the 16-bit address on the vertical axis and the bank byte on the horizontal axis: viewtopic.php?p=235113#p235113 Do not mistake the "LoROM" and "HiROM" maps for absolute definitive authorities; there are a lot of different ways of mapping cartridges to the bus (many of which result in ROM appearing in banks $40-$7D) and these are just generic instances that illustrate the difference between how 32 KB and 64 KB ROM banking are typically handled. The only hard-and-fast rule is that any address that connects to something inside the SNES (RAM, controller ports, PPU registers, etc.) cannot also connect to something in the cartridge.
Last edited by 93143 on Wed May 08, 2019 1:22 am, edited 7 times in total.

calima
Posts: 997
Joined: Tue Oct 06, 2015 10:16 am

Re: Banks and zero page using wla-dx

Post by calima » Wed May 08, 2019 12:48 am

I think you're going about this backwards. The SPC format is meant for playback. GSS has proper exporting for games, in its own binary format.

The supplied code for GSS is in the wla-dx (and tcc-816) format, so you have to convert it to ca65 or your code to wla-dx still, but the GSS code is much smaller than the SPC player code. I do have GSS ca65 code, but that's (C) MCS, used in our past and future SNES titles.

rchoudhary
Posts: 24
Joined: Wed Apr 10, 2019 4:24 pm

Re: Banks and zero page using wla-dx

Post by rchoudhary » Wed May 08, 2019 1:43 am

93143 wrote:However, it only has an 8-bit data bus, which means that when an instruction includes an address, it has to be in 8-bit chunks

...

The 65816 has a more flexible design. First, the zero page has been made movable. There is a register inside the CPU that stores a 16-bit offset to add to any 8-bit address encountered in the code, resulting in the potential for fast, compact access to any 256-byte window within the first 64 KB of address space. This is called "direct page", and ca65 doesn't know about it.
So, if I'm getting this correct, using an 8-bit address directly is super fast since the bus is 8 bits wide. Now since you're confined to [$00, $FF], something like the following is impossible?

Code: Select all

.zeropage
    .byte001:   .byte   $00
    .byte002:   .byte   $00
    ...
    .byte256:   .byte   $00
    .byte257:   .byte   $00        ; Overflow of the zero page!
    .byte258:   .byte   $00
    .byte259:   .byte   $00
Furthermore, with the 65816, the bank register is used in tandem with any 8-bit addresses so that instead of being confined to [$00, $FF], you can access [X+$00, X+$FF] for any 16-bit X. However, you're saying that this movable zero page is not possible with ca65. Does the .zeropage directive then just set this offset to 0, i.e. confining you to [$00, $FF]?

...
93143 wrote:You will only ever get one ROM bank to one SNES bank, but some SNES banks have other stuff in them besides ROM, which gets in the way a little and makes the remapping necessary.
This part confuses me a bit. Are you saying that a single bank in the SNES could have a bank from the ROM and something else? A ROM bank can either be 32KB or 64KB, but a SNES bank is always 64KB right?

And then there is this image:

Image

And the wikibook article says this:

Code: Select all

$00: $000000 - $007FFF
$01: $008000 - $00FFFF
$02: $010000 - $017FFF
...
$3D: $1E8000 - $1EFFFF
$3E: $1F0000 - $1F7FFF
$3F: $1F8000 - $1FFFFF
Does this mean that:

Code: Select all

ROM addresses      | is in |   SNES Bank  |   SNES Addresses   
-------------------+-------+--------------+---------------------    
$000000 - $007FFF  |       |   $00        |   $8000-$FFFF
$008000 - $00FFFF  |       |   $01        |   $8000-$FFFF
$010000 - $017FFF  |       |   $02        |   $8000-$FFFF
...                |       |   ...        |   ...
$3D8000 - $3DFFFF  |       |   $7B        |   $8000-$FFFF
$3E0000 - $3E7FFF  |       |   $7C        |   $8000-$FFFF
$3E8000 - $3EFFFF  |       |   $7D        |   $8000-$FFFF
It looks like by shifting the SNES bank left by 15 gets you the high 12 bits of the ROM address. Does that mean anything...?

If so, this implies that in LoROM mode, you can access up to $3F0000 locations right? The wikibook article said cartridges ROM can have up to 4KB = $400000 memory locations. So where are the missing $10000 locations?

Also where do ROM banks fit in all of this?

...
93143 wrote:a 32 KB LoROM bank appears in its entirety in the top half of a 64 KB SNES bank, so any address in the ROM is higher by $8000 when it appears on the SNES main bus.
So based on that table I made earlier, I think this is the process of turning a SNES address into a ROM address:
  • 1) Take the SNES address and left shift by 15.
    2) Take the SNES address and subtract $8000. Then add it to part the result from part (1).
    3) Go to the address given in part (2) in ROM.
Is that what you were saying essentially? Or am I misunderstanding how all the addresses interact?

...

This kinda brings me to another question actually. Where does the code and ROM data live? I know it starts out on the cartridge, but does it stay there?

Specifically, when the processor enters the fetch state, where does it fetch from? Does the PC contain an address that gets translated into accessing the ROM as I detailed above?

When you refer to an address in the code itself, e.g. lda $2000, that's a SNES address right? So if I want to access $3D8000 in the ROM, I need to set the SNES bank register to contain $7B with a tcd or pld instruction, and then I need to access $8000 with something like lda $8000. Is that the correct process? Or is there a way to do all this much simpler? Again, how do ROM banks fit into this picture as well?

...

But I guess I still have some project-specific questions:
  • 1. How do I store stuff in the zero page with wla-dx?
    2. (New one!) Is there a difference between .byte and .res in ca65? If so, which one does .db from wla-dx correspond to? And what's the equivalent of the other one?
    3. How do I configure the SNES to have more banks and use a bigger ROM? I have a chip that supports up to 8Mbit, so I might as well set the limit to that. Here is what the Header.asm file looks like for my project:

    Code: Select all

    
    .MEMORYMAP                              ; Begin describing the system architecture.
        SLOTSIZE $8000                      ; The slot is $8000 bytes in size. More details on slots later.
        DEFAULTSLOT 0                       ; There's only 1 slot in SNES, there are more in other consoles.
        SLOT 0 $8000                        ; Defines Slot 0's starting address.
    .ENDME                                  ; End MemoryMap definition
    
    .ROMBANKSIZE $8000                      ; Every ROM bank is 32 KBytes in size
    .ROMBANKS 4                             ; 1 Mbit(s) - Tell WLA we want to use 4 ROM Banks
    
    .SNESHEADER
        ID "SNES"                           ; 1-4 letter string, just leave it as "SNES"
    
        NAME "SPC PLAYER           "        ; Program Title - can't be over 21 bytes,
        ;    "123456789012345678901"        ; use spaces for unused bytes of the name.
    
        SLOWROM
        LOROM
    
        CARTRIDGETYPE $00                   ; $00 = ROM only, see WLA documentation for others
        ROMSIZE $08                         ; $08 = 2 Mbits,  see WLA doc for more..
        SRAMSIZE $00                        ; No SRAM         see WLA doc for more..
        COUNTRY $01                         ; $01 = U.S.  $00 = Japan, that's all I know
        LICENSEECODE $00                    ; Just use $00
        VERSION $00                         ; $00 = 1.00, $01 = 1.01, etc.
    .ENDSNES
    
    .SNESNATIVEVECTOR                       ; Define Native Mode interrupt vector table
        COP EmptyHandler
        BRK EmptyHandler
        ABORT EmptyHandler
        NMI VBlank
        IRQ EmptyHandler
    .ENDNATIVEVECTOR
    
    .SNESEMUVECTOR                          ; Define Emulation Mode interrupt vector table
        COP EmptyHandler
        ABORT EmptyHandler
        NMI EmptyHandler
        RESET Start                         ; where execution starts
        IRQBRK EmptyHandler
    .ENDEMUVECTOR
    
    .BANK 0 SLOT 0                          ; Defines the ROM bank and the slot it is inserted in memory.
    .ORG 0                                  ; .ORG 0 is really $8000, because the slot starts at $8000
    .SECTION "EmptyVectors" SEMIFREE
    
    EmptyHandler:
           rti
    
    .ENDS
    
    .EMPTYFILL $00                          ; fill unused areas with $00, opcode for BRK.
                                            ; BRK will crash the snes if executed.
    
    I kept .ROMBANKSIZE as is, set ROMSIZE $0A to correspond to 8 Mbit, did .ROMBANKS 8 so that I have 8 banks that're 1 Mbit each. Unfortunately, when I try to run the assembler, I get

    Code: Select all

    MEM_INSERT: Origin ($14080) overflows from bank (4).
       ^ main.asm:187: Writing .INCBIN data
    


    Which makes no sense because the binaries in bank 4 total up to a single kbit... I figure something ain't right with my header.




-----

Note: I'm completely ignoring HiROM until I can actually grasp LoROM :lol:

93143
Posts: 1104
Joined: Fri Jul 04, 2014 9:31 pm

Re: Banks and zero page using wla-dx

Post by 93143 » Wed May 08, 2019 3:27 am

First off, I think calima's post is important and should not get lost in this mess. SNESGSS is intended to produce game audio. An SPC is not game audio; it is a dump of game audio for playback on PCs.

Now then:
rchoudhary wrote:So, if I'm getting this correct, using an 8-bit address directly is super fast since the bus is 8 bits wide.
There's a caveat. If the direct page offset is not a multiple of 256 (that is, the bottom byte is nonzero), the CPU has to spend a cycle adding it to the 8-bit address and you don't get any speed benefit over 16-bit addressing. The code is still smaller, and there are use cases where the direct page register can be used almost as a third index register, so there may still be benefits to using non-page-aligned DP. Also, in some cases indexing can add a cycle to 8-bit but not 16-bit addressing, with much the same result. Exact instruction timing is probably out of scope for the moment, so let's leave that topic...
Now since you're confined to [$00, $FF], something like the following is impossible?

Code: Select all

.zeropage
    .byte001:   .byte   $00
    .byte002:   .byte   $00
    ...
    .byte256:   .byte   $00
    .byte257:   .byte   $00        ; Overflow of the zero page!
    .byte258:   .byte   $00
    .byte259:   .byte   $00
Yes. Zero page is 256 bytes long. If you want to access $0100 on a 6502, you need to use 16-bit addressing. On a SNES, you could either use 16-bit addressing or change DP so that $0100 is within scope. (Or use 24-bit addressing, because direct page is always in bank zero and if your data bank is nonzero you could end up with 8-bit and 16-bit addressing pointed at different banks. Fortunately the SNES has the exact same identical RAM mirror and system registers in fully half of all the banks in the memory space, which mitigates this issue somewhat.)
Furthermore, with the 65816, the bank register is used in tandem with any 8-bit addresses so that instead of being confined to [$00, $FF], you can access [X+$00, X+$FF] for any 16-bit X.
It's not a "bank" register. The term "bank" in the context of S-CPU addressing is reserved for the top byte of a 24-bit address. The program and data bank registers are 8-bit registers that are used to 'complete' 16-bit addresses. The direct page register is a 16-bit register that does what you describe. And it's not X, it's D (or DP). X is one of the index registers.

So, I guess that's a yes.
However, you're saying that this movable zero page is not possible with ca65.
No, you can still do it, because ca65 allows you to use the instructions that move direct page around at runtime. But the assembler was designed for the 6502 and its automatic organization functionality doesn't recognize the possibility of doing this. I haven't used ca65, so I'm not totally sure how far you'd have to go to work around it, but I'm pretty sure you can.

...
93143 wrote:You will only ever get one ROM bank to one SNES bank, but some SNES banks have other stuff in them besides ROM, which gets in the way a little and makes the remapping necessary.
This part confuses me a bit. Are you saying that a single bank in the SNES could have a bank from the ROM and something else? A ROM bank can either be 32KB or 64KB, but a SNES bank is always 64KB right?
Right. Take a look at the image you posted. In the first and third quarters of the map, there's a bunch of stuff between $0000 and $7FFF in each bank, and it's not ROM. Then you have ROM from $8000 to $FFFF.

This means you can write code that accesses ROM, RAM, the PPU bus and the CPU's internal MMIO registers, all without using 24-bit addressing for anything.
Does this mean that:

Code: Select all

ROM addresses      | is in |   SNES Bank  |   SNES Addresses   
-------------------+-------+--------------+---------------------    
$000000 - $007FFF  |       |   $00        |   $8000-$FFFF
$008000 - $00FFFF  |       |   $01        |   $8000-$FFFF
$010000 - $017FFF  |       |   $02        |   $8000-$FFFF
...                |       |   ...        |   ...
$3D8000 - $3DFFFF  |       |   $7B        |   $8000-$FFFF
$3E0000 - $3E7FFF  |       |   $7C        |   $8000-$FFFF
$3E8000 - $3EFFFF  |       |   $7D        |   $8000-$FFFF
Yes.
It looks like by shifting the SNES bank left by 15 gets you the high 12 bits of the ROM address. Does that mean anything...?
Not really, other than the fact that each ROM bank is a 15-bit address space, and they're contiguous in the actual ROM chip. My understanding is that the cartridge just connects pins 16+ of the A bus address to pins 15+ of the ROM address, and pin 15 of the A bus address either does nothing or helps trigger SRAM select in a small logic chip, but not every cartridge was wired the same and there may be differences in how they handled things.

Also, the bank byte is 8 bits, so I'm not sure how you got 12 just by shifting it...
If so, this implies that in LoROM mode, you can access up to $3F0000 locations right?
How do you figure? The bank byte being bits 15-22 of the address would give you 8 MB of addressable space. Banks $7E and $7F are taken up by WRAM, so you can't actually access a full 8 MB in pure LoROM mode, but if you do the usual trick of mirroring banks $00-$7F of the ROM in banks $80-$FF on the S-CPU bus (ie: the cartridge ignores pin 23 of the A bus address for ROM accesses), you can do 4 MB pretty easily because the ROM that's blocked by WRAM in $7E and $7F appears unobstructed in $FE and $FF.
Also where do ROM banks fit in all of this?
Well, there's not necessarily such a thing, except in so far as certain areas of ROM are mapped to specific SNES banks. So the first 32 KB of a ROM could be considered the first "bank" if you're using a LoROM-type map, and the assembler would need to know this in order to put things where they should go and translate labels and such. But technically the ROM is contiguous. Or for big games it could be multiple chips - a 4 MB game might have been four 1 MB ROMs. The ROM bank structure of 32 or 64 KB chunks is purely conceptual, necessary only to allow the assembler to put together a working SNES program.

...
93143 wrote:a 32 KB LoROM bank appears in its entirety in the top half of a 64 KB SNES bank, so any address in the ROM is higher by $8000 when it appears on the SNES main bus.
So based on that table I made earlier, I think this is the process of turning a ROM address into a SNES address:
  • 1. Take the ROM address and right shift by 15. This is the SNES bank number.
    2. Take the low 15 bits of the ROM address. Then add $8000 to it. This is the SNES address.
    3. SNES bank + SNES address = absolute SNES address?
  • 1. Yes, but no. You can generally add $80 to the SNES bank number thus obtained (and/or possibly $40 or $C0 or whatever, depending on the cartridge) and still have the correct address, because of mirroring. It may even be better to do so, because if you access ROM in bank $80 or above, you can use FastROM mode...
    2. Yes.
    3. Technically the term "absolute" means 16-bit addressing within a bank. (This is probably a holdover from the 6502, which had a 16-bit address space and no banks.) Full 24-bit addressing is called "long" addressing. Other than that, yes, assuming that by "+" you don't literally mean addition. (SNES bank << 16) + absolute SNES address = long SNES address.
Where does the code and ROM data live? I know it starts out on the cartridge, but does it stay there?
Yes, generally. Obviously you have to feed the PPU graphics data, and if you have compressed data you may want to decompress it into WRAM as needed (particularly since it is very advantageous to have graphics data in an uncompressed block that can be rapidly transferred via DMA during VBlank). And obviously any data generated by the program has to go in RAM. But code usually stays in ROM and is executed in place. Exceptions would include certain types of special chip games, where the CPU outright can't access the ROM if the coprocessor is using it, and self-modifying code, which doesn't work real well in read-only memory. In cases like those you'd have to explicitly transfer the code to RAM and jump to it, but since you aren't in a case like those you don't have to worry about it.

The audio module is different. It has no cartridge access, only a 64-byte boot ROM that defines a simple transfer protocol for the APU I/O ports. You have to follow this protocol to load a program and data (or, at minimum, your own data transfer code...) in order to play music and sound effects.
Specifically, when the processor enters the fetch state, where does it fetch from? Does the PC contain an address that gets translated into accessing the ROM as I detailed above?
The S-CPU does not really know the difference between ROM and any other part of its memory space. The address translation circuitry for the ROM is all in the cartridge. You can actually write to ROM just as easily as you can write to RAM, but of course the write won't do anything.
When you refer to an address in the code itself, e.g. lda $2000, that's a SNES address right? So if I want to access $3D8000 in the ROM, I need to set the SNES bank register to contain $7B with a tcd or pld instruction, and then I need to access $8000 with something like lda $8000.
No no. tcd and pld are for the direct page register, not either of the bank registers. You have to set the data bank with plb.

...
  • 1. How do I store stuff in the zero page with wla-dx?
    2. (New one!) Is there a difference between .byte and .res in ca65? If so, which one does .db from wla-dx correspond to? And what's the equivalent of the other one?
    3. How do I configure the SNES to have more banks and use a bigger ROM? I have a chip that supports up to 8Mbit, so I might as well set the limit to that.
  • 1. WLA-DX doesn't care about any of that. You can define variables as 8-bit numbers, and if you use them as addresses it should default to using direct page. It might not; in that case it's best to use .b after the opcode, e.g. lda.b DPVAR, just to make sure it doesn't pad your address to 16 bits...
    2. .db means define byte. It just plops a byte down into the ROM at the specified location (ie: if you use .db in the middle of a code section, you have to jump past it. Don't do that; it's silly.) You can .dw, meaning define 16-bit word, but since you can .db multiple comma-separated bytes you don't absolutely have to .dw; just remember that the SNES is little-endian. I haven't used ca65 so I don't know what its keywords mean.
    3. Don't set the limit any higher than you need to. It will assemble a ROM that big and you will have to wait any time it needs to be written or loaded. That said, it should be as simple as changing .ROMBANKS to something higher than 4.
[/list]
I kept .ROMBANKSIZE as is, set ROMSIZE $0A to correspond to 8 Mbit, did .ROMBANKS 8 so that I have 8 banks that're 1 Mbit each.
That's not how that works. Specifying .ROMBANKS 8 combined with .ROMBANKSIZE $8000 does not give you 8 banks of size $20000.

If you're using LoROM, ROM banks are always 32 KB ($8000 bytes). This is because the assembler has to make sure the ROM constitutes a working SNES program when mapped into the SNES address space, and if your cartridge is ignoring bit 15 of the address, using 32 KB banks is the only way to do that. Do not change .ROMBANKSIZE.

ROMSIZE doesn't do anything. It's part of the header, which is for bookkeeping purposes only (and to tell some emulators and flashcarts what they're supposed to pretend to be, although I'm sure Nintendo didn't plan for that when they designed it). The SNES doesn't read the header, and I'm pretty sure the assembler doesn't care what it says either, other than to include it in the ROM.

As for why what you did didn't work...

Code: Select all

.org $4000
.incbin     SPC_FILE skip $10100 read $0080
That almost adds up to the offset in the error message...

Are you sure everything is shipshape in your data includes? Correct filesizes and all?

WLA DX can be weird sometimes. This may not be your fault.

I have no idea, and I've kinda run out of time. Must go to bed...
Note: I'm completely ignoring HiROM until I can actually grasp LoROM :lol:
Probably wise. I found memory mapping to be the hardest thing to understand about the SNES.

User avatar
koitsu
Posts: 4214
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Banks and zero page using wla-dx

Post by koitsu » Wed May 08, 2019 11:53 am

93143 wrote:
Does this mean that:

Code: Select all

ROM addresses      | is in |   SNES Bank  |   SNES Addresses   
-------------------+-------+--------------+---------------------    
$000000 - $007FFF  |       |   $00        |   $8000-$FFFF
$008000 - $00FFFF  |       |   $01        |   $8000-$FFFF
$010000 - $017FFF  |       |   $02        |   $8000-$FFFF
...                |       |   ...        |   ...
$3D8000 - $3DFFFF  |       |   $7B        |   $8000-$FFFF
$3E0000 - $3E7FFF  |       |   $7C        |   $8000-$FFFF
$3E8000 - $3EFFFF  |       |   $7D        |   $8000-$FFFF
Yes.
How can you say yes to that?! It's a big, strong, definitive no! The terms used in the above ASCII chart are "SNES Bank" and "SNES Address", i.e. 65816 bank and 65816 address.

If those column labels had read "WLA DX Bank" and "WLA DX Address" then "Yes" would be correct.

65816 bank $00 = $000000-00FFFF
65816 bank $01 = $010000-01FFFF
65816 bank $02 = $020000-02FFFF
etc...

The fact banks on the 65816 are 64KBytes each doesn't change. Ever. It doesn't matter if you're in mode 20 (LoROM) or mode 21 (HiROM).

The problem here, as I've discussed time and time again with assemblers (esp. WLA DX), is that the assembler is trying to be "smart" by comprehending mode 20 (LoROM)'s "only half the bank is available for ROM" model, while simultaneously using terms that directly conflict with well-established 65816 terms (read: bank). This confuses the living hell out of programmers, especially new ones. This thread -- and several others -- are hard proof.

Mode 21 (HiROM) may be different because it's more "64KB-friendly", but comes with its own trade-offs as well.

This is why I often say, relying on "more complex assembler features" is often a travesty because unless you're intimately familiar with exactly what the assembler is doing, you really can't be sure what is going to go where or what's going to happen. Once again I become a broken record: this is how/why generating listings files is important. More often than not, you can find the error by examining the code there. "Hey! I told you to load the upper 8 bits of the 24-bit address of this label (e.g. the bank of the label) into the 8-bit accumulator, and that should be a value of 6, but it assembled to 0! What gives?" This approach works well with both featured assemblers and bare-bones ones.

Everyone here knows my general opinion, but I guess it needs repeating: the ancient "just use .org and segment your stuff yourself" model is a lot more logical, because it effectively allows for a more coupled relationship between the native 65816 and the SNES memory model of choice. ca65/ld65 sits somewhere in between that, but getting your ld65 linker config wrong (and thus things like .bankbyte() or ^ return the wrong value) is still very much a reality. (In fact, last night I wasted over an hour dealing with an actual code bug of a single-overwritten-byte-of-data that in absolutely no way/shape/form could ca65/ld65 ever have caught or warned me about, no matter how many "features" it offered.)

Now, as for this subject:
93143 wrote:
However, you're saying that this movable zero page is not possible with ca65.
No, you can still do it, because ca65 allows you to use the instructions that move direct page around at runtime. But the assembler was designed for the 6502 and its automatic organization functionality doesn't recognize the possibility of doing this. I haven't used ca65, so I'm not totally sure how far you'd have to go to work around it, but I'm pretty sure you can.
The "workarounds" are as follows:

1. Do not relocate DP at all -- keep it at $0000, i.e. lda #$0000 / tcd once during RESET -- and make use of type = zp in your ld65 configuration like normal.

2. Relocate DP as you wish, but it's entirely up to you to refer to variables located within the now-relocated DP through some other mechanism -- I strongly recommend use of equates because it becomes very obvious what's different. In other words: "normal" variables located within "standard" direct page (let's assume $0000) might be declared with varA: .res 2 / varB: .res 2 inside of .segment "ZEROPAGE" or the like, while for variables located within a new DP location (let's assume $1000) might be declared with altDP_varC = $00 / altDP_varD = $02 to differentiate them from the others (and probably preceded by a large comment).

Because the assembler/linker has no idea when you've done tcd at run-time, it's up to you to micro-manage this. (This is not that unreasonable, BTW -- even powerful assemblers during the heyday of the 65816 like ORCA/M advocated this as well. You are free to do whatever you want at run-time, but it's up to you to make sure it's correct. Do not expect hand-holding!). And be sure to put D back when you're done.

3. I think there is a third-party patch introducing some new pseudo-op directives to allow for tracking of it by the assembler, but I can't find it right now. It was something (IIRC) rainwarrior found on the ca65 mailing list, I think. There's a post here about it but I can't find it. And no I'm not talking about the GitHub issues he made recently for it.

I tend to recommend #1. You can safely use #1 and #2 together, but in #2 like I said, it's up to you to put things back when you're done / pay very close attention to what variables you're accessing and what addressing modes you're using in all of your instructions. You have to do this anyway regardless, but in general, my view is that ca65/ld65 is still really not "fully" 65816-friendly. It does a pretty good job so far, but there's a lot of "screwing around" that I think results in wasted time.

I think for a 6502/65c02 assembler/linker suite ca65/ld65 is pretty cool (read: fancy), but the more I've used it for 65816 (in the past month or so), including running a patched version of ca65/ld65 for dealing with segment declaration order (rainwarrior wrote this patch and so far it's holding up), the more annoyed I've gotten. I'm very biased right now though -- but quite honestly if I could add up all the man hours I've wasted dealing with ca65/ld65-isms in just 1-2 years, it'd be in the mid-2-digit range. I have better things to do than "babysit" my assembler.

93143
Posts: 1104
Joined: Fri Jul 04, 2014 9:31 pm

Re: Banks and zero page using wla-dx

Post by 93143 » Wed May 08, 2019 2:56 pm

koitsu wrote:
93143 wrote:
Does this mean that:

Code: Select all

ROM addresses      | is in |   SNES Bank  |   SNES Addresses   
-------------------+-------+--------------+---------------------    
$000000 - $007FFF  |       |   $00        |   $8000-$FFFF
$008000 - $00FFFF  |       |   $01        |   $8000-$FFFF
$010000 - $017FFF  |       |   $02        |   $8000-$FFFF
...                |       |   ...        |   ...
$3D8000 - $3DFFFF  |       |   $7B        |   $8000-$FFFF
$3E0000 - $3E7FFF  |       |   $7C        |   $8000-$FFFF
$3E8000 - $3EFFFF  |       |   $7D        |   $8000-$FFFF
Yes.
How can you say yes to that?! It's a big, strong, definitive no! The terms used in the above ASCII chart are "SNES Bank" and "SNES Address", i.e. 65816 bank and 65816 address.
But the entire set of column labels indicates that this chart is specifically about where the ROM goes. The key words are "is in". It doesn't imply that SNES addresses $0000-$7FFF don't exist in the given bank, just that in a canonical LoROM configuration no ROM is mapped to them. As far as I can tell, the ROM mapping expressed in the chart is correct.

...

As for ca65... Do you know if the assembler you've mentioned as under development will/could support Super FX code?

User avatar
koitsu
Posts: 4214
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Banks and zero page using wla-dx

Post by koitsu » Wed May 08, 2019 4:37 pm

93143 wrote:But the entire set of column labels indicates that this chart is specifically about where the ROM goes. The key words are "is in". It doesn't imply that SNES addresses $0000-$7FFF don't exist in the given bank, just that in a canonical LoROM configuration no ROM is mapped to them. As far as I can tell, the ROM mapping expressed in the chart is correct.
That chart is terrible (the number of curse words I want to use here is astounding, BTW). Whoever wrote that has no idea how to write technical documentation. You don't take an English sentence, extract select grammatical pieces from it, and turn them into column headers working from left to right. Good lord. Why do people keep making these absolutely horrible things that confuse people? This has been going on for ALMOST THIRTY YEARS! Every person who complains about this stuff is justified. SiMKiN's old memory layout docs were clearer than this, sheesh.

For the person confused: mode 20 and the whole "what's with the upper 32KBytes?!" thing is really simple to understand (and even easier to program for):
  • The first 32KBytes of your ROM file end up in 65816 bank $00, range $8000-FFFF
  • The next 32KBytes of your ROM file end up in 65816 bank $01, range $8000-FFFF
  • Rinse lather repeat until bank $7D
  • Thus, maximum ROM space in banks $00-7D == 126 * 32768 = 4,128,768 bytes (just short of 32mbit -- this is due to system RAM being in banks $7E-7F)
The exact rinse-lather-repeat model applies to banks $80-FF (e.g. bank $80 = bank $00, bank $81 = bank $01, etc.), which can be used for high speed mode (3.68MHz), a.k.a. "fastrom"
Thus, maximum ROM space in banks $80-FF == 128 * 32768 = 4,194,304 bytes (a full 32mbit)

Make sense?

What your assembler does with "a bank" is entirely up to the assembler and requires extensive review of its documentation. Overloading the term "bank" (within the assembler itself) lends itself to mass confusion, resulting in threads/topics like this. The confusion is totally justified, don't get me wrong, it's just sad that it keeps happening. :(

The term "bank" in the 65816 CPU always means 64KBytes (i.e. 0000-FFFF). It just so happens that mode 20/LoROM SNES cartridges are wired so that each "32KB portion" ends up in the upper half of each 65816 bank. The reason Nintendo did this was certainly to help with ease-of-access to MMIO registers ($21xx, $42xx, $43xx) and having direct page + RAM access everywhere, regardless of what bank your code is actively running in. Considering that their previous console was the NES, which is limited to 64KBytes of total addressing space, where the upper 32KB ($8000-FFFF) was mapped cartridge ROM, I think it makes sense.

Mode 21/HiROM does away with some of this and goes for "true" linear 64KByte banks, with the exception of bank $00. The complication here is what I just described: accessing MMIO registers and RAM requires either use of long addressing (24-bit addressing, ex. lda #$8f / sta $002100 to turn of the screen) or tricks like relocating direct page to the MMIO register base (ex. rep #$20 / lda #$2100 / tcd / sep #$20 / lda #$8f / sta $00 to turn off the screen). Both have their pros and cons. What mode 21 gets you is more ROM space, and is commonly used for games that are 32mbit+ in size.
93143 wrote:As for ca65... Do you know if the assembler you've mentioned as under development will/could support Super FX code?
Extremely off-topic at this point, but the answer is: in its current form, no, but it certainly could be extended to support it since it's a multi-arch assembler. This is probably not going to happen before release, but as said, could be extended later on without too much effort.

tepples
Posts: 21706
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Banks and zero page using wla-dx

Post by tepples » Wed May 08, 2019 4:41 pm

Would it be more accurate to say that a mode $20 ROM has up to 128 "half banks" starting at $80, and a mode $21 ROM has up to 64 "banks" starting at $C0?

User avatar
koitsu
Posts: 4214
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Banks and zero page using wla-dx

Post by koitsu » Wed May 08, 2019 4:56 pm

tepples wrote:Would it be more accurate to say that a mode $20 ROM has up to 128 "half banks" starting at $80, and a mode $21 ROM has up to 64 "banks" starting at $C0?
Sure, that sounds good, but I must stress the importance of mentioning banks $00-7D. The 65816 doesn't power on in bank $80, it powers on in bank $00, it reads vectors from bank $00, vectors addresses are 16-bit (not 24-bit) thus RESET, NMI, IRQ etc. execute out of bank $00 by default, the stack and direct page are "truly" in bank $00, etc...

The first time someone reads the description of mode 20, and they get it, it they go "Ahhh! I get it!!!" followed by a few minutes of pause, and then "But wait a minute... 32mbit would take up 128 banks... 00-7D means only 126 banks... if they're half size, e.g. 32KBytes, what happens to the last 64KBytes of a 32mbit game using mode 20?!?" (I speak from having personally experienced this one, and from seeing many people go through the same thing.)

Mode 20 makes things "so simple!!" that people start forgetting stuff like how sta $1111 isn't necessarily going to write to RAM in any other mode, or on another system using 65816; I'll refer you to the zillion-page Espozo thread where he asked about converting something from mode 20 to mode 21 as an example. Seeing lda #$xx / pha / plb is common on 65816.

rchoudhary
Posts: 24
Joined: Wed Apr 10, 2019 4:24 pm

Re: Banks and zero page using wla-dx

Post by rchoudhary » Wed May 08, 2019 5:33 pm

koitsu wrote:Whoever wrote that has no idea how to write technical documentation.
Oof that'd be me chief... :? You aren't wrong though lol

After reading everything, I think I get how the ROM bank maps into a SNES bank: Every successive ROM bank gets mapped into the upper half of a successive SNES bank (excluding banks $7E and $7F which are WRAM).

So the ROM is mapped to two ranges: banks $00-$7D and banks $80-$FF.

Why would you do mirroring like that? Especially if in the first case your ROM is cut off?

Also, does the programmer have to manually initialize both the mapping and the mirroring? Or are they built in?

Finally, how exactly do you access a space in ROM then? Like say I have a variable in ROM address $1234 that I want to load into the A register. Do I simply do lda $1234?

Now say I want to store the value in the A register in WRAM at address $BEEF. How do I access that? Do I need to calculate the CPU address (the address that gets mapped into a WRAM address) manually?

tepples
Posts: 21706
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Banks and zero page using wla-dx

Post by tepples » Wed May 08, 2019 6:07 pm

I consider banks $80-$FF to be the actual home of ROM in mode $20. Bank $80 is mirrored down to $00 for interrupt handling purposes. And the electrically simplest way to mirror $80 to $00 brings banks $81-$FD along with it to $01-$7D, as the CPU conveniently doesn't generate the ROM enable signal for WRAM banks $7E and $7F.

User avatar
koitsu
Posts: 4214
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Banks and zero page using wla-dx

Post by koitsu » Wed May 08, 2019 7:05 pm

rchoudhary wrote:After reading everything, I think I get how the ROM bank maps into a SNES bank: Every successive ROM bank gets mapped into the upper half of a successive SNES bank (excluding banks $7E and $7F which are WRAM).

So the ROM is mapped to two ranges: banks $00-$7D and banks $80-$FF.
You got it. :-)
rchoudhary wrote:Why would you do mirroring like that? Especially if in the first case your ROM is cut off?
I've talked a bit about this in the past in another thread (re: the "history" of the SNES), but I'll try my best to paraphrase what I know. I've seemingly become a kind of historian around here due to my age and involvement with all of this stuff (earlier than most):

Nintendo didn't originally have PCBs (cartridges) that supported addressing up to 32mbits of ROM in mode 20: the original ones only supported up to 16mbit. We know this not only because of cartridge analysis, but also because official documentation for a long time only depicted banks $00-3F and $80-BF being mapped to ROM. (Mode 21/HiROM has always supported up to 32mbit)

As games and desires grew, they made newer versions of PCBs with more addressing lines thus supported mapping more ROM space. We know this for the exact same reasons as above: cartridge analysis, and newer official documentation depicting banks $00-7D and $80-FF being mapped to ROM. I suspect this happened around the same time they rolled out use of their MAD-1 address decoder chip (which is used for both mode 20 and 21). I suspect folks like byuu can talk more about that stuff though. But I can assure you that if you were to get an old first-gen mode 20 SNES game cartridge (ex. Super Mario World) and compare it to a last-gen cartridge, you'd see exactly what I'm describing.

The other part of your question is essentially "so why would they just 'chop off' those 2 banks due to $7E/7F and force you to use $FE/FF?"

When it comes to addressing lines (read: each individual bit that makes up an address, no matter if it's 4, 8, 17, 24, 32, etc. bits), if you don't "map the address line", then for whatever reason (hardware guys like lidnariq, Memblers, and probably byuu can talk about this -- I might have it backwards?), the address range essentially ends up being "masked" and an effect of that is mirroring.

So what's REALLY is happening, I strongly suspect, is this: banks $00-7F do in fact get mapped to ROM (!!!) via the cartridge addressing lines, for a full 32mbit... however, the SNES console itself has addressing lines that map RAM (WRAM) in banks $7E and $7F, and those "come after" the cartridge/ROM mapping, thus those take higher precedence. Think of it kind of in layers, if that makes sense: mappings overlayed atop mappings.
rchoudhary wrote:Also, does the programmer have to manually initialize both the mapping and the mirroring? Or are they built in?
Excellent question. There is no programming involved; they're dictated by the cartridge/PCB itself. Emulators accomplish this through a series of heuristics, but the most common way is to refer to the "map mode" cartridge header byte ($FFD5 in bank $00). You can read about it elsewhere, like here or here. Just know that there's two versions (older and newer) of the header. The older/original went from $FFC0-FFDF, the newer goes from $FFB0-FFDF. Nintendo also expanded the header by 16 bytes sometime later on. Nintendo was very strict about the cartridge header format -- it's extremely well-documented, officially and unofficially. I prefer the older version because I don't need all the fancy nonsense. So if you were wondering how SNES emulators like SNES9x would show you the "name of the ROM", that's where the data comes from.

Don't confuse this header with, say, copier file format headers (like .smc files that have a 512-byte header)! What I'm talking about is in the actual ROM itself, and is not a "file format" header.

Edit: removed miserable attempt at cartridge header explanation.
rchoudhary wrote:Finally, how exactly do you access a space in ROM then? Like say I have a variable in ROM address $1234 that I want to load into the A register. Do I simply do lda $1234?
Think about it for a second. Again, mode 20:

ROM (file offset) $00000-07FFF get mapped to bank $00 address $8000-FFFF (also bank $80 address $8000-FFFF)
ROM (file offset) $08000-0FFFF get mapped to bank $01 address $8000-FFFF (also bank $81 address $8000-FFFF)
ROM (file offset) $10000-17FFF get mapped to bank $02 address $8000-FFFF (also bank $82 address $8000-FFFF)
ROM (file offset) $18000-1FFFF get mapped to bank $03 address $8000-FFFF (also bank $83 address $8000-FFFF)
...etc...

You tell me: where would file offset $1234 refer to in 65816 addressing space? If you guessed bank $00 address $9234, then give yourself a cookie, i.e. lda $009234 when using long addressing (to keep things simple for now).

But if you were using mode 21, it becomes very different: edit: previously said bank $80 etc., should've been $C0 etc.[/tt]:

ROM (file offset) $00000-0FFFF get mapped to bank $C0 address $0000-FFFF (also upper 32KB at bank $00 address $8000-FFFF)
ROM (file offset) $10000-1FFFF get mapped to bank $C1 address $0000-FFFF (also upper 32KB at bank $01 address $8000-FFFF)
ROM (file offset) $20000-2FFFF get mapped to bank $C2 address $0000-FFFF (also upper 32KB at bank $02 address $8000-FFFF)
ROM (file offset) $30000-3FFFF get mapped to bank $C3 address $0000-FFFF (also upper 32KB at bank $03 address $8000-FFFF)
...etc...

Here, offset $1234 would refer to bank $C0 address $1234, or $C01234, i.e. lda $C01234 when using long addressing. You would have no way of accessing the "lower 32KB half of a ROM bank" via banks $00-7D -- only banks $C0-FF could provide that.

Hopefully this also explains why Tepples said he prefers to think of mode 20 as "originating in bank $80/81/82/..., and mirrored to bank $00/01/02/...". I tend to look at it the other way around, but we both understand how it works.

Your program has to be written with knowledge of what mode (20 or 21) is being used. Your assembler lets you control this via a multitude of directives; .org is common (for code, and sometimes data), but assemblers like ca65/ld65 control it via ld65 configuration. In effect, your assembler memory configuration (all calculated addresses, etc.) have to "match" the memory mode on the SNES you're using, otherwise you'll refer to wrong/bogus data. This is a super common complication with ANY computer, FYI, so the SNES is not unique in this regard at all.

rchoudhary wrote:Now say I want to store the value in the A register in WRAM at address $BEEF. How do I access that? Do I need to calculate the CPU address (the address that gets mapped into a WRAM address) manually?

WRAM on the SNES can be accessed in 2 ways: a) directly via native 65816 addressing or b) through MMIO registers $2180/1/2/3. Let's assume the former, but I'll cover the latter at the end:

WRAM addressing itself is 17-bit, ranging from $00000-1FFFF. WRAM is mapped into 65816 banks $7E and $7F linearly, which makes it easy to understand:

WRAM $00000-0FFFF is mapped to bank $7E address $0000-FFFF
WRAM $10000-1FFFF is mapped to bank $7F address $0000-FFFF

Effectively, the WRAM-to-65816-native-address calculation formula is literally offset+$7E0000.

Thus, sta $7EBEEF would get you what you want. But if you really did want to use the MMIO registers, then rep #$30 / ldx #$BEEF / stx $2181 / stz $2183 / sta $2180 would accomplish the same thing.

Just don't forget that the lower 8KBytes ($0000-1FFF) of WRAM (e.g. $7E0000-1FFF) is mapped to bank $00 address $0000-1FFF as well. This is where (usually) the direct page, stack, etc. reside (in RAM). Hopefully this also helps explain the "little blue box in bank $7E" in Tepples' diagram/map.
Last edited by koitsu on Wed May 08, 2019 9:24 pm, edited 1 time in total.

93143
Posts: 1104
Joined: Fri Jul 04, 2014 9:31 pm

Re: Banks and zero page using wla-dx

Post by 93143 » Wed May 08, 2019 7:52 pm

koitsu wrote:But if you were using mode 21, it becomes very different:

ROM (file offset) $00000-0FFFF get mapped to bank $80 address $0000-FFFF (also upper 32KB at bank $00 address $8000-FFFF)
Hold on, don't you mean bank $40 or $C0, not $80?

Banks $80-$BF mirror banks $00-$3F, with the system area and everything. You can only access the bottom half of a HiROM bank in $40-$7D and $C0-$FF.

rchoudhary
Posts: 24
Joined: Wed Apr 10, 2019 4:24 pm

Re: Banks and zero page using wla-dx

Post by rchoudhary » Wed May 08, 2019 9:15 pm

koitsu wrote:So what's REALLY is happening, I strongly suspect, is this: banks $00-7F do in fact get mapped to ROM (!!!) via the cartridge addressing lines, for a full 32mbit... however, the SNES console itself has addressing lines that map RAM (WRAM) in banks $7E and $7F, and those "come after" the cartridge/ROM mapping, thus those take higher precedence. Think of it kind of in layers, if that makes sense: mappings overlayed atop mappings.
How does this "precedence" take place in hardware? And why does it only happen for banks $7E-$7F and not $FE-$FF?
koitsu wrote:There is no programming involved; they're dictated by the cartridge/PCB itself.
So the PCB itself maps it's ROM into the SNES address space? How? By "dictated" do you mean that within the ROM is some code that tells the SNES how memory should be mapped, e.g. by setting itself to LoROM or HiROM mode?

What I thought happens is whenever the CPU sees lda $009234 it thinks "ah shit gotta go to the cartridge" and sends the address $1234 to the ROM chip on the PCB, which in turn sends a byte or two back. Is that not how that works?

I actually bought a PCB from a guy on Etsy:

Image

It looks like the cartridge pins are just connected to the IC. Is the PCB basically just a breakout board for the ROM chip?
koitsu wrote:You tell me: where would file offset $1234 refer to in 65816 addressing space? If you guessed bank $00 address $9234, then give yourself a cookie, i.e. lda $009234 when using long addressing (to keep things simple for now).
So based on that, let's say my main.asm file had something like this:

Code: Select all

.bank 0
...
...                       ; $FF bytes of other stuff
...

myvar:    .db    $21      ; At address $100

...

    lda myvar             ; Still in bank 0

Would the preprocessor turn lda myvar into lda $008100? Can the assembler handle a full 24 bit address though?

User 93143 pointed out that there is a Data Bank Register. Would the assembler use that instead? Like would it load the Data Bank Register with $00 and then just do lda $8100?

And then finally, in the wikibook article on memory mapping, there is this chart:

Image

The part that confuses me is the hardware registers bit. If I want to force blank, I simply write $80 to $2100 in the code. But in which bank? What's the difference between $002100, $002100, ..., $3F2100? Also, why can I just do a lda $2100 instead of having to do lda $xx2100 (where xx is whatever the right bank is? I'm assuming it's because of the Data Bank Register?

Post Reply