SNES Development and 64tass

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.
Post Reply
Pokun
Posts: 1688
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

SNES Development and 64tass

Post by Pokun » Sun Nov 15, 2020 10:06 am

Continuing the discussion from here, I've started using 64tass for SNES homebrew development, and while it works very well I have trouble understanding some things about 64tass and SNES development in general.


Question 1:
Oziphantom wrote:
Sat Nov 14, 2020 12:47 am
Pokun wrote:
Fri Nov 13, 2020 9:56 am
Now there is just how to do a dummy long jump to bank $80 so that the SNES high-speed mode benefits kicks in.

Code: Select all

NMI:               ;in bank $00
  jml fast
fast:              ;in bank $80
  ...
How would I make this label "fast" have the bank byte $80? In WLA-DX it's as easy as sticking in ".base $80" before the label, but I see no such option in 64tass.

Edit: Answering my own question, after trying out various things this appears to work:

Code: Select all

NMI:
  jml fast+$800000 ;long jump to bank $80
fast:
  ...
This assembles to 5C xx 80 80 (long jump to $8080xx) instead of 5C xx 80 00 (long jump to $0080xx).
Too simple! No need for a .base directive.
Multiple ways to do this.
as you discovered adding $80000 works, but not really the right way.
the proper way would be

Code: Select all

NMI
	jml _fast
.logical $808000
_fast
	...
.here
This way all code will be treated and encoded as if it is in bank 80. Maybe something else will jump to a label and this way all labels here after will be in bank $80 as seen by the assembler.
However ; for a snes project the linear map of the ROM doesn't always match the memory map. LoROM for example so I would expect you have a "map" header where you do something along the lines of

Code: Select all

.logical $808000
.dsection BANK00
.here
; maybe put vectors here?
.align $8000 ; fill up empty bank space 
.logical $818000
.dsection BANK01
.here
....
If the earlier code thinks it is in $008000 vs $808000 that is fine as the vectors will be taking a <>NMI which is only 16 bits, and if the assembler puts the INIT code where it thinks it is 808000 not 008000 it doesn't matter.
I did actually try using .logical but I only got errors as I was already using sections and stuff. Besides NMI is not at $8000 but rather somewhere after that. I also tried doing .logical $808000+_fast but it would produce another error ("can't figure out stable address for _fast" or something like that).
Something I didn't do however, was putting ROM bank 0 in "65816 bank"* $80, but rather in bank $00. I did this because I thought the vector table must be in bank $00, but didn't realize that I can put only that part in bank $00. Now I did just this by moving the code/data section of the first ROM bank into bank $80. This produced a lot of errors first, but they where fixed by using the low-word operator <> on label references to make them 16-bit. Although the program works, this move made the errors I defined using .cerror (like in the examples in the manual) to stop working, even if I try exceeding the section limit. Errors for the RAM map do work as they should however.
BTW I was only using one ROM bank at first (I think anything more than 32 kB ROM is overkill for a hello world or other small program), but increased this to two ROM banks just to make sure multiple ROM banks works.

Here is the shell of my program, which show its structure. The code inside isn't important for this question.

Code: Select all

;=============================================================================
;ROM Size:     64 kB (2 banks of 32 kB each)
;Mapping:      Mode 20 (LoROM)
;Device Speed: Normal (SlowROM)
;Region:       NTSC

  .cpu "65816"                               ;set CPU to 65816
  .mansiz                                    ;set manual register sizes
  .proff                                     ;disable source listing
  .enc "ascii"                               ;define and set encoding "ascii"
  .cdef " ~",32                              ;include all printable characters

;Address space map:

;Internal WRAM (LowRAM $000000~$001FFF and HighRAM $7E2000~$7FFFFF, 128 kB):
*=$000000                                    ;Zero Page RAM
  .dsection zp                               ;$000000~$0000FF (256 B)
  .cerror * > $000100,"Out of ZP RAM!"
