diff --git a/debugger/breakpoints.go b/debugger/breakpoints.go index 2735b711..ceb9de0b 100644 --- a/debugger/breakpoints.go +++ b/debugger/breakpoints.go @@ -6,8 +6,8 @@ package debugger import ( "fmt" + "gopher2600/debugger/console" "gopher2600/debugger/input" - "gopher2600/debugger/ui" "gopher2600/errors" "strconv" "strings" @@ -134,10 +134,10 @@ func (bp *breakpoints) check(previousResult string) string { func (bp breakpoints) list() { if len(bp.breaks) == 0 { - bp.dbg.print(ui.Feedback, "no breakpoints") + bp.dbg.print(console.Feedback, "no breakpoints") } else { for i := range bp.breaks { - bp.dbg.print(ui.Feedback, "% 2d: %s", i, bp.breaks[i]) + bp.dbg.print(console.Feedback, "% 2d: %s", i, bp.breaks[i]) } } } diff --git a/debugger/callbacks.go b/debugger/callbacks.go index 00b1b783..6ba23ca0 100644 --- a/debugger/callbacks.go +++ b/debugger/callbacks.go @@ -2,7 +2,7 @@ package debugger import ( "fmt" - "gopher2600/debugger/ui" + "gopher2600/debugger/console" "gopher2600/gui" ) @@ -18,9 +18,9 @@ func (dbg *Debugger) setupTVCallbacks() error { _, err := dbg.parseCommand(fmt.Sprintf("%s sl %d & hp %d", KeywordBreak, sl, hp)) if err == nil { - dbg.print(ui.Feedback, "mouse break on sl->%d and hp->%d", sl, hp) + dbg.print(console.Feedback, "mouse break on sl->%d and hp->%d", sl, hp) } else { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } }) if err != nil { @@ -48,7 +48,7 @@ func (dbg *Debugger) setupTVCallbacks() error { err = dbg.tv.SetFeature(gui.ReqDecScale) } if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } }) if err != nil { diff --git a/debugger/colorterm/input.go b/debugger/colorterm/input.go index 8017ba00..294c1984 100644 --- a/debugger/colorterm/input.go +++ b/debugger/colorterm/input.go @@ -3,7 +3,7 @@ package colorterm import ( "gopher2600/debugger/colorterm/ansi" "gopher2600/debugger/colorterm/easyterm" - "gopher2600/debugger/ui" + "gopher2600/debugger/console" "gopher2600/errors" "unicode" "unicode/utf8" @@ -46,8 +46,8 @@ func (ct *ColorTerminal) UserRead(input []byte, prompt string, interruptChannel for { ct.Print(ansi.CursorStore) - ct.UserPrint(ui.Prompt, "%s%s", ansi.ClearLine, prompt) - ct.UserPrint(ui.Input, string(input[:inputLen])) + ct.UserPrint(console.Prompt, "%s%s", ansi.ClearLine, prompt) + ct.UserPrint(console.Input, string(input[:inputLen])) ct.Print(ansi.CursorRestore) select { diff --git a/debugger/colorterm/output.go b/debugger/colorterm/output.go index 6af7a66d..a1568fc0 100644 --- a/debugger/colorterm/output.go +++ b/debugger/colorterm/output.go @@ -3,35 +3,35 @@ package colorterm import ( "fmt" "gopher2600/debugger/colorterm/ansi" - "gopher2600/debugger/ui" + "gopher2600/debugger/console" ) // UserPrint is the top level output function -func (ct *ColorTerminal) UserPrint(profile ui.PrintProfile, s string, a ...interface{}) { - if profile != ui.Input { +func (ct *ColorTerminal) UserPrint(profile console.PrintProfile, s string, a ...interface{}) { + if profile != console.Input { ct.Print("\r") } switch profile { - case ui.CPUStep: + case console.CPUStep: ct.Print(ansi.PenColor["yellow"]) - case ui.VideoStep: + case console.VideoStep: ct.Print(ansi.DimPens["yellow"]) - case ui.MachineInfo: + case console.MachineInfo: ct.Print(ansi.PenColor["cyan"]) - case ui.MachineInfoInternal: + case console.MachineInfoInternal: ct.Print(ansi.PenColor["blue"]) - case ui.Error: + case console.Error: ct.Print(ansi.PenColor["red"]) ct.Print("* ") - case ui.Help: + case console.Help: ct.Print(ansi.DimPens["white"]) ct.Print(" ") - case ui.Feedback: + case console.Feedback: ct.Print(ansi.DimPens["white"]) - case ui.Script: + case console.Script: ct.Print("> ") - case ui.Prompt: + case console.Prompt: ct.Print(ansi.PenStyles["bold"]) } @@ -43,7 +43,7 @@ func (ct *ColorTerminal) UserPrint(profile ui.PrintProfile, s string, a ...inter ct.Print(ansi.NormalPen) // add a newline if print profile is anything other than prompt - if profile != ui.Prompt && profile != ui.Input { + if profile != console.Prompt && profile != console.Input { ct.Print("\n") } } diff --git a/debugger/commands.go b/debugger/commands.go index df9d27d6..1ddbb289 100644 --- a/debugger/commands.go +++ b/debugger/commands.go @@ -2,8 +2,8 @@ package debugger import ( "fmt" + "gopher2600/debugger/console" "gopher2600/debugger/input" - "gopher2600/debugger/ui" "gopher2600/errors" "gopher2600/gui" "gopher2600/hardware/cpu/result" @@ -75,7 +75,7 @@ var Help = map[string]string{ KeywordOnHalt: "Commands to run whenever emulation is halted (separate commands with comma)", KeywordOnStep: "Commands to run whenever emulation steps forward an cpu/video cycle (separate commands with comma)", KeywordLast: "Prints the result of the last cpu/video cycle", - KeywordMemMap: "Display high-levl VCS memory map", + KeywordMemMap: "Display high-level VCS memory map", KeywordQuit: "Exits the emulator", KeywordReset: "Reset the emulation to its initial state", KeywordRun: "Run emulator until next halt state", @@ -215,13 +215,13 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { s := strings.ToUpper(keyword) txt, prs := Help[s] if prs == false { - dbg.print(ui.Help, "no help for %s", s) + dbg.print(console.Help, "no help for %s", s) } else { - dbg.print(ui.Help, txt) + dbg.print(console.Help, txt) } } else { for k := range DebuggerCommands { - dbg.print(ui.Help, k) + dbg.print(console.Help, k) } } @@ -231,14 +231,14 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { if err != nil { return false, err } - dbg.print(ui.Feedback, "machine reset with new cartridge (%s)", cart) + dbg.print(console.Feedback, "machine reset with new cartridge (%s)", cart) case KeywordScript: script, _ := tokens.Get() spt, err := dbg.loadScript(script) if err != nil { - dbg.print(ui.Error, "error running debugger initialisation script: %s\n", err) + dbg.print(console.Error, "error running debugger initialisation script: %s\n", err) return false, err } @@ -255,9 +255,9 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { output := strings.Builder{} dbg.disasm.Grep(search, &output, false, 3) if output.Len() == 0 { - dbg.print(ui.Error, "%s not found in disassembly", search) + dbg.print(console.Error, "%s not found in disassembly", search) } else { - dbg.print(ui.Feedback, output.String()) + dbg.print(console.Feedback, output.String()) } case KeywordSymbol: @@ -268,7 +268,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { switch err := err.(type) { case errors.FormattedError: if err.Errno == errors.SymbolUnknown { - dbg.print(ui.Feedback, "%s -> not found", symbol) + dbg.print(console.Feedback, "%s -> not found", symbol) return false, nil } } @@ -280,21 +280,21 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { option = strings.ToUpper(option) switch option { case "ALL": - dbg.print(ui.Feedback, "%s -> %#04x", symbol, address) + dbg.print(console.Feedback, "%s -> %#04x", symbol, address) // find all instances of symbol address in memory space // assumption: the address returned by SearchSymbol is the // first address in the complete list for m := address + 1; m < dbg.vcs.Mem.Cart.Origin(); m++ { if dbg.vcs.Mem.MapAddress(m, table == symbols.ReadSymTable) == address { - dbg.print(ui.Feedback, "%s -> %#04x", symbol, m) + dbg.print(console.Feedback, "%s -> %#04x", symbol, m) } } default: return false, fmt.Errorf("unknown option for SYMBOL command (%s)", option) } } else { - dbg.print(ui.Feedback, "%s -> %#04x", symbol, address) + dbg.print(console.Feedback, "%s -> %#04x", symbol, address) } case KeywordBreak: @@ -335,13 +335,13 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { switch clear { case "BREAKS": dbg.breakpoints.clear() - dbg.print(ui.Feedback, "breakpoints cleared") + dbg.print(console.Feedback, "breakpoints cleared") case "TRAPS": dbg.traps.clear() - dbg.print(ui.Feedback, "traps cleared") + dbg.print(console.Feedback, "traps cleared") case "WATCHES": dbg.watches.clear() - dbg.print(ui.Feedback, "watches cleared") + dbg.print(console.Feedback, "watches cleared") default: return false, fmt.Errorf("unknown clear option (%s)", clear) } @@ -362,26 +362,26 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { if err != nil { return false, err } - dbg.print(ui.Feedback, "breakpoint #%d dropped", num) + dbg.print(console.Feedback, "breakpoint #%d dropped", num) case "TRAP": err := dbg.traps.drop(num) if err != nil { return false, err } - dbg.print(ui.Feedback, "trap #%d dropped", num) + dbg.print(console.Feedback, "trap #%d dropped", num) case "WATCH": err := dbg.watches.drop(num) if err != nil { return false, err } - dbg.print(ui.Feedback, "watch #%d dropped", num) + dbg.print(console.Feedback, "watch #%d dropped", num) default: return false, fmt.Errorf("unknown drop option (%s)", drop) } case KeywordOnHalt: if tokens.Remaining() == 0 { - dbg.print(ui.Feedback, "auto-command on halt: %s", dbg.commandOnHalt) + dbg.print(console.Feedback, "auto-command on halt: %s", dbg.commandOnHalt) return false, nil } @@ -406,9 +406,9 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // display the new/restored onhalt command(s) if dbg.commandOnHalt == "" { - dbg.print(ui.Feedback, "auto-command on halt: OFF") + dbg.print(console.Feedback, "auto-command on halt: OFF") } else { - dbg.print(ui.Feedback, "auto-command on halt: %s", dbg.commandOnHalt) + dbg.print(console.Feedback, "auto-command on halt: %s", dbg.commandOnHalt) } // run the new/restored onhalt command(s) @@ -417,7 +417,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { case KeywordOnStep: if tokens.Remaining() == 0 { - dbg.print(ui.Feedback, "auto-command on step: %s", dbg.commandOnStep) + dbg.print(console.Feedback, "auto-command on step: %s", dbg.commandOnStep) return false, nil } @@ -442,9 +442,9 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // display the new/restored onstep command(s) if dbg.commandOnStep == "" { - dbg.print(ui.Feedback, "auto-command on step: OFF") + dbg.print(console.Feedback, "auto-command on step: OFF") } else { - dbg.print(ui.Feedback, "auto-command on step: %s", dbg.commandOnStep) + dbg.print(console.Feedback, "auto-command on step: %s", dbg.commandOnStep) } // run the new/restored onstep command(s) @@ -457,13 +457,13 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { option = strings.ToUpper(option) switch option { case "DEFN": - dbg.print(ui.Feedback, "%s", dbg.lastResult.Defn) + dbg.print(console.Feedback, "%s", dbg.lastResult.Defn) case "": - var printTag ui.PrintProfile + var printTag console.PrintProfile if dbg.lastResult.Final { - printTag = ui.CPUStep + printTag = console.CPUStep } else { - printTag = ui.VideoStep + printTag = console.VideoStep } dbg.print(printTag, "%s", dbg.lastResult.GetString(dbg.disasm.Symtable, result.StyleFull)) default: @@ -472,7 +472,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { } case KeywordMemMap: - dbg.print(ui.MachineInfo, "%v", dbg.vcs.Mem.MemoryMap()) + dbg.print(console.MachineInfo, "%v", dbg.vcs.Mem.MemoryMap()) case KeywordQuit: dbg.running = false @@ -486,7 +486,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { if err != nil { return false, err } - dbg.print(ui.Feedback, "machine reset") + dbg.print(console.Feedback, "machine reset") case KeywordRun: dbg.runUntilHalt = true @@ -533,21 +533,21 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { } else { mode = "CPU" } - dbg.print(ui.Feedback, "step mode: %s", mode) + dbg.print(console.Feedback, "step mode: %s", mode) case KeywordTerse: dbg.machineInfoVerbose = false - dbg.print(ui.Feedback, "verbosity: terse") + dbg.print(console.Feedback, "verbosity: terse") case KeywordVerbose: dbg.machineInfoVerbose = true - dbg.print(ui.Feedback, "verbosity: verbose") + dbg.print(console.Feedback, "verbosity: verbose") case KeywordVerbosity: if dbg.machineInfoVerbose { - dbg.print(ui.Feedback, "verbosity: verbose") + dbg.print(console.Feedback, "verbosity: verbose") } else { - dbg.print(ui.Feedback, "verbosity: terse") + dbg.print(console.Feedback, "verbosity: terse") } case KeywordDebuggerState: @@ -567,7 +567,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // get first address token a, present := tokens.Get() if !present { - dbg.print(ui.Error, "peek address required") + dbg.print(console.Error, "peek address required") return false, nil } @@ -575,14 +575,14 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // perform peek val, mappedAddress, areaName, addressLabel, err := dbg.dbgmem.peek(a) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } else { // format results msg := fmt.Sprintf("%#04x -> %#02x :: %s", mappedAddress, val, areaName) if addressLabel != "" { msg = fmt.Sprintf("%s [%s]", msg, addressLabel) } - dbg.print(ui.MachineInfo, msg) + dbg.print(console.MachineInfo, msg) } // loop through all addresses @@ -593,62 +593,62 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // get address token a, present := tokens.Get() if !present { - dbg.print(ui.Error, "poke address required") + dbg.print(console.Error, "poke address required") return false, nil } addr, err := dbg.dbgmem.mapAddress(a, true) if err != nil { - dbg.print(ui.Error, "invalid poke address (%v)", a) + dbg.print(console.Error, "invalid poke address (%v)", a) return false, nil } // get value token a, present = tokens.Get() if !present { - dbg.print(ui.Error, "poke value required") + dbg.print(console.Error, "poke value required") return false, nil } val, err := strconv.ParseUint(a, 0, 8) if err != nil { - dbg.print(ui.Error, "poke value must be numeric (%s)", a) + dbg.print(console.Error, "poke value must be numeric (%s)", a) return false, nil } // perform single poke err = dbg.dbgmem.poke(addr, uint8(val)) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } else { - dbg.print(ui.MachineInfo, fmt.Sprintf("%#04x -> %#02x", addr, uint16(val))) + dbg.print(console.MachineInfo, fmt.Sprintf("%#04x -> %#02x", addr, uint16(val))) } case KeywordHexLoad: // get address token a, present := tokens.Get() if !present { - dbg.print(ui.Error, "hexload address required") + dbg.print(console.Error, "hexload address required") return false, nil } addr, err := dbg.dbgmem.mapAddress(a, true) if err != nil { - dbg.print(ui.Error, "invalid hexload address (%s)", a) + dbg.print(console.Error, "invalid hexload address (%s)", a) return false, nil } // get (first) value token a, present = tokens.Get() if !present { - dbg.print(ui.Error, "at least one hexload value required") + dbg.print(console.Error, "at least one hexload value required") return false, nil } for present { val, err := strconv.ParseUint(a, 0, 8) if err != nil { - dbg.print(ui.Error, "hexload value must be numeric (%s)", a) + dbg.print(console.Error, "hexload value must be numeric (%s)", a) a, present = tokens.Get() continue // for loop } @@ -656,9 +656,9 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { // perform individual poke err = dbg.dbgmem.poke(uint16(addr), uint8(val)) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } else { - dbg.print(ui.MachineInfo, fmt.Sprintf("%#04x -> %#02x", addr, uint16(val))) + dbg.print(console.MachineInfo, fmt.Sprintf("%#04x -> %#02x", addr, uint16(val))) } // loop through all values @@ -680,12 +680,12 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { case "FUTURE": dbg.printMachineInfo(dbg.vcs.TIA.Video.OnFutureColorClock) case "HMOVE": - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Hmove.MachineInfoInternal()) - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Video.Player0.MachineInfoInternal()) - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Video.Player1.MachineInfoInternal()) - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Video.Missile0.MachineInfoInternal()) - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Video.Missile1.MachineInfoInternal()) - dbg.print(ui.MachineInfoInternal, dbg.vcs.TIA.Video.Ball.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Hmove.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Video.Player0.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Video.Player1.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Video.Missile0.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Video.Missile1.MachineInfoInternal()) + dbg.print(console.MachineInfoInternal, dbg.vcs.TIA.Video.Ball.MachineInfoInternal()) default: return false, fmt.Errorf("unknown request (%s)", option) } @@ -703,7 +703,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { if err != nil { return false, err } - dbg.print(ui.MachineInfo, info.(string)) + dbg.print(console.MachineInfo, info.(string)) default: return false, fmt.Errorf("unknown request (%s)", option) } @@ -735,7 +735,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { s.WriteString(fmt.Sprintf("%s %s | %s\n", p0[i], strings.Repeat(" ", ml-len(p0[i])), p1[i])) } } - dbg.print(ui.MachineInfo, s.String()) + dbg.print(console.MachineInfo, s.String()) } else { dbg.printMachineInfo(dbg.vcs.TIA.Video.Player0) dbg.printMachineInfo(dbg.vcs.TIA.Video.Player1) @@ -764,7 +764,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { s.WriteString(fmt.Sprintf("%s %s | %s\n", p0[i], strings.Repeat(" ", ml-len(p0[i])), p1[i])) } } - dbg.print(ui.MachineInfo, s.String()) + dbg.print(console.MachineInfo, s.String()) } else { dbg.printMachineInfo(dbg.vcs.TIA.Video.Missile0) dbg.printMachineInfo(dbg.vcs.TIA.Video.Missile1) @@ -849,7 +849,7 @@ func (dbg *Debugger) parseCommand(userInput string) (bool, error) { if err != nil { return false, err } - dbg.print(ui.MachineInfo, info.(string)) + dbg.print(console.MachineInfo, info.(string)) case KeywordStick0: action, present := tokens.Get() diff --git a/debugger/ui/ui.go b/debugger/console/console.go similarity index 97% rename from debugger/ui/ui.go rename to debugger/console/console.go index da247bee..2c430297 100644 --- a/debugger/ui/ui.go +++ b/debugger/console/console.go @@ -1,4 +1,4 @@ -package ui +package console import "gopher2600/debugger/input" diff --git a/debugger/ui/plainterminal.go b/debugger/console/plainterminal.go similarity index 98% rename from debugger/ui/plainterminal.go rename to debugger/console/plainterminal.go index a6eba94b..1d1c2c3e 100644 --- a/debugger/ui/plainterminal.go +++ b/debugger/console/plainterminal.go @@ -1,4 +1,4 @@ -package ui +package console import ( "fmt" diff --git a/debugger/ui/printprofile.go b/debugger/console/printprofile.go similarity index 97% rename from debugger/ui/printprofile.go rename to debugger/console/printprofile.go index 8fe9b87a..bdcd4090 100644 --- a/debugger/ui/printprofile.go +++ b/debugger/console/printprofile.go @@ -1,4 +1,4 @@ -package ui +package console // PrintProfile specifies the printing mode type PrintProfile int diff --git a/debugger/debugger.go b/debugger/debugger.go index 013acc92..ce026f47 100644 --- a/debugger/debugger.go +++ b/debugger/debugger.go @@ -2,13 +2,12 @@ package debugger import ( "fmt" + "gopher2600/debugger/console" "gopher2600/debugger/input" "gopher2600/debugger/monitor" - "gopher2600/debugger/ui" "gopher2600/disassembly" "gopher2600/errors" "gopher2600/gui" - "gopher2600/gui/sdl" "gopher2600/hardware" "gopher2600/hardware/cpu/definitions" "gopher2600/hardware/cpu/result" @@ -62,8 +61,6 @@ type Debugger struct { // single-fire step traps. these are used for the STEP command, allowing // things like "STEP FRAME". - // -- note that the hardware.VCS type has the StepFrames() function, we're - // not using that here because this solution is more general and flexible stepTraps *traps // commandOnHalt says whether an sequence of commands should run automatically @@ -93,9 +90,9 @@ type Debugger struct { // intermediate result when video-stepping lastResult *result.Instruction - // user interface - ui ui.UserInterface - uiSilent bool // controls whether UI is to remain silent + // console interface + console console.UserInterface + consoleSilent bool // controls whether UI is to remain silent // buffer for user input input []byte @@ -111,18 +108,15 @@ type Debugger struct { // NewDebugger creates and initialises everything required for a new debugging // session. Use the Start() method to actually begin the session. -func NewDebugger() (*Debugger, error) { +func NewDebugger(tv gui.GUI) (*Debugger, error) { var err error dbg := new(Debugger) - dbg.ui = new(ui.PlainTerminal) + dbg.console = new(console.PlainTerminal) // prepare hardware - dbg.tv, err = sdl.NewGUI("NTSC", 2.0) - if err != nil { - return nil, fmt.Errorf("error preparing television: %s", err) - } + dbg.tv = tv dbg.tv.SetFeature(gui.ReqSetAllowDebugging, true) // create a new VCS instance @@ -171,19 +165,19 @@ func NewDebugger() (*Debugger, error) { } // Start the main debugger sequence -func (dbg *Debugger) Start(iface ui.UserInterface, filename string, initScript string) error { +func (dbg *Debugger) Start(iface console.UserInterface, filename string, initScript string) error { // prepare user interface if iface != nil { - dbg.ui = iface + dbg.console = iface } - err := dbg.ui.Initialise() + err := dbg.console.Initialise() if err != nil { return err } - defer dbg.ui.CleanUp() + defer dbg.console.CleanUp() - dbg.ui.RegisterTabCompleter(input.NewTabCompletion(DebuggerCommands)) + dbg.console.RegisterTabCompleter(input.NewTabCompletion(DebuggerCommands)) err = dbg.loadCartridge(filename) if err != nil { @@ -220,7 +214,7 @@ func (dbg *Debugger) Start(iface ui.UserInterface, filename string, initScript s if initScript != "" { spt, err := dbg.loadScript(initScript) if err != nil { - dbg.print(ui.Error, "error running debugger initialisation script: %s\n", err) + dbg.print(console.Error, "error running debugger initialisation script: %s\n", err) } err = dbg.inputLoop(spt, true) @@ -231,7 +225,7 @@ func (dbg *Debugger) Start(iface ui.UserInterface, filename string, initScript s // prepare and run main input loop. inputLoop will not return until // debugger is to exit - err = dbg.inputLoop(dbg.ui, true) + err = dbg.inputLoop(dbg.console, true) if err != nil { return err } @@ -253,7 +247,7 @@ func (dbg *Debugger) loadCartridge(cartridgeFilename string) error { symtable, err := symbols.ReadSymbolsFile(cartridgeFilename) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) symtable = symbols.StandardSymbolTable() } @@ -309,7 +303,7 @@ func (dbg *Debugger) checkForInterrupts() { // // inputter is an instance of type UserInput. this will usually be dbg.ui but // it could equally be an instance of debuggingScript. -func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { +func (dbg *Debugger) inputLoop(inputter console.UserInput, mainLoop bool) error { var err error // videoCycleWithInput() to be used with vcs.Step() instead of videoCycle() @@ -320,7 +314,7 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { if dbg.commandOnStep != "" { _, err := dbg.parseInput(dbg.commandOnStep) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } } return dbg.inputLoop(inputter, false) @@ -362,15 +356,15 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { if (dbg.inputloopNext && !dbg.runUntilHalt) || dbg.inputloopHalt { _, err = dbg.parseInput(dbg.commandOnHalt) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } } } // print and reset accumulated break and trap messages - dbg.print(ui.Feedback, dbg.breakMessages) - dbg.print(ui.Feedback, dbg.trapMessages) - dbg.print(ui.Feedback, dbg.watchMessages) + dbg.print(console.Feedback, dbg.breakMessages) + dbg.print(console.Feedback, dbg.trapMessages) + dbg.print(console.Feedback, dbg.watchMessages) dbg.breakMessages = "" dbg.trapMessages = "" dbg.watchMessages = "" @@ -424,7 +418,7 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { fallthrough case errors.ScriptEnd: if mainLoop { - dbg.print(ui.Feedback, err.Error()) + dbg.print(console.Feedback, err.Error()) } return nil } @@ -441,7 +435,7 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { // parse user input dbg.inputloopNext, err = dbg.parseInput(string(dbg.input[:n-1])) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } // prepare for next loop @@ -474,7 +468,7 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { dbg.lastStepError = true // print gopher error message - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) default: return err } @@ -483,8 +477,8 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { if dbg.lastResult.Final { err := dbg.lastResult.IsValid() if err != nil { - dbg.print(ui.Error, "%s", dbg.lastResult.Defn) - dbg.print(ui.Error, "%s", dbg.lastResult) + dbg.print(console.Error, "%s", dbg.lastResult.Defn) + dbg.print(console.Error, "%s", dbg.lastResult) panic(err) } } @@ -493,7 +487,7 @@ func (dbg *Debugger) inputLoop(inputter ui.UserInput, mainLoop bool) error { if dbg.commandOnStep != "" { _, err := dbg.parseInput(dbg.commandOnStep) if err != nil { - dbg.print(ui.Error, "%s", err) + dbg.print(console.Error, "%s", err) } } } else { diff --git a/debugger/machineinfo.go b/debugger/machineinfo.go index 923f6d4d..c345a1b3 100644 --- a/debugger/machineinfo.go +++ b/debugger/machineinfo.go @@ -1,6 +1,6 @@ package debugger -import "gopher2600/debugger/ui" +import "gopher2600/debugger/console" // types that satisfy machineInfo return information about the state of the // emulated machine. String() should return verbose info, while StringTerse() @@ -12,7 +12,7 @@ type machineInfo interface { } func (dbg *Debugger) printMachineInfo(mi machineInfo) { - dbg.print(ui.MachineInfo, "%s", dbg.getMachineInfo(mi)) + dbg.print(console.MachineInfo, "%s", dbg.getMachineInfo(mi)) } func (dbg *Debugger) getMachineInfo(mi machineInfo) string { diff --git a/debugger/print.go b/debugger/print.go index 764ffe2b..1d7bd539 100644 --- a/debugger/print.go +++ b/debugger/print.go @@ -1,16 +1,16 @@ package debugger import ( - "gopher2600/debugger/ui" + "gopher2600/debugger/console" "strings" ) -// wrapper function for UserPrint(). useful for normalising the input string +// wrapper function for UserPrint(). useful for normalising the input string // before passing to the real UserPrint. it also allows us to easily obey // directives such as the silent directive without passing the burden onto UI // implementors -func (dbg *Debugger) print(pp ui.PrintProfile, s string, a ...interface{}) { - if dbg.uiSilent && pp != ui.Error { +func (dbg *Debugger) print(pp console.PrintProfile, s string, a ...interface{}) { + if dbg.consoleSilent && pp != console.Error { return } @@ -20,5 +20,5 @@ func (dbg *Debugger) print(pp ui.PrintProfile, s string, a ...interface{}) { return } - dbg.ui.UserPrint(pp, s, a...) + dbg.console.UserPrint(pp, s, a...) } diff --git a/debugger/traps.go b/debugger/traps.go index 5eb885c8..76db1740 100644 --- a/debugger/traps.go +++ b/debugger/traps.go @@ -6,8 +6,8 @@ package debugger import ( "fmt" + "gopher2600/debugger/console" "gopher2600/debugger/input" - "gopher2600/debugger/ui" "gopher2600/errors" "strings" ) @@ -66,10 +66,10 @@ func (tr *traps) check(previousResult string) string { func (tr traps) list() { if len(tr.traps) == 0 { - tr.dbg.print(ui.Feedback, "no traps") + tr.dbg.print(console.Feedback, "no traps") } else { for i := range tr.traps { - tr.dbg.print(ui.Feedback, "% 2d: %s", i, tr.traps[i].target.ShortLabel()) + tr.dbg.print(console.Feedback, "% 2d: %s", i, tr.traps[i].target.ShortLabel()) } } } @@ -86,7 +86,7 @@ func (tr *traps) parseTrap(tokens *input.Tokens) error { for _, t := range tr.traps { if t.target == tgt { addNewTrap = false - tr.dbg.print(ui.Feedback, "trap already exists") + tr.dbg.print(console.Feedback, "trap already exists") break // for loop } } diff --git a/debugger/watches.go b/debugger/watches.go index 0497ee2b..4ac38bc5 100644 --- a/debugger/watches.go +++ b/debugger/watches.go @@ -2,8 +2,8 @@ package debugger import ( "fmt" + "gopher2600/debugger/console" "gopher2600/debugger/input" - "gopher2600/debugger/ui" "gopher2600/errors" "gopher2600/hardware/memory" "strconv" @@ -119,10 +119,10 @@ func (wtc *watches) check(previousResult string) string { func (wtc *watches) list() { if len(wtc.watches) == 0 { - wtc.dbg.print(ui.Feedback, "no watches") + wtc.dbg.print(console.Feedback, "no watches") } else { for i := range wtc.watches { - wtc.dbg.print(ui.Feedback, "% 2d: %s", i, wtc.watches[i]) + wtc.dbg.print(console.Feedback, "% 2d: %s", i, wtc.watches[i]) } } } diff --git a/gopher2600.go b/gopher2600.go index 6737c007..d22b3956 100644 --- a/gopher2600.go +++ b/gopher2600.go @@ -5,7 +5,7 @@ import ( "fmt" "gopher2600/debugger" "gopher2600/debugger/colorterm" - "gopher2600/debugger/ui" + "gopher2600/debugger/console" "gopher2600/disassembly" "gopher2600/errors" "gopher2600/gui" @@ -77,14 +77,20 @@ func main() { initScript := modeFlags.String("initscript", defaultInitScript, "terminal type to use in debug mode: COLOR, PLAIN") modeFlagsParse() - dbg, err := debugger.NewDebugger() + tv, err := sdl.NewGUI("NTSC", 2.0) + if err != nil { + fmt.Printf("* error preparing television: %s", err) + os.Exit(2) + } + + dbg, err := debugger.NewDebugger(tv) if err != nil { fmt.Printf("* error starting debugger: %s\n", err) os.Exit(2) } // start debugger with choice of interface and cartridge - var term ui.UserInterface + var term console.UserInterface switch strings.ToUpper(*termType) { default: diff --git a/hardware/cpu/cpu.go b/hardware/cpu/cpu.go index 34c8e852..a4a7854f 100644 --- a/hardware/cpu/cpu.go +++ b/hardware/cpu/cpu.go @@ -1192,6 +1192,12 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func(*result.Instruction)) (*res mc.Status.Zero = mc.A.IsZero() mc.Status.Sign = mc.A.IsNegative() + case "xaa": + mc.A.Load(mc.X) + mc.A.AND(value) + mc.Status.Zero = mc.A.IsZero() + mc.Status.Sign = mc.A.IsNegative() + default: // this should never, ever happen log.Fatalf("WTF! unknown mnemonic! (%s)", defn.Mnemonic) diff --git a/hardware/cpu/definitions/generator/instructions.csv b/hardware/cpu/definitions/generator/instructions.csv index 097c0b07..199001fd 100644 --- a/hardware/cpu/definitions/generator/instructions.csv +++ b/hardware/cpu/definitions/generator/instructions.csv @@ -245,6 +245,7 @@ 0xa7, lax, 3, ZERO_PAGE, False 0xb3, lax, 5, POST_INDEX_INDIRECT, True 0xb7, lax, 4, INDEXED_ZERO_PAGE_X, False +0x8b, xaa, 2, IMMEDIATE, False 0xc7, dcp, 5, ZERO_PAGE, False, RMW # dcm 0x4b, asr, 2, IMMEDIATE, False # alr diff --git a/hardware/cpu/definitions/instructions.go b/hardware/cpu/definitions/instructions.go index 9856f9ac..baf2e23d 100644 --- a/hardware/cpu/definitions/instructions.go +++ b/hardware/cpu/definitions/instructions.go @@ -144,7 +144,7 @@ nil, &InstructionDefinition{ObjectCode:0x88, Mnemonic:"DEY", Bytes:1, Cycles:2, AddressingMode:0, PageSensitive:false, Effect:0}, nil, &InstructionDefinition{ObjectCode:0x8a, Mnemonic:"TXA", Bytes:1, Cycles:2, AddressingMode:0, PageSensitive:false, Effect:0}, -nil, +&InstructionDefinition{ObjectCode:0x8b, Mnemonic:"xaa", Bytes:2, Cycles:2, AddressingMode:1, PageSensitive:false, Effect:0}, &InstructionDefinition{ObjectCode:0x8c, Mnemonic:"STY", Bytes:3, Cycles:4, AddressingMode:3, PageSensitive:false, Effect:1}, &InstructionDefinition{ObjectCode:0x8d, Mnemonic:"STA", Bytes:3, Cycles:4, AddressingMode:3, PageSensitive:false, Effect:1}, &InstructionDefinition{ObjectCode:0x8e, Mnemonic:"STX", Bytes:3, Cycles:4, AddressingMode:3, PageSensitive:false, Effect:1}, diff --git a/regression/database.go b/regression/database.go new file mode 100644 index 00000000..a65e6253 --- /dev/null +++ b/regression/database.go @@ -0,0 +1,216 @@ +package regression + +import ( + "crypto/sha1" + "encoding/csv" + "fmt" + "gopher2600/errors" + "gopher2600/hardware" + "gopher2600/television/digesttv" + "io" + "os" + "strconv" +) + +const regressionDBFile = ".gopher2600/regressionDB" + +func getCartridgeHash(cartridgeFile string) (string, error) { + f, err := os.Open(cartridgeFile) + if err != nil { + return "", err + } + defer f.Close() + + key := sha1.New() + if _, err := io.Copy(key, f); err != nil { + return "", err + } + + return fmt.Sprintf("%x", key.Sum(nil)), nil +} + +type regressionEntry struct { + cartridgeHash string + cartridgePath string + tvMode string + numOFrames int + screenDigest string +} + +func (entry regressionEntry) String() string { + return fmt.Sprintf("%s [%s] frames=%d", entry.cartridgePath, entry.tvMode, entry.numOFrames) +} + +type regressionDB struct { + dbfile *os.File + entries map[string]regressionEntry +} + +func startSession() (*regressionDB, error) { + var err error + + db := ®ressionDB{} + + db.dbfile, err = os.OpenFile(regressionDBFile, os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + return nil, err + } + + err = db.readEntries() + if err != nil { + return nil, err + } + + return db, nil +} + +func (db *regressionDB) endSession() error { + // write entries to regression database + csvw := csv.NewWriter(db.dbfile) + + err := db.dbfile.Truncate(0) + if err != nil { + return err + } + + db.dbfile.Seek(0, os.SEEK_SET) + + for _, entry := range db.entries { + rec := make([]string, 5) + rec[0] = entry.cartridgeHash + rec[1] = entry.cartridgePath + rec[2] = entry.tvMode + rec[3] = strconv.Itoa(entry.numOFrames) + rec[4] = entry.screenDigest + + err := csvw.Write(rec) + if err != nil { + return err + } + } + + // make sure everything's been written + csvw.Flush() + err = csvw.Error() + if err != nil { + return err + } + + // end session by closing file + if db.dbfile != nil { + if err := db.dbfile.Close(); err != nil { + return err + } + db.dbfile = nil + } + + return nil +} + +func (db *regressionDB) readEntries() error { + // readEntries clobbers the contents of db.entries + db.entries = make(map[string]regressionEntry, len(db.entries)) + + // treat the file as a CSV file + csvr := csv.NewReader(db.dbfile) + csvr.Comment = rune('#') + csvr.TrimLeadingSpace = true + csvr.ReuseRecord = true + csvr.FieldsPerRecord = 5 + + db.dbfile.Seek(0, os.SEEK_SET) + + for { + // loop through file until EOF is reached + rec, err := csvr.Read() + if err == io.EOF { + break + } + if err != nil { + return err + } + + numOfFrames, err := strconv.Atoi(rec[3]) + if err != nil { + return err + } + + // add entry to database + entry := regressionEntry{ + cartridgeHash: rec[0], + cartridgePath: rec[1], + tvMode: rec[2], + numOFrames: numOfFrames, + screenDigest: rec[4]} + + db.entries[entry.cartridgeHash] = entry + } + + return nil +} + +func run(cartridgeFile string, tvMode string, numOfFrames int) (string, error) { + tv, err := digesttv.NewDigestTV(tvMode) + if err != nil { + return "", fmt.Errorf("error preparing television: %s", err) + } + + vcs, err := hardware.NewVCS(tv) + if err != nil { + return "", fmt.Errorf("error preparing VCS: %s", err) + } + + err = vcs.AttachCartridge(cartridgeFile) + if err != nil { + return "", err + } + + err = vcs.RunForFrameCount(numOfFrames) + if err != nil { + return "", err + } + + // output current digest + return fmt.Sprintf("%s", tv), nil +} + +// RegressAddCartridge adds a cartridge to the regression db +func addCartridge(cartridgeFile string, tvMode string, numOfFrames int, allowUpdate bool) error { + db, err := startSession() + if err != nil { + return err + } + defer db.endSession() + + // run cartdrige and get digest + digest, err := run(cartridgeFile, tvMode, numOfFrames) + if err != nil { + return err + } + + // add new entry to database + key, err := getCartridgeHash(cartridgeFile) + if err != nil { + return err + } + entry := regressionEntry{ + cartridgeHash: key, + cartridgePath: cartridgeFile, + tvMode: tvMode, + numOFrames: numOfFrames, + screenDigest: digest} + + if allowUpdate == false { + if existEntry, ok := db.entries[entry.cartridgeHash]; ok { + if existEntry.cartridgePath == entry.cartridgePath { + return errors.NewFormattedError(errors.RegressionEntryExists, entry) + } + + return errors.NewFormattedError(errors.RegressionEntryCollision, entry.cartridgePath, existEntry.cartridgePath) + } + } + + db.entries[entry.cartridgeHash] = entry + + return nil +} diff --git a/regression/regression.go b/regression/regression.go index e50abd6a..cbc88b4c 100644 --- a/regression/regression.go +++ b/regression/regression.go @@ -1,220 +1,11 @@ package regression import ( - "crypto/sha1" - "encoding/csv" "fmt" "gopher2600/errors" - "gopher2600/hardware" - "gopher2600/television/digesttv" "io" - "os" - "strconv" ) -const regressionDBFile = ".gopher2600/regressionDB" - -func keyify(cartridgeFile string) (string, error) { - f, err := os.Open(cartridgeFile) - if err != nil { - return "", err - } - defer f.Close() - - key := sha1.New() - if _, err := io.Copy(key, f); err != nil { - return "", err - } - - return fmt.Sprintf("%x", key.Sum(nil)), nil -} - -type regressionEntry struct { - key string - cartridgeFile string - tvMode string - numOFrames int - digest string -} - -func (entry regressionEntry) String() string { - return fmt.Sprintf("%s [%s] frames=%d", entry.cartridgeFile, entry.tvMode, entry.numOFrames) -} - -type regressionDB struct { - dbfile *os.File - entries map[string]regressionEntry -} - -func (db *regressionDB) endSession() error { - // write entries to regression database - csvw := csv.NewWriter(db.dbfile) - - err := db.dbfile.Truncate(0) - if err != nil { - return err - } - - db.dbfile.Seek(0, os.SEEK_SET) - - for _, entry := range db.entries { - rec := make([]string, 5) - rec[0] = entry.key - rec[1] = entry.cartridgeFile - rec[2] = entry.tvMode - rec[3] = strconv.Itoa(entry.numOFrames) - rec[4] = entry.digest - - err := csvw.Write(rec) - if err != nil { - return err - } - } - - // make sure everything's been written - csvw.Flush() - err = csvw.Error() - if err != nil { - return err - } - - // end session by closing file - if db.dbfile != nil { - if err := db.dbfile.Close(); err != nil { - return err - } - db.dbfile = nil - } - - return nil -} - -func (db *regressionDB) readEntries() error { - // readEntries clobbers the contents of db.entries - db.entries = make(map[string]regressionEntry, len(db.entries)) - - // treat the file as a CSV file - csvr := csv.NewReader(db.dbfile) - csvr.Comment = rune('#') - csvr.TrimLeadingSpace = true - csvr.ReuseRecord = true - csvr.FieldsPerRecord = 5 - - db.dbfile.Seek(0, os.SEEK_SET) - - for { - // loop through file until EOF is reached - rec, err := csvr.Read() - if err == io.EOF { - break - } - if err != nil { - return err - } - - numOfFrames, err := strconv.Atoi(rec[3]) - if err != nil { - return err - } - - // add entry to database - entry := regressionEntry{ - key: rec[0], - cartridgeFile: rec[1], - tvMode: rec[2], - numOFrames: numOfFrames, - digest: rec[4]} - - db.entries[entry.key] = entry - } - - return nil -} - -func startSession() (*regressionDB, error) { - var err error - - db := ®ressionDB{} - - db.dbfile, err = os.OpenFile(regressionDBFile, os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - return nil, err - } - - err = db.readEntries() - if err != nil { - return nil, err - } - - return db, nil -} - -func run(cartridgeFile string, tvMode string, numOfFrames int) (string, error) { - tv, err := digesttv.NewDigestTV(tvMode) - if err != nil { - return "", fmt.Errorf("error preparing television: %s", err) - } - - vcs, err := hardware.NewVCS(tv) - if err != nil { - return "", fmt.Errorf("error preparing VCS: %s", err) - } - - err = vcs.AttachCartridge(cartridgeFile) - if err != nil { - return "", err - } - - err = vcs.RunForFrameCount(numOfFrames) - if err != nil { - return "", err - } - - // output current digest - return fmt.Sprintf("%s", tv), nil -} - -// RegressAddCartridge adds a cartridge to the regression db -func addCartridge(cartridgeFile string, tvMode string, numOfFrames int, allowUpdate bool) error { - db, err := startSession() - if err != nil { - return err - } - defer db.endSession() - - // run cartdrige and get digest - digest, err := run(cartridgeFile, tvMode, numOfFrames) - if err != nil { - return err - } - - // add new entry to database - key, err := keyify(cartridgeFile) - if err != nil { - return err - } - entry := regressionEntry{ - key: key, - cartridgeFile: cartridgeFile, - tvMode: tvMode, - numOFrames: numOfFrames, - digest: digest} - - if allowUpdate == false { - if existEntry, ok := db.entries[entry.key]; ok { - if existEntry.cartridgeFile == entry.cartridgeFile { - return errors.NewFormattedError(errors.RegressionEntryExists, entry) - } - - return errors.NewFormattedError(errors.RegressionEntryCollision, entry.cartridgeFile, existEntry.cartridgeFile) - } - } - - db.entries[entry.key] = entry - - return nil -} - // RegressDeleteCartridge removes a cartridge from the regression db func RegressDeleteCartridge(cartridgeFile string) error { db, err := startSession() @@ -223,7 +14,7 @@ func RegressDeleteCartridge(cartridgeFile string) error { } defer db.endSession() - key, err := keyify(cartridgeFile) + key, err := getCartridgeHash(cartridgeFile) if err != nil { return err } @@ -258,9 +49,9 @@ func RegressRunTests(output io.Writer, failOnError bool) (int, int, error) { numSucceed := 0 numFail := 0 for _, entry := range db.entries { - digest, err := run(entry.cartridgeFile, entry.tvMode, entry.numOFrames) + digest, err := run(entry.cartridgePath, entry.tvMode, entry.numOFrames) - if err != nil || entry.digest != digest { + if err != nil || entry.screenDigest != digest { if err == nil { err = errors.NewFormattedError(errors.RegressionEntryFail, entry) }