mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
o cpu
- added XAA command o debugger - renamed ui package to console package - GUI instance now passed to NewDebugger, rather than created internally o regression - begun refactor
This commit is contained in:
parent
8ddaec1233
commit
3205951fe1
19 changed files with 364 additions and 350 deletions
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ui
|
||||
package console
|
||||
|
||||
import "gopher2600/debugger/input"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ui
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package ui
|
||||
package console
|
||||
|
||||
// PrintProfile specifies the printing mode
|
||||
type PrintProfile int
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Can't render this file because it contains an unexpected character in line 16 and column 5.
|
|
@ -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},
|
||||
|
|
216
regression/database.go
Normal file
216
regression/database.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue