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"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"git.sentinel65x.com/dave/gary/debugger/memory"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
lip "github.com/charmbracelet/lipgloss"
|
lip "github.com/charmbracelet/lipgloss"
|
||||||
"github.com/creachadair/mds/slice"
|
"github.com/creachadair/mds/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HexViewUpdateMem struct {
|
type HexViewSetHeight struct {
|
||||||
addr int
|
Height int
|
||||||
bytes []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HexView struct {
|
type HexView struct {
|
||||||
|
@ -21,23 +21,23 @@ type HexView struct {
|
||||||
ZeroStyle lip.Style
|
ZeroStyle lip.Style
|
||||||
HexStyle 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
|
firstAddr int // top of viewport
|
||||||
bytes []byte
|
|
||||||
selectedAddr int
|
selectedAddr int
|
||||||
editing bool
|
editing bool
|
||||||
editNibble int // 0 or 1
|
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()
|
st := lip.NewStyle()
|
||||||
ret := HexView{
|
ret := HexView{
|
||||||
AddrStyle: st,
|
AddrStyle: st,
|
||||||
ZeroStyle: st,
|
ZeroStyle: st,
|
||||||
HexStyle: st,
|
HexStyle: st,
|
||||||
write: writeByte,
|
mem: mem,
|
||||||
bytes: make([]byte, size),
|
commit: commit,
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,25 @@ func (m HexView) SelectedAddr() int {
|
||||||
return m.selectedAddr
|
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 {
|
func (m HexView) addrFormat() string {
|
||||||
return fmt.Sprintf("%%%dx", m.addrNibbles())
|
return fmt.Sprintf("%%%dx", m.addrNibbles())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m HexView) addrNibbles() int {
|
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) {
|
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) {
|
func (m HexView) updateEditMode(msg tea.Msg) (HexView, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case HexViewUpdateMem:
|
case HexViewSetHeight:
|
||||||
copy(m.bytes[msg.addr:], msg.bytes)
|
m.height = msg.Height
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
case tea.KeyLeft:
|
case tea.KeyLeft:
|
||||||
if m.editNibble == 1 {
|
if m.editNibble == 1 {
|
||||||
m.editNibble = 0
|
m.editNibble = 0
|
||||||
|
} else if m.moveSelection(-1) {
|
||||||
|
m.editNibble = 1
|
||||||
}
|
}
|
||||||
case tea.KeyRight:
|
case tea.KeyRight:
|
||||||
if m.editNibble == 0 {
|
if m.editNibble == 0 {
|
||||||
m.editNibble = 1
|
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:
|
case tea.KeyEsc:
|
||||||
m.editing = false
|
m.editing = false
|
||||||
case tea.KeyEnter:
|
case tea.KeyEnter:
|
||||||
m.editing = false
|
m.editing = false
|
||||||
return m, m.write(m.selectedAddr, m.newByte)
|
return m, m.commit()
|
||||||
case tea.KeyRunes:
|
case tea.KeyRunes:
|
||||||
if unicode.In(msg.Runes[0], unicode.ASCII_Hex_Digit) {
|
if unicode.In(msg.Runes[0], unicode.ASCII_Hex_Digit) {
|
||||||
nibble := hexToNibble(msg.Runes[0])
|
nibble := hexToNibble(msg.Runes[0])
|
||||||
|
prev := m.mem.At(m.selectedAddr).Value
|
||||||
if m.editNibble == 0 {
|
if m.editNibble == 0 {
|
||||||
m.newByte = (nibble << 4) + (m.newByte & 0xF)
|
m.mem.Write(m.selectedAddr, (nibble<<4)+(prev&0xF))
|
||||||
m.editNibble++
|
m.editNibble++
|
||||||
} else {
|
} 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) {
|
func (m HexView) updateViewMode(msg tea.Msg) (HexView, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case HexViewUpdateMem:
|
case HexViewSetHeight:
|
||||||
copy(m.bytes[msg.addr:], msg.bytes)
|
m.height = msg.Height
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "up":
|
case "up":
|
||||||
if m.selectedAddr >= 16 {
|
m.moveSelection(-16)
|
||||||
m.selectedAddr -= 16
|
|
||||||
}
|
|
||||||
case "down":
|
case "down":
|
||||||
if m.selectedAddr < len(m.bytes)-16 {
|
m.moveSelection(16)
|
||||||
m.selectedAddr += 16
|
|
||||||
}
|
|
||||||
case "left":
|
case "left":
|
||||||
if m.selectedAddr > 0 {
|
m.moveSelection(-1)
|
||||||
m.selectedAddr--
|
|
||||||
}
|
|
||||||
case "right":
|
case "right":
|
||||||
if m.selectedAddr < len(m.bytes)-1 {
|
m.moveSelection(1)
|
||||||
m.selectedAddr++
|
|
||||||
}
|
|
||||||
case "pgdown":
|
case "pgdown":
|
||||||
// TODO
|
m.moveSelection(m.height * 16)
|
||||||
case "pgup":
|
case "pgup":
|
||||||
// TODO
|
m.moveSelection(-m.height * 16)
|
||||||
case "w":
|
case "w":
|
||||||
m.editing = true
|
m.editing = true
|
||||||
m.editNibble = 0
|
m.editNibble = 0
|
||||||
m.newByte = m.bytes[m.selectedAddr]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m HexView) View(height int) string {
|
func (m HexView) View(height int) string {
|
||||||
maxLen := 16 * height
|
startAddr, endAddr := m.VisibleBytes()
|
||||||
endAddr := min(len(m.bytes), m.firstAddr+maxLen)
|
bytes := m.mem.Slice(startAddr, endAddr)
|
||||||
|
|
||||||
var ret strings.Builder
|
var ret strings.Builder
|
||||||
addrFormat := m.addrFormat()
|
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)
|
lineAddr := m.firstAddr + (16 * line)
|
||||||
ret.WriteString(m.AddrStyle.Render(fmt.Sprintf(addrFormat, lineAddr)))
|
ret.WriteString(m.AddrStyle.Render(fmt.Sprintf(addrFormat, lineAddr)))
|
||||||
ret.WriteString(" ")
|
ret.WriteString(" ")
|
||||||
|
@ -166,22 +183,24 @@ func (m HexView) View(height int) string {
|
||||||
ret.WriteByte(' ')
|
ret.WriteByte(' ')
|
||||||
}
|
}
|
||||||
if m.editing && byteAddr == m.selectedAddr {
|
if m.editing && byteAddr == m.selectedAddr {
|
||||||
st := m.HexStyle.Underline(true)
|
st := m.HexStyle.Bold(true)
|
||||||
s1, s2 := st.Reverse(true), st
|
s1, s2 := st.Reverse(true), st
|
||||||
if m.editNibble == 1 {
|
if m.editNibble == 1 {
|
||||||
s1, s2 = s2, s1
|
s1, s2 = s2, s1
|
||||||
}
|
}
|
||||||
b = m.newByte
|
ret.WriteString(s1.Render(fmt.Sprintf("%01x", b.Value>>4)))
|
||||||
ret.WriteString(s1.Render(fmt.Sprintf("%01x", b>>4)))
|
ret.WriteString(s2.Render(fmt.Sprintf("%01x", b.Value&0xF)))
|
||||||
ret.WriteString(s2.Render(fmt.Sprintf("%01x", b&0xF)))
|
|
||||||
} else {
|
} else {
|
||||||
st := m.HexStyle
|
st := m.HexStyle
|
||||||
if m.selectedAddr == byteAddr {
|
if m.selectedAddr == byteAddr {
|
||||||
st = m.HexStyle.Reverse(true)
|
st = m.HexStyle.Reverse(true)
|
||||||
} else if b == 0 {
|
} else if !b.Valid || b.Value == 0 {
|
||||||
st = m.ZeroStyle
|
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')
|
ret.WriteByte('\n')
|
||||||
|
|
103
debugger/main.go
103
debugger/main.go
|
@ -1,115 +1,22 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"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() {
|
func main() {
|
||||||
dbg, err := Open()
|
dbg, err := serial.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("connecting to debugger: %v", err)
|
log.Fatalf("connecting to debugger: %v", err)
|
||||||
}
|
}
|
||||||
defer dbg.Close()
|
defer dbg.Close()
|
||||||
|
|
||||||
if err := dbg.Write(0x42, 123); err != nil {
|
mem := memory.New(dbg, 768)
|
||||||
log.Fatalf("writing to memory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := UI(dbg); err != nil {
|
if err := UI(mem); err != nil {
|
||||||
log.Fatal(err)
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"log"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
|
"git.sentinel65x.com/dave/gary/debugger/memory"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
lip "github.com/charmbracelet/lipgloss"
|
lip "github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UI(dbg *Debugger) error {
|
func UI(mem *memory.Memory) error {
|
||||||
p := tea.NewProgram(initialModel(dbg)) //failed{0, 0, true}, tea.WithAltScreen())
|
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 {
|
if _, err := p.Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -27,6 +38,10 @@ var (
|
||||||
BorderBackground(slate)
|
BorderBackground(slate)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type msgMemoryChanged struct {
|
||||||
|
start, count int
|
||||||
|
}
|
||||||
|
|
||||||
type msgErr struct {
|
type msgErr struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
@ -38,29 +53,34 @@ type msgUpdateStatus struct {
|
||||||
type debugger struct {
|
type debugger struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
dbg *Debugger
|
mem *memory.Memory
|
||||||
hex HexView
|
hex HexView
|
||||||
lastErr error
|
lastErr error
|
||||||
bottomMsg string
|
bottomMsg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialModel(dbg *Debugger) debugger {
|
func (m debugger) Init() tea.Cmd {
|
||||||
ret := debugger{
|
return tea.Sequence(
|
||||||
width: 0,
|
m.readFullMemory(),
|
||||||
height: 0,
|
staticMsg(msgUpdateStatus{"Initial load complete."}),
|
||||||
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 {
|
func (m debugger) readFullMemory() tea.Cmd {
|
||||||
return m.dumpMemory(0x200)
|
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 {
|
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 {
|
func (m debugger) commitWrites() 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 {
|
|
||||||
return tea.Sequence(
|
return tea.Sequence(
|
||||||
staticMsg(msgUpdateStatus{"Writing..."}),
|
staticMsg(msgUpdateStatus{"Writing..."}),
|
||||||
func() tea.Msg {
|
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 msgUpdateStatus{fmt.Sprintf("Write failed: %v", err)}
|
||||||
}
|
}
|
||||||
return tea.BatchMsg{
|
return msgUpdateStatus{"Writes complete."}
|
||||||
staticMsg(HexViewUpdateMem{addr, []byte{val}}),
|
|
||||||
staticMsg(msgUpdateStatus{"Written!"}),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
m.dumpMemory(0x200),
|
m.readFullMemory(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
log.Printf("msv: %#v", msg)
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
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:
|
case tea.KeyMsg:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "ctrl+c", "q":
|
case "ctrl+c", "q":
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
case "r":
|
case "r":
|
||||||
return m, m.dumpMemory(0x200)
|
start, end := m.hex.VisibleBytes()
|
||||||
|
return m, m.readMemory(start, end-start)
|
||||||
}
|
}
|
||||||
case msgErr:
|
case msgErr:
|
||||||
|
log.Print(msg.err)
|
||||||
m.lastErr = msg.err
|
m.lastErr = msg.err
|
||||||
return m, nil
|
return m, nil
|
||||||
case msgUpdateStatus:
|
case msgUpdateStatus:
|
||||||
|
@ -166,36 +175,3 @@ func (m debugger) View() string {
|
||||||
top := lip.JoinVertical(lip.Center, topStatus, hex, bottomStatus)
|
top := lip.JoinVertical(lip.Center, topStatus, hex, bottomStatus)
|
||||||
return top
|
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
|
toolchain go1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/bubbles v0.20.0
|
|
||||||
github.com/charmbracelet/bubbletea v1.1.1
|
github.com/charmbracelet/bubbletea v1.1.1
|
||||||
github.com/charmbracelet/lipgloss v0.13.0
|
github.com/charmbracelet/lipgloss v0.13.0
|
||||||
github.com/creachadair/mds v0.21.2
|
github.com/creachadair/mds v0.21.2
|
||||||
|
@ -13,7 +12,6 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.0 // 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 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
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 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY=
|
||||||
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
||||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
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/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 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
|
||||||
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
|
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 h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
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 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
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 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
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 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
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/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 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
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.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
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 h1:kn9LRX3sdm+WxWKufMlIRndwGfPWsH1/9lCWXQCasq8=
|
||||||
go.bug.st/serial v1.6.2/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
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 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
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-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.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 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
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 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
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