fingerprinting of controllers

moved controllers and savekeys package to new peripherals package

removed auto controller

changed lazy.Controllers to lazy.Peripherals and Controllers window to
Peripherals window

changed CONTROLLER command to PERIPHERAL command. removed AUTO option

added savekey as an option to PERIPHERAL command and Peripheral window
This commit is contained in:
JetSetIlly 2022-01-28 21:46:08 +00:00
parent 0fb9a502d3
commit ab92d01f73
27 changed files with 475 additions and 420 deletions

View file

@ -143,7 +143,7 @@ func NewLoader(filename string, mapping string) (Loader, error) {
},
}
// create an empty slice as the default data
// create an empty slice for the Data field to refer to
data := make([]byte, 0)
cl.Data = &data
@ -349,7 +349,7 @@ func (cl *Loader) Load() error {
return nil
}
if len(*cl.Data) > 0 {
if cl.Data != nil && len(*cl.Data) > 0 {
return nil
}

View file

@ -36,8 +36,9 @@ import (
"github.com/jetsetilly/gopher2600/hardware/cpu/registers"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/plusrom"
"github.com/jetsetilly/gopher2600/hardware/memory/memorymap"
"github.com/jetsetilly/gopher2600/hardware/peripherals/controllers"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/controllers"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
"github.com/jetsetilly/gopher2600/hardware/television"
"github.com/jetsetilly/gopher2600/hardware/television/coords"
@ -1353,7 +1354,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
dbg.printLine(terminal.StyleFeedback, coproc.CoProcID())
}
case cmdController:
case cmdPeripheral:
player, _ := tokens.Get()
var id plugging.PortID
@ -1369,14 +1370,16 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
controller, ok := tokens.Get()
if ok {
switch strings.ToUpper(controller) {
case "AUTO":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewAuto)
case "STICK":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewStick)
case "PADDLE":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewPaddle)
case "KEYPAD":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewKeypad)
case "GAMEPAD":
err = dbg.vcs.RIOT.Ports.Plug(id, controllers.NewGamepad)
case "SAVEKEY":
err = dbg.vcs.RIOT.Ports.Plug(id, savekey.NewSaveKey)
}
}
@ -1392,12 +1395,7 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
p = dbg.vcs.RIOT.Ports.RightPlayer
}
s := strings.Builder{}
if _, ok := p.(*controllers.Auto); ok {
s.WriteString("[auto] ")
}
s.WriteString(p.String())
dbg.printLine(terminal.StyleInstrument, s.String())
dbg.printLine(terminal.StyleInstrument, p.String())
case cmdPanel:
mode, ok := tokens.Get()

View file

