peripheral swapping with the -swap argument

the PERIPHERAL SWAP command also added

no corresponding setup entry yet. the setup package needs rewriting to
be more flexible before we do that
This commit is contained in:
JetSetIlly 2023-12-10 09:01:57 +00:00
parent 26eab2cb17
commit 5524be92fe
7 changed files with 129 additions and 88 deletions

View file

@ -31,6 +31,7 @@ type CommandLineOptions struct {
Mapping string
Left string
Right string
Swap bool
Profile string
ELF string

View file

@ -1743,11 +1743,19 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
player, _ := tokens.Get()
var id plugging.PortID
switch strings.ToUpper(player) {
case "LEFT":
id = plugging.PortLeft
case "RIGHT":
id = plugging.PortRight
case "SWAP":
if dbg.controllers.Swap() {
dbg.printLine(terminal.StyleFeedback, "player peripherals are in the swapped state")
} else {
dbg.printLine(terminal.StyleFeedback, "player peripherals are in the normal state")
}
return nil
}
var err error

View file

@ -386,7 +386,9 @@ The optional DERIVATION switch to the LOCAL argument prints out the location lis
variable. Normal Atari 2600 developers do not need to worry about location lists.`,
// user input
cmdPeripheral: `Change the current peripheral for the specified player port.`,
cmdPeripheral: `Change the current peripheral for the specified player port. Can also be used
to swap the ports so that the left player in the emulator controls what would be the right player
on the real console.`,
cmdPanel: "Inspect and set front panel settings. Switches can be set or toggled.",

View file

@ -127,7 +127,7 @@ var commandTemplate = []string{
cmdDWARF + " [FUNCTIONS|GLOBALS|LOCALS {DERIVATION|RANGES|ERROR}|FRAMEBASE {DERIVATION}|LINE %<file:line>S|CALLSTACK|CALLERS %<function>S]",
// user input
cmdPeripheral + " [LEFT|RIGHT] (AUTO|STICK|PADDLE|KEYPAD|GAMEPAD|SAVEKEY|ATARIVOX)",
cmdPeripheral + " ([LEFT|RIGHT] (AUTO|STICK|PADDLE|KEYPAD|GAMEPAD|SAVEKEY|ATARIVOX)|SWAP)",
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

@ -681,7 +681,7 @@ func (dbg *Debugger) StartInDebugMode(filename string) error {
return fmt.Errorf("debugger: %w", err)
}
err = dbg.insertPeripheralsOnStartup(dbg.opts.Left, dbg.opts.Right)
err = dbg.setPeripheralsOnStartup()
if err != nil {
return fmt.Errorf("debugger: %w", err)
}
@ -718,18 +718,26 @@ func (dbg *Debugger) StartInDebugMode(filename string) error {
return nil
}
func (dbg *Debugger) insertPeripheralsOnStartup(left string, right string) error {
func (dbg *Debugger) setPeripheralsOnStartup() error {
dbg.term.Silence(true)
defer dbg.term.Silence(false)
err := dbg.parseCommand(fmt.Sprintf("PERIPHERAL LEFT %s", left), false, false)
err := dbg.parseCommand(fmt.Sprintf("PERIPHERAL LEFT %s", dbg.opts.Left), false, false)
if err != nil {
return err
}
err = dbg.parseCommand(fmt.Sprintf("PERIPHERAL RIGHT %s", right), false, false)
err = dbg.parseCommand(fmt.Sprintf("PERIPHERAL RIGHT %s", dbg.opts.Right), false, false)
if err != nil {
return err
}
if dbg.opts.Swap {
err = dbg.parseCommand(fmt.Sprintf("PERIPHERAL SWAP"), false, false)
if err != nil {
return err
}
}
return nil
}
@ -761,7 +769,7 @@ func (dbg *Debugger) StartInPlayMode(filename string) error {
return fmt.Errorf("debugger: %w", err)
}
err = dbg.insertPeripheralsOnStartup(dbg.opts.Left, dbg.opts.Right)
err = dbg.setPeripheralsOnStartup()
if err != nil {
return fmt.Errorf("debugger: %w", err)
}

View file

@ -304,6 +304,7 @@ func emulate(mode string, sync *mainSync, args []string) error {
flgs.StringVar(&opts.Mapping, "mapping", "AUTO", "force cartridge mapper selection")
flgs.StringVar(&opts.Left, "left", "AUTO", "left player port: AUTO, STICK, PADDLE, KEYPAD, GAMEPAD")
flgs.StringVar(&opts.Right, "right", "AUTO", "left player port: AUTO, STICK, PADDLE, KEYPAD, GAMEPAD")
flgs.BoolVar(&opts.Swap, "swap", false, "swap player ports")
flgs.StringVar(&opts.Profile, "profile", "none", "run performance check with profiling: CPU, MEM, TRACE, ALL (comma sep)")
flgs.StringVar(&opts.ELF, "elf", "", "path to ELF file. only valid for some coproc supporting ROMs")

View file

@ -26,6 +26,7 @@ const ThumbstickDeadzone = 10000
// Controllers keeps track of hardware userinput options.
type Controllers struct {
inputHandler HandleInput
swapped bool
}
// Controllers is the preferred method of initialisation for the Controllers type.
@ -33,6 +34,31 @@ func NewControllers(h HandleInput) *Controllers {
return &Controllers{inputHandler: h}
}
// Swap exchanges which controls affects which player port. For example, the
// cursor keys on the keyboard will normally control the left player. However,
// when swapped, the cursor keys will control the right player
//
// Returns true if the ports are now swapped and false if the ports are in the
// original state
func (c *Controllers) Swap() bool {
c.swapped = !c.swapped
return c.swapped
}
// handleSwap returns the required PortID relative to the supplied PortID if the
// controls have been swapped
func (c Controllers) handleSwap(port plugging.PortID) plugging.PortID {
if c.swapped {
switch port {
case plugging.PortLeft:
port = plugging.PortRight
case plugging.PortRight:
port = plugging.PortLeft
}
}
return port
}
// handleEvents sends the port/event information to each HandleInput
// implementation.
//
@ -47,7 +73,7 @@ func (c *Controllers) handleEvents(id plugging.PortID, ev ports.Event, d ports.E
}
func (c *Controllers) mouseMotion(ev EventMouseMotion) (bool, error) {
return c.handleEvents(plugging.PortLeft, ports.PaddleSet, ports.EventDataPaddle{
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.PaddleSet, ports.EventDataPaddle{
A: ev.X,
Relative: true,
})
@ -57,9 +83,9 @@ func (c *Controllers) mouseButton(ev EventMouseButton) (bool, error) {
switch ev.Button {
case MouseButtonLeft:
if ev.Down {
return c.handleEvents(plugging.PortLeft, ports.Fire, true)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, true)
} else {
return c.handleEvents(plugging.PortLeft, ports.Fire, false)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, false)
}
}
@ -98,62 +124,57 @@ func (c *Controllers) differentiateKeyboard(key string, down bool) (bool, error)
switch key {
case "Y":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '6'
} else {
portID = plugging.PortRight
ev = ports.Up
d = stickEvData
}
case "F":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '7'
} else {
portID = plugging.PortRight
ev = ports.Fire
d = fireEvData
}
case "G":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '8'
} else {
portID = plugging.PortRight
ev = ports.Left
d = stickEvData
}
case "H":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '9'
} else {
portID = plugging.PortRight
ev = ports.Down
d = stickEvData
}
case "B":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '0'
} else {
portID = plugging.PortLeft
portID = c.handleSwap(plugging.PortLeft)
ev = ports.SecondFire
d = fireEvData
}
case "6":
if c.inputHandler.PeripheralID(plugging.PortRight) == plugging.PeriphKeypad {
portID = plugging.PortRight
portID = c.handleSwap(plugging.PortRight)
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
ev = keyEv
d = '6'
} else {
portID = plugging.PortRight
ev = ports.SecondFire
d = fireEvData
}
@ -187,57 +208,57 @@ func (c *Controllers) keyboard(ev EventKeyboard) (bool, error) {
// joystick (left player)
case "Left":
return c.handleEvents(plugging.PortLeft, ports.Left, ports.DataStickTrue)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Left, ports.DataStickTrue)
case "Right":
return c.handleEvents(plugging.PortLeft, ports.Right, ports.DataStickTrue)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Right, ports.DataStickTrue)
case "Up":
return c.handleEvents(plugging.PortLeft, ports.Up, ports.DataStickTrue)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Up, ports.DataStickTrue)
case "Down":
return c.handleEvents(plugging.PortLeft, ports.Down, ports.DataStickTrue)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Down, ports.DataStickTrue)
case "Space":
return c.handleEvents(plugging.PortLeft, ports.Fire, true)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, true)
// joystick (right player)
// * keypad and joystick share some keys (see below for other inputs)
case "J":
return c.handleEvents(plugging.PortRight, ports.Right, ports.DataStickTrue)
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.Right, ports.DataStickTrue)
// keypad (left player)
case "1", "2", "3":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, rune(ev.Key[0]))
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, rune(ev.Key[0]))
case "Q":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '4')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '4')
case "W":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '5')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '5')
case "E":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '6')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '6')
case "A":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '7')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '7')
case "S":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '8')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '8')
case "D":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '9')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '9')
case "Z":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '*')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '*')
case "X":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '0')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '0')
case "C":
return c.handleEvents(plugging.PortLeft, ports.KeypadDown, '#')
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '#')
// keypad (right player)
// * keypad and joystick share some keys (see below for other inputs)
case "4":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '1')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '1')
case "5":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '2')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '2')
case "R":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '4')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '4')
case "T":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '5')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '5')
case "V":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '*')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '*')
case "N":
return c.handleEvents(plugging.PortRight, ports.KeypadDown, '#')
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '#')
default:
// keypad (right player) *OR* joystick (right player)
@ -254,29 +275,29 @@ func (c *Controllers) keyboard(ev EventKeyboard) (bool, error) {
// josytick (left player)
case "Left":
return c.handleEvents(plugging.PortLeft, ports.Left, ports.DataStickFalse)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Left, ports.DataStickFalse)
case "Right":
return c.handleEvents(plugging.PortLeft, ports.Right, ports.DataStickFalse)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Right, ports.DataStickFalse)
case "Up":
return c.handleEvents(plugging.PortLeft, ports.Up, ports.DataStickFalse)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Up, ports.DataStickFalse)
case "Down":
return c.handleEvents(plugging.PortLeft, ports.Down, ports.DataStickFalse)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Down, ports.DataStickFalse)
case "Space":
return c.handleEvents(plugging.PortLeft, ports.Fire, false)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, false)
// joystick (right player)
// * keypad and joystick share some keys (see below for other inputs)
case "J":
return c.handleEvents(plugging.PortRight, ports.Right, ports.DataStickFalse)
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.Right, ports.DataStickFalse)
// keyboard (left player)
case "1", "2", "3", "Q", "W", "E", "A", "S", "D", "Z", "X", "C":
return c.handleEvents(plugging.PortLeft, ports.KeypadUp, nil)
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadUp, nil)
// keyboard (right player)
// * keypad and joystick share some keys (see below for other inputs)
case "4", "5", "R", "T", "V", "N":
return c.handleEvents(plugging.PortRight, ports.KeypadUp, nil)
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadUp, nil)
default:
// keypad (right player) *OR* joystick (right player)
@ -289,31 +310,31 @@ func (c *Controllers) keyboard(ev EventKeyboard) (bool, error) {
func (c *Controllers) gamepadDPad(ev EventGamepadDPad) (bool, error) {
switch ev.Direction {
case DPadCentre:
return c.handleEvents(ev.ID, ports.Centre, nil)
return c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
case DPadUp:
return c.handleEvents(ev.ID, ports.Up, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
case DPadDown:
return c.handleEvents(ev.ID, ports.Down, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
case DPadLeft:
return c.handleEvents(ev.ID, ports.Left, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
case DPadRight:
return c.handleEvents(ev.ID, ports.Right, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
case DPadLeftUp:
return c.handleEvents(ev.ID, ports.LeftUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
case DPadLeftDown:
return c.handleEvents(ev.ID, ports.LeftDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
case DPadRightUp:
return c.handleEvents(ev.ID, ports.RightUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
case DPadRightDown:
return c.handleEvents(ev.ID, ports.RightDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
}
return false, nil
@ -324,9 +345,9 @@ func (c *Controllers) gamepadButton(ev EventGamepadButton) (bool, error) {
case GamepadButtonStart:
return c.handleEvents(plugging.PortPanel, ports.PanelReset, ev.Down)
case GamepadButtonA:
return c.handleEvents(ev.ID, ports.Fire, ev.Down)
return c.handleEvents(c.handleSwap(ev.ID), ports.Fire, ev.Down)
case GamepadButtonB:
return c.handleEvents(ev.ID, ports.SecondFire, ev.Down)
return c.handleEvents(c.handleSwap(ev.ID), ports.SecondFire, ev.Down)
}
return false, nil
}
@ -338,22 +359,22 @@ func (c *Controllers) gamepadThumbstick(ev EventGamepadThumbstick) (bool, error)
if ev.Horiz > ThumbstickDeadzone {
if ev.Vert > ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.RightDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
} else if ev.Vert < -ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.RightUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
}
return c.handleEvents(ev.ID, ports.Right, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
} else if ev.Horiz < -ThumbstickDeadzone {
if ev.Vert > ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.LeftDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
} else if ev.Vert < -ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.LeftUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
}
return c.handleEvents(ev.ID, ports.Left, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
} else if ev.Vert > ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.Down, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
} else if ev.Vert < -ThumbstickDeadzone {
return c.handleEvents(ev.ID, ports.Up, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
}
// never report that the centre event has been handled by the emulated
@ -362,7 +383,7 @@ func (c *Controllers) gamepadThumbstick(ev EventGamepadThumbstick) (bool, error)
// for example, it prevents deadzone signals causing the emulation to unpause
//
// this might be wrong behaviour in some situations.
_, err := c.handleEvents(ev.ID, ports.Centre, nil)
_, err := c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
return false, err
}
@ -384,28 +405,28 @@ func (c *Controllers) stelladaptor(ev EventStelladaptor) (bool, error) {
if ev.Horiz > boundaryValue {
if ev.Vert > boundaryValue {
return c.handleEvents(ev.ID, ports.RightDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
} else if ev.Vert < -boundaryValue {
return c.handleEvents(ev.ID, ports.RightUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
}
return c.handleEvents(ev.ID, ports.Right, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
} else if ev.Horiz < -boundaryValue {
if ev.Vert > boundaryValue {
return c.handleEvents(ev.ID, ports.LeftDown, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
} else if ev.Vert < -boundaryValue {
return c.handleEvents(ev.ID, ports.LeftUp, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
}
return c.handleEvents(ev.ID, ports.Left, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
} else if ev.Vert > boundaryValue {
return c.handleEvents(ev.ID, ports.Down, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
} else if ev.Vert < -boundaryValue {
return c.handleEvents(ev.ID, ports.Up, ports.DataStickSet)
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
}
return c.handleEvents(ev.ID, ports.Centre, nil)
return c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
case plugging.PeriphPaddles:
return c.handleEvents(ev.ID, ports.PaddleSet, ports.EventDataPaddle{
return c.handleEvents(c.handleSwap(ev.ID), ports.PaddleSet, ports.EventDataPaddle{
A: ev.Horiz,
B: ev.Vert,
})