diff --git a/debugger/debugger.go b/debugger/debugger.go index 89212a96..736a7aad 100644 --- a/debugger/debugger.go +++ b/debugger/debugger.go @@ -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 } diff --git a/errors/errors.go b/errors/errors.go index 62180499..2211f737 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -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 } diff --git a/gopher2600.go b/gopher2600.go index 689fd234..49ed54a4 100644 --- a/gopher2600.go +++ b/gopher2600.go @@ -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") } diff --git a/hardware/controller/controller.go b/hardware/controller/controller.go new file mode 100644 index 00000000..750f54f5 --- /dev/null +++ b/hardware/controller/controller.go @@ -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 +} diff --git a/hardware/tia/video/missile.go b/hardware/tia/video/missile.go index e64e30f6..36898804 100644 --- a/hardware/tia/video/missile.go +++ b/hardware/tia/video/missile.go @@ -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) } diff --git a/hardware/vcs.go b/hardware/vcs.go index 0993f89e..3936b4a3 100644 --- a/hardware/vcs.go +++ b/hardware/vcs.go @@ -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 diff --git a/television/sdl.go b/television/sdl.go index 41441e4c..c040387c 100644 --- a/television/sdl.go +++ b/television/sdl.go @@ -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