It is currently Mon Jul 15, 2019 7:40 pm

All times are UTC - 7 hours



Forum rules





Post new topic Reply to topic  [ 20 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed May 08, 2019 9:23 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4149
Location: A world gone mad
93143 wrote:
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.

You're right, I meant banks $C0-FF. I'll edit my previous post to correct the error. Thanks!

This is why these "gargantuan information threads asking 900 questions per post" are very hard to manage/organise; chance of mistakes greatly increases as scope increases.

As for $40-7D: this is only the case on some/specific PCB revisions and is not a universal norm for mode 21. These details (re: $40-7D) are omitted from old official documentation, and new official documentation only denotes this for specific boards -- some mode 21 boards have nothing in the lower half of those banks, but the upper half has parts of the upper half of ROM (e.g. acts like mode 20); see SVHC-8PV5B for example (very diverse/complicated board since it supports mode 20 or mode 21, as well as multiple mask ROM sizes). And OTHER boards, like SVHC-4QW5B (mode 21 only) only map banks $40-5F of all places. In other words: it's a clusterf***.

The mode 21/HiROM memory map Tepples did establishes Good Habits and is pretty universal. Teaching people to exclusively use $C0-FF is good. "Edge cases" where you have some crazy programmer doing crazy things (do you know of one? ;-) ) should not be catered to in commonplace learning documentation (reference material yes, learning no).


Top
 Profile  
 
PostPosted: Wed May 08, 2019 9:56 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8475
Location: Seattle
rchoudhary wrote:
So the PCB itself maps its ROM into the SNES address space? How?
By the physical locations of the wires on the PCB, and specific parts on the PCB.

Quote:
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?
No software. It's purely electrical. (Exceptions exist. Pretend they don't)

Quote:
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?
It's about ... 70% right? The actual order of events is
SNES says "Hey, I'm talking about $009234" - this number is expressed in binary, and placed on the 24 address lines coming out.
SNES says "By the way, that's a place that is part of the 'normal ROM locations'" - we call this signal /ROMSEL but in practice that's a over-simplification.
SNES says "Oh, and I'm reading"

After that, the answer is "it depends". Specifically in the PCB you showed, the "normal ROM location" signal is just used by the ROM directly, and is used to tell the ROM to wake up at the right time. The ROM gets only some of the address lines from the SNES, looks up the right value inside itself, and provides that value to the SNES when the ROM is told that the SNES is reading.

Quote:
It looks like the cartridge pins are just connected to the IC. Is the PCB basically just a breakout board for the ROM chip?
For boards like this, with just one ROM and the CIC - yes, exactly that.


Top
 Profile  
 
PostPosted: Wed May 08, 2019 10:07 pm 
Offline

Joined: Wed Apr 10, 2019 4:24 pm
Posts: 24
lidnariq wrote:
lidnariq wrote:
By the physical locations of the wires on the PCB, and specific parts on the PCB.
...
No software. It's purely electrical.


How does setting the mode to $20 or $21 factor in then? Because for both of those modes it seems that the mapping changes. The same SNES address might refer to different locations in the cartridge ROM depending on the mapping scheme right? How does wiring by itself fix this?

UPDATE

I found an excerpt online that says this:

Quote:
The SNES has 24 address lines, that’s 3 bytes. that’s why we have
$00:0000 to $FF:FFFF bytes of addressing possible on SNES.
In traditional memory maps, the ROM in banks 00-7f is mirrored in $80-$ff. This is because the ROM is ignorant of SNES address line 23 (0-23). In a LoRom setup, SNES Address line 15 is not connected to the ROM, so even though the SNES accesses the cart with address $00:8000, the ROM is being accessed at $00:0000. Bit 15 is the one that makes $0000 turn to $8000. So when you disregard it, you go from $0000-7fff, then back to $0000,$7fff, then after that, bit 16 will be set, and the ROM is connected, so it knows to send other location data. Clearly it is shifted. SNES A15 NC ROM A15, therefore, SNES A16 = ROM A15. This means that ROM will be accessed without gaps, even though the SNES is gapping.


So it looks like by just ignoring bit 15 we can achieve the mapping to banks $00-$7F, and by ignoring bit 23 we can achieve the mirroring to banks $80-$FF. So I guess that's how the wiring itself gives you LoROM mapping.

But how does setting the mode to say $21 work? The memory layout is a bit different right? How can you achieve it? You can't "rewire" the cartridge after all (right?)...


Top
 Profile  
 
PostPosted: Wed May 08, 2019 10:31 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8475
Location: Seattle
rchoudhary wrote:
How does setting the mode to $20 or $21 factor in then? Because for both of those modes it seems that the mapping changes.
Because Nintendo required that the right number be there when they manufactured the ROMs. And emulators have since used that information to choose how to handle the file on your disk. But there's nothing in the SNES itself that cares what's there.