*=$000100                                    ;LowRAM bss
  .dsection bss_lo                           ;$000100~$001EFF (7 680 B)
  .cerror * > $001F00,"Out of LowRAM!"
*=$001F00                                    ;stack page
  .dsection stackpage                        ;$001F00~$001FFF (256 B)
  .cerror * > $002000,"Out of stack RAM!"
*=$7E2000                                    ;HighRAM bss
  .dsection bss_hi                           ;$7E2000~$7FFFFF (120 kB)
  .cerror * > $800000,"Out of HighRAM!"

;ROM bank 0 (808000~$80FFAF and $00FFB0~$00FFFF, 32 kB):
*=$000000
  .logical $808000                           ;ROM bank 0 program ($80+ mirror)
  .dsection rom0_prg                         ;$808000~$80FFAF (32 688 B)
  .cerror * > $80FFB0,"Out of ROM 0!"
  .here
*=$007FB0
  .logical $00FFB0                           ;header and vectors ROM area
  .dsection rom0_vector                      ;$00FFB0~$00FFFF (80 B)
  .here  
;ROM bank 1 (818000~$81FFFF, 32 kB):
*=$008000
  .logical $818000                           ;ROM bank 1 start ($80+ mirror)
  .dsection rom1                             ;$818000~$81FFFF (32 kB)
  .cerror * > $81FFFF,"Out of ROM 1!"
  .here

;[FIXME] "Out of ROM x" error messages stopped working since ROM banks where
;moved into the $8x banks.

;=============================================================================
; Constants
;=============================================================================
[defines here]

;=============================================================================
; Variables
;=============================================================================
;Internal WRAM definitions:
  .section zp               ;Zero page ($000000~$0000FF, 256 B)
temp0          .fill 8      ;general-purpose work register
temp1          .fill 8      ;general-purpose work register
pointer0       .fill 2      ;general-purpose pointer
pointer1       .fill 2      ;general-purpose pointer

  .send
  .section bss_lo           ;LowRAM bss ($000100~$001EFF, 7 680 B)
  
  .send
  .section stackpage        ;Stack page ($001F00~$001FFF, 256 B)
stack          .fill 256    ;top of LowRAM reserved for stack
  .send
  .section bss_hi           ;HighRAM bss ($7E2000~$7FFFFF, 120 kB)
  
  .send

;=============================================================================
; ROM Bank 0 ($808000~$80FFAF and $00FFB0~$00FFFF)
;=============================================================================
  .pron               ;enable source listing
  .section rom0_prg   ;ROM bank 0 program area ($808000~$80FFAF, 32 688 B)
RESET:
  [init code here]
  
  jml _fast
_fast:                ;jump to mirrored bank $80 to allow high-speed mode

;-----------------------------------------------------------------------------
main:
  wai
  jmp main
  
;-----------------------------------------------------------------------------
NMIn:
  jml _fast
_fast:
  rti
  
;-----------------------------------------------------------------------------
BRKn:
  jml _fast
_fast:
  rti
  
;-----------------------------------------------------------------------------
IRQn:
  jml _fast
_fast:
  rti

;-----------------------------------------------------------------------------
;Unused interrupt handlers:
COPn:
ABORTn:
COPe:
ABORTe:
NMIe:
IRQ_BRKe:
  jml _fast
_fast:
  rti
  
;-----------------------------------------------------------------------------
;Data:
[data here]

  .send
  
;=============================================================================
; Header ($00FFB0~$00FFDF) and CPU Exception Vectors ($00FFE0~$00FFFF)
;=============================================================================
  .section rom0_vector           ;headers and vectors ($00FFB0~$00FFFF, 80 B)
;Header:
[header here]

