debugger: more hacking, hex editor with full editing now works

This commit is contained in:
David Anderson 2024-09-16 15:43:36 -07:00
parent 12ddbeb508
commit e2f4103fdc
8 changed files with 361 additions and 224 deletions

View File

@ -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')

View File

@ -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
}

138
debugger/memory/memory.go Normal file
View File

@ -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
}

86
debugger/serial/serial.go Normal file
View File

@ -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
}

View File

@ -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
View File

@ -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
View File

@ -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=

11
log.txt Normal file
View File

@ -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}