Quote:
So it looks like by just ignoring bit 15 we can achieve the mapping to banks $00-$7F
... no, that's something else. Specifically for the PCB you showed, ignoring bit 15 means that the contents seen at $400000 are the same as the contents seen at $408000. A15 is that $8000.

However, the SNES itself puts something different at $000000 from $008000. (That's the "ordinary ROM area" signal I was talking about)

Quote:
But how does setting the mode to say $21 work? The memory layout is a bit different right? How can you achieve it? You can't "rewire" the cartridge after all (right?)...
Sure you can. Take a close look at the two places to place the ROM on the board. See how for many of the signals, the two adjacent holes are connected, but for several they're not?


Top
 Profile  
 
PostPosted: Wed May 08, 2019 10:57 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4149
Location: A world gone mad
I've edited my reply to remove the hardware-oriented parts since lidnariq et al can answer those. I did do write-ups but this thread is turning into what I call a "time vampire" and that isn't making me happy.

rchoudhary wrote:
So based on that, let's say my main.asm file had something like this:
{snipping for brevity}
Would the preprocessor turn lda myvar into lda $008100? Can the assembler handle a full 24 bit address though?

There's no preprocessor involved. I think you mean "assembler pass", which would be true -- sort of. It's more complicated with assembler+linker combos but I'll explain it.

I can't answer the first question because I don't know what WLA DX does with .bank 0. I don't use WLA DX for reasons exactly like this, and exactly why I chastise the thing to no end. I would expect to see an .org in there somewhere, or possibly a linker config that specifies bank-to-address values. .bank 0 to me doesn't smell like "65816 bank $00", it smells more like "the first bank". There must be something else that correlates that with an actual 65816 bank/address.

As for your 2nd question: of course the assembler can handle long addressing! Nothing stops you from doing lda.l myvar to use opcode $AF (lda abslong), which is 4 total bytes in size.

But what address it assembles to is going to depend on WLA DX's configuration (either the assembler or the linker, it depends. "It depends" applies to ca65/ld65 too, just for the record. Linkers make something that's pretty logical/simple into something much more complicated.) This is why, again, I strongly suggest generating listings files. From these you can often discern if the address you expect matches what the assembler generated.

For symbols that get deferred to the linker, the situation is more complicated, as the addresses are "relative" or "relocatable" rather than literal. I think it's easier if I just demonstrate using ca65/ld65 (because there's no way in hell I'm touching WLA DX). Below is a line of code I wrote, and this output is from the listings file ca65 (the assembler) generated:

Code:
00038Ar 1  BF rr rr rr      lda f:TextLookupTable,x

