mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 12:08:01 -04:00
da83fc311b
all cartridge data is read through cartridgeloader io.Reader interface
224 lines
6 KiB
Go
224 lines
6 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"
|
|
"io"
|
|
|
|
"github.com/jetsetilly/gopher2600/cartridgeloader"
|
|
"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, loader cartridgeloader.Loader) (mapper.CartMapper, error) {
|
|
data, err := io.ReadAll(loader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("CV: %w", err)
|
|
}
|
|
|
|
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
|
|
}
|