o debugger

- refactored user interface types and functions - now in ui
	package
	- sketched in tab completion
This commit is contained in:
steve 2018-05-31 08:36:52 +01:00
parent 9919016ef0
commit 9d3d9d10ac
14 changed files with 352 additions and 299 deletions

View file

@ -2,6 +2,7 @@ package debugger
import (
"fmt"
"gopher2600/debugger/ui"
"strconv"
)
@ -64,7 +65,7 @@ func (bp *breakpoints) check() bool {
// make sure that we're not breaking on an ignore state
bv, prs := bp.ignoredBreakerStates[bp.breaks[i].target]
if !prs || prs && bp.breaks[i].target.ToInt() != bv {
bp.dbg.print(Feedback, "break on %s=%d", bp.breaks[i].target.ShortLabel(), bp.breaks[i].value)
bp.dbg.print(ui.Feedback, "break on %s=%d", bp.breaks[i].target.ShortLabel(), bp.breaks[i].value)
broken = true
}
}
@ -86,10 +87,10 @@ func (bp *breakpoints) check() bool {
func (bp breakpoints) list() {
if len(bp.breaks) == 0 {
bp.dbg.print(Feedback, "no breakpoints")
bp.dbg.print(ui.Feedback, "no breakpoints")
} else {
for i := range bp.breaks {
bp.dbg.print(Feedback, "%s->%d", bp.breaks[i].target.ShortLabel(), bp.breaks[i].value)
bp.dbg.print(ui.Feedback, "%s->%d", bp.breaks[i].target.ShortLabel(), bp.breaks[i].value)
}
}
}
@ -121,7 +122,7 @@ func (bp *breakpoints) parseBreakpoint(parts []string) error {
for _, mv := range bp.breaks {
if mv.target == tgt && mv.value == int(val) {
addNewBreak = false
bp.dbg.print(Feedback, "breakpoint already exists")
bp.dbg.print(ui.Feedback, "breakpoint already exists")
break // for loop
}
}
@ -140,7 +141,7 @@ func (bp *breakpoints) parseBreakpoint(parts []string) error {
switch parts[i] {
case "CLEAR":
bp.clear()
bp.dbg.print(Feedback, "breakpoints cleared")
bp.dbg.print(ui.Feedback, "breakpoints cleared")
return nil
case "LIST":
bp.list()

View file

@ -2,12 +2,9 @@ package colorterm
import (
"bufio"
"gopher2600/debugger"
"gopher2600/debugger/colorterm/ansi"
"gopher2600/debugger/colorterm/easyterm"
"gopher2600/debugger/ui"
"os"
"unicode"
"unicode/utf8"
)
// ColorTerminal implements debugger UI interface with a basic ANSI terminal
@ -16,6 +13,8 @@ type ColorTerminal struct {
reader *bufio.Reader
commandHistory []command
tabCompleter ui.TabCompleter
}
type command struct {
@ -42,217 +41,8 @@ func (ct *ColorTerminal) CleanUp() {
ct.Terminal.CleanUp()
}
// UserPrint implementation for debugger.UI interface
func (ct *ColorTerminal) UserPrint(pp debugger.PrintProfile, s string, a ...interface{}) {
if pp != debugger.Input {
ct.Print("\r")
}
switch pp {
case debugger.CPUStep:
ct.Print(ansi.PenColor["yellow"])
case debugger.VideoStep:
ct.Print(ansi.DimPens["yellow"])
case debugger.MachineInfo:
ct.Print(ansi.PenColor["cyan"])
case debugger.Error:
ct.Print(ansi.PenColor["red"])
ct.Print(ansi.PenColor["bold"])
ct.Print("* ")
ct.Print(ansi.NormalPen)
ct.Print(ansi.PenColor["red"])
case debugger.Feedback:
ct.Print(ansi.DimPens["white"])
case debugger.Script:
ct.Print("> ")
case debugger.Prompt:
ct.Print(ansi.PenStyles["bold"])
}
ct.Print(s, a...)
ct.Print(ansi.NormalPen)
// add a newline if print profile is anything other than prompt
if pp != debugger.Prompt && pp != debugger.Input {
ct.Print("\n")
}
}
// UserRead implementation for debugger.UI interface
func (ct *ColorTerminal) UserRead(input []byte, prompt string) (int, error) {
ct.RawMode()
defer ct.CanonicalMode()
// er is used to store encoded runes (length of 4 should be enough)
er := make([]byte, 4)
n := 0
cursor := 0
history := len(ct.commandHistory)
// buffInput is used to store the latest input when we scroll through
// history - we don't want to lose what we've typed in case the user wants
// to resume where we left off
buffInput := make([]byte, cap(input))
buffN := 0
// the method for cursor placement is as follows:
// 1. for each iteration in the loop
// 2. store current cursor position
// 3. clear the current line
// 4. output the prompt
// 5. output the input buffer
// 6. restore the cursor position
//
// for this to work we need to place the cursor in it's initial position,
ct.Print("\r%s", ansi.CursorMove(len(prompt)))
for {
ct.Print(ansi.CursorStore)
ct.UserPrint(debugger.Prompt, "%s%s", ansi.ClearLine, prompt)
ct.UserPrint(debugger.Input, string(input[:n]))
ct.Print(ansi.CursorRestore)
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.KeyCtrlC:
// CTRL-C
ct.Print("\n")
return n + 1, &debugger.UserInterrupt{Message: "user interrupt: CTRL-C"}
case easyterm.KeyTab:
case easyterm.KeyCarriageReturn:
// CARRIAGE RETURN
// check to see if input is the same as the last history entry
newEntry := false
if n > 0 {
newEntry = true
if len(ct.commandHistory) > 0 {
lastHistoryEntry := ct.commandHistory[len(ct.commandHistory)-1].input
if len(lastHistoryEntry) == n {
newEntry = false
for i := 0; i < n; i++ {
if input[i] != lastHistoryEntry[i] {
newEntry = true
break
}
}
}
}
}
// if input is not the same as the last history entry then append a
// new entry to the history list
if newEntry {
nh := make([]byte, n)
copy(nh, input[:n])
ct.commandHistory = append(ct.commandHistory, command{input: nh})
}
ct.Print("\n")
return n + 1, nil
case easyterm.KeyEsc:
// ESCAPE SEQUENCE BEGIN
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.EscCursor:
// CURSOR KEY
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.CursorUp:
// move up through command history
if len(ct.commandHistory) > 0 {
// if we're at the end of the command history then store
// the current input in buffInput for possible later editing
if history == len(ct.commandHistory) {
copy(buffInput, input[:n])
buffN = n
}
if history > 0 {
history--
copy(input, ct.commandHistory[history].input)
n = len(ct.commandHistory[history].input)
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
}
}
case easyterm.CursorDown:
// move down through command history
if len(ct.commandHistory) > 0 {
if history < len(ct.commandHistory)-1 {
history++
copy(input, ct.commandHistory[history].input)
n = len(ct.commandHistory[history].input)
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
} else if history == len(ct.commandHistory)-1 {
history++
copy(input, buffInput)
n = buffN
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
}
}
case easyterm.CursorForward:
// move forward through current command input
if cursor < n {
ct.Print(ansi.CursorForwardOne)
cursor++
}
case easyterm.CursorBackward:
// move backward through current command input
if cursor > 0 {
ct.Print(ansi.CursorBackwardOne)
cursor--
}
case easyterm.EscDelete:
// DELETE
if cursor < n {
copy(input[cursor:], input[cursor+1:])
cursor--
n--
history = len(ct.commandHistory)
}
}
}
case easyterm.KeyBackspace:
// BACKSPACE
if cursor > 0 {
copy(input[cursor-1:], input[cursor:])
ct.Print(ansi.CursorBackwardOne)
cursor--
n--
history = len(ct.commandHistory)
}
default:
if unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsSpace(r) {
ct.Print("%c", r)
m := utf8.EncodeRune(er, r)
copy(input[cursor+m:], input[cursor:])
copy(input[cursor:], er[:m])
cursor++
n += m
history = len(ct.commandHistory)
}
}
}
// RegisterTabCompleter adds an implementation of TabCompleter to the
// ColorTerminal
func (ct *ColorTerminal) RegisterTabCompleter(tc ui.TabCompleter) {
ct.tabCompleter = tc
}

193
debugger/colorterm/input.go Normal file
View file

@ -0,0 +1,193 @@
package colorterm
import (
"gopher2600/debugger/colorterm/ansi"
"gopher2600/debugger/colorterm/easyterm"
"gopher2600/debugger/ui"
"unicode"
"unicode/utf8"
)
// UserRead is the top level input function
func (ct *ColorTerminal) UserRead(input []byte, prompt string) (int, error) {
ct.RawMode()
defer ct.CanonicalMode()
// er is used to store encoded runes (length of 4 should be enough)
er := make([]byte, 4)
n := 0
cursor := 0
history := len(ct.commandHistory)
// buffInput is used to store the latest input when we scroll through
// history - we don't want to lose what we've typed in case the user wants
// to resume where we left off
buffInput := make([]byte, cap(input))
buffN := 0
// the method for cursor placement is as follows:
// 1. for each iteration in the loop
// 2. store current cursor position
// 3. clear the current line
// 4. output the prompt
// 5. output the input buffer
// 6. restore the cursor position
//
// for this to work we need to place the cursor in it's initial position,
ct.Print("\r%s", ansi.CursorMove(len(prompt)))
for {
ct.Print(ansi.CursorStore)
ct.UserPrint(ui.Prompt, "%s%s", ansi.ClearLine, prompt)
ct.UserPrint(ui.Input, string(input[:n]))
ct.Print(ansi.CursorRestore)
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.KeyCtrlC:
// CTRL-C
ct.Print("\n")
return n + 1, &ui.UserInterrupt{Message: "user interrupt: CTRL-C"}
case easyterm.KeyTab:
if ct.tabCompleter != nil {
s := ct.tabCompleter.GuessWord(string(input[:n]))
n = len(s)
copy(input, []byte(s))
}
case easyterm.KeyCarriageReturn:
// CARRIAGE RETURN
// check to see if input is the same as the last history entry
newEntry := false
if n > 0 {
newEntry = true
if len(ct.commandHistory) > 0 {
lastHistoryEntry := ct.commandHistory[len(ct.commandHistory)-1].input
if len(lastHistoryEntry) == n {
newEntry = false
for i := 0; i < n; i++ {
if input[i] != lastHistoryEntry[i] {
newEntry = true
break
}
}
}
}
}
// if input is not the same as the last history entry then append a
// new entry to the history list
if newEntry {
nh := make([]byte, n)
copy(nh, input[:n])
ct.commandHistory = append(ct.commandHistory, command{input: nh})
}
ct.Print("\n")
return n + 1, nil
case easyterm.KeyEsc:
// ESCAPE SEQUENCE BEGIN
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.EscCursor:
// CURSOR KEY
r, _, err := ct.reader.ReadRune()
if err != nil {
return n, err
}
switch r {
case easyterm.CursorUp:
// move up through command history
if len(ct.commandHistory) > 0 {
// if we're at the end of the command history then store
// the current input in buffInput for possible later editing
if history == len(ct.commandHistory) {
copy(buffInput, input[:n])
buffN = n
}
if history > 0 {
history--
copy(input, ct.commandHistory[history].input)
n = len(ct.commandHistory[history].input)
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
}
}
case easyterm.CursorDown:
// move down through command history
if len(ct.commandHistory) > 0 {
if history < len(ct.commandHistory)-1 {
history++
copy(input, ct.commandHistory[history].input)
n = len(ct.commandHistory[history].input)
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
} else if history == len(ct.commandHistory)-1 {
history++
copy(input, buffInput)
n = buffN
ct.Print(ansi.CursorMove(n - cursor))
cursor = n
}
}
case easyterm.CursorForward:
// move forward through current command input
if cursor < n {
ct.Print(ansi.CursorForwardOne)
cursor++
}
case easyterm.CursorBackward:
// move backward through current command input
if cursor > 0 {
ct.Print(ansi.CursorBackwardOne)
cursor--
}
case easyterm.EscDelete:
// DELETE
if cursor < n {
copy(input[cursor:], input[cursor+1:])
cursor--
n--
history = len(ct.commandHistory)
}
}
}
case easyterm.KeyBackspace:
// BACKSPACE
if cursor > 0 {
copy(input[cursor-1:], input[cursor:])
ct.Print(ansi.CursorBackwardOne)
cursor--
n--
history = len(ct.commandHistory)
}
default:
if unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsSpace(r) {
ct.Print("%c", r)
m := utf8.EncodeRune(er, r)
copy(input[cursor+m:], input[cursor:])
copy(input[cursor:], er[:m])
cursor++
n += m
history = len(ct.commandHistory)
}
}
}
}

View file

@ -0,0 +1,42 @@
package colorterm
import (
"gopher2600/debugger/colorterm/ansi"
"gopher2600/debugger/ui"
)
// UserPrint is the top level output function
func (ct *ColorTerminal) UserPrint(pp ui.PrintProfile, s string, a ...interface{}) {
if pp != ui.Input {
ct.Print("\r")
}
switch pp {
case ui.CPUStep:
ct.Print(ansi.PenColor["yellow"])
case ui.VideoStep:
ct.Print(ansi.DimPens["yellow"])
case ui.MachineInfo:
ct.Print(ansi.PenColor["cyan"])
case ui.Error:
ct.Print(ansi.PenColor["red"])
ct.Print(ansi.PenColor["bold"])
ct.Print("* ")
ct.Print(ansi.NormalPen)
ct.Print(ansi.PenColor["red"])
case ui.Feedback:
ct.Print(ansi.DimPens["white"])
case ui.Script:
ct.Print("> ")
case ui.Prompt:
ct.Print(ansi.PenStyles["bold"])
}
ct.Print(s, a...)
ct.Print(ansi.NormalPen)
// add a newline if print profile is anything other than prompt
if pp != ui.Prompt && pp != ui.Input {
ct.Print("\n")
}
}

View file

@ -2,6 +2,7 @@ package debugger
import (
"fmt"
"gopher2600/debugger/ui"
"gopher2600/hardware"
"gopher2600/hardware/cpu"
"gopher2600/television"
@ -39,7 +40,7 @@ type Debugger struct {
inputloopVideoClock bool // step mode
// user interface
ui UserInterface
ui ui.UserInterface
uiSilent bool // controls whether UI is to remain silent
}
@ -73,15 +74,15 @@ func NewDebugger() (*Debugger, error) {
}
// Start the main debugger sequence
func (dbg *Debugger) Start(ui UserInterface, filename string) error {
func (dbg *Debugger) Start(interf ui.UserInterface, filename string) error {
// prepare user interface
if ui == nil {
dbg.ui = new(PlainTerminal)
if interf == nil {
dbg.ui = new(ui.PlainTerminal)
if dbg.ui == nil {
return fmt.Errorf("error allocationg memory for UI")
}
} else {
dbg.ui = ui
dbg.ui = interf
}
err := dbg.ui.Initialise()
@ -90,6 +91,8 @@ func (dbg *Debugger) Start(ui UserInterface, filename string) error {
}
defer dbg.ui.CleanUp()
dbg.ui.RegisterTabCompleter(dbg)
err = dbg.vcs.AttachCartridge(filename)
if err != nil {
return err
@ -123,7 +126,7 @@ func (dbg *Debugger) Start(ui UserInterface, filename string) error {
// videoCycleInputLoop is a wrapper function to be used when calling vcs.Step()
func (dbg *Debugger) videoCycleInputLoop(result *cpu.InstructionResult) error {
if dbg.inputloopVideoClock {
dbg.print(VideoStep, "%v", result)
dbg.print(ui.VideoStep, "%v", result)
}
return dbg.inputLoop(false)
}
@ -182,8 +185,8 @@ func (dbg *Debugger) inputLoop(mainLoop bool) error {
n, err := dbg.ui.UserRead(dbg.input, prompt)
if err != nil {
switch err.(type) {
case *UserInterrupt:
dbg.print(Feedback, err.Error())
case *ui.UserInterrupt:
dbg.print(ui.Feedback, err.Error())
return nil
default:
return err
@ -193,7 +196,7 @@ func (dbg *Debugger) inputLoop(mainLoop bool) error {
// parse user input
dbg.inputloopNext, err = dbg.parseInput(string(dbg.input[:n-1]))
if err != nil {
dbg.print(Error, "%s", err)
dbg.print(ui.Error, "%s", err)
}
// prepare for next loop
@ -218,7 +221,7 @@ func (dbg *Debugger) inputLoop(mainLoop bool) error {
if err != nil {
return err
}
dbg.print(CPUStep, "%v", result)
dbg.print(ui.CPUStep, "%v", result)
} else {
return nil
}
@ -302,7 +305,7 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
} else {
if parts[1] == "OFF" {
dbg.commandOnHalt = ""
dbg.print(Feedback, "no auto-command on halt")
dbg.print(ui.Feedback, "no auto-command on halt")
return false, nil
}
@ -320,10 +323,10 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
dbg.commandOnHaltStored = dbg.commandOnHalt
}
dbg.print(Feedback, "auto-command on halt: %s", dbg.commandOnHalt)
dbg.print(ui.Feedback, "auto-command on halt: %s", dbg.commandOnHalt)
case "MEMMAP":
dbg.print(MachineInfo, "%v", dbg.vcs.Mem.MemoryMap())
dbg.print(ui.MachineInfo, "%v", dbg.vcs.Mem.MemoryMap())
case "QUIT":
dbg.running = false
@ -333,7 +336,7 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
if err != nil {
return false, err
}
dbg.print(Feedback, "machine reset")
dbg.print(ui.Feedback, "machine reset")
case "RUN":
dbg.runUntilHalt = true
@ -367,21 +370,21 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
} else {
stepMode = "cpu"
}
dbg.print(Feedback, "step mode: %s", stepMode)
dbg.print(ui.Feedback, "step mode: %s", stepMode)
case "TERSE":
dbg.machineInfoVerbose = false
dbg.print(Feedback, "verbosity: terse")
dbg.print(ui.Feedback, "verbosity: terse")
case "VERBOSE":
dbg.machineInfoVerbose = true
dbg.print(Feedback, "verbosity: verbose")
dbg.print(ui.Feedback, "verbosity: verbose")
case "VERBOSITY":
if dbg.machineInfoVerbose {
dbg.print(Feedback, "verbosity: verbose")
dbg.print(ui.Feedback, "verbosity: verbose")
} else {
dbg.print(Feedback, "verbosity: terse")
dbg.print(ui.Feedback, "verbosity: terse")
}
case "DEBUGGERSTATE":
@ -403,14 +406,14 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
for i := 1; i < len(parts); i++ {
addr, err := strconv.ParseUint(parts[i], 0, 16)
if err != nil {
dbg.print(Error, "bad argument to PEEK (%s)", parts[i])
dbg.print(ui.Error, "bad argument to PEEK (%s)", parts[i])
continue
}
// peform peek
val, mappedAddress, areaName, addressLabel, err := dbg.vcs.Mem.Peek(uint16(addr))
if err != nil {
dbg.print(Error, "%s", err)
dbg.print(ui.Error, "%s", err)
continue
}
@ -423,7 +426,7 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
if addressLabel != "" {
s = fmt.Sprintf("%s [%s]", s, addressLabel)
}
dbg.print(MachineInfo, s)
dbg.print(ui.MachineInfo, s)
}
case "RIOT":
@ -458,3 +461,8 @@ func (dbg *Debugger) parseCommand(input string) (bool, error) {
return stepNext, nil
}
// GuessWord implements tabcompletion.TabCompleter interface
func (dbg *Debugger) GuessWord(input string) string {
return input
}

View file

@ -1,5 +1,7 @@
package debugger
import "gopher2600/debugger/ui"
// types that satisfy machineInfo return information about the state of the
// emulated machine. String() should return verbose info, while StringTerse()
// the more terse equivalent.
@ -10,7 +12,7 @@ type machineInfo interface {
}
func (dbg Debugger) printMachineInfo(mi machineInfo) {
dbg.print(MachineInfo, "%s", dbg.sprintMachineInfo(mi))
dbg.print(ui.MachineInfo, "%s", dbg.sprintMachineInfo(mi))
}
func (dbg Debugger) sprintMachineInfo(mi machineInfo) string {

View file

@ -1,38 +1,16 @@
package debugger
import (
"gopher2600/debugger/ui"
"strings"
)
// PrintProfile specifies the printing mode
type PrintProfile int
// enumeration of print profile types
const (
// the following profiles ar generated as a result of the emulation
CPUStep PrintProfile = iota
VideoStep
MachineInfo
// the following profiles are generated by the debugger in response to user
// input
Feedback
Prompt
Script
// user input (not used by all user interface types [eg. echoing terminals])
Input
// errors can be generated by the emulation or the debugger
Error
)
// 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 PrintProfile, s string, a ...interface{}) {
if dbg.uiSilent && pp != Error {
func (dbg Debugger) print(pp ui.PrintProfile, s string, a ...interface{}) {
if dbg.uiSilent && pp != ui.Error {
return
}

View file

@ -2,6 +2,7 @@ package debugger
import (
"fmt"
"gopher2600/debugger/ui"
"os"
"strings"
)
@ -62,14 +63,14 @@ func (dbg *Debugger) RunScript(scriptfile string, silent bool) error {
for i := 0; i < len(lines); i++ {
if strings.Trim(lines[i], " ") != "" {
if !silent {
dbg.print(Script, lines[i])
dbg.print(ui.Script, lines[i])
}
next, err := dbg.parseInput(lines[i])
if err != nil {
dbg.print(Error, fmt.Sprintf("script error (%s): %s", scriptfile, err.Error()))
dbg.print(ui.Error, fmt.Sprintf("script error (%s): %s", scriptfile, err.Error()))
}
if next {
dbg.print(Error, fmt.Sprintf("script error (%s): use of '%s' is not recommended in scripts", scriptfile, lines[i]))
dbg.print(ui.Error, fmt.Sprintf("script error (%s): use of '%s' is not recommended in scripts", scriptfile, lines[i]))
// make sure run state is still sane
dbg.runUntilHalt = false

View file

@ -2,6 +2,7 @@ package debugger
import (
"fmt"
"gopher2600/debugger/ui"
)
// traps keeps track of all the currently defined trappers
@ -37,7 +38,7 @@ func (tr *traps) check() bool {
ntr := tr.traps[i].target.ToInt() != tr.traps[i].origValue
if ntr {
tr.traps[i].origValue = tr.traps[i].target.ToInt()
tr.dbg.print(Feedback, "trap on %s", tr.traps[i].target.ShortLabel())
tr.dbg.print(ui.Feedback, "trap on %s", tr.traps[i].target.ShortLabel())
}
trapped = ntr || trapped
}
@ -47,7 +48,7 @@ func (tr *traps) check() bool {
func (tr traps) list() {
if len(tr.traps) == 0 {
tr.dbg.print(Feedback, "no traps")
tr.dbg.print(ui.Feedback, "no traps")
} else {
s := ""
sep := ""
@ -55,7 +56,7 @@ func (tr traps) list() {
s = fmt.Sprintf("%s%s%s", s, sep, tr.traps[i].target.ShortLabel())
sep = ", "
}
tr.dbg.print(Feedback, s)
tr.dbg.print(ui.Feedback, s)
}
}
@ -70,7 +71,7 @@ func (tr *traps) parseTrap(parts []string) error {
switch parts[i] {
case "CLEAR":
tr.clear()
tr.dbg.print(Feedback, "traps cleared")
tr.dbg.print(ui.Feedback, "traps cleared")
return nil
case "LIST":
tr.list()

View file

@ -1,29 +1,10 @@
package debugger
package ui
import (
"fmt"
"os"
)
// UserInterface defines the user interface operations required by the debugger
type UserInterface interface {
Initialise() error
CleanUp()
UserPrint(PrintProfile, string, ...interface{})
UserRead([]byte, string) (int, error)
}
// UserInterrupt can be returned by UserRead() when user has cause an
// interrupt (ie. CTRL-C)
type UserInterrupt struct {
Message string
}
// implement Error interface for UserInterrupt
func (intr UserInterrupt) Error() string {
return intr.Message
}
// PlainTerminal is the default, most basic terminal interface
type PlainTerminal struct {
}
@ -37,6 +18,10 @@ func (pt *PlainTerminal) Initialise() error {
func (pt *PlainTerminal) CleanUp() {
}
// RegisterTabCompleter adds an implementation of TabCompleter to the terminal
func (pt *PlainTerminal) RegisterTabCompleter(tc TabCompleter) {
}
// UserPrint is the plain terminal print routine
func (pt PlainTerminal) UserPrint(pp PrintProfile, s string, a ...interface{}) {
switch pp {

View file

@ -0,0 +1,24 @@
package ui
// PrintProfile specifies the printing mode
type PrintProfile int
// enumeration of print profile types
const (
// the following profiles ar generated as a result of the emulation
CPUStep PrintProfile = iota
VideoStep
MachineInfo
// the following profiles are generated by the debugger in response to user
// input
Feedback
Prompt
Script
// user input (not used by all user interface types [eg. echoing terminals])
Input
// errors can be generated by the emulation or the debugger
Error
)

View file

@ -0,0 +1,6 @@
package ui
// TabCompleter defines the types that can be used for tab completion
type TabCompleter interface {
GuessWord(string) string
}

21
debugger/ui/ui.go Normal file
View file

@ -0,0 +1,21 @@
package ui
// UserInterface defines the user interface operations required by the debugger
type UserInterface interface {
Initialise() error
CleanUp()
RegisterTabCompleter(TabCompleter)
UserPrint(PrintProfile, string, ...interface{})
UserRead([]byte, string) (int, error)
}
// UserInterrupt can be returned by UserRead() when user has cause an
// interrupt (ie. CTRL-C)
type UserInterrupt struct {
Message string
}
// implement Error interface for UserInterrupt
func (intr UserInterrupt) Error() string {
return intr.Message
}

View file

@ -5,6 +5,7 @@ import (
"fmt"
"gopher2600/debugger"
"gopher2600/debugger/colorterm"
"gopher2600/debugger/ui"
"gopher2600/hardware"
"gopher2600/television"
"os"
@ -41,7 +42,7 @@ func main() {
}
// start debugger with choice of interface and cartridge
var term debugger.UserInterface
var term ui.UserInterface
switch strings.ToUpper(*termType) {
case "COLOR":
@ -50,7 +51,7 @@ func main() {
fmt.Printf("! unknown terminal type (%s) defaulting to plain\n", *termType)
fallthrough
case "PLAIN":
term = new(debugger.PlainTerminal)
term = new(ui.PlainTerminal)
}
err = dbg.Start(term, cartridgeFile)