;Native mode vectors (65816 mode):
  .byte $00,$00,$00,$00
  .word <>COPn          ;COP opcode interrupt, unused on Super Famicom/NES
  .word <>BRKn          ;BRK opcode interrupt
  .word <>ABORTn        ;unused on Super Famicom/NES
  .word <>NMIn          ;Vertical blanking interrupt
  .byte $00,$00         ;65816 mode has no RESET vector
  .word <>IRQn          ;H/V-Timer or external interrupt
  
;Emulation mode vectors (6502 mode):
  .byte $00,$00,$00,$00
  .word <>COPe          ;emulation mode version of COP
  .byte $00,$00         ;6502 mode has no separate BRK vector
  .word <>ABORTe        ;unused on Super Famicom/NES
  .word <>NMIe          ;NMI vector for 6502 mode
  .word <>RESET         ;CPU is always in 6502 mode on boot/reset
  .word <>IRQ_BRKe      ;emulation mode IRQ/BRK vector
  .send

;=============================================================================
;ROM bank 1 (818000~$81FFFF)
;=============================================================================
  .section rom1         ;ROM bank 1 (818000~$81FFFF, 32 kB)
rom1:
  nop
  .align $8000,$00      ;0-pad to end of ROM bank
  .send
  
;-----------------------------------------------------------------------------
Am I doing the memory map correctly? I'm doing it similarly to how it's done in the examples in the manual, but the manual doesn't use the complicated SNES address space.



Question 2:
Do interrupts work if I jump to ROM bank 1 ($818000)? I don't need another vector table in that bank right?


*Rant: Why do we use the same word "bank" for two different things anyway? This is probably one reason SNES dev was such a headache to get into in the past before I realized that ROM banks and 65816 banks are two different concepts.

User avatar
dougeff
Posts: 2793
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: SNES Development and 64tass

Post by dougeff » Sun Nov 15, 2020 2:03 pm

question 2. you only need vector tables and NMI/IRQ code in bank 00. If either of those happen, it automatically sets the program bank to zero and jumps to the address at the vector table (and restores the old bank when RTI reached)

bank 80 is a mirror of bank 00, and assembling with addresses at 80,8000-ffff shouldn't break anything.
nesdoug.com -- blog/tutorial on programming for the NES

Oziphantom
Posts: 1014
Joined: Tue Feb 07, 2017 2:03 am

Re: SNES Development and 64tass

Post by Oziphantom » Sun Nov 15, 2020 10:35 pm

I also tried doing .logical $808000+_fast but it would produce another error ("can't figure out stable address for _fast" or something like that).
Yeah you can't make a logical from an address that is not explicitly defined, as moving the address might change the way you need to access it, 8bits vs 16bits vs 24bits which then moves the address which then changes if something needs a short branch or a long branch which then shifts a byte and then earlier optimisation changes and so you end up it a locked loop.

You seem to not quite get there is no Bank $80 it never exists. The SNES address space is only 23 bits so you have
SAAAAAAAAAAAAAAAAAAAAAAA
So S is speed access and A is the address. Thus Bank $00 and Bank $80 are the same bank.

So the vectors must be in bank $00 Lo Speed, and it must point to somewhere in $00 Lo Speed, but from there you can jump to any other bank and speed you want. But yes if you have something in the slow speed banks and the high speed banks, that needs a 16bit address you will need <> to force a 16bit address.

The .cerrors should still work, looks fine. add a .warn * which will print the current * so you can check it is where you think it is.

Code: Select all

;-----------------------------------------------------------------------------
BRKn:
  jml _fast
_fast:
COPn:		; these can just RTI 
ABORTn:		; who cares about spending more clocks
COPe:		; so you can return faster ;)
ABORTe:
NMIe:
IRQ_BRKe:
  rti
  
;-----------------------------------------------------------------------------
IRQn:
  jml _fast
_fast:
  rti
Q2. You can jump to bank 1. You don't need a vector table in anything other thank bank 0. The 65816 will only ever do a VECTOR PULL in bank 00.

