debugger: more hacking, hex editor with full editing now works
This commit is contained in:
parent
12ddbeb508
commit
e2f4103fdc
|
@ -6,14 +6,14 @@ import (
|
|||
"strings"
|
||||
"unicode"
|
||||
|
||||
"git.sentinel65x.com/dave/gary/debugger/memory"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
lip "github.com/charmbracelet/lipgloss"
|
||||
"github.com/creachadair/mds/slice"
|
||||
)
|
||||
|
||||
type HexViewUpdateMem struct {
|
||||
addr int
|
||||
bytes []byte
|
||||
type HexViewSetHeight struct {
|
||||
Height int
|
||||
}
|
||||
|
||||
type HexView struct {
|
||||
|
@ -21,23 +21,23 @@ type HexView struct {
|
|||
ZeroStyle lip.Style
|
||||
HexStyle lip.Style
|
||||
|
||||
write func(int, byte) tea.Cmd
|
||||
mem *memory.Memory
|
||||
commit func() tea.Cmd
|
||||
height int
|
||||
firstAddr int // top of viewport
|
||||
bytes []byte
|
||||
selectedAddr int
|
||||
editing bool
|
||||
editNibble int // 0 or 1
|
||||
newByte byte
|
||||
}
|
||||
|
||||
func NewHexView(size int, writeByte func(addr int, val byte) tea.Cmd) HexView {
|
||||
func NewHexView(mem *memory.Memory, commit func() tea.Cmd) HexView {
|
||||
st := lip.NewStyle()
|
||||
ret := HexView{
|
||||
AddrStyle: st,
|
||||
ZeroStyle: st,
|
||||
HexStyle: st,
|
||||
write: writeByte,
|
||||
bytes: make([]byte, size),
|
||||
mem: mem,
|
||||
commit: commit,
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -52,12 +52,25 @@ func (m HexView) SelectedAddr() int {
|
|||
return m.selectedAddr
|
||||
}
|
||||
|
||||
func (m *HexView) moveSelection(delta int) bool {
|
||||
newAddr := m.selectedAddr + delta
|
||||
if newAddr >= 0 && newAddr < m.mem.Len() {
|
||||
m.selectedAddr = newAddr
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m HexView) VisibleBytes() (start, end int) {
|
||||
return m.firstAddr, m.firstAddr + (m.height * 16)
|
||||
}
|
||||
|
||||
func (m HexView) addrFormat() string {
|
||||
return fmt.Sprintf("%%%dx", m.addrNibbles())
|
||||
}
|
||||
|
||||
func (m HexView) addrNibbles() int {
|
||||
return int(math.Ceil(math.Log2(float64(len(m.bytes)-1)))) / 4
|
||||
return int(math.Ceil(math.Log2(float64(m.mem.Len())*8))) / 4
|
||||
}
|
||||
|
||||
func (m HexView) Update(msg tea.Msg) (HexView, tea.Cmd) {
|
||||
|
@ -70,31 +83,43 @@ func (m HexView) Update(msg tea.Msg) (HexView, tea.Cmd) {
|
|||
|
||||
func (m HexView) updateEditMode(msg tea.Msg) (HexView, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case HexViewUpdateMem:
|
||||
copy(m.bytes[msg.addr:], msg.bytes)
|
||||
case HexViewSetHeight:
|
||||
m.height = msg.Height
|
||||
case tea.KeyMsg:
|
||||
switch msg.Type {
|
||||
case tea.KeyLeft:
|
||||
if m.editNibble == 1 {
|
||||
m.editNibble = 0
|
||||
} else if m.moveSelection(-1) {
|
||||
m.editNibble = 1
|
||||
}
|
||||
case tea.KeyRight:
|
||||
if m.editNibble == 0 {
|
||||
m.editNibble = 1
|
||||
} else if m.moveSelection(1) {
|
||||
m.editNibble = 0
|
||||
}
|
||||
case tea.KeyUp:
|
||||
m.moveSelection(-16)
|
||||
case tea.KeyDown:
|
||||
m.moveSelection(16)
|
||||
case tea.KeyEsc:
|
||||
m.editing = false
|
||||
case tea.KeyEnter:
|
||||
m.editing = false
|
||||
return m, m.write(m.selectedAddr, m.newByte)
|
||||
return m, m.commit()
|
||||
case tea.KeyRunes:
|
||||
if unicode.In(msg.Runes[0], unicode.ASCII_Hex_Digit) {
|
||||
nibble := hexToNibble(msg.Runes[0])
|
||||
prev := m.mem.At(m.selectedAddr).Value
|
||||
if m.editNibble == 0 {
|
||||
m.newByte = (nibble << 4) + (m.newByte & 0xF)
|
||||
m.mem.Write(m.selectedAddr, (nibble<<4)+(prev&0xF))
|
||||
m.editNibble++
|
||||
} else {
|
||||
m.newByte = (m.newByte & 0xF0) + nibble
|
||||
m.mem.Write(m.selectedAddr, (prev&0xF0)+nibble)
|
||||
if m.moveSelection(1) {
|
||||
m.editNibble = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,45 +141,37 @@ func hexToNibble(r rune) byte {
|
|||
|
||||
func (m HexView) updateViewMode(msg tea.Msg) (HexView, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case HexViewUpdateMem:
|
||||
copy(m.bytes[msg.addr:], msg.bytes)
|
||||
case HexViewSetHeight:
|
||||
m.height = msg.Height
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "up":
|
||||
if m.selectedAddr >= 16 {
|
||||
m.selectedAddr -= 16
|
||||
}
|
||||
m.moveSelection(-16)
|
||||
case "down":
|
||||
if m.selectedAddr < len(m.bytes)-16 {
|
||||
m.selectedAddr += 16
|
||||
}
|
||||
m.moveSelection(16)
|
||||
case "left":
|
||||
if m.selectedAddr > 0 {
|
||||
m.selectedAddr--
|
||||
}
|
||||
m.moveSelection(-1)
|
||||
case "right":
|
||||
if m.selectedAddr < len(m.bytes)-1 {
|
||||
m.selectedAddr++
|
||||
}
|
||||
m.moveSelection(1)
|
||||
case "pgdown":
|
||||
// TODO
|
||||
m.moveSelection(m.height * 16)
|
||||
case "pgup":
|
||||
// TODO
|
||||
m.moveSelection(-m.height * 16)
|
||||
case "w":
|
||||
m.editing = true
|
||||
m.editNibble = 0
|
||||
m.newByte = m.bytes[m.selectedAddr]
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m HexView) View(height int) string {
|
||||
maxLen := 16 * height
|
||||
endAddr := min(len(m.bytes), m.firstAddr+maxLen)
|
||||
startAddr, endAddr := m.VisibleBytes()
|
||||
bytes := m.mem.Slice(startAddr, endAddr)
|
||||
|
||||
var ret strings.Builder
|
||||
addrFormat := m.addrFormat()
|
||||
for line, bytes := range slice.Chunks(m.bytes[m.firstAddr:endAddr], 16) {
|
||||
for line, bytes := range slice.Chunks(bytes, 16) {
|
||||
lineAddr := m.firstAddr + (16 * line)
|
||||
ret.WriteString(m.AddrStyle.Render(fmt.Sprintf(addrFormat, lineAddr)))
|
||||
ret.WriteString(" ")
|
||||
|
@ -166,22 +183,24 @@ func (m HexView) View(height int) string {
|
|||
ret.WriteByte(' ')
|
||||
}
|
||||
if m.editing && byteAddr == m.selectedAddr {
|
||||
st := m.HexStyle.Underline(true)
|
||||
st := m.HexStyle.Bold(true)
|
||||
s1, s2 := st.Reverse(true), st
|
||||
if m.editNibble == 1 {
|
||||
s1, s2 = s2, s1
|
||||
}
|
||||
b = m.newByte
|
||||
ret.WriteString(s1.Render(fmt.Sprintf("%01x", b>>4)))
|
||||
ret.WriteString(s2.Render(fmt.Sprintf("%01x", b&0xF)))
|
||||
ret.WriteString(s1.Render(fmt.Sprintf("%01x", b.Value>>4)))
|
||||
ret.WriteString(s2.Render(fmt.Sprintf("%01x", b.Value&0xF)))
|
||||
} else {
|
||||
st := m.HexStyle
|
||||
if m.selectedAddr == byteAddr {
|
||||
st = m.HexStyle.Reverse(true)
|
||||
} else if b == 0 {
|
||||
} else if !b.Valid || b.Value == 0 {
|
||||
st = m.ZeroStyle
|
||||
}
|
||||
ret.WriteString(st.Render(fmt.Sprintf("%02x", b)))
|
||||
if b.Changed {
|
||||
st = st.Underline(true).Bold(true)
|
||||
}
|
||||
ret.WriteString(st.Render(b.Hex()))
|
||||
}
|
||||
}
|
||||
ret.WriteByte('\n')
|
||||
|
|
103
debugger/main.go
103
debugger/main.go
|
@ -1,115 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"git.sentinel65x.com/dave/gary/debugger/memory"
|
||||
"git.sentinel65x.com/dave/gary/debugger/serial"
|
||||
)
|
||||
|
||||
const portDev = "/dev/ttyUSB0"
|
||||
|
||||
func main() {
|
||||
dbg, err := Open()
|
||||
dbg, err := serial.Open()
|
||||
if err != nil {
|
||||
log.Fatalf("connecting to debugger: %v", err)
|
||||
}
|
||||
defer dbg.Close()
|
||||
|
||||
if err := dbg.Write(0x42, 123); err != nil {
|
||||
log.Fatalf("writing to memory: %v", err)
|
||||
}
|
||||
mem := memory.New(dbg, 768)
|
||||
|
||||
if err := UI(dbg); err != nil {
|
||||
if err := UI(mem); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Debugger struct {
|
||||
port serial.Port
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func Open() (*Debugger, error) {
|
||||
mode := &serial.Mode{
|
||||
BaudRate: 115_200,
|
||||
}
|
||||
port, err := serial.Open(portDev, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Debugger{port: port}, nil
|
||||
}
|
||||
|
||||
func (d *Debugger) Close() error {
|
||||
d.mu.Lock() // note, deliberately no unlocking, to poison.
|
||||
return d.port.Close()
|
||||
}
|
||||
|
||||
func encode(addr int, write bool, data byte) [4]byte {
|
||||
writeVal := uint8(0)
|
||||
if write {
|
||||
writeVal = 1
|
||||
}
|
||||
var ret [4]byte
|
||||
ret[3] = data
|
||||
ret[2] = byte(addr<<1) | writeVal
|
||||
ret[1] = byte(addr >> 7)
|
||||
ret[0] = byte(addr >> 15)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (d *Debugger) Read(addr int) (byte, error) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if addr >= 2<<17 {
|
||||
return 0, fmt.Errorf("read %d out of bounds", addr)
|
||||
}
|
||||
|
||||
packet := encode(addr, false, 0)
|
||||
//fmt.Printf("Writing: %02x %02x %02x %02x\n", packet[0], packet[1], packet[2], packet[3])
|
||||
if _, err := d.port.Write(packet[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
d.port.SetReadTimeout(2 * time.Second)
|
||||
|
||||
n, err := d.port.Read(packet[:1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if n == 0 {
|
||||
return 0, errors.New("no read")
|
||||
}
|
||||
return packet[0], nil
|
||||
}
|
||||
|
||||
func (d *Debugger) Write(addr int, val byte) error {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if addr >= 2<<17 {
|
||||
return fmt.Errorf("write %d out of bounds", addr)
|
||||
}
|
||||
|
||||
packet := encode(addr, true, val)
|
||||
//fmt.Printf("Writing: %02x %02x %02x %02x\n", packet[0], packet[1], packet[2], packet[3])
|
||||
if _, err := d.port.Write(packet[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debugger) Dump(startAddr int, count int) ([]byte, error) {
|
||||
ret := make([]byte, count)
|
||||
var err error
|
||||
for i := range ret {
|
||||
ret[i], err = d.Read(startAddr + i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MemoryByte struct {
|
||||
Value byte
|
||||
Valid bool
|
||||
Changed bool
|
||||
}
|
||||
|
||||
func (b MemoryByte) Hex() string {
|
||||
if !b.Valid {
|
||||
return "??"
|
||||
}
|
||||
return fmt.Sprintf("%02x", b.Value)
|
||||
}
|
||||
|
||||
func (b MemoryByte) Dec() string {
|
||||
if !b.Valid {
|
||||
return "???"
|
||||
}
|
||||
return fmt.Sprintf("%d", b.Value)
|
||||
}
|
||||
|
||||
func (b MemoryByte) Bin() string {
|
||||
if !b.Valid {
|
||||
return "????????"
|
||||
}
|
||||
return fmt.Sprintf("%08b", b.Value)
|
||||
}
|
||||
|
||||
type MemorySrc interface {
|
||||
io.ReaderAt
|
||||
io.WriterAt
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
src MemorySrc
|
||||
|
||||
mu sync.Mutex
|
||||
bytes []MemoryByte
|
||||
pendingWrites map[int]byte
|
||||
}
|
||||
|
||||
func New(src MemorySrc, size int) *Memory {
|
||||
ret := &Memory{
|
||||
src: src,
|
||||
bytes: make([]MemoryByte, size),
|
||||
pendingWrites: map[int]byte{},
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *Memory) Len() int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return len(m.bytes)
|
||||
}
|
||||
|
||||
func (m *Memory) Load(start, count int) error {
|
||||
if m.needsRefresh(start, count) {
|
||||
return m.Refresh(start, count)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) needsRefresh(start, count int) bool {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for _, b := range m.bytes {
|
||||
if !b.Valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Memory) Refresh(start, count int) error {
|
||||
bs := make([]byte, count)
|
||||
if _, err := m.src.ReadAt(bs, int64(start)); err != nil {
|
||||
return err
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for i, v := range bs {
|
||||
m.bytes[start+i] = MemoryByte{v, true, false}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) At(addr int) MemoryByte {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if addr >= len(m.bytes) {
|
||||
return MemoryByte{}
|
||||
}
|
||||
if v, ok := m.pendingWrites[addr]; ok {
|
||||
return MemoryByte{v, true, true}
|
||||
}
|
||||
return m.bytes[addr]
|
||||
}
|
||||
|
||||
func (m *Memory) Slice(start, end int) []MemoryByte {
|
||||
ret := make([]MemoryByte, end-start)
|
||||
for i := range ret {
|
||||
ret[i] = m.At(start + i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *Memory) Write(addr int, val byte) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.pendingWrites[addr] = val
|
||||
}
|
||||
|
||||
func (m *Memory) Commit() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for addr, val := range m.pendingWrites {
|
||||
if _, err := m.src.WriteAt([]byte{val}, int64(addr)); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(m.pendingWrites, addr)
|
||||
m.bytes[addr] = MemoryByte{val, true, false}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) HasPendingWrites() bool {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return len(m.pendingWrites) > 0
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package serial
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.bug.st/serial"
|
||||
)
|
||||
|
||||
const portDev = "/dev/ttyUSB0"
|
||||
const maxAddr = 1 << 17
|
||||
|
||||
type Serial struct {
|
||||
portMu sync.Mutex
|
||||
port serial.Port
|
||||
}
|
||||
|
||||
func Open() (*Serial, error) {
|
||||
mode := &serial.Mode{
|
||||
BaudRate: 115_200,
|
||||
}
|
||||
port, err := serial.Open(portDev, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Serial{port: port}, nil
|
||||
}
|
||||
|
||||
func (d *Serial) Close() error {
|
||||
d.portMu.Lock()
|
||||
defer d.portMu.Unlock()
|
||||
return d.port.Close()
|
||||
}
|
||||
|
||||
func encode(addr int, write bool, data byte) [4]byte {
|
||||
writeVal := uint8(0)
|
||||
if write {
|
||||
writeVal = 1
|
||||
}
|
||||
var ret [4]byte
|
||||
ret[3] = data
|
||||
ret[2] = byte(addr<<1) | writeVal
|
||||
ret[1] = byte(addr >> 7)
|
||||
ret[0] = byte(addr >> 15)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (d *Serial) ReadAt(bs []byte, off int64) (int, error) {
|
||||
d.portMu.Lock()
|
||||
defer d.portMu.Unlock()
|
||||
if off+int64(len(bs)) >= maxAddr {
|
||||
return 0, errors.New("OOB read")
|
||||
}
|
||||
for i := range bs {
|
||||
packet := encode(int(off)+i, false, 0)
|
||||
if _, err := d.port.Write(packet[:]); err != nil {
|
||||
return i, err
|
||||
}
|
||||
d.port.SetReadTimeout(2 * time.Second)
|
||||
n, err := d.port.Read(packet[:1])
|
||||
if err != nil {
|
||||
return i, err
|
||||
} else if n == 0 {
|
||||
return i, errors.New("short read")
|
||||
}
|
||||
bs[i] = packet[0]
|
||||
}
|
||||
return len(bs), nil
|
||||
}
|
||||
|
||||
func (d *Serial) WriteAt(bs []byte, off int64) (int, error) {
|
||||
d.portMu.Lock()
|
||||
defer d.portMu.Unlock()
|
||||
if off+int64(len(bs)) >= maxAddr {
|
||||
return 0, errors.New("OOB write")
|
||||
}
|
||||
for i, v := range bs {
|
||||
packet := encode(int(off)+i, true, v)
|
||||
if _, err := d.port.Write(packet[:]); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return len(bs), nil
|
||||
}
|
126
debugger/ui.go
126
debugger/ui.go
|
@ -2,15 +2,26 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"log"
|
||||
|
||||
"git.sentinel65x.com/dave/gary/debugger/memory"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
lip "github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
func UI(dbg *Debugger) error {
|
||||
p := tea.NewProgram(initialModel(dbg)) //failed{0, 0, true}, tea.WithAltScreen())
|
||||
func UI(mem *memory.Memory) error {
|
||||
initial := debugger{
|
||||
width: 0,
|
||||
height: 0,
|
||||
mem: mem,
|
||||
bottomMsg: "Loading...",
|
||||
}
|
||||
initial.hex = NewHexView(mem, initial.commitWrites)
|
||||
initial.hex.AddrStyle = text
|
||||
initial.hex.ZeroStyle = faintText
|
||||
initial.hex.HexStyle = text
|
||||
|
||||
p := tea.NewProgram(initial)
|
||||
if _, err := p.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -27,6 +38,10 @@ var (
|
|||
BorderBackground(slate)
|
||||
)
|
||||
|
||||
type msgMemoryChanged struct {
|
||||
start, count int
|
||||
}
|
||||
|
||||
type msgErr struct {
|
||||
err error
|
||||
}
|
||||
|
@ -38,29 +53,34 @@ type msgUpdateStatus struct {
|
|||
type debugger struct {
|
||||
width int
|
||||
height int
|
||||
dbg *Debugger
|
||||
mem *memory.Memory
|
||||
hex HexView
|
||||
lastErr error
|
||||
bottomMsg string
|
||||
}
|
||||
|
||||
func initialModel(dbg *Debugger) debugger {
|
||||
ret := debugger{
|
||||
width: 0,
|
||||
height: 0,
|
||||
dbg: dbg,
|
||||
bottomMsg: "",
|
||||
}
|
||||
hex := NewHexView(128*1024, ret.writeByte)
|
||||
hex.AddrStyle = text
|
||||
hex.ZeroStyle = faintText
|
||||
hex.HexStyle = text
|
||||
ret.hex = hex
|
||||
return ret
|
||||
func (m debugger) Init() tea.Cmd {
|
||||
return tea.Sequence(
|
||||
m.readFullMemory(),
|
||||
staticMsg(msgUpdateStatus{"Initial load complete."}),
|
||||
)
|
||||
}
|
||||
|
||||
func (m debugger) Init() tea.Cmd {
|
||||
return m.dumpMemory(0x200)
|
||||
func (m debugger) readFullMemory() tea.Cmd {
|
||||
var ret []tea.Cmd
|
||||
for i := 0; i < m.mem.Len(); i += 128 {
|
||||
ret = append(ret, m.readMemory(i, 128))
|
||||
}
|
||||
return tea.Sequence(ret...)
|
||||
}
|
||||
|
||||
func (m debugger) readMemory(start, count int) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
if err := m.mem.Load(start, count); err != nil {
|
||||
return msgErr{fmt.Errorf("loading region 0x%x+%x: %w", start, count, err)}
|
||||
}
|
||||
return msgMemoryChanged{start, count}
|
||||
}
|
||||
}
|
||||
|
||||
func staticMsg(msg tea.Msg) tea.Cmd {
|
||||
|
@ -69,49 +89,38 @@ func staticMsg(msg tea.Msg) tea.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
func (m debugger) dumpMemory(count int) tea.Cmd {
|
||||
var ret []tea.Cmd
|
||||
for i := 0; i < count; i += 16 {
|
||||
ret = append(ret, func() tea.Msg {
|
||||
mem, err := m.dbg.Dump(i, 16)
|
||||
if err != nil {
|
||||
return msgErr{err}
|
||||
}
|
||||
return HexViewUpdateMem{i, mem}
|
||||
})
|
||||
}
|
||||
return tea.Sequence(ret...)
|
||||
}
|
||||
|
||||
func (m debugger) writeByte(addr int, val byte) tea.Cmd {
|
||||
func (m debugger) commitWrites() tea.Cmd {
|
||||
return tea.Sequence(
|
||||
staticMsg(msgUpdateStatus{"Writing..."}),
|
||||
func() tea.Msg {
|
||||
if err := m.dbg.Write(addr, val); err != nil {
|
||||
if err := m.mem.Commit(); err != nil {
|
||||
return msgUpdateStatus{fmt.Sprintf("Write failed: %v", err)}
|
||||
}
|
||||
return tea.BatchMsg{
|
||||
staticMsg(HexViewUpdateMem{addr, []byte{val}}),
|
||||
staticMsg(msgUpdateStatus{"Written!"}),
|
||||
}
|
||||
return msgUpdateStatus{"Writes complete."}
|
||||
},
|
||||
m.dumpMemory(0x200),
|
||||
m.readFullMemory(),
|
||||
)
|
||||
}
|
||||
|
||||
func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Printf("msv: %#v", msg)
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
m.height = msg.Height
|
||||
var cmd tea.Cmd
|
||||
m.hex, cmd = m.hex.Update(HexViewSetHeight{m.height - 6}) // TODO: make less shit
|
||||
return m, cmd
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "r":
|
||||
return m, m.dumpMemory(0x200)
|
||||
start, end := m.hex.VisibleBytes()
|
||||
return m, m.readMemory(start, end-start)
|
||||
}
|
||||
case msgErr:
|
||||
log.Print(msg.err)
|
||||
m.lastErr = msg.err
|
||||
return m, nil
|
||||
case msgUpdateStatus:
|
||||
|
@ -166,36 +175,3 @@ func (m debugger) View() string {
|
|||
top := lip.JoinVertical(lip.Center, topStatus, hex, bottomStatus)
|
||||
return top
|
||||
}
|
||||
|
||||
func renderByte(b byte, format string) string {
|
||||
ret := fmt.Sprintf(format, b)
|
||||
if b == 0 {
|
||||
return faintText.Render(ret)
|
||||
}
|
||||
return text.Render(ret)
|
||||
}
|
||||
|
||||
func renderBytes(bs []byte, format string, sep string) string {
|
||||
var s strings.Builder
|
||||
for i, b := range bs {
|
||||
s.WriteString(renderByte(b, format))
|
||||
if i < len(bs)-1 {
|
||||
s.WriteString(faintText.Render(sep))
|
||||
}
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func dimZero(s string) string {
|
||||
allZero := true
|
||||
for _, r := range s {
|
||||
if !unicode.IsSpace(r) && r != '0' {
|
||||
allZero = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allZero {
|
||||
return faintText.Render(s)
|
||||
}
|
||||
return text.Render(s)
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,6 @@ go 1.23
|
|||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/bubbles v0.20.0
|
||||
github.com/charmbracelet/bubbletea v1.1.1
|
||||
github.com/charmbracelet/lipgloss v0.13.0
|
||||
github.com/creachadair/mds v0.21.2
|
||||
|
@ -13,7 +12,6 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.0 // indirect
|
||||
|
|
18
go.sum
18
go.sum
|
@ -1,9 +1,5 @@
|
|||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
||||
github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY=
|
||||
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||
|
@ -16,16 +12,18 @@ github.com/creachadair/mds v0.21.2 h1:D5130qi/kqmu+gGUQyDNOhrocGQp075ziTCgttxhh3
|
|||
github.com/creachadair/mds v0.21.2/go.mod h1:1ltMWZd9yXhaHEoZwBialMaviWVUpRPvMwVP7saFAzM=
|
||||
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
|
@ -34,18 +32,22 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
|
|||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.bug.st/serial v1.6.2 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
|
||||
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
2024/09/16 15:45:59 msv: tea.sequenceMsg{(tea.Cmd)(0x50b580), (tea.Cmd)(0x50b380)}
|
||||
2024/09/16 15:45:59 msv: tea.sequenceMsg{(tea.Cmd)(0x50b5c0), (tea.Cmd)(0x50b5c0), (tea.Cmd)(0x50b5c0), (tea.Cmd)(0x50b5c0), (tea.Cmd)(0x50b5c0), (tea.Cmd)(0x50b5c0)}
|
||||
2024/09/16 15:45:59 msv: tea.WindowSizeMsg{Width:141, Height:39}
|
||||
2024/09/16 15:45:59 msv: main.msgUpdateStatus{status:"Initial load complete."}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:0, count:128}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:128, count:128}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:256, count:128}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:384, count:128}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:512, count:128}
|
||||
2024/09/16 15:46:00 msv: main.msgMemoryChanged{start:640, count:128}
|
||||
2024/09/16 15:46:01 msv: tea.KeyMsg{Type:-1, Runes:[]int32{113}, Alt:false, Paste:false}
|
Loading…
Reference in New Issue