This is an accumulator load from a 24-bit address indexed with X. The address is label called TextLookupTable. I forced 24-bit addressing using the f: prefix (this is akin to WLA DX's .l suffix on the opcode, or on the operand). But you can see there's no actual address there -- it shows rr rr rr where there would normally be literal values (example: had I written lda $7e1234,x I would've gotten BF 34 12 7E for the actual bytes in the listing file itself). So I have to refer to the symbol-to-addressing map file that the linker (ld65) generates. Here we find it:

Code:
al 039800 .TextLookupTable

And there we have our answer: address $039800. Thus, we can safely assume the full instruction is BF 00 98 03 -- and sure enough that is what's in the ROM file.

Because you're asking all sorts of questions, the next thing you're going to ask me is how exactly the linker knew to put that variable in bank $03 address $9800? Those details come from a combination of the actual code itself (where TextLookupTable is declared) and the linker configuration. So let's look at that. First the code:

Code:
.segment "BANK03" : far

...

TextLookupTable:
.repeat 12, row
    .repeat 8, i
        .word ((i*2)+(row*$20) & $03FF)
    .endrepeat
.endrepeat
...

The important part is on the first line: this variable/label is inside of a segment called BANK03, and it's declared as far (which means the assembler is supposed to know intelligently when to use 24-bit vs. 16-bit addressing, but I wanted to take no chances). The important thing is that it's not in bank $00 where my code runs from. So what about this magic BANK03 thing? On to the linker configuration:

Code:
MEMORY {
    ...
    ROM00: start = $008000, size = $8000, type = ro, file = %O, fill = yes, fillval = $FF;
    ROM01: start = $018000, size = $8000, type = ro, file = %O, fill = yes, fillval = $FF;
    ROM02: start = $028000, size = $8000, type = ro, file = %O, fill = yes, fillval = $FF;
    ROM03: start = $038000, size = $8000, type = ro, file = %O, fill = yes, fillval = $FF;
    ...
}
SEGMENTS {
    CODE:     load = ROM00, align = $8000, type = ro, optional = no;
    BANK01:   load = ROM01, align = $8000, type = ro, optional = yes;
    BANK02:   load = ROM02, align = $8000, type = ro, optional = yes;
    BANK03:   load = ROM03, align = $8000, type = ro, optional = yes;
    ...
}

This is where I start getting annoyed at ld65 (I'm one of those people who prefer assemblers without linkers, i.e. I'd rather just use .org statements), but hopefully you can "sort of" piece together what's happening there: segment BANK03 refers to a memory layout entry called ROM03. ROM03's memory layout says the start of the segment is at $038000 in 65816 addressing space. All this correlates/works properly with mode 20 given its memory mapping.

Remember: the linker is what generates the ROM file itself, which is how it's able to "fill in all the blanks" that the assembler couldn't.

I am certain WLA DX works similarly in this regard, I just want nothing to do with its terrible syntactical sugar to try and make heads/tails of it. It doesn't really make any sense to me either, while ca65/ld65's setup at least makes MORE sense, once you put all the pieces together.

rchoudhary wrote:
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?

No, the assembler will not do this for you automatically. You can do it with code yourself. Again: I don't speak WLA DX but this can be done with pretty much any assembler using pseudo-ops that allow you to get the bank address of "something" (a symbol, label, whatever) and do whatever you want with it. Using my above ca65/ld65 example, I could effectively do this in my code:

Code:
sep #$20    ; A=8
lda #.bankbyte(TextLookupTable)    ; or even .bankbyte(BANK03)
pha
plb

You might know .bankbyte as ^ in WLA DX or other assemblers -- it gets the upper 8-bits of the 24-bit address (calculated either at assemble-time or link-time; see above).

There is currently no assembler out there, that I know of, which "tracks" B (a.k.a. DB, i.e. data bank register) changes. In fact, you can't do this reliably anyway in the assembler, at least not reliably 100% of the time, for the exact same reasons as why you can't reliably 100% of the time track sep/rep sizing: because those instructions happen at runtime, not assemble-time. An assembler is not an emulator. The best the assemblers can do is try to "follow your code" (linearly) and handle it from there. This is why every assembler has pseudo-ops that let you tell it "8-bit X/Y" or "16-bit A", etc.. Just nobody has done it with B/DB.

There is an assembler in the works that will handle D (direct page) and B/DB changes through pseudo-ops, but it's not out yet and still being worked on (but actively/daily).

The rule of thumb is this: if you change S (stack), D (direct page), or B/DB (data bank register), it is _your responsibility_ to make sure your code does the right thing and is accessing the right variables, behaves correctly, backs up/restores those values (if doing something temporary), etc.. The assembler cannot do everything for you magically.

rchoudhary wrote:
And then finally, in the wikibook article on memory mapping, there is this chart:
{snipping for brevity}
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?

I think these massive posts and walls of text (replies, questions, everything) are causing you to miss/overlook things already stated.

There is no difference in mode 20 between $2100 at bank $00, bank $01, or bank $3E. The $2000-5FFF region is mirrored as well. PLEASE STOP LOOKING AT THAT TERRIBLE MEMORY MAP ON THE WIKIBOOKS WIKI. Instead, look at Tepples' mode 20 memory map again: download/file.php?id=15008 :-)

Address ranges $2000-5FFF are labelled I/O for a reason (MMIO registers). Now look at the bank numbers at the top of the picture. Now you understand the mirroring; i.e. you can have B set to $2E and do sta $2100 and you'll affect the screen brightness register. This is why I said earlier mode 20 is "easy to program for"!

I stated earlier that the 65816 on power-on/reset starts in bank $00 -- that's where your code starts executing from. B is also set to $00. The 65816 CPU starts in 6502 emulation mode as well. This is why one of the most common set of instructions at a RESET vector are these:

Code:
sei   ; Inhibit interrupts
phk
plb   ; Sets B/DB to the same bank as K (active code bank)
clc
xce   ; Enable native 65816 mode (i.e. get out of emulation mode)

Often followed by this, or a variation of:
Code:
rep #$30
lda #$0000
tcd
ldx #$01FF
txs

And a cli later on to re-enable interrupts (this doesn't affect NMI, only IRQ and some others).

BTW, you cannot lda $2100. Not all MMIO registers are readable -- in fact, it's best to assume they aren't unless explicitly stated in documentation. Try referring to this. Because of this fact (not all MMIO registers are readable), it's up to you to retain MMIO register contents through use of a separate variable somewhere in direct page or RAM, if you need to. It's based entirely on circumstance/situation. $2100 is not a great example because it pretty much just does forced blanking (screen off) and brightness. But if you look at $2101 (also not readable), you can see how there would be situations where you may want to tweak a single bit in that MMIO register, but since you can't read it directly, you need to "keep track" somehow...


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group