*Rant - well no ROM Banks are 65816 Banks, just the physical layout in SMC files etc skips the empty parts. But only think about the memory map how the SNES sees it and a Bank is a Bank which is a Bank

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

Re: SNES Development and 64tass

Post by 93143 » Sun Nov 15, 2020 11:09 pm

Oziphantom wrote:
Sun Nov 15, 2020 10:35 pm
You seem to not quite get there is no Bank $80 it never exists. The SNES address space is only 23 bits
Sorry, that's not true. The SNES address space is 24 bits. The most obvious difference is that banks $7E and $7F are WRAM, but $FE and $FF are ROM. But it shows up all over the place once you get more complicated mappings - the first chipless Star Ocean hack was 12 MB across every ROM area from bank $00 to bank $FF, and a Super FX cart can officially have up to 6 MB of ROM in banks $80-$FF that doesn't show up at all in $00-$7D where the Super FX stuff is.

Bank $80 is usually a mirror of bank $00. If your ROM is smaller than 4 MB and you aren't using a Super FX or SA-1, there's no reason to ever worry about it, but it would be bad to get the idea that they're the same bank.

creaothceann
Posts: 274
Joined: Mon Jan 23, 2006 7:47 am
Location: Germany
Contact:

Re: SNES Development and 64tass

Post by creaothceann » Sun Nov 15, 2020 11:27 pm

Oziphantom wrote:
Sun Nov 15, 2020 10:35 pm
You seem to not quite get there is no Bank $80 it never exists. The SNES address space is only 23 bits so you have
SAAAAAAAAAAAAAAAAAAAAAAA
So S is speed access and A is the address. Thus Bank $00 and Bank $80 are the same bank.
It is easier to think of it as 24-bit imo, we can't just use bit 23 for speed.

Code: Select all

banks    | addresses   | speed | mapping                                       banks          | addresses   | speed | mapping
---------+-------------+-------+---------------------------------------------------------------------------------------------------------------------------------
$00..$3F | $0000-$1FFF |  slow | address bus A + /WRAM (mirror of bank $7E)    $80 + $00..$3F | $0000-$1FFF |  slow | address bus A + /WRAM (mirror of bank $7E)
.        | $2000-$20FF |  fast | address bus A                                 .              | $2000-$20FF |  fast | address bus A
.        | $2100-$21FF |  fast | address bus B                                 .              | $2100-$21FF |  fast | address bus B
.        | $2200-$3FFF |  fast | address bus A                                 .              | $2200-$3FFF |  fast | address bus A
.        | $4000-$41FF | xslow | internal CPU registers                        .              | $4000-$41FF | xslow | internal CPU registers
.        | $4200-$43FF |  fast | internal CPU registers                        .              | $4200-$43FF |  fast | internal CPU registers
.        | $4400-$5FFF |  fast | address bus A                                 .              | $4400-$5FFF |  fast | address bus A
.        | $6000-$7FFF |  slow | address bus A                                 .              | $6000-$7FFF |  slow | address bus A
.        | $8000-$FFFF |  slow | address bus A + /CART                         .              | $8000-$FFFF |     * | address bus A + /CART
$40..$7D | $0000-$FFFF |  slow | address bus A + /CART                         $80 + $40..$7D | $0000-$FFFF |     * | address bus A + /CART
$7E..$7F | $0000-$FFFF |  slow | address bus A + /WRAM                         $80 + $7E..$7F | $0000-$FFFF |     * | address bus A + /CART

* if bit $420D.0 is set then fast, else slow
My current setup:
Super Famicom ("2/1/3" SNS-CPU-GPM-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10

Oziphantom
Posts: 1014
Joined: Tue Feb 07, 2017 2:03 am

Re: SNES Development and 64tass

Post by Oziphantom » Mon Nov 16, 2020 12:16 am

I debated about adding an * to it, but felt it was better to not complicate it further.

They are still mostly looking at the MAP as the ROM layout and not how the SNES sees it. To which the SNES mostly maxes out 8MB*.
But yes its not a "speed" bit and once you get in to more extreme SNES layouts from the 65816 perspective using SuperFX, SA-1, SDD-1 and generally other weirdness because copy protection the "edge cases" appear. But that is a battle far in their future, when they will have a much better grasp of the SNES and the 65816. For now they will be in a land of 23bits ;)