@ -367,9 +367,7 @@ all PlusROM cartridges.`,
cmdCoProc: `Returns information about any coprocessor in the inserted cartridge.`,
// user input
cmdController: `Change the current controller type for the specified player. The AUTO
controller handles changes of controller according to user input and where possible what
can be inferred from the ROM.`,
cmdPeripheral: `Change the current peripheral for the specified player port.`,
cmdPanel: "Inspect and set front panel settings. Switches can be set or toggled.",

View file

@ -57,7 +57,7 @@ const (
cmdCoProc = "COPROC"
// user input.
cmdController = "CONTROLLER"
cmdPeripheral = "PERIPHERAL"
cmdPanel = "PANEL"
cmdStick = "STICK"
cmdKeypad = "KEYPAD"
@ -120,7 +120,7 @@ var commandTemplate = []string{
cmdCoProc + " (ID|LIST [ILLEGAL|SOURCEFILES]|TOP (%<top>N))",
// user input
cmdController + " [LEFT|RIGHT] (AUTO|STICK|PADDLE|KEYBOARD)",
cmdPeripheral + " [LEFT|RIGHT] (STICK|PADDLE|KEYPAD|GAMEPAD|SAVEKEY)",
cmdPanel + " (SET [P0PRO|P1PRO|P0AM|P1AM|COL|BW]|TOGGLE [P0|P1|COL]|[HOLD|RELEASE] [SELECT|RESET])",
cmdStick + " [LEFT|RIGHT] [LEFT|RIGHT|UP|DOWN|FIRE|NOLEFT|NORIGHT|NOUP|NODOWN|NOFIRE]",
cmdKeypad + " [LEFT|RIGHT] [NONE|0|1|2|3|4|5|6|7|8|9|*|#]",

View file

@ -41,8 +41,8 @@ import (
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/plusrom"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/supercharger"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/savekey"
"github.com/jetsetilly/gopher2600/hardware/television"
"github.com/jetsetilly/gopher2600/hardware/television/coords"
"github.com/jetsetilly/gopher2600/logger"

View file

@ -51,7 +51,7 @@ type LazyValues struct {
Ball *LazyBall
TV *LazyTV
Cart *LazyCart
Controllers *LazyControllers
Peripherals *LazyPeripherals
Collisions *LazyCollisions
Ports *LazyPorts
Tracker *LazyTracker
@ -92,7 +92,7 @@ func NewLazyValues(e emulation.Emulation) *LazyValues {
val.Ball = newLazyBall(val)
val.TV = newLazyTV(val)
val.Cart = newLazyCart(val)
val.Controllers = newLazyControllers(val)
val.Peripherals = newLazyPeripherals(val)
val.Collisions = newLazyCollisions(val)
val.Ports = newLazyPorts(val)
val.Tracker = newLazyTracker(val)
@ -128,7 +128,7 @@ func (val *LazyValues) Refresh() {
val.Ball.update()
val.TV.update()
val.Cart.update()
val.Controllers.update()
val.Peripherals.update()
val.Collisions.update()
val.Ports.update()
val.Tracker.update()
@ -156,7 +156,7 @@ func (val *LazyValues) Refresh() {
val.Ball.push()
val.TV.push()
val.Cart.push()
val.Controllers.push()
val.Peripherals.push()
val.Collisions.push()
val.Ports.push()
val.Tracker.push()

View file

@ -26,8 +26,8 @@ type periphShim struct {
periph ports.Peripheral
}
// LazyControllers lazily accesses controller information from the emulator.
type LazyControllers struct {
// LazyPeripherals lazily accesses controller information from the emulator.
type LazyPeripherals struct {
val *LazyValues
// unlike the other lazy types we can't use atomic values here because the
@ -40,8 +40,8 @@ type LazyControllers struct {
RightPlayer ports.Peripheral
}
func newLazyControllers(val *LazyValues) *LazyControllers {
lz := &LazyControllers{
func newLazyPeripherals(val *LazyValues) *LazyPeripherals {
lz := &LazyPeripherals{
val: val,
}
lz.left.Store(periphShim{})
@ -49,12 +49,12 @@ func newLazyControllers(val *LazyValues) *LazyControllers {
return lz
}
func (lz *LazyControllers) push() {
func (lz *LazyPeripherals) push() {
lz.left.Store(periphShim{periph: lz.val.vcs.RIOT.Ports.LeftPlayer})
lz.right.Store(periphShim{periph: lz.val.vcs.RIOT.Ports.RightPlayer})
}
func (lz *LazyControllers) update() {
func (lz *LazyPeripherals) update() {
lz.LeftPlayer = lz.left.Load().(periphShim).periph
lz.RightPlayer = lz.right.Load().(periphShim).periph
}

View file

@ -18,7 +18,7 @@ package lazyvalues
import (
"sync/atomic"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/savekey"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
)
// LazyChipRegisters lazily accesses chip registere information from the emulator.

View file

@ -97,11 +97,11 @@ var windowDefs = [...]windowDef{
// windows that appear in the "vcs" menu
{create: newWinCollisions, menu: menuEntry{group: menuVCS}},
{create: newWinControl, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinControllers, menu: menuEntry{group: menuVCS}},
{create: newWinCPU, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinDisasm, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinDbgScr, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinRAM, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinPeripherals, menu: menuEntry{group: menuVCS}},
{create: newWinPorts, menu: menuEntry{group: menuVCS}},
{create: newWinTIA, menu: menuEntry{group: menuVCS}, open: true},
{create: newWinTimer, menu: menuEntry{group: menuVCS}, open: true},

View file

@ -19,13 +19,13 @@ import (
"fmt"
"github.com/inkyblackness/imgui-go/v4"
"github.com/jetsetilly/gopher2600/hardware/peripherals"
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/controllers"
)
const winControllersID = "Controllers"
const winPeripheralsID = "Peripherals"
type winControllers struct {
type winPeripherals struct {
img *SdlImgui
open bool
@ -33,38 +33,38 @@ type winControllers struct {
controllerComboDim imgui.Vec2
}
func newWinControllers(img *SdlImgui) (window, error) {
win := &winControllers{
func newWinPeripherals(img *SdlImgui) (window, error) {
win := &winPeripherals{
img: img,
}
return win, nil
}
func (win *winControllers) init() {
win.controllerComboDim = imguiGetFrameDim("", controllers.ControllerList...)
func (win *winPeripherals) init() {
win.controllerComboDim = imguiGetFrameDim("", peripherals.Available...)
}
func (win *winControllers) id() string {
return winControllersID
func (win *winPeripherals) id() string {
return winPeripheralsID
}
func (win *winControllers) isOpen() bool {
func (win *winPeripherals) isOpen() bool {
return win.open
}
func (win *winControllers) setOpen(open bool) {
func (win *winPeripherals) setOpen(open bool) {
win.open = open
}
func (win *winControllers) draw() {
func (win *winPeripherals) draw() {
if !win.open {
return
}
// don't show the window if either of the controllers are unplugged
// !!TODO: show something meaningful for unplugged controllers
if win.img.lz.Controllers.LeftPlayer == nil || win.img.lz.Controllers.RightPlayer == nil {
if win.img.lz.Peripherals.LeftPlayer == nil || win.img.lz.Peripherals.RightPlayer == nil {
return
}
@ -75,7 +75,7 @@ func (win *winControllers) draw() {
imgui.Spacing()
imgui.Text("Left")
imgui.Spacing()
win.drawController(win.img.lz.Controllers.LeftPlayer)
win.drawPeripheral(win.img.lz.Peripherals.LeftPlayer)
imgui.EndGroup()
imgui.SameLine()
@ -83,18 +83,18 @@ func (win *winControllers) draw() {
imgui.BeginGroup()
imgui.Text("Right")
imgui.Spacing()
win.drawController(win.img.lz.Controllers.RightPlayer)
win.drawPeripheral(win.img.lz.Peripherals.RightPlayer)
imgui.EndGroup()
imgui.End()
}
func (win *winControllers) drawController(p ports.Peripheral) {
func (win *winPeripherals) drawPeripheral(p ports.Peripheral) {
imgui.PushItemWidth(win.controllerComboDim.X)
if imgui.BeginComboV(fmt.Sprintf("##%v", p.PortID()), string(p.ID()), imgui.ComboFlagsNoArrowButton) {
for _, s := range controllers.ControllerList {
for _, s := range peripherals.Available {
if imgui.Selectable(s) {
termCmd := fmt.Sprintf("CONTROLLER %s %s", p.PortID(), s)
termCmd := fmt.Sprintf("PERIPHERAL %s %s", p.PortID(), s)
win.img.term.pushCommand(termCmd)
}
}
@ -102,15 +102,4 @@ func (win *winControllers) drawController(p ports.Peripheral) {
imgui.EndCombo()
}
imgui.PopItemWidth()
_, auto := p.(*controllers.Auto)
if imgui.Checkbox(fmt.Sprintf("Auto##%v", p.PortID()), &auto) {
var termCmd string
if auto {
termCmd = fmt.Sprintf("CONTROLLER %s AUTO", p.PortID())
} else {
termCmd = fmt.Sprintf("CONTROLLER %s %s", p.PortID(), string(p.ID()))
}
win.img.term.pushCommand(termCmd)
}
}

View file

@ -17,7 +17,7 @@ package sdlimgui
import (
"github.com/inkyblackness/imgui-go/v4"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/savekey"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
)
const winSaveKeyEEPROMID = "SaveKey EEPROM"

View file

@ -20,7 +20,7 @@ import (
"strconv"
"github.com/inkyblackness/imgui-go/v4"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/savekey"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
)
const winSaveKeyI2CID = "SaveKey I2C"

View file

@ -13,9 +13,6 @@
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
// Package controllers contains the implementations for all the emulated
// controllers for the VCS.
package controllers
// ControllerList is the list of controllers. These are the values that can be
// returned by the ID() function of the ports.Peripheral implementations in
// this package.
var ControllerList = []string{"Stick", "Paddle", "Keyboard"}

View file

@ -0,0 +1,21 @@
// 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 peripherals is a container package for all the various peripherals
// for the Atari 2600 that are emulated. Most of these are controllers but it
// also includes the SaveKey for example.
//
// Perhaps most importantly this package provides the Fingerprint() function.
package peripherals

View file

@ -0,0 +1,371 @@
// 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 peripherals
import (
"fmt"
"github.com/jetsetilly/gopher2600/hardware/peripherals/controllers"
"github.com/jetsetilly/gopher2600/hardware/peripherals/savekey"
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
)
// Fingerprint scans the raw cartridge data for patterns that indicate the
// requirement of a specific controller type.
//
// The patterns in this file are taken from the Stella project. Specifically
// the following file (last retreived on 28th January 2022).
//
// https://github.com/stella-emu/stella/blob/76914ded629db887ef612b1e5c9889220808191a/src/emucore/ControllerDetector.cxx
//
// Stella is licenced under the GNU General Public License as published by the
// Free Software Foundation, version 2 or any later version.
//
// https://github.com/stella-emu/stella/blob/76914ded629db887ef612b1e5c9889220808191a/Copyright.txt
func Fingerprint(port plugging.PortID, data []byte) ports.NewPeripheral {
if port != plugging.PortRightPlayer && port != plugging.PortLeftPlayer {
panic(fmt.Sprintf("cannot fingerprint for port %v", port))
}
// savekey is the most specific peripheral. check that first
if fingerprintSaveKey(port, data) {
return savekey.NewSaveKey
}
// the other peripherals require a process of differentiation. the order is
// important.
if fingerprintStick(port, data) {
if fingerprintKeypad(port, data) {
return controllers.NewKeypad
}
if fingerprintGamepad(port, data) {
return controllers.NewGamepad
}
} else {
if fingerprintPaddle(port, data) {
return controllers.NewPaddle
}
}
// default to normal joystick
return controllers.NewStick
}
func matchPattern(patterns [][]byte, data []byte) bool {
for i := 0; i < len(data); i++ {
for _, p := range patterns {
if len(p) > len(data)-i {
continue // patterns loop
}
match := true
for j := 0; j < len(p); j++ {
match = match && data[i+j] == p[j]
}
if match {
return true
}
}
}
return false
}
func fingerprintSaveKey(port plugging.PortID, data []byte) bool {
if port != plugging.PortRightPlayer {
return false
}
patterns := [][]byte{
{ // from I2C_START (i2c.inc)
0xa9, 0x08, // lda #I2C_SCL_MASK
0x8d, 0x80, 0x02, // sta SWCHA
0xa9, 0x0c, // lda #I2C_SCL_MASK|I2C_SDA_MASK
0x8d, 0x81, // sta SWACNT
},
{ // from I2C_START (i2c_v2.1..3.inc)
0xa9, 0x18, // #(I2C_SCL_MASK|I2C_SDA_MASK)*2
0x8d, 0x80, 0x02, // sta SWCHA
0x4a, // lsr
0x8d, 0x81, 0x02, // sta SWACNT
},
{ // from I2C_START (Strat-O-Gems)
0xa2, 0x08, // ldx #I2C_SCL_MASK
0x8e, 0x80, 0x02, // stx SWCHA
0xa2, 0x0c, // ldx #I2C_SCL_MASK|I2C_SDA_MASK
0x8e, 0x81, // stx SWACNT
},
{ // from I2C_START (AStar, Fall Down, Go Fish!)
0xa9, 0x08, // lda #I2C_SCL_MASK
0x8d, 0x80, 0x02, // sta SWCHA
0xea, // nop
0xa9, 0x0c, // lda #I2C_SCL_MASK|I2C_SDA_MASK
0x8d, // sta SWACNT
},
}
return matchPattern(patterns, data)
}
func fingerprintStick(port plugging.PortID, data []byte) bool {
var patterns [][]byte
switch port {
case plugging.PortLeftPlayer:
patterns = [][]byte{
{0x24, 0x0c, 0x10}, // bit INPT4; bpl (joystick games only)
{0x24, 0x0c, 0x30}, // bit INPT4; bmi (joystick games only)
{0xa5, 0x0c, 0x10}, // lda INPT4; bpl (joystick games only)
{0xa5, 0x0c, 0x30}, // lda INPT4; bmi (joystick games only)
{0xb5, 0x0c, 0x10}, // lda INPT4,x; bpl (joystick games only)
{0xb5, 0x0c, 0x30}, // lda INPT4,x; bmi (joystick games only)
{0x24, 0x3c, 0x10}, // bit INPT4|$30; bpl (joystick games + Compumate)
{0x24, 0x3c, 0x30}, // bit INPT4|$30; bmi (joystick, keyboard and mindlink games)
{0xa5, 0x3c, 0x10}, // lda INPT4|$30; bpl (joystick and keyboard games)
{0xa5, 0x3c, 0x30}, // lda INPT4|$30; bmi (joystick, keyboard and mindlink games)
{0xb5, 0x3c, 0x10}, // lda INPT4|$30,x; bpl (joystick, keyboard and driving games)
{0xb5, 0x3c, 0x30}, // lda INPT4|$30,x; bmi (joystick and keyboard games)
{0xb4, 0x0c, 0x30}, // ldy INPT4|$30,x; bmi (joystick games only)
{0xa5, 0x3c, 0x2a}, // ldy INPT4|$30; rol (joystick games only)
{0xa6, 0x3c, 0x8e}, // ldx INPT4|$30; stx (joystick games only)
{0xa6, 0x0c, 0x8e}, // ldx INPT4; stx (joystick games only)
{0xa4, 0x3c, 0x8c}, // ldy INPT4; sty (joystick games only, Scramble)
{0xa5, 0x0c, 0x8d}, // lda INPT4; sta (joystick games only, Super Cobra Arcade)
{0xa4, 0x0c, 0x30}, // ldy INPT4|; bmi (only Game of Concentration)
{0xa4, 0x3c, 0x30}, // ldy INPT4|$30; bmi (only Game of Concentration)
{0xa5, 0x0c, 0x25}, // lda INPT4; and (joystick games only)
{0xa6, 0x3c, 0x30}, // ldx INPT4|$30; bmi (joystick games only)
{0xa6, 0x0c, 0x30}, // ldx INPT4; bmi
{0xa5, 0x0c, 0x0a}, // lda INPT4; asl (joystick games only)
{0xb9, 0x0c, 0x00, 0x10}, // lda INPT4,y; bpl (joystick games only)
{0xb9, 0x0c, 0x00, 0x30}, // lda INPT4,y; bmi (joystick games only)
{0xb9, 0x3c, 0x00, 0x10}, // lda INPT4,y; bpl (joystick games only)
{0xb9, 0x3c, 0x00, 0x30}, // lda INPT4,y; bmi (joystick games only)
{0xa5, 0x0c, 0x0a, 0xb0}, // lda INPT4; asl; bcs (joystick games only)
{0xb5, 0x0c, 0x29, 0x80}, // lda INPT4,x; and #$80 (joystick games only)
{0xb5, 0x3c, 0x29, 0x80}, // lda INPT4|$30,x; and #$80 (joystick games only)
{0xa5, 0x0c, 0x29, 0x80}, // lda INPT4; and #$80 (joystick games only)
{0xa5, 0x3c, 0x29, 0x80}, // lda INPT4|$30; and #$80 (joystick games only)
{0xa5, 0x0c, 0x25, 0x0d, 0x10}, // lda INPT4; and INPT5; bpl (joystick games only)
{0xa5, 0x0c, 0x25, 0x0d, 0x30}, // lda INPT4; and INPT5; bmi (joystick games only)
{0xa5, 0x3c, 0x25, 0x3d, 0x10}, // lda INPT4|$30; and INPT5|$30; bpl (joystick games only)
{0xa5, 0x3c, 0x25, 0x3d, 0x30}, // lda INPT4|$30; and INPT5|$30; bmi (joystick games only)
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0|$30,y; and #$80; bne (Basic Programming)
{0xa9, 0x80, 0x24, 0x0c, 0xd0}, // lda #$80; bit INPT4; bne (bBasic)
{0xa5, 0x0c, 0x29, 0x80, 0xd0}, // lda INPT4; and #$80; bne (joystick games only)
{0xa5, 0x3c, 0x29, 0x80, 0xd0}, // lda INPT4|$30; and #$80; bne (joystick games only)
{0xad, 0x0c, 0x00, 0x29, 0x80}, // lda.w INPT4|$30; and #$80 (joystick games only)
}
case plugging.PortRightPlayer:
patterns = [][]byte{
{0x24, 0x0d, 0x10}, // bit INPT5; bpl (joystick games only)
{0x24, 0x0d, 0x30}, // bit INPT5; bmi (joystick games only)
{0xa5, 0x0d, 0x10}, // lda INPT5; bpl (joystick games only)
{0xa5, 0x0d, 0x30}, // lda INPT5; bmi (joystick games only)
{0xb5, 0x0c, 0x10}, // lda INPT4,x; bpl (joystick games only)
{0xb5, 0x0c, 0x30}, // lda INPT4,x; bmi (joystick games only)
{0x24, 0x3d, 0x10}, // bit INPT5|$30; bpl (joystick games, Compumate)
{0x24, 0x3d, 0x30}, // bit INPT5|$30; bmi (joystick and keyboard games)
{0xa5, 0x3d, 0x10}, // lda INPT5|$30; bpl (joystick games only)
{0xa5, 0x3d, 0x30}, // lda INPT5|$30; bmi (joystick and keyboard games)
{0xb5, 0x3c, 0x10}, // lda INPT4|$30,x; bpl (joystick, keyboard and driving games)
{0xb5, 0x3c, 0x30}, // lda INPT4|$30,x; bmi (joystick and keyboard games)
{0xa4, 0x3d, 0x30}, // ldy INPT5; bmi (only Game of Concentration)
{0xa5, 0x0d, 0x25}, // lda INPT5; and (joystick games only)
{0xa6, 0x3d, 0x30}, // ldx INPT5|$30; bmi (joystick games only)
{0xa6, 0x0d, 0x30}, // ldx INPT5; bmi
{0xb9, 0x0c, 0x00, 0x10}, // lda INPT4,y; bpl (joystick games only)
{0xb9, 0x0c, 0x00, 0x30}, // lda INPT4,y; bmi (joystick games only)
{0xb9, 0x3c, 0x00, 0x10}, // lda INPT4,y; bpl (joystick games only)
{0xb9, 0x3c, 0x00, 0x30}, // lda INPT4,y; bmi (joystick games only)
{0xb5, 0x0c, 0x29, 0x80}, // lda INPT4,x; and #$80 (joystick games only)
{0xb5, 0x3c, 0x29, 0x80}, // lda INPT4|$30,x; and #$80 (joystick games only)
{0xa5, 0x3d, 0x29, 0x80}, // lda INPT5|$30; and #$80 (joystick games only)
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0|$30,y; and #$80; bne (Basic Programming)
{0xa9, 0x80, 0x24, 0x0d, 0xd0}, // lda #$80; bit INPT5; bne (bBasic)
{0xad, 0x0d, 0x00, 0x29, 0x80}, // lda.w INPT5|$30; and #$80 (joystick games only)
}
}
return matchPattern(patterns, data)
}
func fingerprintKeypad(port plugging.PortID, data []byte) bool {
var patterns [][]byte
switch port {
case plugging.PortLeftPlayer:
patterns = [][]byte{
{0x24, 0x38, 0x30}, // bit INPT0|$30; bmi
{0xa5, 0x38, 0x10}, // lda INPT0|$30; bpl
{0xa4, 0x38, 0x30}, // ldy INPT0|$30; bmi
{0xb5, 0x38, 0x30}, // lda INPT0|$30,x; bmi
{0x24, 0x08, 0x30}, // bit INPT0; bmi
{0xa6, 0x08, 0x30}, // ldx INPT0; bmi
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0,x; and #80; bne
}
// keypad fingerprinting is slightly different to the other fingerprint
// functions in that any matched pattern from the list above is ANDed
// with a pattern with the list below
if matchPattern(patterns, data) {
patterns = [][]byte{
{0x24, 0x39, 0x10}, // bit INPT1|$30; bpl
{0x24, 0x39, 0x30}, // bit INPT1|$30; bmi
{0xa5, 0x39, 0x10}, // lda INPT1|$30; bpl
{0xa4, 0x39, 0x30}, // ldy INPT1|$30; bmi
{0xb5, 0x38, 0x30}, // lda INPT0|$30,x; bmi
{0x24, 0x09, 0x30}, // bit INPT1; bmi
{0xa6, 0x09, 0x30}, // ldx INPT1; bmi
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0,x; and #80; bne
}
return matchPattern(patterns, data)
}
case plugging.PortRightPlayer:
patterns = [][]byte{
{0x24, 0x3a, 0x30}, // bit INPT2|$30; bmi
{0xa5, 0x3a, 0x10}, // lda INPT2|$30; bpl
{0xa4, 0x3a, 0x30}, // ldy INPT2|$30; bmi
{0x24, 0x0a, 0x30}, // bit INPT2; bmi
{0x24, 0x0a, 0x10}, // bit INPT2; bpl
{0xa6, 0x0a, 0x30}, // ldx INPT2; bmi
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT2,x; and #80; bne
}
// see comment above
if matchPattern(patterns, data) {
patterns = [][]byte{
{0x24, 0x3b, 0x30}, // bit INPT3|$30; bmi
{0xa5, 0x3b, 0x10}, // lda INPT3|$30; bpl
{0xa4, 0x3b, 0x30}, // ldy INPT3|$30; bmi
{0x24, 0x0b, 0x30}, // bit INPT3; bmi
{0x24, 0x0b, 0x10}, // bit INPT3; bpl
{0xa6, 0x0b, 0x30}, // ldx INPT3; bmi
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT2,x; and #80; bne
}
return matchPattern(patterns, data)
}
}
return false
}
func fingerprintGamepad(port plugging.PortID, data []byte) bool {
var patterns [][]byte
switch port {
case plugging.PortLeftPlayer:
patterns = [][]byte{
{0x24, 0x09, 0x10}, // bit INPT1; bpl (Genesis only)
{0x24, 0x09, 0x30}, // bit INPT1; bmi (paddle ROMS too)
{0xa5, 0x09, 0x10}, // lda INPT1; bpl (paddle ROMS too)
{0xa5, 0x09, 0x30}, // lda INPT1; bmi (paddle ROMS too)
{0xa4, 0x09, 0x30}, // ldy INPT1; bmi (Genesis only)
{0xa6, 0x09, 0x30}, // ldx INPT1; bmi (Genesis only)
{0x24, 0x39, 0x10}, // bit INPT1|$30; bpl (keyboard and paddle ROMS too)
{0x24, 0x39, 0x30}, // bit INPT1|$30; bmi (keyboard and paddle ROMS too)
{0xa5, 0x39, 0x10}, // lda INPT1|$30; bpl (keyboard ROMS too)
{0xa5, 0x39, 0x30}, // lda INPT1|$30; bmi (keyboard and paddle ROMS too)
{0xa4, 0x39, 0x30}, // ldy INPT1|$30; bmi (keyboard ROMS too)
{0xa5, 0x39, 0x6a}, // lda INPT1|$30; ror (Genesis only)
{0xa6, 0x39, 0x8e}, // ldx INPT1|$30; stx (Genesis only)
{0xa6, 0x09, 0x8e}, // ldx INPT1; stx (Genesis only)
{0xa4, 0x39, 0x8c}, // ldy INPT1|$30; sty (Genesis only, Scramble)
{0xa5, 0x09, 0x8d}, // lda INPT1; sta (Genesis only, Super Cobra Arcade)
{0xa5, 0x09, 0x29}, // lda INPT1; and (Genesis only)
{0x25, 0x39, 0x30}, // and INPT1|$30; bmi (Genesis only)
{0x25, 0x09, 0x10}, // and INPT1; bpl (Genesis only)
}
case plugging.PortRightPlayer:
patterns = [][]byte{
{0x24, 0x0b, 0x10}, // bit INPT3; bpl
{0x24, 0x0b, 0x30}, // bit INPT3; bmi
{0xa5, 0x0b, 0x10}, // lda INPT3; bpl
{0xa5, 0x0b, 0x30}, // lda INPT3; bmi
{0x24, 0x3b, 0x10}, // bit INPT3|$30; bpl
{0x24, 0x3b, 0x30}, // bit INPT3|$30; bmi
{0xa5, 0x3b, 0x10}, // lda INPT3|$30; bpl
{0xa5, 0x3b, 0x30}, // lda INPT3|$30; bmi
{0xa6, 0x3b, 0x8e}, // ldx INPT3|$30; stx
{0x25, 0x0b, 0x10}, // and INPT3; bpl (Genesis only)
}
}
return matchPattern(patterns, data)
}
func fingerprintPaddle(port plugging.PortID, data []byte) bool {
var patterns [][]byte
switch port {
case plugging.PortLeftPlayer:
patterns = [][]byte{
//{ 0x24, 0x08, 0x10 }, // bit INPT0; bpl (many joystick games too!)
//{ 0x24, 0x08, 0x30 }, // bit INPT0; bmi (joystick games: Spike's Peak, Sweat, Turbo!)
{0xa5, 0x08, 0x10}, // lda INPT0; bpl (no joystick games)
{0xa5, 0x08, 0x30}, // lda INPT0; bmi (no joystick games)
//{ 0xb5, 0x08, 0x10 }, // lda INPT0,x; bpl (Duck Attack (graphics)!, Toyshop Trouble (Easter Egg))
{0xb5, 0x08, 0x30}, // lda INPT0,x; bmi (no joystick games)
{0x24, 0x38, 0x10}, // bit INPT0|$30; bpl (no joystick games)
{0x24, 0x38, 0x30}, // bit INPT0|$30; bmi (no joystick games)
{0xa5, 0x38, 0x10}, // lda INPT0|$30; bpl (no joystick games)
{0xa5, 0x38, 0x30}, // lda INPT0|$30; bmi (no joystick games)
{0xb5, 0x38, 0x10}, // lda INPT0|$30,x; bpl (Circus Atari, old code!)
{0xb5, 0x38, 0x30}, // lda INPT0|$30,x; bmi (no joystick games)
{0x68, 0x48, 0x10}, // pla; pha; bpl (i.a. Bachelor Party)
{0xa5, 0x08, 0x4c}, // lda INPT0; jmp (only Backgammon)
{0xa4, 0x38, 0x30}, // ldy INPT0; bmi (no joystick games)
{0xb9, 0x08, 0x00, 0x30}, // lda INPT0,y; bmi (i.a. Encounter at L-5)
{0xb9, 0x38, 0x00, 0x30}, // lda INPT0|$30,y; bmi (i.a. SW-Jedi Arena, Video Olympics)
{0xb9, 0x08, 0x00, 0x10}, // lda INPT0,y; bpl (Drone Wars)
{0x24, 0x08, 0x30, 0x02}, // bit INPT0; bmi +2 (Picnic)
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0|$30,x; and #$80; bne (Basic Programming)
{0x24, 0x38, 0x85, 0x08, 0x10}, // bit INPT0|$30; sta COLUPF, bpl (Fireball)
{0xb5, 0x38, 0x49, 0xff, 0x0a}, // lda INPT0|$30,x; eor #$ff; asl (Blackjack)
{0xb1, 0xf2, 0x30, 0x02, 0xe6}, // lda ($f2),y; bmi...; inc (Warplock)
}
case plugging.PortRightPlayer:
patterns = [][]byte{
{0x24, 0x0a, 0x10}, // bit INPT2; bpl (no joystick games)
{0x24, 0x0a, 0x30}, // bit INPT2; bmi (no joystick games)
{0xa5, 0x0a, 0x10}, // lda INPT2; bpl (no joystick games)
{0xa5, 0x0a, 0x30}, // lda INPT2; bmi
{0xb5, 0x0a, 0x10}, // lda INPT2,x; bpl
{0xb5, 0x0a, 0x30}, // lda INPT2,x; bmi
{0xb5, 0x08, 0x10}, // lda INPT0,x; bpl (no joystick games)
{0xb5, 0x08, 0x30}, // lda INPT0,x; bmi (no joystick games)
{0x24, 0x3a, 0x10}, // bit INPT2|$30; bpl
{0x24, 0x3a, 0x30}, // bit INPT2|$30; bmi
{0xa5, 0x3a, 0x10}, // lda INPT2|$30; bpl
{0xa5, 0x3a, 0x30}, // lda INPT2|$30; bmi
{0xb5, 0x3a, 0x10}, // lda INPT2|$30,x; bpl
{0xb5, 0x3a, 0x30}, // lda INPT2|$30,x; bmi
{0xb5, 0x38, 0x10}, // lda INPT0|$30,x; bpl (Circus Atari, old code!)
{0xb5, 0x38, 0x30}, // lda INPT0|$30,x; bmi (no joystick games, except G.I. Joe)
{0xa4, 0x3a, 0x30}, // ldy INPT2|$30; bmi (no joystick games)
{0xa5, 0x3b, 0x30}, // lda INPT3|$30; bmi (only Tac Scan, ports and paddles swapped)
{0xb9, 0x38, 0x00, 0x30}, // lda INPT0|$30,y; bmi (Video Olympics)
{0xb5, 0x38, 0x29, 0x80, 0xd0}, // lda INPT0|$30,x; and #$80; bne (Basic Programming)
{0x24, 0x38, 0x85, 0x08, 0x10}, // bit INPT2|$30; sta COLUPF, bpl (Fireball, patched at runtime!)
{0xb5, 0x38, 0x49, 0xff, 0x0a}, // lda INPT0|$30,x; eor #$ff; asl (Blackjack)
}
}
return matchPattern(patterns, data)
}

View file

@ -0,0 +1,21 @@
// 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 peripherals
// Available is the list of peripherals that can feasibly be plugged
// into the left player port. These are the values that can be returned by the
// ID() function of the ports.Peripheral implementations in this package.
var Available = []string{"Stick", "Paddle", "Keypad", "Gamepad", "SaveKey"}

View file

@ -1,319 +0,0 @@
// 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 controllers
import (
"strconv"
"time"
"github.com/jetsetilly/gopher2600/hardware/memory/chipbus"
"github.com/jetsetilly/gopher2600/hardware/memory/cpubus"
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
)
// Auto handles the automatic switching between controller types.
type Auto struct {
port plugging.PortID
bus ports.PeripheralBus
controller ports.Peripheral
monitor plugging.PlugMonitor
lastStickVal ports.Event
lastStickTime time.Time
stickCt int
lastPaddleValue float32
lastPaddleTime time.Time
paddleTouchCt int
// if a keypad is detected via SWACNT then there is no auto-switching
//
// to prevent false positives of the SWACNT being in "non-keypad" mode
// and unecessarily switching to a non-keypad controller and then
// switching immediately back, we time the duration between unplug
// attempts. if the duration is long enough (keypadUnplugDelay) then the
// unplugging is allowed.
//
// a good example of a false positive of this type is the Coke Zero demo.
//
// it would be better perhaps if we did this the other way around and
// introduced a delay before switching to the keypad controller. but at
// least one title (Codebreaker) sets the SWACNT once and leaves it at
// that so there's nothing to work on. one possibility is to set up a timer
// and switching to the keypad when it expires, unless it's been
// interrupted, but I don't fancy the idea of having to service a timer on
// Update()
keypadUnplugAttempt bool
keypadUnplugTime time.Time
keypadUnplugDelay time.Duration
// value used to compare SWACNT value with. the value is different
// depending on which port the controller is plugged into
keypadDetectValue uint8
}
// the sensitivity values for switching between controller types.
//
// note that changing these values may well break existing playback scripts. do
// not change unless absolutely necessary.
//
// !!TODO: consider versioning the auto-controller type to help the recorder package.
const (
autoStickSensitivity = 6
autoPaddleSensitivity = 6
// the amount of time an input device will be "awake" and counting inputs before falling asleep again.
//
// in other words, activity must be completed in this time frame for the auto-switch to occur.
wakeTime = 2e09 // two seconds in nanoseconds
)
// NewAuto is the preferred method of initialisation for the Auto type.
// Satisifies the ports.NewPeripheral interface and can be used as an argument
// to ports.AttachPlayer0() and ports.AttachPlayer1().
func NewAuto(port plugging.PortID, bus ports.PeripheralBus) ports.Peripheral {
aut := &Auto{
port: port,
bus: bus,
}
switch port {
case plugging.PortLeftPlayer:
aut.keypadDetectValue = 0xf0
case plugging.PortRightPlayer:
aut.keypadDetectValue = 0x0f
}
// a two second delay should be sufficient time to require SWACNT to be in
// "non-keypad" mode before allowing the controller type to switch
aut.keypadUnplugDelay, _ = time.ParseDuration("1s")
aut.Reset()
return aut
}
// Snapshot implements the Peripheral interface.
func (aut *Auto) Snapshot() ports.Peripheral {
n := *aut
n.controller = aut.controller.Snapshot()
return &n
}
// Plumb implements the Peripheral interface.
func (aut *Auto) Plumb(bus ports.PeripheralBus) {
aut.bus = bus
aut.controller.Plumb(bus)
}
// String implements the ports.Peripheral interface.
func (aut *Auto) String() string {
return aut.controller.String()
}
// PortID implements the ports.Peripheral interface.
func (aut *Auto) PortID() plugging.PortID {
return aut.port
}
// ID implements the ports.Peripheral interface.
func (aut *Auto) ID() plugging.PeripheralID {
return aut.controller.ID()
}
// HandleEvent implements the ports.Peripheral interface.
func (aut *Auto) HandleEvent(event ports.Event, data ports.EventData) (bool, error) {
// no autoswitching if keypad is detected
if _, ok := aut.controller.(*Keypad); !ok {
switch event {
case ports.Left:
aut.checkStick(event)
case ports.Right:
aut.checkStick(event)
case ports.Up:
aut.checkStick(event)
case ports.Down:
aut.checkStick(event)
case ports.Fire:
// no check for fire events
case ports.PaddleSet:
aut.checkPaddle(data)
case ports.KeypadDown:
// no check on keypad down
case ports.KeypadUp:
// no check on keypad up
}
}
return aut.controller.HandleEvent(event, data)
}
// Update implements the ports.Peripheral interface.
func (aut *Auto) Update(data chipbus.ChangedRegister) bool {
switch data.Register {
case cpubus.SWACNT:
if data.Value&aut.keypadDetectValue == aut.keypadDetectValue {
// attach keypad IF NOT attached already
if _, ok := aut.controller.(*Keypad); !ok {
aut.controller = NewKeypad(aut.port, aut.bus)
aut.plug()
} else if aut.keypadUnplugAttempt {
// reset keypadUnplugAttempt if an unplug attempt has been made
aut.keypadUnplugAttempt = false
}
} else if data.Value&aut.keypadDetectValue == 0x00 {
if _, ok := aut.controller.(*Keypad); ok {
if aut.keypadUnplugAttempt {
if time.Since(aut.keypadUnplugTime) > aut.keypadUnplugDelay {
aut.controller = NewGamepad(aut.port, aut.bus)
aut.plug()
}
} else {
aut.keypadUnplugAttempt = true
aut.keypadUnplugTime = time.Now()
}
}
}
}
return aut.controller.Update(data)
}
// Step implements the ports.Peripheral interface.
func (aut *Auto) Step() {
aut.controller.Step()
}
// Reset implements the ports.Peripheral interface.
func (aut *Auto) Reset() {
aut.controller = NewGamepad(aut.port, aut.bus)
aut.resetStickDetection()
aut.resetPaddleDetection()
}
func (aut *Auto) checkStick(event ports.Event) {
aut.resetPaddleDetection()
if _, ok := aut.controller.(*Stick); !ok {
// stick must be "awake" before counting begins
if time.Since(aut.lastStickTime) < wakeTime {
// detect stick being waggled. stick detection works a little
// differently to paddle and keypad detection. instead of the stick
// data we record the stick event.
if event != aut.lastStickVal {
aut.stickCt++
if aut.stickCt >= autoStickSensitivity {
aut.controller = NewGamepad(aut.port, aut.bus)
aut.plug()
}
}
aut.lastStickVal = event
} else {
// reset paddle detection date before recording time for next paddle event
aut.resetStickDetection()
aut.lastStickTime = time.Now()
}
}
}
func (aut *Auto) checkPaddle(data ports.EventData) {
aut.resetStickDetection()
if _, ok := aut.controller.(*Paddle); !ok {
// paddle must be "awake" before counting begins
if time.Since(aut.lastPaddleTime) < wakeTime {
var pv float32
// handle possible underlying EventData types
switch d := data.(type) {
case ports.EventDataPlayback:
f, err := strconv.ParseFloat(string(d), 32)
if err != nil {
return // ignore error
}
pv = float32(f)
case float32:
pv = d
default:
return
}
// detect mouse moving into extreme left/right positions
if (pv < 0.1 && aut.lastPaddleValue > 0.1) || (pv > 0.9 && aut.lastPaddleValue < 0.9) {
aut.paddleTouchCt++
// if mouse has touched extremeties a set number of times then
// switch to paddle control. for example if the sensitivity value is
// three:
//
// centre -> right -> left -> switch
if aut.paddleTouchCt >= autoPaddleSensitivity {
aut.controller = NewPaddle(aut.port, aut.bus)
aut.plug()
}
aut.lastPaddleValue = pv
}
} else {
// reset paddle detection date before recording time for next paddle event
aut.resetPaddleDetection()
aut.lastPaddleTime = time.Now()
}
}
}
// resetPaddleDetection called when non-paddle input is detected.
func (aut *Auto) resetPaddleDetection() {
aut.lastPaddleValue = 0.5
aut.lastPaddleTime = time.Time{}
aut.paddleTouchCt = 0
}
// resetPaddleDetection called when non-stick input is detected.
func (aut *Auto) resetStickDetection() {
aut.lastStickVal = ports.Centre
aut.lastStickTime = time.Time{}
aut.stickCt = 0
}
// plug is called by chceckStick(), checkPaddle() and handles the
// plug monitor.
func (aut *Auto) plug() {
// notify any peripheral monitors
if aut.monitor != nil {
aut.monitor.Plugged(aut.port, aut.controller.ID())
}
// attach any monitors to newly plugged controllers
if a, ok := aut.controller.(plugging.Monitorable); ok {
a.AttachPlugMonitor(aut.monitor)
}
}
// AttachPlugMonitor implements the plugging.Monitorable interface.
func (aut *Auto) AttachPlugMonitor(m plugging.PlugMonitor) {
aut.monitor = m
if a, ok := aut.controller.(plugging.Monitorable); ok {
a.AttachPlugMonitor(m)
}
}
// IsActive implements the ports.Peripheral interface.
func (aut *Auto) IsActive() bool {
return aut.controller.IsActive()
}

View file

@ -1,32 +0,0 @@
// 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 controllers contains the implementations for all the emulated
// controllers for the VCS.
//
// The Auto type handles flipping of the other controller types according to
// user input and the state of the machine. The Auto type will forward all
// functions to the "real" controller (ie. the stick, paddle or keyboard)
// transparently. So for example, ID() will return the ID() of the "real"
// controller. If you really need to know whether the real controller has been
// automatically selected via the Auto type then you can (test the Player 0
// port, for example):
//
// if _, ok := ports.Player0.(controllers.Auto); ok {
// // is auto
// } else {
// // is not auto
// }
package controllers

View file

@ -24,9 +24,10 @@ import (
"github.com/jetsetilly/gopher2600/hardware/memory"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge"
"github.com/jetsetilly/gopher2600/hardware/memory/cpubus"
"github.com/jetsetilly/gopher2600/hardware/peripherals"
"github.com/jetsetilly/gopher2600/hardware/peripherals/controllers"
"github.com/jetsetilly/gopher2600/hardware/preferences"
"github.com/jetsetilly/gopher2600/hardware/riot"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/controllers"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/panel"
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
"github.com/jetsetilly/gopher2600/hardware/television"
@ -104,12 +105,12 @@ func NewVCS(tv *television.Television, prefs *preferences.Preferences) (*VCS, er
return nil, err
}
err = vcs.RIOT.Ports.Plug(plugging.PortLeftPlayer, controllers.NewAuto)
err = vcs.RIOT.Ports.Plug(plugging.PortLeftPlayer, controllers.NewStick)
if err != nil {
return nil, err
}
err = vcs.RIOT.Ports.Plug(plugging.PortRightPlayer, controllers.NewAuto)
err = vcs.RIOT.Ports.Plug(plugging.PortRightPlayer, controllers.NewStick)
if err != nil {
return nil, err
}
@ -159,6 +160,16 @@ func (vcs *VCS) AttachCartridge(cartload cartridgeloader.Loader) error {
if err != nil {
return err
}
err = vcs.RIOT.Ports.Plug(plugging.PortLeftPlayer, peripherals.Fingerprint(plugging.PortLeftPlayer, *cartload.Data))
if err != nil {
return err
}
err = vcs.RIOT.Ports.Plug(plugging.PortRightPlayer, peripherals.Fingerprint(plugging.PortRightPlayer, *cartload.Data))
if err != nil {
return err
}
}
// resetting after cartridge attachment because the cartridge needs a reset