Skip to content

Commit

Permalink
added BF mapper, including SC option
Browse files Browse the repository at this point in the history
added proper fingerprinting for SB mapper
  • Loading branch information
JetSetIlly committed Nov 23, 2024
1 parent 54d9cf6 commit 70ad97c
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cartridgeloader/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var explicitFileExtensions = []string{
".2K", ".4K", ".F8", ".WF8", ".F6", ".F4", ".2K+", ".2KSC",
".4K+", ".4KSC", ".F8+", ".F8SC", ".F6+", ".F6SC", ".F4+", ".F4SC", ".CV",
".FA", ".FA2", ".FE", ".E0", ".E7", ".JANE", ".3F", ".UA", ".AR", ".DF", ".3E", ".E3P",
".E3+", ".3E+", ".EF", ".EFSC", ".SB", ".WD", ".ACE", ".CDF0", ".CDF1", ".CDFJ",
".E3+", ".3E+", ".EF", ".EFSC", ".BF", ".BFSC", ".SB", ".WD", ".ACE", ".CDF0", ".CDF1", ".CDFJ",
".CDFJ+", ".DP+", ".DPC", ".CDF", ".MVC", ".ELF",
}

Expand Down
7 changes: 7 additions & 0 deletions hardware/memory/cartridge/cartridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ func (cart *Cartridge) Attach(cartload cartridgeloader.Loader) error {
}

switch mapping {
case unrecognisedMapper:
return fmt.Errorf("cartridge: unrecognised mapper")
case "2K":
cart.mapper, err = newAtari2k(cart.env, cartload)
case "4K":
Expand Down Expand Up @@ -304,6 +306,11 @@ func (cart *Cartridge) Attach(cartload cartridgeloader.Loader) error {
case "EFSC":
cart.mapper, err = newEF(cart.env, cartload)
forceSuperchip = true
case "BF":
cart.mapper, err = newBF(cart.env, cartload)
case "BFSC":
cart.mapper, err = newBF(cart.env, cartload)
forceSuperchip = true
case "SB":
cart.mapper, err = newSuperbank(cart.env, cartload)
case "WD":
Expand Down
64 changes: 61 additions & 3 deletions hardware/memory/cartridge/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,40 @@ func fingerprintWF8(loader cartridgeloader.Loader) bool {
return loader.HashMD5 == smurf || loader.HashMD5 == zaxxon
}

func fingerprintEF(loader cartridgeloader.Loader) (superchip bool, ok bool) {
if loader.Contains([]byte{'E', 'F', 'E', 'F'}) {
return false, true
}
if loader.Contains([]byte{'E', 'F', 'S', 'C'}) {
return true, true
}
return false, false
}

func fingerprintBF(loader cartridgeloader.Loader) (superchip bool, ok bool) {
if loader.Contains([]byte{'B', 'F', 'B', 'F'}) {
return false, true
}
if loader.Contains([]byte{'B', 'F', 'S', 'C'}) {
return true, true
}
return false, false
}

func fingerprintSB(loader cartridgeloader.Loader) bool {
// SB fingerprint taken from Stella
fingerprint := [][]byte{
{0xbd, 0x00, 0x08}, // LDA $0800,X
{0xad, 0x00, 0x08}, // LDA $0800
}
for _, f := range fingerprint {
if loader.Contains(f) {
return true
}
}
return false
}

func fingerprint8k(loader cartridgeloader.Loader) string {
if fingerprintWF8(loader) {
return "WF8"
Expand Down Expand Up @@ -421,20 +455,44 @@ func fingerprint32k(loader cartridgeloader.Loader) string {
}

func fingerprint64k(loader cartridgeloader.Loader) string {
return "EF"
if sc, ok := fingerprintEF(loader); ok {
if sc {
return "EFSC"
}
return "EF"
}
return unrecognisedMapper
}

func fingerprint128k(loader cartridgeloader.Loader) string {
if fingerprintDF(loader) {
return "DF"
}
return "SB"
if fingerprintSB(loader) {
return "SB"
}
return unrecognisedMapper
}

func fingerprint256k(loader cartridgeloader.Loader) string {
return "SB"
if sc, ok := fingerprintBF(loader); ok {
if sc {
return "BFSC"
}
return "BF"
}
if fingerprintSB(loader) {
return "SB"
}
return unrecognisedMapper
}

// returned by fingerprint if the mapper is not recognised. most files will
// result in something and an attachement to the console is always attempted.
// but in some cases (particularly for larger, modern cartridges) we can say for
// certain whether or nor a file is a valid ROM file
const unrecognisedMapper = "unrecognised mapper"

func (cart *Cartridge) fingerprint(loader cartridgeloader.Loader) (string, error) {
// moviecart fingerprinting is done in cartridge loader. this is to avoid
// loading the entire file into memory, which we definitely don't want to do
Expand Down
127 changes: 127 additions & 0 deletions hardware/memory/cartridge/mapper_atari_bf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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"
)

// the BF mapper is an concpetual extension of the EF mapper. therefore, it is
// really just a 256k standard atari cartridge
type bf struct {
atari
}

// newBF is the preferred method of initialisation for the ef type
func newBF(env *environment.Environment, loader cartridgeloader.Loader) (mapper.CartMapper, error) {
data, err := io.ReadAll(loader)
if err != nil {
return nil, fmt.Errorf("BF: %w", err)
}

cart := &bf{
atari: atari{
env: env,
bankSize: 4096,
mappingID: "BF",
needsSuperchip: hasEmptyArea(data),
state: newAtariState(),
},
}

if len(data) != cart.bankSize*cart.NumBanks() {
return nil, fmt.Errorf("BF: wrong number of bytes in the cartridge data")
}

cart.banks = make([][]uint8, cart.NumBanks())
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
}

// Snapshot implements the mapper.CartMapper interface.
func (cart *bf) Snapshot() mapper.CartMapper {
n := *cart
n.state = cart.state.Snapshot()
return &n
}

// Plumb implements the mapper.CartMapper interface.
func (cart *bf) Plumb(env *environment.Environment) {
cart.env = env
}

// Access implements the mapper.CartMapper interface.
func (cart *bf) Access(addr uint16, peek bool) (uint8, uint8, error) {
if data, mask, ok := cart.atari.access(addr); ok {
return data, mask, nil
}

if !peek {
cart.bankswitch(addr)
}

return cart.banks[cart.state.bank][addr], mapper.CartDrivenPins, nil
}

// AccessVolatile implements the mapper.CartMapper interface.
func (cart *bf) AccessVolatile(addr uint16, data uint8, poke bool) error {
if !poke {
if cart.bankswitch(addr) {
return nil
}
}

return cart.accessVolatile(addr, data, poke)
}

// bankswitch on hotspot access.
func (cart *bf) bankswitch(addr uint16) bool {
if addr >= 0x0f80 && addr <= 0x0fbf {
cart.state.bank = int(addr - 0x0f80)
return true
}
return false
}

// Reset implements the mapper.CartMapper interface.
func (cart *bf) Reset() {
cart.reset()
}

// NumBanks implements the mapper.CartMapper interface.
func (cart *bf) NumBanks() int {
return 64
}

// ReadHotspots implements the mapper.CartHotspotsBus interface.
func (cart *bf) ReadHotspots() map[uint16]mapper.CartHotspotInfo {
return map[uint16]mapper.CartHotspotInfo{}
}

// WriteHotspots implements the mapper.CartHotspotsBus interface.
func (cart *bf) WriteHotspots() map[uint16]mapper.CartHotspotInfo {
return cart.ReadHotspots()
}

0 comments on commit 70ad97c

Please sign in to comment.