mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 12:08:01 -04:00
84ad23c03e
this means that a mapper only needs to implement the Patch() if it makes sense mappers that don't need it have had the Patch function removed. implemented function for SCABS and UA corrected error messages for atari mappers - some messages weren't referencing the correct atari mapper and simply stated "atari"
185 lines
5 KiB
Go
185 lines
5 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package cartridge
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jetsetilly/gopher2600/environment"
|
|
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
|
|
"github.com/jetsetilly/gopher2600/hardware/memory/memorymap"
|
|
)
|
|
|
|
type superbank struct {
|
|
env *environment.Environment
|
|
|
|
mappingID string
|
|
|
|
// superbank cartridges can have 32 banks (128k) or 64 banks (256k)
|
|
bankSize int
|
|
banks [][]uint8
|
|
|
|
// the mask we use to decide whether the listen() address will trigger a
|
|
// bankswitch. this will change depending on the exact size of the
|
|
// cartridge.
|
|
bankSwitchMask uint16
|
|
|
|
// rewindable state
|
|
state *superbankState
|
|
|
|
// !!TODO: hotspot info for superbank
|
|
}
|
|
|
|
func newSuperbank(env *environment.Environment, data []byte) (mapper.CartMapper, error) {
|
|
cart := &superbank{
|
|
env: env,
|
|
mappingID: "SB",
|
|
bankSize: 4096,
|
|
state: newSuperbankState(),
|
|
}
|
|
|
|
if len(data)%cart.bankSize != 0 {
|
|
return nil, fmt.Errorf("SB: wrong number of bytes in the cartridge data")
|
|
}
|
|
|
|
cart.banks = make([][]uint8, len(data)/cart.bankSize)
|
|
cart.bankSwitchMask = uint16(cart.NumBanks() - 1)
|
|
|
|
for k := 0; k < cart.NumBanks(); k++ {
|
|
cart.banks[k] = make([]uint8, cart.bankSize)
|
|
offset := k * cart.bankSize
|
|
copy(cart.banks[k], data[offset:offset+cart.bankSize])
|
|
}
|
|
|
|
return cart, nil
|
|
}
|
|
|
|
// MappedBanks implements the mapper.CartMapper interface.
|
|
func (cart *superbank) MappedBanks() string {
|
|
return fmt.Sprintf("Bank: %d", cart.state.bank)
|
|
}
|
|
|
|
// ID implements the mapper.CartMapper interface.
|
|
func (cart *superbank) ID() string {
|
|
return cart.mappingID
|
|
}
|
|
|
|
// Snapshot implements the mapper.CartMapper interface.
|
|
func (cart *superbank) Snapshot() mapper.CartMapper {
|
|
n := *cart
|
|
n.state = cart.state.Snapshot()
|
|
return &n
|
|
}
|
|
|
|
// Plumb implements the mapper.CartMapper interface.
|
|
func (cart *superbank) Plumb(env *environment.Environment) {
|
|
cart.env = env
|
|
}
|
|
|
|
// Reset implements the mapper.CartMapper interface.
|
|
func (cart *superbank) Reset() {
|
|
cart.state.bank = len(cart.banks) - 1
|
|
}
|
|
|
|
// Access implements the mapper.CartMapper interface.
|
|
func (cart *superbank) Access(addr uint16, _ bool) (uint8, uint8, error) {
|
|
return cart.banks[cart.state.bank][addr], mapper.CartDrivenPins, nil
|
|
}
|
|
|
|
// AccessVolatile implements the mapper.CartMapper interface.
|
|
func (cart *superbank) AccessVolatile(addr uint16, data uint8, poke bool) error {
|
|
if poke {
|
|
cart.banks[cart.state.bank][addr] = data
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NumBanks implements the mapper.CartMapper interface.
|
|
func (cart *superbank) NumBanks() int {
|
|
return len(cart.banks)
|
|
}
|
|
|
|
// GetBank implements the mapper.CartMapper interface.
|
|
func (cart *superbank) GetBank(addr uint16) mapper.BankInfo {
|
|
// superbank cartridges are like atari cartridges in that the entire address
|
|
// space points to the selected bank
|
|
return mapper.BankInfo{Number: cart.state.bank, IsRAM: false}
|
|
}
|
|
|
|
// Patch implements the mapper.CartPatchable interface.
|
|
func (cart *superbank) Patch(offset int, data uint8) error {
|
|
if offset >= cart.bankSize*len(cart.banks) {
|
|
return fmt.Errorf("SB: patch offset too high (%d)", offset)
|
|
}
|
|
|
|
bank := offset / cart.bankSize
|
|
offset %= cart.bankSize
|
|
cart.banks[bank][offset] = data
|
|
return nil
|
|
}
|
|
|
|
// AccessPassive implements the mapper.CartMapper interface.
|
|
func (cart *superbank) AccessPassive(addr uint16, data uint8) error {
|
|
// return with no side effect if address is not a TIA mirror
|
|
if addr&0x1800 != 0x0800 {
|
|
return nil
|
|
}
|
|
|
|
// switch banks if address is in range of the number of banks
|
|
bank := int(addr & cart.bankSwitchMask)
|
|
|
|
// belt and braces check (probably not necessary)
|
|
if bank < len(cart.banks) {
|
|
cart.state.bank = bank
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Step implements the mapper.CartMapper interface.
|
|
func (cart *superbank) Step(_ float32) {
|
|
}
|
|
|
|
// IterateBank implements the mapper.CartMapper interface.
|
|
func (cart *superbank) CopyBanks() []mapper.BankContent {
|
|
c := make([]mapper.BankContent, len(cart.banks))
|
|
for b := 0; b < len(cart.banks); b++ {
|
|
c[b] = mapper.BankContent{Number: b,
|
|
Data: cart.banks[b],
|
|
Origins: []uint16{memorymap.OriginCart},
|
|
}
|
|
}
|
|
return c
|
|
}
|
|
|
|
// rewindable state for the superbank cartridge.
|
|
type superbankState struct {
|
|
// identifies the currently selected bank
|
|
bank int
|
|
}
|
|
|
|
func newSuperbankState() *superbankState {
|
|
return &superbankState{}
|
|
}
|
|
|
|
// Snapshot implements the mapper.CartMapper interface.
|
|
func (s *superbankState) Snapshot() *superbankState {
|
|
n := *s
|
|
return &n
|
|
}
|