diff --git a/snes/mapping/exhirom/mapping.go b/snes/mapping/exhirom/mapping.go index 3ee6ace..d1455ae 100644 --- a/snes/mapping/exhirom/mapping.go +++ b/snes/mapping/exhirom/mapping.go @@ -4,106 +4,105 @@ import "sni/snes/mapping/util" // https://thepoorstudenthobbyist.com/2019/05/18/custom-pcb-explanation/#exhirom -func BusAddressToPak(busAddr uint32) uint32 { +func BusAddressToPak(busAddr uint32) (pakAddr uint32, err error) { if busAddr >= 0xC00000 && busAddr < 0x1_000000 { // program area 1 // ROM access: $C0:0000-$FF:FFFF rom := (busAddr & 0x3FFFFF) + 0x000000 - return rom + return rom, nil } else if busAddr >= 0xA00000 && busAddr < 0xC00000 { if busAddr&0x8000 != 0 { // program area 1 // ROM access: $A0:8000-$BF:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0x7FFF >= 0x6000 { // TODO: A0-AF, B0-BF mirroring? // SRAM access: $A0:6000-$BF:7FFF bank := (busAddr >> 16) - 0xA0 sram := ((bank << 13) + (busAddr & 0x1FFF)) + 0xE00000 - return sram + return sram, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $A0:0000-$BF:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x800000 && busAddr < 0xA00000 { if busAddr&0x8000 != 0 { // program area 1 // ROM access: $80:8000-$9F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $80:0000-$9F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x7E0000 && busAddr < 0x800000 { // WRAM access: wram := (busAddr - 0x7E0000) + 0xF50000 - return wram + return wram, nil } else if busAddr >= 0x400000 && busAddr < 0x7E0000 { // program area 2 // ROM access: $40:0000-$7D:FFFF rom := (busAddr & 0x3FFFFF) + 0x400000 - return rom + return rom, nil } else if busAddr >= 0x3E0000 && busAddr < 0x400000 { if busAddr&0x8000 != 0 { // program area 3 $3E-3F // ROM access: $3E:8000-$3F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x400000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$1F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x200000 && busAddr < 0x3E0000 { if busAddr&0x8000 != 0 { // program area 2 // ROM access: $20:8000-$3D:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x400000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$1F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr < 0x200000 { if busAddr&0x8000 != 0 { // program area 2 // ROM access: $00:8000-$1F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x400000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$1F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } - return busAddr + return 0, util.ErrUnmappedAddress } -func PakAddressToBus(pakAddr uint32) uint32 { - // WRAM is easy: +func PakAddressToBus(pakAddr uint32) (busAddr uint32, err error) { if pakAddr >= 0xF50000 { + // WRAM is easy: // mirror bank $F7..FF back down into WRAM because these banks in FX Pak Pro space // are not available on the SNES bus; they are copies of otherwise inaccessible memory // like VRAM, CGRAM, OAM, etc.: - return ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 - } - // SRAM is a little more complex, but not much: - if pakAddr >= 0xE00000 && pakAddr < 0xF00000 { + busAddr = ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 + return + } else if pakAddr >= 0xE00000 && pakAddr < 0xF00000 { + // SRAM is a little more complex, but not much: + // TODO: handle A0-AF, B0-BF mirroring depending on SRAM size // A0:6000-7FFF to BF:6000-7FFF - busAddr := pakAddr - 0xE00000 + busAddr = pakAddr - 0xE00000 offs := busAddr & 0x1FFF bank := (busAddr >> 13) & 0x1F busAddr = ((0xA0 + bank) << 16) + (offs + 0x6000) - return busAddr - } - // ROM access: - if pakAddr < 0xE00000 { - var busAddr uint32 + return + } else if pakAddr < 0xE00000 { + // ROM access: if pakAddr >= 0x800000 { // map FX Pak Pro $800000 to SlowROM banks $00-3F (mirrored to $40..$5F) busAddr = (pakAddr - 0x800000) & 0x3FFFFF @@ -132,8 +131,7 @@ func PakAddressToBus(pakAddr uint32) uint32 { // $C0:0000-FFFF to $FF:0000-FFFF busAddr = 0xC00000 + (pakAddr & 0x3FFFFF) } - return busAddr + return } - // /shrug - return pakAddr + return 0, util.ErrUnmappedAddress } diff --git a/snes/mapping/exhirom/mapping_test.go b/snes/mapping/exhirom/mapping_test.go index 064ada8..b38d778 100644 --- a/snes/mapping/exhirom/mapping_test.go +++ b/snes/mapping/exhirom/mapping_test.go @@ -325,7 +325,7 @@ func TestPakAddressToBus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := PakAddressToBus(tt.args.pakAddr); got != tt.want { + if got, _ := PakAddressToBus(tt.args.pakAddr); got != tt.want { t.Errorf("PakAddressToBus() = %06x, want %06x", got, tt.want) } }) @@ -708,7 +708,7 @@ func TestBusAddressToPak(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BusAddressToPak(tt.args.busAddr); got != tt.want { + if got, _ := BusAddressToPak(tt.args.busAddr); got != tt.want { t.Errorf("BusAddressToPak() = %v, want %v", got, tt.want) } }) diff --git a/snes/mapping/hirom/mapping.go b/snes/mapping/hirom/mapping.go index 6d6bf33..9ba8c25 100644 --- a/snes/mapping/hirom/mapping.go +++ b/snes/mapping/hirom/mapping.go @@ -4,111 +4,112 @@ import ( "sni/snes/mapping/util" ) -func BusAddressToPak(busAddr uint32) uint32 { +func BusAddressToPak(busAddr uint32) (pakAddr uint32, err error) { if busAddr >= 0xFE0000 && busAddr < 0x1_000000 { // ROM access: $FE:0000-$FF:FFFF rom := (busAddr & 0x3FFFFF) + 0x000000 - return rom + return rom, nil } else if busAddr >= 0xC00000 && busAddr < 0xFE0000 { // ROM access: $C0:0000-$FD:FFFF rom := (busAddr & 0x3FFFFF) + 0x000000 - return rom + return rom, nil } else if busAddr >= 0xA00000 && busAddr < 0xC00000 { if busAddr&0x8000 != 0 { // ROM access: $A0:8000-$BF:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0x7FFF >= 0x6000 { // TODO: A0-AF, B0-BF mirroring? // SRAM access: $A0:6000-$BF:7FFF bank := (busAddr >> 16) - 0xA0 sram := ((bank << 13) + (busAddr & 0x1FFF)) + 0xE00000 - return sram + return sram, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $A0:0000-$BF:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x800000 && busAddr < 0xA00000 { if busAddr&0x8000 != 0 { // ROM access: $80:8000-$9F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $80:0000-$9F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x7E0000 && busAddr < 0x800000 { // WRAM access: wram := (busAddr - 0x7E0000) + 0xF50000 - return wram + return wram, nil } else if busAddr >= 0x400000 && busAddr < 0x7E0000 { // ROM access: $40:0000-$7D:FFFF rom := (busAddr & 0x3FFFFF) + 0x000000 - return rom + return rom, nil } else if busAddr >= 0x200000 && busAddr < 0x400000 { if busAddr&0x8000 != 0 { // ROM access: $20:8000-$3F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0x7FFF >= 0x6000 { // TODO: 20-2F, 30-3F mirroring? // SRAM access: $20:6000-$3F:7FFF bank := (busAddr >> 16) - 0x20 sram := ((bank << 13) + (busAddr & 0x1FFF)) + 0xE00000 - return sram + return sram, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$1F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x000000 && busAddr < 0x200000 { if busAddr&0x8000 != 0 { // ROM access: $00:8000-$1F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$1F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } - return busAddr + return 0, util.ErrUnmappedAddress } -func PakAddressToBus(pakAddr uint32) uint32 { +func PakAddressToBus(pakAddr uint32) (busAddr uint32, err error) { // WRAM is easy: if pakAddr >= 0xF50000 { // mirror bank $F7..FF back down into WRAM because these banks in FX Pak Pro space // are not available on the SNES bus; they are copies of otherwise inaccessible memory // like VRAM, CGRAM, OAM, etc.: - return ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 + busAddr = ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 + return } else if pakAddr >= 0xE00000 && pakAddr < 0xF00000 { // SRAM is a little more complex, but not much: + // TODO: handle A0-AF, B0-BF mirroring depending on SRAM size // bank $A0-$BF - busAddr := pakAddr - 0xE00000 + busAddr = pakAddr - 0xE00000 offs := busAddr & 0x1FFF bank := (busAddr >> 13) & 0x1F busAddr = ((0xA0 + bank) << 16) + (offs + 0x6000) - return busAddr + return } else if pakAddr >= 0x200000 && pakAddr < 0xE00000 { - busAddr := pakAddr & 0x3FFFFF + busAddr = pakAddr & 0x3FFFFF // Accessing memory in banks $80-$FF is done at 3.58 MHz (120 ns) if the value at address $420D (hardware register) is set to 1. // Starting at $C0 gets us the full linear mapping of ROM without having to cut up in $8000 sized chunks: busAddr = 0xC00000 + busAddr - return busAddr + return } else if pakAddr < 0x200000 { // ROM access: // HiROM is limited to $40 full banks // use SlowROM banks $00-$3F: - busAddr := pakAddr & 0x3FFFFF + busAddr = pakAddr & 0x3FFFFF offs := busAddr & 0x7FFF bank := busAddr >> 15 busAddr = ((0x00 + bank) << 16) + (offs | 0x8000) - return busAddr + return } - // /shrug - return pakAddr + return 0, util.ErrUnmappedAddress } diff --git a/snes/mapping/hirom/mapping_test.go b/snes/mapping/hirom/mapping_test.go index 13776eb..7e8c59e 100644 --- a/snes/mapping/hirom/mapping_test.go +++ b/snes/mapping/hirom/mapping_test.go @@ -332,7 +332,7 @@ func TestPakAddressToBus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := PakAddressToBus(tt.args.pakAddr); got != tt.want { + if got, _ := PakAddressToBus(tt.args.pakAddr); got != tt.want { t.Errorf("PakAddressToBus() = %06x, want %06x", got, tt.want) } }) @@ -741,7 +741,7 @@ func TestBusAddressToPak(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BusAddressToPak(tt.args.busAddr); got != tt.want { + if got, _ := BusAddressToPak(tt.args.busAddr); got != tt.want { t.Errorf("BusAddressToPak() = %v, want %v", got, tt.want) } }) diff --git a/snes/mapping/lorom/mapping.go b/snes/mapping/lorom/mapping.go index e8fdaa9..85090fd 100644 --- a/snes/mapping/lorom/mapping.go +++ b/snes/mapping/lorom/mapping.go @@ -4,80 +4,78 @@ import ( "sni/snes/mapping/util" ) -func BusAddressToPak(busAddr uint32) uint32 { +func BusAddressToPak(busAddr uint32) (pakAddr uint32, err error) { if busAddr >= 0xF00000 && busAddr < 0x1_000000 { if busAddr&0x8000 != 0 { // ROM access: $F0:8000-$F0:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else { // SRAM access: $F0:0000-$FF:7FFF sram := util.BankToLinear(busAddr-0xF00000) + 0xE00000 - return sram + return sram, nil } } else if busAddr >= 0x800000 && busAddr < 0xF00000 { if busAddr&0x8000 != 0 { // ROM access: $80:8000-$EF:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $80:0000-$EF:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } else if busAddr >= 0x7E0000 && busAddr < 0x800000 { // WRAM access: wram := (busAddr - 0x7E0000) + 0xF50000 - return wram + return wram, nil } else if busAddr >= 0x700000 && busAddr < 0x7E0000 { if busAddr&0x8000 != 0 { // ROM access: $70:8000-$7D:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else { // SRAM access: $70:0000-$7D:7FFF sram := util.BankToLinear(busAddr-0x700000) + 0xE00000 - return sram + return sram, nil } } else if busAddr < 0x700000 { if busAddr&0x8000 != 0 { // ROM access: $00:8000-$6F:FFFF rom := util.BankToLinear(busAddr&0x3F7FFF) + 0x000000 - return rom + return rom, nil } else if busAddr&0xFFFF < 0x2000 { // Lower 8KiB of WRAM: $00:0000-$6F:1FFF wram := (busAddr & 0x1FFF) + 0xF50000 - return wram + return wram, nil } } - return busAddr + return 0, util.ErrUnmappedAddress } -func PakAddressToBus(pakAddr uint32) uint32 { +func PakAddressToBus(pakAddr uint32) (busAddr uint32, err error) { // WRAM is easy: - if pakAddr >= 0xF50000 { + if pakAddr >= 0xF50000 && pakAddr < 0x1_000000 { // mirror bank $F7..FF back down into WRAM because these banks in FX Pak Pro space // are not available on the SNES bus; they are copies of otherwise inaccessible memory // like VRAM, CGRAM, OAM, etc.: - return ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 - } - // SRAM is a little more complex, but not much: - if pakAddr >= 0xE00000 && pakAddr < 0xF00000 { + busAddr = ((pakAddr - 0xF50000) & 0x01FFFF) + 0x7E0000 + return + } else if pakAddr >= 0xE00000 && pakAddr < 0xF00000 { + // SRAM is a little more complex, but not much: // bank $F0-$FF, $0000-$7FFF - busAddr := (pakAddr - 0xE00000) & 0x07FFFF + busAddr = (pakAddr - 0xE00000) & 0x07FFFF offs := busAddr & 0x7FFF bank := busAddr >> 15 busAddr = ((0xF0 + bank) << 16) + offs - return busAddr - } - // ROM access: - if pakAddr < 0xE00000 { - busAddr := pakAddr & 0x3FFFFF + return + } else if pakAddr < 0xE00000 { + // ROM access: + busAddr = pakAddr & 0x3FFFFF offs := busAddr & 0x7FFF bank := busAddr >> 15 busAddr = ((0x80 + bank) << 16) + (offs | 0x8000) - return busAddr + return } - // /shrug - return pakAddr + return 0, util.ErrUnmappedAddress } diff --git a/snes/mapping/lorom/mapping_test.go b/snes/mapping/lorom/mapping_test.go index 7b0da45..ff39d37 100644 --- a/snes/mapping/lorom/mapping_test.go +++ b/snes/mapping/lorom/mapping_test.go @@ -252,7 +252,7 @@ func TestPakAddressToBus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := PakAddressToBus(tt.args.pakAddr); got != tt.want { + if got, _ := PakAddressToBus(tt.args.pakAddr); got != tt.want { t.Errorf("PakAddressToBus() = %06x, want %06x", got, tt.want) } }) @@ -516,7 +516,7 @@ func TestBusAddressToPak(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := BusAddressToPak(tt.args.busAddr); got != tt.want { + if got, _ := BusAddressToPak(tt.args.busAddr); got != tt.want { t.Errorf("BusAddressToPak() = %v, want %v", got, tt.want) } }) diff --git a/snes/mapping/translate.go b/snes/mapping/translate.go index 5448538..7421df3 100644 --- a/snes/mapping/translate.go +++ b/snes/mapping/translate.go @@ -28,11 +28,11 @@ func TranslateAddress( case sni.AddressSpace_SnesABus: switch mapping { case sni.MemoryMapping_LoROM: - return lorom.PakAddressToBus(address), nil + return lorom.PakAddressToBus(address) case sni.MemoryMapping_HiROM: - return hirom.PakAddressToBus(address), nil + return hirom.PakAddressToBus(address) case sni.MemoryMapping_ExHiROM: - return exhirom.PakAddressToBus(address), nil + return exhirom.PakAddressToBus(address) default: return 0, ErrUnknownMapping } @@ -46,11 +46,11 @@ func TranslateAddress( case sni.AddressSpace_FxPakPro: switch mapping { case sni.MemoryMapping_LoROM: - return lorom.BusAddressToPak(address), nil + return lorom.BusAddressToPak(address) case sni.MemoryMapping_HiROM: - return hirom.BusAddressToPak(address), nil + return hirom.BusAddressToPak(address) case sni.MemoryMapping_ExHiROM: - return exhirom.BusAddressToPak(address), nil + return exhirom.BusAddressToPak(address) default: return 0, ErrUnknownMapping } diff --git a/snes/mapping/util/errors.go b/snes/mapping/util/errors.go new file mode 100644 index 0000000..c5555c0 --- /dev/null +++ b/snes/mapping/util/errors.go @@ -0,0 +1,5 @@ +package util + +import "fmt" + +var ErrUnmappedAddress = fmt.Errorf("address is not mapped")