Good catch with FE:FF not mapping to 7E:7F I forgot about that little "trap", although in the maps they are using now it is actually RAM but its SRAM not WRAM. Just to make it more confusing ;)



*7.9375MB - although other chips can perform extra banking to expand, but this replaces rather than expands; and as noted the Star Ocean 12MB hacks

Pokun
Posts: 1688
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: SNES Development and 64tass

Post by Pokun » Wed Nov 18, 2020 5:05 pm

Thanks for all answers, this clears up many things.


My understanding sure is still full of holes, but I'm not sure what it is I'm not understanding about address space layout? The 65816 address space is 24-bit whether or not memory or MMIO devices are mapped to it all or not for the SNES and current cartridge. I like to have addresses with leading zeroes for the full 6 hexadecimal digit address to reflect this clearly. Of course this makes some of the mapping descriptions a bit odd, as part of ROM bank 0 is in bank $80 and part is in bank $00. But I think these answers has cleared up it all for me.

The reason the user-defined errors didn't work is probably just me doing something wrong when trying to overfill the ROM to test the error. I will deal with that later. For now it's enough that the section layout is correct.


The JML in the unused interrupt handlers was due to paranoia that interrupts could somehow mess with the high-speed banks. But with the answers to question 2, I now know that I won't need this long jump.


What I mean by two types of banks are the 65816's concept of "bank" which is the high byte of the 24-bit address. In other words, such a bank spans a 64 kB address space. The SNES ROM banks used in a typical LoROM mapping though, is only half a 65816 bank at 32 kB. I'm not entirely against using the same word for both things (ROM banks are usually called banks on other systems as well, no matter the size), but an introductory document or tutorial about SNES homebrew should probably be aware of this kind of problematic naming and explain it early on to avoid confusion.

I will probably post more questions about SNES development or 64tass here in the future.

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

Re: SNES Development and 64tass

Post by tepples » Wed Nov 18, 2020 7:05 pm

Would it be helpful to define "ROM bank" for Super NES purposes as the portion of ROM occupying some fraction of a 65816 bank?

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

Re: SNES Development and 64tass

Post by 93143 » Wed Nov 18, 2020 7:11 pm

Oziphantom wrote:
Mon Nov 16, 2020 12:16 am
But that is a battle far in their future
My philosophy is that it's better to start with exclusively correct facts, and not attempt to make things easier with convenient fictions. Those convenient fictions can easily become inconvenient and impede further understanding. Best not to have to "un-learn" anything in the process of gaining expertise.

I don't think it's more confusing to deal with the concept of mirroring up front (you need it for shadow RAM anyway) than to try to substitute the concept of a 23-bit address bus plus a top bit that controls access speed and switches between WRAM and the top two banks of ROM. And the advantage of the former is that it's future-proof - he won't have to rip out and replace embedded knowledge to work on an SA-1 game.

Pokun wrote:
Wed Nov 18, 2020 5:05 pm
The JML in the unused interrupt handlers was due to paranoia that interrupts could somehow mess with the high-speed banks. But with the answers to question 2, I now know that I won't need this long jump.
Uh... the vector table is 16-bit. All interrupt handlers have to be in bank $00, so if you want the interrupt code to execute at high speed, you do need the JML. I do this in my game, because I'm using an H-IRQ for raster effects, and it needs to run at full speed to avoid visible glitching.

