o controller

- added basic controller support
	- uses third-party package:
		- github.com/splace/joysticks
This commit is contained in:
steve 2018-06-23 14:07:41 +01:00
parent 1a59db31d1
commit 6957a52adc
7 changed files with 97 additions and 17 deletions

View file

@ -65,7 +65,7 @@ func NewDebugger() (*Debugger, error) {
}
// prepare hardware
tv, err := television.NewSDLTV("NTSC", 3.0)
tv, err := television.NewSDLTV("NTSC", television.IdealScale)
if err != nil {
return nil, err
}

View file

@ -4,26 +4,41 @@ import "fmt"
// list of sub-systems used when defining errors
const (
CategoryDebugger = 0
CategoryVCS = 64
CategoryCPU = 128
CategoryMemory = 192
CategoryTIA = 256
CategoryRIOT = 320
CategoryTV = 384
CategoryDebugger = 0
CategoryVCS = 64
CategoryCPU = 128
CategoryMemory = 192
CategoryTIA = 256
CategoryRIOT = 320
CategoryTV = 384
CategoryController = 448
)
// list of error numbers
const (
// Debugger
NoSymbolsFile = CategoryDebugger + iota
SymbolsFileError
// VCS
// CPU
UnimplementedInstruction = CategoryCPU + iota
NullInstruction
// Memory
UnservicedChipWrite = CategoryMemory + iota
UnknownRegisterName
UnreadableAddress
// TIA
// RIOT
// TV
// Controller
NoControllersFound = CategoryController + iota
)
var messages = map[int]string{
@ -47,6 +62,9 @@ var messages = map[int]string{
// RIOT
// TV
// Controller
NoControllersFound: "no controllers found",
}
// Values is the type used to specify arguments for a GopherError
@ -64,6 +82,9 @@ func (er GopherError) Error() string {
// Category returns the broad categorisation of a GopherError
func (er GopherError) Category() int {
if er.Errno >= CategoryController {
return CategoryController
}
if er.Errno >= CategoryTV {
return CategoryTV
}

View file

@ -110,7 +110,7 @@ func fps(cartridgeFile string, justTheVCS bool) error {
return fmt.Errorf("error creating television for fps profiler")
}
} else {
tv, err = television.NewSDLTV("NTSC", 3.0)
tv, err = television.NewSDLTV("NTSC", television.IdealScale)
if err != nil {
return fmt.Errorf("error creating television for fps profiler")
}
@ -159,7 +159,7 @@ func run(cartridgeFile string) error {
var tv television.Television
var err error
tv, err = television.NewSDLTV("NTSC", 3.0)
tv, err = television.NewSDLTV("NTSC", television.IdealScale)
if err != nil {
return fmt.Errorf("error creating television for fps profiler")
}

View file

@ -0,0 +1,52 @@
package controller
import (
"gopher2600/errors"
"gopher2600/hardware/memory"
"github.com/splace/joysticks"
)
// Stick emulaes the digital VCS joystick
type Stick struct {
device *joysticks.HID
err error
}
// NewStick is the preferred method of initialisation for the Stick type
func NewStick(tia *memory.ChipMemory) *Stick {
stick := new(Stick)
// there is a flaw (either in splace/joysticks or somewehere else lower
// down in the kernel driver) which means that Connect() will not return
// until it recieves some input from the controller. to get around this,
// we've put the main body of the NewStick() function in a go routine.
go func() {
// try connecting to specific controller.
// system assigned index: typically increments on each new controller added.
stick.device = joysticks.Connect(1)
if stick.device == nil {
stick.err = errors.GopherError{errors.NoControllersFound, nil}
return
}
// get/assign channels for specific events
buttonPress := stick.device.OnClose(1)
buttonRelease := stick.device.OnOpen(1)
// start feeding OS events onto the event channels.
go stick.device.ParcelOutEvents()
// handle event channels
for {
select {
case <-buttonPress:
tia.ChipWrite("INPT4", 0x00)
case <-buttonRelease:
tia.ChipWrite("INPT4", 0x80)
}
}
}()
return stick
}

View file

@ -145,5 +145,5 @@ func (ms *missileSprite) scheduleReset(hblank *bool) {
}
func (ms *missileSprite) scheduleEnable(value uint8) {
ms.futureEnable.schedule(delayEnableSprite, value&0x20 == 0x20)
ms.futureEnable.schedule(delayEnableSprite, value&0x02 == 0x02)
}

View file

@ -2,6 +2,7 @@ package hardware
import (
"fmt"
"gopher2600/hardware/controller"
"gopher2600/hardware/cpu"
"gopher2600/hardware/memory"
"gopher2600/hardware/riot"
@ -25,6 +26,8 @@ type VCS struct {
// tv is not part of the VCS but is attached to it
TV television.Television
controller *controller.Stick
}
// New is the preferred method of initialisation for the VCS structure
@ -54,6 +57,13 @@ func New(tv television.Television) (*VCS, error) {
return nil, fmt.Errorf("can't allocate memory for VCS RIOT")
}
// TODO: better contoller support
vcs.controller = controller.NewStick(vcs.Mem.TIA)
// TODO: console switch support
// - set colour switch bit
vcs.Mem.RIOT.ChipWrite("SWCHB", 0x08)
return vcs, nil
}
@ -115,12 +125,6 @@ func (vcs *VCS) Step(videoCycleCallback func(*cpu.InstructionResult) error) (int
videoCycleCallback(r)
}
// TODO: full controller support -- this is emulating the rest state for the
// two joystick controllers
vcs.Mem.TIA.ChipWrite("INPT4", 0x80)
vcs.Mem.TIA.ChipWrite("INPT5", 0x80)
vcs.Mem.RIOT.ChipWrite("SWCHA", 0xFF)
r, err = vcs.MC.ExecuteInstruction(cycleVCS)
if err != nil {
return cpuCycles, nil, err

View file

@ -32,6 +32,9 @@ type SDLTV struct {
lastFrameRender time.Time
}
// IdealScale is the suggested scaling for the screen
const IdealScale = 2.0
// NewSDLTV is the preferred method for initalising an SDL TV
func NewSDLTV(tvType string, scale float32) (*SDLTV, error) {
var err error