Debugger Complete

This commit is contained in:
Brian Buller 2016-12-23 15:45:12 -06:00
parent 54a91e53c7
commit 22dd9915c3
1 changed files with 182 additions and 58 deletions

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"strconv"
"strings"
"time"
@ -23,6 +24,7 @@ var instructions, lastInst []string
var curr, cursor int
var pause, step, skip bool
var breakpoints []int
var eventChan chan termbox.Event
func main() {
var debug bool
@ -49,12 +51,13 @@ func main() {
}
defer termbox.Close()
eventChan := make(chan termbox.Event)
eventChan = make(chan termbox.Event)
go readUserInput(eventChan)
go sendNoneEvent(eventChan)
curr = 0
var ins []string
for curr < len(instructions) || done {
isBreak := isBreakpoint(curr)
if debug {
lastInst = ins
}
@ -62,68 +65,20 @@ func main() {
if len(ins) == 0 {
break
}
// Optimize multiplication
/*
> cpy b c
inc a
dec c
jnz c -2
dec d
jnz d -5
== add (b * d) to a
set c & d to 0
curr += 5
*/
if ins[0] == "cpy" {
if len(instructions) >= curr+6 {
ins1 := strings.Fields(instructions[curr+1])
ins2 := strings.Fields(instructions[curr+2])
ins3 := strings.Fields(instructions[curr+3])
ins4 := strings.Fields(instructions[curr+4])
ins5 := strings.Fields(instructions[curr+5])
if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" {
allGood := true
if allGood {
// Do the multiplication
// ins[1] * ins4[1]
// add that value to ins1[1]
// set ins[2] to 0
// set ins4[1] to 0
// Then add 5 to the pc
src1 := ins[1]
src2 := ins4[1]
dst := ins1[1]
if _, ok := regs[dst]; !ok {
// Invalid destination register
allGood = false
}
if _, ok := regs[src1]; !ok {
// Invalid source register
allGood = false
}
if _, ok := regs[src2]; !ok {
allGood = false
}
if allGood {
regs[dst] += (regs[src1] * regs[src2])
curr += 6
skip = true
}
}
}
}
if isMultOp(curr) {
curr = doMultOp(curr)
skip = true
}
if debug {
if isBreakpoint(curr + 1) {
if isBreak {
pause = true
step = false
}
for pause && !step {
for pause && !step && !done {
// print state and wait for user to step
PrintState()
fmt.Println("Last Instruction:", lastInst)
PrintInstructionState()
fmt.Println("(q): quit (space): pause/unpause (s): step (b): toggle breakpoint")
PrintDebugHelp()
ev := <-eventChan
if ev.Type == termbox.EventKey {
switch {
@ -135,6 +90,8 @@ func main() {
toggleBreakpoint(cursor)
case ev.Ch == 's':
step = true
case ev.Ch == 'u':
updateRegister()
case ev.Key == termbox.KeyArrowUp:
if cursor > 0 {
cursor--
@ -268,6 +225,7 @@ func PrintRegs() {
}
func PrintInstructionState() {
optim := color.New(color.FgGreen)
for pi := range instructions {
if pi == curr {
fmt.Print(">")
@ -281,12 +239,28 @@ func PrintInstructionState() {
}
if cursor == pi {
cursor := color.New(color.FgBlack).Add(color.BgWhite)
cursor.Println(instructions[pi])
if isMultOp(pi) {
cursor.Println("+ " + instructions[pi] + " == optimized")
} else if mo, _ := isInMultOp(pi); mo {
cursor.Println("| " + instructions[pi])
} else {
cursor.Println(instructions[pi])
}
} else {
fmt.Println(instructions[pi])
if isMultOp(pi) {
optim.Println("+ " + instructions[pi] + " == optimized")
} else if mo, _ := isInMultOp(pi); mo {
optim.Println("| " + instructions[pi])
} else {
fmt.Println(instructions[pi])
}
}
}
fmt.Println(instructions[curr])
}
func PrintDebugHelp() {
fmt.Println("(s): step | (space): pause/unpause | (b): toggle breakpoint")
fmt.Println("(u): update register to value | (q): quit")
}
func readUserInput(e chan termbox.Event) {
@ -304,7 +278,111 @@ func sendNoneEvent(e chan termbox.Event) {
}
}
// isInMultOp returns true and the pc for the
// start of the multiplication operation if pos is
// located within it
func isInMultOp(pos int) (bool, int) {
// We need to check instructions[pos +- 5] to see if any
// are a multiplication op and return the pos for the (first) one
// that is
if isMultOp(pos) {
return true, pos
}
for i := 1; i < 6; i++ {
if isMultOp(pos - i) {
return true, (pos - i)
}
}
return false, pos
}
// isMultOpStart return whether pos is the start of
// what can be optimized as multiplication
/*
> cpy b c
inc a
dec c
jnz c -2
dec d
jnz d -5
== add (b * d) to a
set c & d to 0
curr += 5
*/
func isMultOp(pos int) bool {
if pos < 0 || pos > len(instructions)-1 {
return false
}
ins := strings.Fields(instructions[pos])
if len(ins) < 3 {
return false
}
if ins[0] == "cpy" {
if len(instructions) >= pos+6 {
ins1 := strings.Fields(instructions[pos+1])
ins2 := strings.Fields(instructions[pos+2])
ins3 := strings.Fields(instructions[pos+3])
ins4 := strings.Fields(instructions[pos+4])
ins5 := strings.Fields(instructions[pos+5])
if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" {
allGood := true
if allGood {
// Do the multiplication
// ins[1] * ins4[1]
// add that value to ins1[1]
// set ins[2] to 0
// set ins4[1] to 0
// Then add 5 to the pc
src1 := ins[1]
src2 := ins4[1]
dst := ins1[1]
if _, ok := regs[dst]; !ok {
// Invalid destination register
allGood = false
}
if _, ok := regs[src1]; !ok {
// Invalid source register
allGood = false
}
if _, ok := regs[src2]; !ok {
allGood = false
}
return allGood
}
}
}
}
return false
}
// doMultOp performs a multiplcation operation and returns the new pc
func doMultOp(pos int) int {
isOp, pos := isInMultOp(pos)
if !isOp {
// No optimization to do
return pos
}
ins := strings.Fields(instructions[pos])
ins1 := strings.Fields(instructions[pos+1])
ins4 := strings.Fields(instructions[pos+4])
// Do the multiplication
// ins[1] * ins4[1]
// add that value to ins1[1]
// set ins[2] to 0
// set ins4[1] to 0
// Then add 5 to the pc
src1 := ins[1]
src2 := ins4[1]
dst := ins1[1]
regs[dst] += (regs[src1] * regs[src2])
regs[ins[2]] = 0
regs[ins4[1]] = 0
pos += 6
return pos
}
func toggleBreakpoint(pos int) {
_, pos = isInMultOp(pos)
for i := 0; i < len(breakpoints); i++ {
if breakpoints[i] == pos {
breakpoints = append(breakpoints[:i], breakpoints[i+1:]...)
@ -315,6 +393,7 @@ func toggleBreakpoint(pos int) {
}
func isBreakpoint(pos int) bool {
_, pos = isInMultOp(pos)
for i := 0; i < len(breakpoints); i++ {
if breakpoints[i] == pos {
return true
@ -322,3 +401,48 @@ func isBreakpoint(pos int) bool {
}
return false
}
func updateRegister() {
var updReg string
fmt.Println("Update which register? (a,b,c,d)")
ev := <-eventChan
if ev.Type == termbox.EventKey {
switch {
case ev.Ch == 'a':
updReg = "a"
case ev.Ch == 'b':
updReg = "b"
case ev.Ch == 'c':
updReg = "c"
case ev.Ch == 'd':
updReg = "d"
default:
fmt.Println("Invalid register (" + string(ev.Ch) + ")")
fmt.Println("Press any key to continue")
ev = <-eventChan
return
}
}
fmt.Println("Enter new value (must be integer, end with enter)")
var newVal string
for ev.Key != termbox.KeyEnter {
ev = <-eventChan
if ev.Ch >= '0' && ev.Ch <= '9' {
newVal += string(ev.Ch)
fmt.Print(string(ev.Ch))
}
}
fmt.Println("Setting Register (" + updReg + ") to " + newVal)
var v int
var e error
if v, e = strconv.Atoi(newVal); e != nil {
fmt.Println()
fmt.Println("Error parsing integer")
fmt.Println(e)
fmt.Println("Press any key to continue")
ev = <-eventChan
return
}
regs[updReg] = v
}