Gopher2600/hardware/memory/cartridge/mapper_commavid.go
JetSetIlly 84ad23c03e moved Patch() from CartMapper to CartPatchable interface
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"
2023-11-26 09:32:28 +00:00

217 lines
5.8 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"
"github.com/jetsetilly/gopher2600/logger"
)
// from http://www.taswegian.com/WoodgrainWizard/tiki-index.php?page=CV
//
// $F000-$F3FF read from RAM
// $F400-$F7FF write to RAM
// $F800-$FFFF ROM
//
// this seems to be the same principle as other simple RAM cartridges, like the
// Atari Superchip or CBS cartridges
//
// from bankswitch_sizes.txt:
//
// cartridges:
// - MagiCard
// - Video Lfe
//
// and the reason why I implemented this format for Gopher2600:
//
// https://forums.atariage.com/topic/342021-new-512-bytes-demo-released-gar-nix/
type commavid struct {
env *environment.Environment
mappingID string
bankSize int
bankData []uint8
// rewindable state
state *commavidState
}
func newCommaVid(env *environment.Environment, data []byte) (mapper.CartMapper, error) {
cart := &commavid{
env: env,
bankSize: 4096,
mappingID: "CV",
state: newCommaVidState(),
}
cart.bankData = make([]uint8, cart.bankSize)
if len(data) < 2048 {
// place undersized binaries at the end of memory
copy(cart.bankData[4096-len(data):], data)
logger.Logf("CV", "placing undersized commavid data at end of cartridge memory")
} else if len(data) == 2048 {
copy(cart.bankData[2048:], data[:2048])
logger.Logf("CV", "placing 2k commavid data at end of cartridge memory")
} else {
return nil, fmt.Errorf("CV: unhandled size for commavid cartridges (%d)", len(data))
}
return cart, nil
}
// MappedBanks implements the mapper.CartMapper interface.
func (cart *commavid) MappedBanks() string {
return fmt.Sprintf("Bank: 0")
}
// ID implements the mapper.CartMapper interface.
func (cart *commavid) ID() string {
return cart.mappingID
}
// Snapshot implements the mapper.CartMapper interface.
func (cart *commavid) Snapshot() mapper.CartMapper {
n := *cart
n.state = cart.state.Snapshot()
return &n
}
// Plumb implements the mapper.CartMapper interface.
func (cart *commavid) Plumb(env *environment.Environment) {
cart.env = env
}
// Reset implements the mapper.CartMapper interface.
func (cart *commavid) Reset() {
// always starting with random state. this is because Video Life, one of
// the few original CommaVid cartridges, expects it for the opening effect
for i := range cart.state.ram {
cart.state.ram[i] = uint8(cart.env.Random.NoRewind(0xff))
}
}
// Access implements the mapper.CartMapper interface.
func (cart *commavid) Access(addr uint16, _ bool) (uint8, uint8, error) {
if addr >= 0x0000 && addr <= 0x03ff {
return cart.state.ram[addr], mapper.CartDrivenPins, nil
}
if addr >= 0x0400 && addr <= 0x07ff {
return 0, 0, nil
}
return cart.bankData[addr], mapper.CartDrivenPins, nil
}
// AccessVolatile implements the mapper.CartMapper interface.
func (cart *commavid) AccessVolatile(addr uint16, data uint8, poke bool) error {
if addr >= 0x0400 && addr <= 0x07ff {
cart.state.ram[addr&0x03ff] = data
return nil
}
if poke {
cart.bankData[addr] = data
return nil
}
return nil
}
// NumBanks implements the mapper.CartMapper interface.
func (cart *commavid) NumBanks() int {
return 1
}
// GetBank implements the mapper.CartMapper interface.
func (cart *commavid) GetBank(addr uint16) mapper.BankInfo {
// commavid cartridges are like atari cartridges in that the entire address
// space points to the selected bank
return mapper.BankInfo{Number: 0, IsRAM: addr <= 0x07ff}
}
// Patch implements the mapper.CartPatchable interface
func (cart *commavid) Patch(offset int, data uint8) error {
if offset >= cart.bankSize {
return fmt.Errorf("CV: patch offset too high (%d)", offset)
}
cart.bankData[offset] = data
return nil
}
// AccessPassive implements the mapper.CartMapper interface.
func (cart *commavid) AccessPassive(addr uint16, data uint8) error {
return nil
}
// Step implements the mapper.CartMapper interface.
func (cart *commavid) Step(_ float32) {
}
// GetRAM implements the mapper.CartRAMBus interface.
func (cart *commavid) GetRAM() []mapper.CartRAM {
r := make([]mapper.CartRAM, 1)
r[0] = mapper.CartRAM{
Label: "CommaVid",
Origin: 0x0,
Data: make([]uint8, len(cart.state.ram)),
Mapped: true,
}
copy(r[0].Data, cart.state.ram)
return r
}
// PutRAM implements the mapper.CartRAMBus interface.
func (cart *commavid) PutRAM(_ int, idx int, data uint8) {
cart.state.ram[idx] = data
}
// IterateBank implements the mapper.CartMapper interface.
func (cart *commavid) CopyBanks() []mapper.BankContent {
c := make([]mapper.BankContent, 1)
c[0] = mapper.BankContent{Number: 0,
Data: cart.bankData,
Origins: []uint16{memorymap.OriginCart},
}
return c
}
// rewindable state for commavid cartridges
type commavidState struct {
// commavid cartridges are distinguished for having 1k of onboard ram
ram []uint8
}
func newCommaVidState() *commavidState {
const commavidRAMsize = 1024
return &commavidState{
ram: make([]uint8, commavidRAMsize),
}
}
// Snapshot implements the mapper.CartMapper interface.
func (s *commavidState) Snapshot() *commavidState {
n := *s
n.ram = make([]uint8, len(s.ram))
copy(n.ram, s.ram)
return &n
}