- refined error package
This commit is contained in:
steve 2018-08-04 13:27:59 +01:00
parent ec173372c9
commit 35e31cccb7
40 changed files with 304 additions and 411 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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 {

View file

@ -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
View 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
)

View file

@ -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
View 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"
)

View file

@ -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)

View file

@ -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

View file

@ -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())
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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())
}

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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")

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)
}