If it's unused, then no, you shouldn't need a JML. Even if it accidentally gets triggered, it will return to whatever bank you were running in, and having the JML in there will just waste more time.
What I mean by two types of banks are the 65816's concept of "bank" which is the high byte of the 24-bit address. In other words, such a bank spans a 64 kB address space. The SNES ROM banks used in a typical LoROM mapping though, is only half a 65816 bank at 32 kB. I'm not entirely against using the same word for both things (ROM banks are usually called banks on other systems as well, no matter the size), but an introductory document or tutorial about SNES homebrew should probably be aware of this kind of problematic naming and explain it early on to avoid confusion.
It might be helpful to consider ROM banks as being exclusively an assembler-level formality that helps you not trip over the SNES banking system. The actual ROM isn't divided into banks; it's simply mapped into the SNES address space, which is. LoROM skips bit A15; HiROM doesn't.

(LoROM and HiROM are not an exhaustive description of mapping possibilities either. The glue logic on the cart, if it's smart enough, can put any chunk of ROM of any size anywhere you want in the SNES address space as long as it doesn't conflict with something else (like MMIO or WRAM). As long as you keep that in mind, the LoROM/HiROM distinction won't become a "convenient fiction".)

tepples wrote:
Wed Nov 18, 2020 7:05 pm
Would it be helpful to define "ROM bank" for Super NES purposes as the portion of ROM occupying some fraction of a 65816 bank?
That sounds about right. It's probably possible to break that definition, but it's also probably pointless/inadvisable to do so.

It gets interesting in cases like HiLoROM or whatever people call it, like the Star Ocean hack map or the extended Super FX map. In those cases, you have 32 KB "ROM banks" in SNES banks with system area in the bottom half, and 64 KB "ROM banks" in SNES banks that are 100% ROM. But the definition still holds.

Pokun
Posts: 1688
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: SNES Development and 64tass

Post by Pokun » Thu Nov 19, 2020 3:35 am

tepples wrote:
Wed Nov 18, 2020 7:05 pm
Would it be helpful to define "ROM bank" for Super NES purposes as the portion of ROM occupying some fraction of a 65816 bank?
Possibly, but the important thing is to make it clear that they are two different things using the same term. Confusion happens when you think they are somehow the same thing but also appears to have conflicting characteristics (like size), and you simply can't see where it doesn't click for you. This is probably highly individual, but I've seen this discussion before on these forums, so it's a reappearing pattern.

93143 wrote:
Wed Nov 18, 2020 7:11 pm
Pokun wrote:
Wed Nov 18, 2020 5:05 pm
The JML in the unused interrupt handlers was due to paranoia that interrupts could somehow mess with the high-speed banks. But with the answers to question 2, I now know that I won't need this long jump.
Uh... the vector table is 16-bit. All interrupt handlers have to be in bank $00, so if you want the interrupt code to execute at high speed, you do need the JML. I do this in my game, because I'm using an H-IRQ for raster effects, and it needs to run at full speed to avoid visible glitching.

If it's unused, then no, you shouldn't need a JML. Even if it accidentally gets triggered, it will return to whatever bank you were running in, and having the JML in there will just waste more time.
Sure, I'm talking about unused interrupt handlers here, which in my case is COPn, ABORTn, COPe, ABORTe, NMIe and IRQ_BRKe (e=emulation mode, n=native mode). These have no code besides a common RTI. I still have the JML for the interrupt handlers that I do plan to use (NMIn, BRKn and IRQn) as a reminder that I will need it when I put code in them.

93143 wrote:
Wed Nov 18, 2020 7:11 pm
It might be helpful to consider ROM banks as being exclusively an assembler-level formality that helps you not trip over the SNES banking system. The actual ROM isn't divided into banks; it's simply mapped into the SNES address space, which is. LoROM skips bit A15; HiROM doesn't.

(LoROM and HiROM are not an exhaustive description of mapping possibilities either. The glue logic on the cart, if it's smart enough, can put any chunk of ROM of any size anywhere you want in the SNES address space as long as it doesn't conflict with something else (like MMIO or WRAM). As long as you keep that in mind, the LoROM/HiROM distinction won't become a "convenient fiction".)
Yeah I know there are a bunch of "mappers" for SNES that makes memory appear in various different places, and that emulators like bsnes uses some metadata database (called "manifests") to keep track of what games uses what kind of mapping.
I only know the typical LoROM for now, but I take it that it's possible for the cartridge to remap MMIO, internal WRAM or other things in the SNES base unit as well with the right glue logic.



BTW I recently fell into another trap (which I've fallen into before on the PC Engine I think). I was trying to write into VRAM address $6000 and it kept ending up at $C000 in Mesen-S' Memory Tools. I knew that VRAM uses word addressing (another common trap) but I didn't realize that Memory Tools is simply a hex editor that just displays the byte offset for each byte, not the actual VRAM word address number, and $C000 would be the byte offset for word address $6000. I went to bed frustrated in not being able to solve it, and suddenly realized it once in bed. Eureka!

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

Re: SNES Development and 64tass

Post by 93143 » Thu Nov 19, 2020 8:12 pm

Pokun wrote:
Thu Nov 19, 2020 3:35 am
I take it that it's possible for the cartridge to remap MMIO, internal WRAM or other things in the SNES base unit as well with the right glue logic.
Huh? No no no. The internal stuff is all hardwired. The cartridge can only remap stuff that's in the cartridge. It can try to respond to accesses in ranges occupied by system stuff, but that would create a bus conflict.

Which is what I meant. I meant that you can't usefully map ROM to an area of the address space that's already taken up by something else. If you map ROM to bank $7F, for instance, you get a conflict with WRAM, because you can't move WRAM.

However, you can map ROM - or SRAM, or expansion chip MMIO - to (for example) $5000-$5FFF in banks $00-$3F. The SNES doesn't have any internal functionality mapped there, so it will work fine even though it's officially system area and not one of the designated ROM areas. Better yet, that area is considered "fast" by the CPU, so you'll get FastROM timings on accesses in that range regardless of the value of $420D.

The base SNES memory map is full of holes. As long as you put the cartridge resources in the holes, instead of trying to take over address ranges the base unit already has dibs on, you're good.

creaothceann
Posts: 274
Joined: Mon Jan 23, 2006 7:47 am
Location: Germany
Contact:

Re: SNES Development and 64tass

Post by creaothceann » Thu Nov 19, 2020 11:50 pm

The Pro Action Replay "controls" the contents of WRAM, but it does this by redirecting the NMI handler and constantly writing the values to WRAM.

However, wasn't there a thread about a homebrew expansion port device on this forum that could read address bus B and clear data bus bits / drive data bus bits with more strength than the SNES itself?
My current setup:
Super Famicom ("2/1/3" SNS-CPU-GPM-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10

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

Re: SNES Development and 64tass

Post by 93143 » Fri Nov 20, 2020 12:29 am

Oh yeah, I forgot about that.

That's true, but overpowering the bus to map cartridge stuff to reserved areas would block read access to the internal facilities that use those areas. Very bad for large-scale static mapping, although potentially useful for a hardware hacking device like the 21fx. (Also, I'm still not convinced that large-scale bus hijacking isn't dangerous to the hardware - doesn't the 21fx mostly just use it for vector spoofing?)

Regardless of the practicality of overriding access to internal facilities with cartridge facilities, I don't think it's reasonable to expect any external device to be able to remap internal facilities, like for example moving WRAM to banks $FE and $FF. I could be wrong - I'm not a hardware expert - but the only way I can think of to do that would be to read the address bus very fast and then overdrive it with an alternate address fast enough for the targeted device to respond with a stable result before the end of the cycle. Sounds nasty.

...

Oh - there's one other thing you can do. You can duplicate writes to internal facilities to a cartridge facility, because they do show up on the cartridge pins. I seem to recall an SD2SNES project that was talking about snooping writes to the APU ports and using them to run an emulation of the APU for savestate purposes. The actual APU would be loaded with an infinite loop first so it wouldn't make any noise, and then the game audio would be fed through the cartridge pins from the emulated APU. And I know the S-DD1 snoops the bus for DMA control writes so it knows when to start decoding.

Write snooping/forking is much, much more sane and feasible than read spoofing IMO. But still limited in application compared to normal one-device-per-address read/write mapping.

Pokun
Posts: 1688
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: SNES Development and 64tass

Post by Pokun » Sat Dec 26, 2020 8:59 am

I see, so the address space used by the internal hardware is reserved and can't normally be changed. Makes sense as I guess address decoding for the internal hardware is done in the console like on NES.
Yeah the address map is really full of holes, I usually call it a Swiss cheese. I made an address map of my understanding of the reserved space:

Code: Select all

SFC internal hardware address map:
Address           size    description
$xx0000~$xx1FFF   8 kB    LowRAM mirror (xx is all banks between $00~$3F)
$xx2100~$xx21FF           MMIO page for PPU and APU mirror (xx=$00~$3F)
$xx4016~$xx4017   2 B     manual joypad I/O registers mirror (xx=$00~$3F)
$xx4200~$xx42FF           MMIO page $42 for CPU mirror (xx=$00~$3F)
$xx4300~$xx43FF           MMIO page $43 for CPU mirror (xx=$00~$3F)
$7E0000~$7E1FFF   8 kB    LowRAM
$7E2000~$7FFFFF   120 kB  HighRAM
$xx0000~$xx1FFF   8 kB    LowRAM mirror (xx is all banks between $80~$BF)
$xx2100~$xx21FF           MMIO page for PPU and APU mirror (xx=$80~$BF)
$xx4016~$xx4017   2 B     manual joypad I/O registers mirror (xx=$80~$BF)
$xx4200~$xx42FF           MMIO page $42 for CPU mirror (xx=$80~$BF)
$xx4300~$xx43FF           MMIO page $43 for CPU mirror (xx=$80~$BF)

Note: Because not all of the space for the MMIO pages are used, the size of
those are not specified in the above table.
So unless I'm mistaken, anything not included above should be available for the cartridge connector and the EXT connector.



I started with sound and was eventually able to play a square wave sound successfully with various pitch and volume, and also noise.
I concluded that bass and ca65 (with the SPC700 macro pack) are the only two assemblers that seems to produce correct SPC700 code. TASM has a classic syntax and seems good, but it's kind of tricky to make some instructions work and it's not very flexible. Bass has a much more flexible table syntax, so I made my own SPC700 table file for it as I couldn't find a premade one using the native SPC700 syntax. It comes with a file for testing all instructions (though it doesn't test everything using symbols). Download here:
bass_spcn.7z
Native SPC700 syntax table for bass.
(3.58 KiB) Downloaded 12 times
I don't like the C-like syntax in bass though, and it's missing many things I would normally expect in an assembler. Many things must be done using macro. I'm probably going to switch over to ca65 for my SPC700 needs.


Also for anyone following this and trying to do the same things as me, I highly recommend the Retro Game Mechanics Explained SNES videos. Although I already know much of it, that only makes less overwhelming for the things I don't know. I especially recommend the video-related explanations and the one about memory mapping. Those are subjects that are really hard to describe in words, and really benefits from the animations. The DMA stuff on the other hand doesn't work well in a youtube video as there's not much to visualize, but it's not really hard stuff anyway. It's mainly a matter of knowing what registers to write to, to setup a DMA, which many tutorials already explain. The DMA video is still worth a watch though as it also demonstrates the use of the Window among other things.


I've also played around a bit with sprites, scrolling and controllers (and notices a bug in Mesen-S regarding sprites).
Next up I think I'm going to do a small game or game demo.

Post Reply