mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
o errors
- refined error package
This commit is contained in:
parent
ec173372c9
commit
35e31cccb7
40 changed files with 304 additions and 411 deletions
|
@ -38,6 +38,9 @@ type Debugger struct {
|
|||
breakpoints *breakpoints
|
||||
traps *traps
|
||||
|
||||
// any error from previous emulation step
|
||||
lastStepError bool
|
||||
|
||||
// commandOnHalt says whether an sequence of commands should run automatically
|
||||
// when emulation halts. commandOnHaltPrev is the stored command sequence
|
||||
// used when ONHALT is called with no arguments
|
||||
|
@ -73,25 +76,24 @@ type Debugger struct {
|
|||
input []byte
|
||||
}
|
||||
|
||||
// NewDebugger is the preferred method of initialisation for the Debugger structure
|
||||
// NewDebugger creates and initialises everything required for a new debugging
|
||||
// session. Use the Start() method to actually begin the session.
|
||||
func NewDebugger() (*Debugger, error) {
|
||||
var err error
|
||||
|
||||
dbg := new(Debugger)
|
||||
|
||||
dbg.ui = new(ui.PlainTerminal)
|
||||
if dbg.ui == nil {
|
||||
return nil, fmt.Errorf("error allocationg memory for UI")
|
||||
}
|
||||
|
||||
// prepare hardware
|
||||
tv, err := sdltv.NewSDLTV("NTSC", sdltv.IdealScale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error preparing television: %s", err)
|
||||
}
|
||||
dbg.vcs, err = hardware.New(tv)
|
||||
|
||||
dbg.vcs, err = hardware.NewVCS(tv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error preparing VCS: %s", err)
|
||||
}
|
||||
|
||||
// set up breakpoints/traps
|
||||
|
@ -169,7 +171,7 @@ func (dbg *Debugger) Start(interf ui.UserInterface, filename string, initScript
|
|||
if initScript != "" {
|
||||
err = dbg.RunScript(initScript, true)
|
||||
if err != nil {
|
||||
dbg.print(ui.Error, "* error running debugger initialisation script (%s)\n", err)
|
||||
dbg.print(ui.Error, "* error running debugger initialisation script: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,9 +258,12 @@ func (dbg *Debugger) inputLoop(mainLoop bool) error {
|
|||
if dbg.inputloopNext {
|
||||
bpCheck := dbg.breakpoints.check()
|
||||
trCheck := dbg.traps.check()
|
||||
dbg.inputloopHalt = bpCheck || trCheck
|
||||
dbg.inputloopHalt = bpCheck || trCheck || dbg.lastStepError
|
||||
}
|
||||
|
||||
// reset last step error
|
||||
dbg.lastStepError = false
|
||||
|
||||
// *CRITICAL SECTION*
|
||||
dbg.runLock.Lock()
|
||||
|
||||
|
@ -351,8 +356,20 @@ func (dbg *Debugger) inputLoop(mainLoop bool) error {
|
|||
} else {
|
||||
_, dbg.lastResult, err = dbg.vcs.Step(dbg.noVideoCycleCallback)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
switch err := err.(type) {
|
||||
case errors.GopherError:
|
||||
// do not exit input loop when error is a gopher error
|
||||
// set lastStepError instead and allow emulation to
|
||||
// halt
|
||||
dbg.lastStepError = true
|
||||
|
||||
// print gopher error message
|
||||
dbg.print(ui.Error, "%s", err)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dbg.commandOnStep != "" {
|
||||
|
@ -467,7 +484,7 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
|
|||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case errors.GopherError:
|
||||
if err.Errno == errors.UnknownSymbol {
|
||||
if err.Errno == errors.SymbolUnknown {
|
||||
dbg.print(ui.Feedback, "%s -> not found", parts[1])
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package debugger
|
|||
import (
|
||||
"fmt"
|
||||
"gopher2600/debugger/ui"
|
||||
"gopher2600/errors"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
@ -12,7 +13,7 @@ func (dbg *Debugger) loadScript(scriptfile string) ([]string, error) {
|
|||
// open script and defer closing
|
||||
sf, err := os.Open(scriptfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening script (%s)", err)
|
||||
return nil, errors.NewGopherError(errors.ScriptFileCannotOpen, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = sf.Close()
|
||||
|
@ -33,7 +34,7 @@ func (dbg *Debugger) loadScript(scriptfile string) ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
if n != len(buffer) {
|
||||
return nil, fmt.Errorf("error reading scriptfile file (%s)", scriptfile)
|
||||
return nil, errors.NewGopherError(errors.ScriptFileError, errors.FileTruncated)
|
||||
}
|
||||
|
||||
// convert buffer to an array of lines
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopher2600/errors"
|
||||
"gopher2600/hardware"
|
||||
"gopher2600/television"
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ func parseTarget(vcs *hardware.VCS, keyword string) (target, error) {
|
|||
case "HORIZPOS", "HP":
|
||||
trg, err = vcs.TV.RequestTVState(television.ReqHorizPos)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid target (%s)", keyword)
|
||||
return nil, errors.NewGopherError(errors.InvalidTarget, keyword)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -32,7 +32,7 @@ func (dsm *Disassembly) ParseMemory(memory *memory.VCSMemory, symtable *symbols.
|
|||
dsm.SequencePoints = make([]uint16, 0, memory.Cart.Memtop()-memory.Cart.Origin())
|
||||
|
||||
// create a new non-branching CPU to disassemble memory
|
||||
mc, err := cpu.New(memory)
|
||||
mc, err := cpu.NewCPU(memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func NewDisassembly(cartridgeFilename string) (*Disassembly, error) {
|
|||
}
|
||||
}
|
||||
|
||||
mem, err := memory.New()
|
||||
mem, err := memory.NewVCSMemory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
37
errors/categories.go
Normal file
37
errors/categories.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package errors
|
||||
|
||||
// list of error numbers
|
||||
const (
|
||||
// Debugger
|
||||
SymbolsFileCannotOpen Errno = iota
|
||||
SymbolsFileError
|
||||
SymbolUnknown
|
||||
ScriptFileCannotOpen
|
||||
ScriptFileError
|
||||
InvalidTarget
|
||||
|
||||
// CPU
|
||||
UnimplementedInstruction
|
||||
NullInstruction
|
||||
ProgramCounterCycled
|
||||
|
||||
// Memory
|
||||
UnservicedChipWrite
|
||||
UnknownRegisterName
|
||||
UnreadableAddress
|
||||
UnwritableAddress
|
||||
UnrecognisedAddress
|
||||
|
||||
// Cartridges
|
||||
CartridgeFileCannotOpen
|
||||
CartridgeFileError
|
||||
CartridgeInvalidSize
|
||||
|
||||
// TV
|
||||
UnknownStateRequest
|
||||
UnknownCallbackRequest
|
||||
InvalidStateRequest
|
||||
|
||||
// Peripherals
|
||||
NoControllersFound
|
||||
)
|
109
errors/errors.go
109
errors/errors.go
|
@ -5,81 +5,6 @@ import "fmt"
|
|||
// Errno is used specified the specific error
|
||||
type Errno int
|
||||
|
||||
// list of sub-systems used when defining errors
|
||||
const (
|
||||
CategoryDebugger = iota * 8
|
||||
CategoryVCS
|
||||
CategoryCPU
|
||||
CategoryMemory
|
||||
CategoryTIA
|
||||
CategoryRIOT
|
||||
CategoryTV
|
||||
CategoryController
|
||||
)
|
||||
|
||||
// list of error numbers
|
||||
const (
|
||||
// Debugger
|
||||
NoSymbolsFile Errno = CategoryDebugger + iota
|
||||
SymbolsFileError
|
||||
UnknownSymbol
|
||||
|
||||
// VCS
|
||||
|
||||
// CPU
|
||||
UnimplementedInstruction Errno = CategoryCPU + iota
|
||||
NullInstruction
|
||||
ProgramCounterCycled
|
||||
|
||||
// Memory
|
||||
UnservicedChipWrite Errno = CategoryMemory + iota
|
||||
UnknownRegisterName
|
||||
UnreadableAddress
|
||||
|
||||
// TIA
|
||||
|
||||
// RIOT
|
||||
|
||||
// TV
|
||||
UnknownStateRequest
|
||||
UnknownCallbackRequest
|
||||
InvalidStateRequest
|
||||
|
||||
// Controller
|
||||
NoControllersFound Errno = CategoryController + iota
|
||||
)
|
||||
|
||||
var messages = map[Errno]string{
|
||||
// Debugger
|
||||
NoSymbolsFile: "no symbols file for %s",
|
||||
SymbolsFileError: "error processing symbols file (%s)",
|
||||
UnknownSymbol: "unrecognised symbol (%s)",
|
||||
|
||||
// VCS
|
||||
|
||||
// CPU
|
||||
UnimplementedInstruction: "unimplemented instruction (%0#x) at (%#04x)",
|
||||
NullInstruction: "unimplemented instruction (0xff)",
|
||||
ProgramCounterCycled: "program counter cycled back to 0x0000",
|
||||
|
||||
// Memory
|
||||
UnservicedChipWrite: "chip memory write signal has not been serviced since previous write (%s)",
|
||||
UnknownRegisterName: "can't find register name (%s) in list of read addreses in %s memory",
|
||||
UnreadableAddress: "memory location is not readable",
|
||||
|
||||
// TIA
|
||||
|
||||
// RIOT
|
||||
|
||||
// TV
|
||||
UnknownStateRequest: "TV does not support %v state",
|
||||
UnknownCallbackRequest: "TV does not support %v callback",
|
||||
InvalidStateRequest: "state request for %v is currently invalid",
|
||||
|
||||
// Controller
|
||||
NoControllersFound: "no controllers found",
|
||||
}
|
||||
|
||||
// Values is the type used to specify arguments for a GopherError
|
||||
type Values []interface{}
|
||||
|
||||
|
@ -89,32 +14,14 @@ type GopherError struct {
|
|||
Values Values
|
||||
}
|
||||
|
||||
// NewGopherError is used to create a Gopher2600 specific error
|
||||
func NewGopherError(errno Errno, values ...interface{}) GopherError {
|
||||
ge := new(GopherError)
|
||||
ge.Errno = errno
|
||||
ge.Values = values
|
||||
return *ge
|
||||
}
|
||||
|
||||
func (er GopherError) Error() string {
|
||||
return fmt.Sprintf(messages[er.Errno], er.Values...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
if er.Errno >= CategoryRIOT {
|
||||
return CategoryRIOT
|
||||
}
|
||||
if er.Errno >= CategoryTIA {
|
||||
return CategoryTIA
|
||||
}
|
||||
if er.Errno >= CategoryMemory {
|
||||
return CategoryMemory
|
||||
}
|
||||
if er.Errno >= CategoryCPU {
|
||||
return CategoryCPU
|
||||
}
|
||||
if er.Errno >= CategoryVCS {
|
||||
return CategoryVCS
|
||||
}
|
||||
return CategoryDebugger
|
||||
}
|
||||
|
|
41
errors/messages.go
Normal file
41
errors/messages.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package errors
|
||||
|
||||
var messages = map[Errno]string{
|
||||
// Debugger
|
||||
SymbolsFileCannotOpen: "no symbols file for %s",
|
||||
SymbolsFileError: "error processing symbols file (%s)",
|
||||
SymbolUnknown: "unrecognised symbol (%s)",
|
||||
ScriptFileCannotOpen: "cannot open script file (%s)",
|
||||
InvalidTarget: "invalid target (%s)",
|
||||
|
||||
// CPU
|
||||
UnimplementedInstruction: "unimplemented instruction (%0#x) at (%#04x)",
|
||||
NullInstruction: "unimplemented instruction (0xff)",
|
||||
ProgramCounterCycled: "program counter cycled back to 0x0000",
|
||||
|
||||
// Memory
|
||||
UnservicedChipWrite: "chip memory write signal has not been serviced since previous write (%s)",
|
||||
UnknownRegisterName: "can't find register name (%s) in list of read addreses in %s memory",
|
||||
UnreadableAddress: "memory location is not readable (%#04x)",
|
||||
UnwritableAddress: "memory location is not writable (%#04x)",
|
||||
UnrecognisedAddress: "address unrecognised (%v)",
|
||||
|
||||
// Cartridges
|
||||
CartridgeFileCannotOpen: "cannot open cartridge (%s)",
|
||||
CartridgeFileError: "error reading cartridge file (%s)",
|
||||
CartridgeInvalidSize: "cartridge size is not recognised (%d)",
|
||||
|
||||
// TV
|
||||
UnknownStateRequest: "TV does not support %v state",
|
||||
UnknownCallbackRequest: "TV does not support %v callback",
|
||||
InvalidStateRequest: "state request for %v is currently invalid",
|
||||
|
||||
// Peripherals
|
||||
NoControllersFound: "no controllers found",
|
||||
}
|
||||
|
||||
// more error strings -- these are strings that are used as arguments to error
|
||||
// string messages
|
||||
const (
|
||||
FileTruncated string = "file truncated"
|
||||
)
|
|
@ -37,7 +37,7 @@ func main() {
|
|||
case "DEBUG":
|
||||
dbg, err := debugger.NewDebugger()
|
||||
if err != nil {
|
||||
fmt.Printf("* error starting debugger (%s)\n", err)
|
||||
fmt.Printf("* error starting debugger: %s\n", err)
|
||||
os.Exit(10)
|
||||
}
|
||||
|
||||
|
@ -109,19 +109,19 @@ func fps(cartridgeFile string, justTheVCS bool) error {
|
|||
if justTheVCS {
|
||||
tv = new(television.DummyTV)
|
||||
if tv == nil {
|
||||
return fmt.Errorf("error creating television for fps profiler")
|
||||
return fmt.Errorf("error preparing television: %s", err)
|
||||
}
|
||||
} else {
|
||||
tv, err = sdltv.NewSDLTV("NTSC", sdltv.IdealScale)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating television for fps profiler")
|
||||
return fmt.Errorf("error preparing television: %s", err)
|
||||
}
|
||||
}
|
||||
tv.SetVisibility(true)
|
||||
|
||||
vcs, err := hardware.New(tv)
|
||||
vcs, err := hardware.NewVCS(tv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting fps profiler (%s)", err)
|
||||
return fmt.Errorf("error preparing VCS: %s", err)
|
||||
}
|
||||
|
||||
err = vcs.AttachCartridge(cartridgeFile)
|
||||
|
@ -160,13 +160,13 @@ func fps(cartridgeFile string, justTheVCS bool) error {
|
|||
func run(cartridgeFile string) error {
|
||||
tv, err := sdltv.NewSDLTV("NTSC", sdltv.IdealScale)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating television for fps profiler")
|
||||
return fmt.Errorf("error preparing television: %s", err)
|
||||
}
|
||||
tv.SetVisibility(true)
|
||||
|
||||
vcs, err := hardware.New(tv)
|
||||
vcs, err := hardware.NewVCS(tv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting fps profiler (%s)", err)
|
||||
return fmt.Errorf("error preparing VCS: %s", err)
|
||||
}
|
||||
|
||||
err = vcs.AttachCartridge(cartridgeFile)
|
||||
|
|
|
@ -43,40 +43,23 @@ type CPU struct {
|
|||
// side-effects. we use this in the disassembly package to make sure
|
||||
// we reach every part of the program
|
||||
NoSideEffects bool
|
||||
|
||||
// silently ignore addressing errors unless StrictAddressing is true
|
||||
StrictAddressing bool
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for the CPU structure
|
||||
func New(mem memory.CPUBus) (*CPU, error) {
|
||||
// NewCPU is the preferred method of initialisation for the CPU structure
|
||||
func NewCPU(mem memory.CPUBus) (*CPU, error) {
|
||||
var err error
|
||||
|
||||
mc := new(CPU)
|
||||
mc.mem = mem
|
||||
|
||||
mc.PC, err = register.New(0, 16, "PC", "PC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc.A, err = register.New(0, 8, "A", "A")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc.X, err = register.New(0, 8, "X", "X")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc.Y, err = register.New(0, 8, "Y", "Y")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc.SP, err = register.New(0, 8, "SP", "SP")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc.PC = register.NewRegister(0, 16, "PC", "PC")
|
||||
mc.A = register.NewRegister(0, 8, "A", "A")
|
||||
mc.X = register.NewRegister(0, 8, "X", "X")
|
||||
mc.Y = register.NewRegister(0, 8, "Y", "Y")
|
||||
mc.SP = register.NewRegister(0, 8, "SP", "SP")
|
||||
mc.Status = NewStatusRegister("Status", "SR")
|
||||
|
||||
mc.opCodes, err = definitions.GetInstructionDefinitions()
|
||||
|
@ -113,7 +96,7 @@ func (mc *CPU) IsExecuting() bool {
|
|||
func (mc *CPU) Reset() error {
|
||||
// sanity check
|
||||
if mc.IsExecuting() {
|
||||
return fmt.Errorf("can't reset CPU in the middle of an instruction")
|
||||
panic(fmt.Errorf("can't reset CPU in the middle of an instruction"))
|
||||
}
|
||||
|
||||
mc.PC.Load(0)
|
||||
|
@ -136,7 +119,7 @@ func (mc *CPU) Reset() error {
|
|||
func (mc *CPU) LoadPC(indirectAddress uint16) error {
|
||||
// sanity check
|
||||
if mc.IsExecuting() {
|
||||
return fmt.Errorf("can't alter program counter in the middle of an instruction")
|
||||
panic(fmt.Errorf("can't alter program counter in the middle of an instruction"))
|
||||
}
|
||||
|
||||
// because we call this LoadPC() outside of the CPU's ExecuteInstruction()
|
||||
|
@ -156,11 +139,41 @@ func (mc *CPU) LoadPC(indirectAddress uint16) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (mc *CPU) write8Bit(address uint16, value uint8) error {
|
||||
err := mc.mem.Write(address, value)
|
||||
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case errors.GopherError:
|
||||
// don't worry about unwritable addresses (unless strict addressing
|
||||
// is on)
|
||||
if mc.StrictAddressing || err.Errno != errors.UnwritableAddress {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *CPU) read8Bit(address uint16) (uint8, error) {
|
||||
val, err := mc.mem.Read(address)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
switch err := err.(type) {
|
||||
case errors.GopherError:
|
||||
// don't worry about unreadable addresses (unless strict addressing
|
||||
// is on)
|
||||
if mc.StrictAddressing || err.Errno != errors.UnreadableAddress {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
mc.endCycle()
|
||||
|
||||
return val, nil
|
||||
|
@ -192,7 +205,7 @@ func (mc *CPU) read8BitPC() (uint8, error) {
|
|||
}
|
||||
carry, _ := mc.PC.Add(1, false)
|
||||
if carry {
|
||||
return 0, errors.GopherError{Errno: errors.ProgramCounterCycled, Values: nil}
|
||||
return 0, errors.NewGopherError(errors.ProgramCounterCycled, nil)
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
|
@ -207,7 +220,7 @@ func (mc *CPU) read16BitPC() (uint16, error) {
|
|||
// the next instruction but I don't believe this has any side-effects
|
||||
carry, _ := mc.PC.Add(2, false)
|
||||
if carry {
|
||||
return 0, errors.GopherError{Errno: errors.ProgramCounterCycled, Values: nil}
|
||||
return 0, errors.NewGopherError(errors.ProgramCounterCycled, nil)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
|
@ -276,8 +289,7 @@ func (mc *CPU) branch(flag bool, address uint16, result *result.Instruction) err
|
|||
}
|
||||
|
||||
// ExecuteInstruction steps CPU forward one instruction, calling
|
||||
// cycleCallback() after every cycle. note that the CPU will panic if a CPU
|
||||
// method is called during a callback.
|
||||
// cycleCallback() after every cycle
|
||||
func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*result.Instruction, error) {
|
||||
// sanity check
|
||||
if mc.IsExecuting() {
|
||||
|
@ -314,9 +326,9 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
defn, found := mc.opCodes[operator]
|
||||
if !found {
|
||||
if operator == 0xff {
|
||||
return nil, errors.GopherError{Errno: errors.NullInstruction, Values: nil}
|
||||
return nil, errors.NewGopherError(errors.NullInstruction, nil)
|
||||
}
|
||||
return nil, errors.GopherError{Errno: errors.UnimplementedInstruction, Values: errors.Values{operator, mc.PC.ToUint16() - 1}}
|
||||
return nil, errors.NewGopherError(errors.UnimplementedInstruction, operator, mc.PC.ToUint16()-1)
|
||||
}
|
||||
result.Defn = defn
|
||||
|
||||
|
@ -439,10 +451,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
}
|
||||
|
||||
// using 8bit addition because we don't want a page-fault
|
||||
adder, err := register.NewAnonymous(mc.X, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(mc.X, 8)
|
||||
adder.Add(indirectAddress, false)
|
||||
|
||||
// +1 cycle
|
||||
|
@ -470,10 +479,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
return nil, err
|
||||
}
|
||||
|
||||
adder, err := register.NewAnonymous(mc.Y, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(mc.Y, 16)
|
||||
adder.Add(indexedAddress&0x00ff, false)
|
||||
address = adder.ToUint16()
|
||||
|
||||
|
@ -501,10 +507,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
return nil, err
|
||||
}
|
||||
|
||||
adder, err := register.NewAnonymous(mc.X, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(mc.X, 16)
|
||||
|
||||
// add index to LSB of address
|
||||
adder.Add(indirectAddress&0x00ff, false)
|
||||
|
@ -533,10 +536,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
return nil, err
|
||||
}
|
||||
|
||||
adder, err := register.NewAnonymous(mc.Y, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(mc.Y, 16)
|
||||
|
||||
// add index to LSB of address
|
||||
adder.Add(indirectAddress&0x00ff, false)
|
||||
|
@ -564,10 +564,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder, err := register.NewAnonymous(indirectAddress, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(indirectAddress, 8)
|
||||
adder.Add(mc.X, false)
|
||||
address = adder.ToUint16()
|
||||
result.InstructionData = indirectAddress
|
||||
|
@ -583,10 +580,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder, err := register.NewAnonymous(indirectAddress, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adder := register.NewAnonRegister(indirectAddress, 8)
|
||||
adder.Add(mc.Y, false)
|
||||
address = adder.ToUint16()
|
||||
result.InstructionData = indirectAddress
|
||||
|
@ -622,7 +616,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
// phantom write
|
||||
// +1 cycle
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(address, value)
|
||||
err = mc.write8Bit(address, value)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -660,7 +654,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
|
||||
case "PHA":
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(mc.SP.ToUint16(), mc.A.ToUint8())
|
||||
err = mc.write8Bit(mc.SP.ToUint16(), mc.A.ToUint8())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -680,7 +674,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
|
||||
case "PHP":
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(mc.SP.ToUint16(), mc.Status.ToUint8())
|
||||
err = mc.write8Bit(mc.SP.ToUint16(), mc.Status.ToUint8())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -759,7 +753,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
|
||||
case "STA":
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(address, mc.A.ToUint8())
|
||||
err = mc.write8Bit(address, mc.A.ToUint8())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -767,7 +761,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
|
||||
case "STX":
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(address, mc.X.ToUint8())
|
||||
err = mc.write8Bit(address, mc.X.ToUint8())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -775,7 +769,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
|
||||
case "STY":
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(address, mc.Y.ToUint8())
|
||||
err = mc.write8Bit(address, mc.Y.ToUint8())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -804,7 +798,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
case "ASL":
|
||||
var r *register.Register
|
||||
if defn.Effect == definitions.RMW {
|
||||
r, err = register.NewAnonymous(value, mc.A.Size())
|
||||
r = register.NewAnonRegister(value, mc.A.Size())
|
||||
} else {
|
||||
r = mc.A
|
||||
}
|
||||
|
@ -816,7 +810,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
case "LSR":
|
||||
var r *register.Register
|
||||
if defn.Effect == definitions.RMW {
|
||||
r, err = register.NewAnonymous(value, mc.A.Size())
|
||||
r = register.NewAnonRegister(value, mc.A.Size())
|
||||
} else {
|
||||
r = mc.A
|
||||
}
|
||||
|
@ -848,7 +842,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
case "ROR":
|
||||
var r *register.Register
|
||||
if defn.Effect == definitions.RMW {
|
||||
r, err = register.NewAnonymous(value, mc.A.Size())
|
||||
r = register.NewAnonRegister(value, mc.A.Size())
|
||||
} else {
|
||||
r = mc.A
|
||||
}
|
||||
|
@ -860,7 +854,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
case "ROL":
|
||||
var r *register.Register
|
||||
if defn.Effect == definitions.RMW {
|
||||
r, err = register.NewAnonymous(value, mc.A.Size())
|
||||
r = register.NewAnonRegister(value, mc.A.Size())
|
||||
} else {
|
||||
r = mc.A
|
||||
}
|
||||
|
@ -870,30 +864,21 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
value = r.ToUint8()
|
||||
|
||||
case "INC":
|
||||
r, err := register.NewAnonymous(value, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := register.NewAnonRegister(value, 8)
|
||||
r.Add(1, false)
|
||||
mc.Status.Zero = r.IsZero()
|
||||
mc.Status.Sign = r.IsNegative()
|
||||
value = r.ToUint8()
|
||||
|
||||
case "DEC":
|
||||
r, err := register.NewAnonymous(value, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := register.NewAnonRegister(value, 8)
|
||||
r.Add(255, false)
|
||||
mc.Status.Zero = r.IsZero()
|
||||
mc.Status.Sign = r.IsNegative()
|
||||
value = r.ToUint8()
|
||||
|
||||
case "CMP":
|
||||
cmp, err := register.NewAnonymous(mc.A, mc.A.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmp := register.NewAnonRegister(mc.A, mc.A.Size())
|
||||
|
||||
// maybe surprisingly, CMP can be implemented with binary subtract even
|
||||
// if decimal mode is active (the meaning is the same)
|
||||
|
@ -902,28 +887,19 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
mc.Status.Sign = cmp.IsNegative()
|
||||
|
||||
case "CPX":
|
||||
cmp, err := register.NewAnonymous(mc.X, mc.X.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmp := register.NewAnonRegister(mc.X, mc.X.Size())
|
||||
mc.Status.Carry, _ = cmp.Subtract(value, true)
|
||||
mc.Status.Zero = cmp.IsZero()
|
||||
mc.Status.Sign = cmp.IsNegative()
|
||||
|
||||
case "CPY":
|
||||
cmp, err := register.NewAnonymous(mc.Y, mc.Y.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmp := register.NewAnonRegister(mc.Y, mc.Y.Size())
|
||||
mc.Status.Carry, _ = cmp.Subtract(value, true)
|
||||
mc.Status.Zero = cmp.IsZero()
|
||||
mc.Status.Sign = cmp.IsNegative()
|
||||
|
||||
case "BIT":
|
||||
cmp, err := register.NewAnonymous(value, mc.A.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmp := register.NewAnonRegister(value, mc.A.Size())
|
||||
mc.Status.Sign = cmp.IsNegative()
|
||||
mc.Status.Overflow = cmp.IsBitV()
|
||||
cmp.AND(mc.A)
|
||||
|
@ -1000,7 +976,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
if !mc.NoSideEffects {
|
||||
// push MSB of PC onto stack, and decrement SP
|
||||
// +1 cycle
|
||||
err = mc.mem.Write(mc.SP.ToUint16(), uint8((mc.PC.ToUint16()&0xFF00)>>8))
|
||||
err = mc.write8Bit(mc.SP.ToUint16(), uint8((mc.PC.ToUint16()&0xFF00)>>8))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1011,7 +987,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
if !mc.NoSideEffects {
|
||||
// push LSB of PC onto stack, and decrement SP
|
||||
// +1 cycle
|
||||
err = mc.mem.Write(mc.SP.ToUint16(), uint8(mc.PC.ToUint16()&0x00FF))
|
||||
err = mc.write8Bit(mc.SP.ToUint16(), uint8(mc.PC.ToUint16()&0x00FF))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1083,7 +1059,7 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res
|
|||
// write altered value back to memory for RMW instructions
|
||||
if defn.Effect == definitions.RMW {
|
||||
if !mc.NoSideEffects {
|
||||
err = mc.mem.Write(address, value)
|
||||
err = mc.write8Bit(address, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
|
|
|
@ -465,7 +465,7 @@ func testDecimalMode(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|||
|
||||
func TestCPU(t *testing.T) {
|
||||
mem := NewMockMem()
|
||||
mc, err := cpu.New(mem)
|
||||
mc, err := cpu.NewCPU(mem)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ func NewMockVCSMem() (*MockVCSMem, error) {
|
|||
mem := new(MockVCSMem)
|
||||
|
||||
// use the memory.VCS implementation of memory.Bus
|
||||
mem.CPUBus, err = memory.New()
|
||||
mem.CPUBus, err = memory.NewVCSMemory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ func addDecimal(a, b uint8, carry bool) (r uint8, rcarry bool) {
|
|||
// representations. Returns new carry state
|
||||
func (r *Register) AddDecimal(v interface{}, carry bool) (rcarry bool) {
|
||||
if r.size != 8 {
|
||||
panic(fmt.Sprintf("decimal mode addition only supported for uint8 values with 8 bit registers"))
|
||||
panic(fmt.Errorf("decimal mode addition only supported for uint8 values with 8 bit registers"))
|
||||
}
|
||||
|
||||
val, ok := v.(uint8)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("decimal mode addition only supported for uint8 values with 8 bit registers"))
|
||||
panic(fmt.Errorf("decimal mode addition only supported for uint8 values with 8 bit registers"))
|
||||
}
|
||||
|
||||
runits := uint8(r.value) & 0x0f
|
||||
|
@ -56,12 +56,12 @@ func subtractDecimal(a, b int, carry bool) (r int, rcarry bool) {
|
|||
// representations. Returns new carry state
|
||||
func (r *Register) SubtractDecimal(v interface{}, carry bool) (rcarry bool) {
|
||||
if r.size != 8 {
|
||||
panic(fmt.Sprintf("decimal mode subtraction only supported for uint8 values with 8 bit registers"))
|
||||
panic(fmt.Errorf("decimal mode subtraction only supported for uint8 values with 8 bit registers"))
|
||||
}
|
||||
|
||||
val, ok := v.(uint8)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("decimal mode subtraction only supported for uint8 values with 8 bit registers"))
|
||||
panic(fmt.Errorf("decimal mode subtraction only supported for uint8 values with 8 bit registers"))
|
||||
}
|
||||
|
||||
runits := int(r.value) & 0x0f
|
||||
|
|
|
@ -20,22 +20,19 @@ type Register struct {
|
|||
binformat string
|
||||
}
|
||||
|
||||
// NewAnonymous initialises a new register without a name
|
||||
func NewAnonymous(value interface{}, size int) (*Register, error) {
|
||||
return New(value, size, "", "")
|
||||
// NewAnonRegister initialises a new register without a name
|
||||
func NewAnonRegister(value interface{}, size int) *Register {
|
||||
return NewRegister(value, size, "", "")
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for Register
|
||||
func New(value interface{}, size int, label string, shortLabel string) (*Register, error) {
|
||||
// NewRegister creates a new register of a givel size and name, and initialises
|
||||
// the value
|
||||
func NewRegister(value interface{}, size int, label string, shortLabel string) *Register {
|
||||
if size != 8 && size != 16 {
|
||||
return nil, fmt.Errorf("can't create register (%s) - unsupported bit size (%d)", label, size)
|
||||
panic(fmt.Errorf("cannot create register (%s) - unsupported bit size (%d)", label, size))
|
||||
}
|
||||
|
||||
r := new(Register)
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for CPU register (%s)", label)
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case *Register:
|
||||
r.value = value.value
|
||||
|
@ -48,7 +45,7 @@ func New(value interface{}, size int, label string, shortLabel string) (*Registe
|
|||
case uint16:
|
||||
r.value = uint32(value)
|
||||
default:
|
||||
return nil, fmt.Errorf("can't create register (%s) - unsupported value type (%s)", label, reflect.TypeOf(value))
|
||||
panic(fmt.Errorf("cannot create register (%s) - unsupported value type (%s)", label, reflect.TypeOf(value)))
|
||||
}
|
||||
|
||||
r.size = size
|
||||
|
@ -69,7 +66,7 @@ func New(value interface{}, size int, label string, shortLabel string) (*Registe
|
|||
r.binformat = "%016b"
|
||||
}
|
||||
|
||||
return r, nil
|
||||
return r
|
||||
}
|
||||
|
||||
// Size returns the number of bits in register
|
||||
|
@ -100,7 +97,7 @@ func (r Register) IsBitV() bool {
|
|||
func (r Register) FromInt(v interface{}) string {
|
||||
switch v.(type) {
|
||||
case int:
|
||||
tr, _ := New(v, r.size, r.label, r.shortLabel)
|
||||
tr := NewRegister(v, r.size, r.label, r.shortLabel)
|
||||
return fmt.Sprintf("%s=%s", tr.shortLabel, tr.ToHex())
|
||||
default:
|
||||
return r.shortLabel
|
||||
|
@ -174,7 +171,7 @@ func (r *Register) Load(v interface{}) {
|
|||
case uint16:
|
||||
r.value = uint32(v) & r.mask
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,7 +208,7 @@ func (r *Register) Add(v interface{}, carry bool) (bool, bool) {
|
|||
}
|
||||
postNeg = uint32(v)&r.signBit == r.signBit
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
|
||||
carry = ^r.mask&r.value != 0
|
||||
|
@ -236,7 +233,7 @@ func (r *Register) Subtract(v interface{}, carry bool) (bool, bool) {
|
|||
case uint8:
|
||||
val = int(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
|
||||
// no need to do anything if operand is zero
|
||||
|
@ -261,7 +258,7 @@ func (r *Register) AND(v interface{}) {
|
|||
case uint8:
|
||||
r.value &= uint32(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
r.value &= r.mask
|
||||
}
|
||||
|
@ -286,7 +283,7 @@ func (r *Register) EOR(v interface{}) {
|
|||
case uint8:
|
||||
r.value ^= uint32(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
r.value &= r.mask
|
||||
}
|
||||
|
@ -311,7 +308,7 @@ func (r *Register) ORA(v interface{}) {
|
|||
case uint8:
|
||||
r.value |= uint32(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
panic(fmt.Errorf("unsupported value type (%s)", reflect.TypeOf(v)))
|
||||
}
|
||||
r.value &= r.mask
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@ func r16(t *testing.T) {
|
|||
var carry, overflow bool
|
||||
|
||||
// initialisation
|
||||
r16, err := register.New(0, 16, "TEST", "TST")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r16 := register.NewRegister(0, 16, "TEST", "TST")
|
||||
assert.CheckValueVCS(t, r16.IsZero(), true)
|
||||
assert.CheckValueVCS(t, r16, 0)
|
||||
|
||||
|
@ -37,10 +34,7 @@ func r16(t *testing.T) {
|
|||
assert.CheckValueVCS(t, r16.IsZero(), true)
|
||||
|
||||
// register operand
|
||||
r16b, err := register.New(10, 16, "TEST B", "TSTB")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r16b := register.NewRegister(10, 16, "TEST B", "TSTB")
|
||||
assert.CheckValueVCS(t, r16b, 10)
|
||||
r16.Add(r16b, true)
|
||||
assert.CheckValueVCS(t, r16, 11)
|
||||
|
@ -89,10 +83,7 @@ func r8(t *testing.T) {
|
|||
var carry, overflow bool
|
||||
|
||||
// initialisation
|
||||
r8, err := register.New(0, 8, "TEST", "TST")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r8 := register.NewRegister(0, 8, "TEST", "TST")
|
||||
assert.CheckValueVCS(t, r8.IsZero(), true)
|
||||
assert.CheckValueVCS(t, r8, 0)
|
||||
|
||||
|
@ -111,10 +102,7 @@ func r8(t *testing.T) {
|
|||
assert.CheckValueVCS(t, r8.IsZero(), true)
|
||||
|
||||
// register operand
|
||||
r8b, err := register.New(10, 8, "TEST B", "TSTB")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r8b := register.NewRegister(10, 8, "TEST B", "TSTB")
|
||||
assert.CheckValueVCS(t, r8b, 10)
|
||||
r8.Add(r8b, true)
|
||||
assert.CheckValueVCS(t, r8, 11)
|
||||
|
|
|
@ -85,7 +85,7 @@ func (result Instruction) GetString(symtable *symbols.Table, style Style) string
|
|||
case 1:
|
||||
hex = fmt.Sprintf("%02x %s", result.Defn.ObjectCode, hex)
|
||||
default:
|
||||
panic("unsupported number of bytes in instruction")
|
||||
hex = fmt.Sprintf("(%d bytes) %s", result.Defn.Bytes, hex)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (result Instruction) GetString(symtable *symbols.Table, style Style) string
|
|||
|
||||
// -- we create a mock register with the instruction's
|
||||
// address as the initial value
|
||||
pc, _ := register.NewAnonymous(result.Address, 16)
|
||||
pc := register.NewAnonRegister(result.Address, 16)
|
||||
|
||||
// -- add the number of instruction bytes to get the PC as
|
||||
// it would be at the end of the instruction
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopher2600/errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
@ -28,9 +28,6 @@ type Cartridge struct {
|
|||
// newCart is the preferred method of initialisation for the cartridges
|
||||
func newCart() *Cartridge {
|
||||
cart := new(Cartridge)
|
||||
if cart == nil {
|
||||
return nil
|
||||
}
|
||||
cart.label = "Cartridge"
|
||||
cart.origin = 0x1000
|
||||
cart.memtop = 0x1fff
|
||||
|
@ -70,14 +67,14 @@ func (cart Cartridge) Read(address uint16) (uint8, error) {
|
|||
|
||||
// Implementation of CPUBus.Write
|
||||
func (cart *Cartridge) Write(address uint16, data uint8) error {
|
||||
return fmt.Errorf("refusing to write to cartridge")
|
||||
return errors.NewGopherError(errors.UnwritableAddress, address)
|
||||
}
|
||||
|
||||
// Attach loads the bytes from a cartridge (represented by 'filename')
|
||||
func (cart *Cartridge) Attach(filename string) error {
|
||||
cf, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening cartridge (%s)", err)
|
||||
return errors.NewGopherError(errors.CartridgeFileCannotOpen, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = cf.Close()
|
||||
|
@ -92,7 +89,7 @@ func (cart *Cartridge) Attach(filename string) error {
|
|||
// check that cartridge is of a supported size
|
||||
// TODO: ensure that this is a complete and accurate check
|
||||
if cfi.Size()%bankSize != 0 {
|
||||
return fmt.Errorf("cartridge (%s) is not of a supported size (%d)", filename, cfi.Size())
|
||||
return errors.NewGopherError(errors.CartridgeFileCannotOpen, cfi.Size())
|
||||
}
|
||||
|
||||
// allocate enough memory for new cartridge
|
||||
|
@ -104,7 +101,7 @@ func (cart *Cartridge) Attach(filename string) error {
|
|||
return err
|
||||
}
|
||||
if n != len(cart.memory) {
|
||||
return fmt.Errorf("error reading cartridge file (%s)", filename)
|
||||
return errors.NewGopherError(errors.CartridgeFileError, errors.FileTruncated)
|
||||
}
|
||||
|
||||
// make sure we're pointing to the first bank
|
||||
|
|
|
@ -33,8 +33,8 @@ type ChipMemory struct {
|
|||
lastReadRegister string
|
||||
|
||||
// the periphQueue is used to write to chip memory in a goroutine friendly
|
||||
// manner. peripherals can be implemented with goroutines and so we need to
|
||||
// be careful when accessing memory.
|
||||
// manner (peripherals have been implemented with goroutines and so we need
|
||||
// to be careful when accessing the memory array)
|
||||
periphQueue chan *periphPayload
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ func (area ChipMemory) Memtop() uint16 {
|
|||
func (area ChipMemory) Peek(address uint16) (uint8, uint16, string, string, error) {
|
||||
sym := vcssymbols.ReadSymbols[address&area.readMask]
|
||||
if sym == "" {
|
||||
return 0, 0, "", "", errors.GopherError{Errno: errors.UnreadableAddress, Values: nil}
|
||||
return 0, 0, "", "", errors.NewGopherError(errors.UnreadableAddress, address)
|
||||
}
|
||||
return area.memory[address-area.origin], address & area.readMask, area.Label(), sym, nil
|
||||
}
|
||||
|
|
|
@ -17,14 +17,14 @@ func (area *ChipMemory) ChipRead() (bool, string, uint8) {
|
|||
return false, "", 0
|
||||
}
|
||||
|
||||
// ChipWrite writes the data to the memory area's address specified by
|
||||
// registerName
|
||||
// ChipWrite is an implementation of ChipBus.ChipWrite. it writes the data to
|
||||
// the memory area's address specified by registerName
|
||||
func (area *ChipMemory) ChipWrite(address uint16, data uint8) {
|
||||
area.memory[address] = data
|
||||
}
|
||||
|
||||
// LastReadRegister returns the register name of the last memory
|
||||
// location *read* by the CPU
|
||||
// LastReadRegister is an implementation of ChipBus.LastReadRegister. it
|
||||
// returns the register name of the last memory location *read* by the CPU
|
||||
func (area ChipMemory) LastReadRegister() string {
|
||||
return area.lastReadRegister
|
||||
}
|
||||
|
|
|
@ -16,12 +16,7 @@ func (area *ChipMemory) Read(address uint16) (uint8, error) {
|
|||
|
||||
sym := vcssymbols.ReadSymbols[address]
|
||||
if sym == "" {
|
||||
// silently ignore illegal reads (we're definitely reading from the correct
|
||||
// memory space but some registers are not readable)
|
||||
//
|
||||
// TODO: add a GopherError that can be ignored or noted as appropriate
|
||||
// for the application
|
||||
return 0, nil
|
||||
return 0, errors.NewGopherError(errors.UnreadableAddress, address)
|
||||
}
|
||||
|
||||
return area.memory[address-area.origin], nil
|
||||
|
@ -33,14 +28,12 @@ func (area *ChipMemory) Write(address uint16, data uint8) error {
|
|||
|
||||
// check that the last write to this memory area has been serviced
|
||||
if area.writeSignal {
|
||||
return errors.GopherError{Errno: errors.UnservicedChipWrite, Values: errors.Values{vcssymbols.WriteSymbols[area.lastWriteAddress]}}
|
||||
return errors.NewGopherError(errors.UnservicedChipWrite, vcssymbols.WriteSymbols[area.lastWriteAddress])
|
||||
}
|
||||
|
||||
sym := vcssymbols.WriteSymbols[address]
|
||||
if sym == "" {
|
||||
// silently ignore illegal writes (we're definitely writing to the correct
|
||||
// memory space but some registers are not writable)
|
||||
return nil
|
||||
return errors.NewGopherError(errors.UnwritableAddress, address)
|
||||
}
|
||||
|
||||
// note address of write
|
||||
|
|
|
@ -3,9 +3,6 @@ package memory
|
|||
// newRIOT is the preferred method of initialisation for the RIOT memory area
|
||||
func newRIOT() *ChipMemory {
|
||||
area := newChipMem()
|
||||
if area == nil {
|
||||
return nil
|
||||
}
|
||||
area.label = "RIOT"
|
||||
area.origin = 0x0280
|
||||
area.memtop = 0x0287
|
||||
|
|
|
@ -3,14 +3,10 @@ package memory
|
|||
// newTIA is the preferred method of initialisation for the TIA memory area
|
||||
func newTIA() *ChipMemory {
|
||||
area := newChipMem()
|
||||
if area == nil {
|
||||
return nil
|
||||
}
|
||||
area.label = "TIA"
|
||||
area.origin = 0x0000
|
||||
area.memtop = 0x003f
|
||||
area.memory = make([]uint8, area.memtop-area.origin+1)
|
||||
area.readMask = 0x000f
|
||||
|
||||
return area
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ const (
|
|||
)
|
||||
|
||||
func TestMemory(t *testing.T) {
|
||||
mem, err := memory.New()
|
||||
mem, err := memory.NewVCSMemory()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
|
|
@ -17,9 +17,6 @@ type PIA struct {
|
|||
// newPIA is the preferred method of initialisation for the PIA pia memory area
|
||||
func newPIA() *PIA {
|
||||
pia := new(PIA)
|
||||
if pia == nil {
|
||||
return nil
|
||||
}
|
||||
pia.label = "PIA RAM"
|
||||
pia.origin = 0x0080
|
||||
pia.memtop = 0x00ff
|
||||
|
|
|
@ -2,6 +2,7 @@ package memory
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"gopher2600/errors"
|
||||
"gopher2600/hardware/memory/vcssymbols"
|
||||
)
|
||||
|
||||
|
@ -24,43 +25,33 @@ type VCSMemory struct {
|
|||
|
||||
// TODO: allow reading only when 02 clock is high and writing when it is low
|
||||
|
||||
// New is the preferred method of initialisation for VCSMemory
|
||||
func New() (*VCSMemory, error) {
|
||||
// NewVCSMemory is the preferred method of initialisation for VCSMemory
|
||||
func NewVCSMemory() (*VCSMemory, error) {
|
||||
mem := new(VCSMemory)
|
||||
if mem == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for VCS")
|
||||
}
|
||||
|
||||
mem.memmap = make(map[uint16]Area)
|
||||
|
||||
mem.RIOT = newRIOT()
|
||||
mem.TIA = newTIA()
|
||||
mem.PIA = newPIA()
|
||||
mem.Cart = newCart()
|
||||
|
||||
if mem.memmap == nil || mem.RIOT == nil || mem.TIA == nil || mem.PIA == nil || mem.Cart == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for VCS")
|
||||
if mem.RIOT == nil || mem.TIA == nil || mem.PIA == nil || mem.Cart == nil {
|
||||
return nil, fmt.Errorf("error creating memory areas")
|
||||
}
|
||||
|
||||
// create the memory map; each address in the memory map points to the
|
||||
// memory area it resides in. we only record 'primary' addresses; all
|
||||
// addresses should be passed through the MapAddress() function in order
|
||||
// to iron out any mirrors
|
||||
|
||||
var i uint16
|
||||
|
||||
for i = mem.TIA.origin; i <= mem.TIA.memtop; i++ {
|
||||
for i := mem.TIA.origin; i <= mem.TIA.memtop; i++ {
|
||||
mem.memmap[i] = mem.TIA
|
||||
}
|
||||
|
||||
for i = mem.PIA.origin; i <= mem.PIA.memtop; i++ {
|
||||
for i := mem.PIA.origin; i <= mem.PIA.memtop; i++ {
|
||||
mem.memmap[i] = mem.PIA
|
||||
}
|
||||
|
||||
for i = mem.RIOT.origin; i <= mem.RIOT.memtop; i++ {
|
||||
for i := mem.RIOT.origin; i <= mem.RIOT.memtop; i++ {
|
||||
mem.memmap[i] = mem.RIOT
|
||||
}
|
||||
|
||||
for i = mem.Cart.origin; i <= mem.Cart.memtop; i++ {
|
||||
for i := mem.Cart.origin; i <= mem.Cart.memtop; i++ {
|
||||
mem.memmap[i] = mem.Cart
|
||||
}
|
||||
|
||||
|
@ -102,7 +93,7 @@ func (mem VCSMemory) Read(address uint16) (uint8, error) {
|
|||
ma := mem.MapAddress(address)
|
||||
area, present := mem.memmap[ma]
|
||||
if !present {
|
||||
return 0, fmt.Errorf("%04x not mapped correctly", address)
|
||||
panic(fmt.Errorf("%04x not mapped correctly", address))
|
||||
}
|
||||
return area.(CPUBus).Read(ma)
|
||||
}
|
||||
|
@ -138,12 +129,12 @@ func (mem VCSMemory) Peek(address interface{}) (uint8, uint16, string, string, e
|
|||
}
|
||||
}
|
||||
default:
|
||||
return 0, 0, "", "", fmt.Errorf("unrecognised address (%v)", address)
|
||||
return 0, 0, "", "", errors.NewGopherError(errors.UnrecognisedAddress, address)
|
||||
}
|
||||
|
||||
area, present := mem.memmap[ma]
|
||||
if !present {
|
||||
return 0, 0, area.Label(), "", fmt.Errorf("%04x not mapped correctly", address)
|
||||
panic(fmt.Errorf("%04x not mapped correctly", address))
|
||||
}
|
||||
|
||||
return area.(Area).Peek(ma)
|
||||
|
|
|
@ -33,7 +33,7 @@ func NewStick(tia memory.PeriphBus, riot memory.PeriphBus, panel *Panel) *Stick
|
|||
// system assigned index: typically increments on each new controller added.
|
||||
stick.device = joysticks.Connect(1)
|
||||
if stick.device == nil {
|
||||
stick.err = errors.GopherError{Errno: errors.NoControllersFound, Values: nil}
|
||||
stick.err = errors.NewGopherError(errors.NoControllersFound, nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -33,16 +33,11 @@ type RIOT struct {
|
|||
timerCycles int
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for the PIA structure
|
||||
func New(mem memory.ChipBus) *RIOT {
|
||||
// NewRIOT creates a RIOT, to be used in a VCS emulation
|
||||
func NewRIOT(mem memory.ChipBus) *RIOT {
|
||||
riot := new(RIOT)
|
||||
if riot == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
riot.timerRegister = "no timer"
|
||||
riot.mem = mem
|
||||
|
||||
return riot
|
||||
}
|
||||
|
||||
|
@ -87,7 +82,7 @@ func (riot *RIOT) ReadRIOTMemory() {
|
|||
riot.timerINTIMvalue = value
|
||||
riot.timerCycles = 2
|
||||
default:
|
||||
fmt.Printf("unserviced RIOT register", register)
|
||||
fmt.Printf("unserviced RIOT register (%v)", register)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,7 @@ type ColorClock struct {
|
|||
// New is the preferred method of initialisation for the ColorClock
|
||||
func New() *ColorClock {
|
||||
cc := new(ColorClock)
|
||||
if cc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cc.SetResetPattern("010100") // count==56,
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func init() {
|
|||
table6bits[i] = fmt.Sprintf("%06b", p)
|
||||
}
|
||||
if table6bits[63] != "000000" {
|
||||
panic("error during 6 bit polycounter generation")
|
||||
panic(fmt.Errorf("error during 6 bit polycounter generation"))
|
||||
}
|
||||
|
||||
// force the final value to be the invalid polycounter value. this is only
|
||||
|
@ -32,13 +32,13 @@ func init() {
|
|||
}
|
||||
|
||||
// LookupPattern returns the index of the specified pattern
|
||||
func LookupPattern(pattern string) (int, error) {
|
||||
func LookupPattern(pattern string) int {
|
||||
for i := 0; i < len(table6bits); i++ {
|
||||
if table6bits[i] == pattern {
|
||||
return i, nil
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("could not find pattern (%s) in 6 bit lookup table", pattern)
|
||||
panic(fmt.Errorf("could not find pattern (%s) in 6 bit lookup table", pattern))
|
||||
}
|
||||
|
||||
// Polycounter implements the VCS method of counting. It is doesn't require
|
||||
|
@ -59,10 +59,7 @@ type Polycounter struct {
|
|||
// resets during a Tick(). this should be called at least once or the reset
|
||||
// pattern will be "000000" which is probably not what you want
|
||||
func (pk *Polycounter) SetResetPattern(resetPattern string) {
|
||||
i, err := LookupPattern(resetPattern)
|
||||
if err != nil {
|
||||
panic("couldn't find reset pattern in polycounter table")
|
||||
}
|
||||
i := LookupPattern(resetPattern)
|
||||
pk.ResetPoint = i
|
||||
}
|
||||
|
||||
|
|
|
@ -50,13 +50,9 @@ func (tia TIA) String() string {
|
|||
return tia.MachineInfo()
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for the TIA structure
|
||||
func New(tv television.Television, mem memory.ChipBus) *TIA {
|
||||
// NewTIA creates a TIA, to be used in a VCS emulation
|
||||
func NewTIA(tv television.Television, mem memory.ChipBus) *TIA {
|
||||
tia := new(TIA)
|
||||
if tia == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tia.tv = tv
|
||||
tia.mem = mem
|
||||
|
||||
|
@ -79,7 +75,7 @@ func New(tv television.Television, mem memory.ChipBus) *TIA {
|
|||
|
||||
tia.hblank = true
|
||||
|
||||
tia.Video = video.New(tia.colorClock)
|
||||
tia.Video = video.NewVideo(tia.colorClock)
|
||||
if tia.Video == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,9 +15,6 @@ type future struct {
|
|||
// newFuture is the preferred method of initialisation for the pending type
|
||||
func newFuture() *future {
|
||||
dc := new(future)
|
||||
if dc == nil {
|
||||
return nil
|
||||
}
|
||||
dc.remainingCycles = -1
|
||||
dc.payload = true
|
||||
return dc
|
||||
|
|
|
@ -44,10 +44,6 @@ type sprite struct {
|
|||
|
||||
func newSprite(label string, colorClock *colorclock.ColorClock) *sprite {
|
||||
sp := new(sprite)
|
||||
if sp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sp.label = label
|
||||
sp.colorClock = colorClock
|
||||
|
||||
|
|
|
@ -22,13 +22,9 @@ type Video struct {
|
|||
Collisions collisions
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for the Video structure
|
||||
func New(colorClock *colorclock.ColorClock) *Video {
|
||||
// NewVideo is the preferred method of initialisation for the Video structure
|
||||
func NewVideo(colorClock *colorclock.ColorClock) *Video {
|
||||
vd := new(Video)
|
||||
if vd == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
vd.colorClock = colorClock
|
||||
|
||||
// playfield
|
||||
|
|
|
@ -32,31 +32,32 @@ type VCS struct {
|
|||
controller *peripherals.Stick
|
||||
}
|
||||
|
||||
// New is the preferred method of initialisation for the VCS structure
|
||||
func New(tv television.Television) (*VCS, error) {
|
||||
// NewVCS creates a new VCS and everything associated with the hardware. It is
|
||||
// used for all aspects of emulation: debugging sessions, and regular play
|
||||
func NewVCS(tv television.Television) (*VCS, error) {
|
||||
var err error
|
||||
|
||||
vcs := new(VCS)
|
||||
vcs.TV = tv
|
||||
|
||||
vcs.Mem, err = memory.New()
|
||||
vcs.Mem, err = memory.NewVCSMemory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vcs.MC, err = cpu.New(vcs.Mem)
|
||||
vcs.MC, err = cpu.NewCPU(vcs.Mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vcs.TIA = tia.New(vcs.TV, vcs.Mem.TIA)
|
||||
vcs.TIA = tia.NewTIA(vcs.TV, vcs.Mem.TIA)
|
||||
if vcs.TIA == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for VCS TIA")
|
||||
return nil, fmt.Errorf("can't create TIA")
|
||||
}
|
||||
|
||||
vcs.RIOT = riot.New(vcs.Mem.RIOT)
|
||||
vcs.RIOT = riot.NewRIOT(vcs.Mem.RIOT)
|
||||
if vcs.RIOT == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for VCS RIOT")
|
||||
return nil, fmt.Errorf("can't create RIOT")
|
||||
}
|
||||
|
||||
vcs.panel = peripherals.NewPanel(vcs.Mem.RIOT)
|
||||
|
@ -66,8 +67,8 @@ func New(tv television.Television) (*VCS, error) {
|
|||
|
||||
// TODO: better contoller support
|
||||
vcs.controller = peripherals.NewStick(vcs.Mem.TIA, vcs.Mem.RIOT, vcs.panel)
|
||||
if vcs.panel == nil {
|
||||
return nil, fmt.Errorf("can't create new stick controller")
|
||||
if vcs.controller == nil {
|
||||
return nil, fmt.Errorf("can't create stick controller")
|
||||
}
|
||||
|
||||
return vcs, nil
|
||||
|
|
|
@ -11,5 +11,5 @@ func (sym *Table) SearchLocation(location string) (uint16, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return 0, errors.GopherError{Errno: errors.UnknownSymbol, Values: errors.Values{location}}
|
||||
return 0, errors.NewGopherError(errors.SymbolUnknown, location)
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ func ReadSymbolsFile(cartridgeFilename string) (*Table, error) {
|
|||
if cartridgeFilename == "" {
|
||||
return table, nil
|
||||
}
|
||||
return table, errors.GopherError{Errno: errors.NoSymbolsFile, Values: errors.Values{cartridgeFilename}}
|
||||
return table, errors.NewGopherError(errors.SymbolsFileCannotOpen, cartridgeFilename)
|
||||
}
|
||||
defer func() {
|
||||
_ = sf.Close()
|
||||
|
@ -66,17 +66,17 @@ func ReadSymbolsFile(cartridgeFilename string) (*Table, error) {
|
|||
// get file info
|
||||
sfi, err := sf.Stat()
|
||||
if err != nil {
|
||||
return table, errors.GopherError{Errno: errors.SymbolsFileError, Values: errors.Values{err}}
|
||||
return table, errors.NewGopherError(errors.SymbolsFileError, err)
|
||||
}
|
||||
|
||||
// read symbols file and split into lines
|
||||
sym := make([]byte, sfi.Size())
|
||||
n, err := sf.Read(sym)
|
||||
if err != nil {
|
||||
return table, errors.GopherError{Errno: errors.SymbolsFileError, Values: errors.Values{err}}
|
||||
return table, errors.NewGopherError(errors.SymbolsFileError, err)
|
||||
}
|
||||
if n != len(sym) {
|
||||
return table, errors.GopherError{Errno: errors.SymbolsFileError, Values: errors.Values{"file truncated"}}
|
||||
return table, errors.NewGopherError(errors.SymbolsFileError, errors.FileTruncated)
|
||||
}
|
||||
lines := strings.Split(string(sym), "\n")
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@ const (
|
|||
)
|
||||
|
||||
// HeadlessTV is the minimalist implementation of the Television interface - a
|
||||
// television without a screen. fuller implementations of the television can
|
||||
// use this as the basis of the emulation
|
||||
// television without a screen. Fuller implementations of the television can
|
||||
// use this as the basis of the emulation by struct embedding. The
|
||||
// InitHeadlessTV() method is useful in this regard.
|
||||
type HeadlessTV struct {
|
||||
// spec is the specification of the tv type (NTSC or PAL)
|
||||
Spec *specification
|
||||
|
@ -52,12 +53,10 @@ type HeadlessTV struct {
|
|||
forceUpdate func() error
|
||||
}
|
||||
|
||||
// NewHeadlessTV is the preferred method for initalising a headless TV
|
||||
// NewHeadlessTV creates a new instance of HeadlessTV for a minimalist
|
||||
// implementation of a televsion for the VCS emulation
|
||||
func NewHeadlessTV(tvType string) (*HeadlessTV, error) {
|
||||
tv := new(HeadlessTV)
|
||||
if tv == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for headless tv")
|
||||
}
|
||||
|
||||
err := InitHeadlessTV(tv, tvType)
|
||||
if err != nil {
|
||||
|
@ -228,7 +227,7 @@ func (tv *HeadlessTV) SetPause(pause bool) error {
|
|||
func (tv *HeadlessTV) RequestTVState(request TVStateReq) (*TVState, error) {
|
||||
switch request {
|
||||
default:
|
||||
return nil, errors.GopherError{Errno: errors.UnknownStateRequest, Values: errors.Values{request}}
|
||||
return nil, errors.NewGopherError(errors.UnknownStateRequest, request)
|
||||
case ReqFramenum:
|
||||
return tv.frameNum, nil
|
||||
case ReqScanline:
|
||||
|
@ -240,5 +239,5 @@ func (tv *HeadlessTV) RequestTVState(request TVStateReq) (*TVState, error) {
|
|||
|
||||
// RegisterCallback (with dummyTV reciever) is the null implementation
|
||||
func (tv *HeadlessTV) RegisterCallback(request CallbackReq, callback func()) error {
|
||||
return errors.GopherError{Errno: errors.UnknownCallbackRequest, Values: errors.Values{request}}
|
||||
return errors.NewGopherError(errors.UnknownCallbackRequest, request)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package sdltv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopher2600/television"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -54,14 +53,11 @@ type SDLTV struct {
|
|||
guiLoopLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewSDLTV is the preferred method for initalising an SDL TV
|
||||
// NewSDLTV initiliases a new instance of an SDL based display for the VCS
|
||||
func NewSDLTV(tvType string, scale float32) (*SDLTV, error) {
|
||||
var err error
|
||||
|
||||
tv := new(SDLTV)
|
||||
if tv == nil {
|
||||
return nil, fmt.Errorf("can't allocate memory for sdl tv")
|
||||
}
|
||||
|
||||
err = television.InitHeadlessTV(&tv.HeadlessTV, tvType)
|
||||
if err != nil {
|
||||
|
@ -79,7 +75,8 @@ func NewSDLTV(tvType string, scale float32) (*SDLTV, error) {
|
|||
// for the renderer
|
||||
tv.pixelWidth = 2
|
||||
|
||||
// pixel scale
|
||||
// pixel scale is the number of pixels each VCS "pixel" is to be occupy on
|
||||
// the screen
|
||||
tv.pixelScale = scale
|
||||
|
||||
// SDL initialisation
|
||||
|
@ -124,13 +121,8 @@ func NewSDLTV(tvType string, scale float32) (*SDLTV, error) {
|
|||
|
||||
// register callbacks from HeadlessTV to SDLTV
|
||||
tv.NewFrame = func() error {
|
||||
err := tv.update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tv.scr.swapBuffer()
|
||||
|
||||
return nil
|
||||
defer tv.scr.swapBuffer()
|
||||
return tv.update()
|
||||
}
|
||||
|
||||
// update tv with a black image
|
||||
|
|
|
@ -85,7 +85,7 @@ func (tv *SDLTV) RegisterCallback(request television.CallbackReq, callback func(
|
|||
tv.onWindowClose = callback
|
||||
tv.guiLoopLock.Unlock()
|
||||
default:
|
||||
return errors.GopherError{Errno: errors.UnknownCallbackRequest, Values: errors.Values{request}}
|
||||
return errors.NewGopherError(errors.UnknownCallbackRequest, request)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -20,9 +20,6 @@ var specPAL *specification
|
|||
|
||||
func init() {
|
||||
specNTSC = new(specification)
|
||||
if specNTSC == nil {
|
||||
panic("error during initialisation of NTSC specification")
|
||||
}
|
||||
specNTSC.ClocksPerHblank = 68
|
||||
specNTSC.ClocksPerVisible = 160
|
||||
specNTSC.ClocksPerScanline = 228
|
||||
|
@ -34,9 +31,6 @@ func init() {
|
|||
specNTSC.Colors = ntscColors
|
||||
|
||||
specPAL = new(specification)
|
||||
if specPAL == nil {
|
||||
panic("error during initialisation of PAL specification")
|
||||
}
|
||||
specPAL.ClocksPerHblank = 68
|
||||
specPAL.ClocksPerVisible = 160
|
||||
specPAL.ClocksPerScanline = 228
|
||||
|
|
|
@ -61,10 +61,10 @@ func (DummyTV) SetPause(pause bool) error {
|
|||
|
||||
// RequestTVState (with dummyTV reciever) is the null implementation
|
||||
func (DummyTV) RequestTVState(request TVStateReq) (*TVState, error) {
|
||||
return nil, errors.GopherError{Errno: errors.UnknownStateRequest, Values: errors.Values{request}}
|
||||
return nil, errors.NewGopherError(errors.UnknownStateRequest, request)
|
||||
}
|
||||
|
||||
// RegisterCallback (with dummyTV reciever) is the null implementation
|
||||
func (DummyTV) RegisterCallback(request CallbackReq, callback func()) error {
|
||||
return errors.GopherError{Errno: errors.UnknownCallbackRequest, Values: errors.Values{request}}
|
||||
return errors.NewGopherError(errors.UnknownCallbackRequest, request)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue