2024-09-15 09:04:32 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-09-20 08:52:45 +02:00
|
|
|
"crypto/rand"
|
2024-09-15 09:04:32 +02:00
|
|
|
"fmt"
|
2024-09-20 08:52:45 +02:00
|
|
|
"strings"
|
2024-09-15 09:04:32 +02:00
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
"git.sentinel65x.com/dave/gary/debugger/memory"
|
2024-09-15 09:04:32 +02:00
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
lip "github.com/charmbracelet/lipgloss"
|
|
|
|
)
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func UI(mem *memory.Memory) error {
|
|
|
|
initial := debugger{
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
mem: mem,
|
2024-09-17 01:10:17 +02:00
|
|
|
statusMsg: "Loading...",
|
2024-09-17 00:43:36 +02:00
|
|
|
}
|
|
|
|
initial.hex = NewHexView(mem, initial.commitWrites)
|
|
|
|
initial.hex.AddrStyle = text
|
|
|
|
initial.hex.ZeroStyle = faintText
|
|
|
|
initial.hex.HexStyle = text
|
|
|
|
|
|
|
|
p := tea.NewProgram(initial)
|
2024-09-15 09:04:32 +02:00
|
|
|
if _, err := p.Run(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2024-09-17 01:10:17 +02:00
|
|
|
amber = lip.Color("#ffb00")
|
|
|
|
slate = lip.Color("235")
|
|
|
|
text = lip.NewStyle().
|
|
|
|
Foreground(amber).
|
|
|
|
Background(slate).
|
|
|
|
BorderForeground(amber).
|
|
|
|
BorderBackground(slate).
|
|
|
|
Border(lip.NormalBorder(), false, false, false, false)
|
2024-09-15 09:04:32 +02:00
|
|
|
faintText = text.Faint(true)
|
2024-09-17 01:10:17 +02:00
|
|
|
|
|
|
|
box = lip.NewStyle().
|
|
|
|
Border(lip.NormalBorder()).
|
|
|
|
BorderForeground(amber).
|
|
|
|
BorderBackground(slate).
|
|
|
|
Padding(1, 2)
|
2024-09-15 09:04:32 +02:00
|
|
|
)
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
type msgMemoryChanged struct {
|
|
|
|
start, count int
|
|
|
|
}
|
|
|
|
|
2024-09-15 09:04:32 +02:00
|
|
|
type msgErr struct {
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type msgUpdateStatus struct {
|
|
|
|
status string
|
|
|
|
}
|
|
|
|
|
|
|
|
type debugger struct {
|
|
|
|
width int
|
|
|
|
height int
|
2024-09-17 00:43:36 +02:00
|
|
|
mem *memory.Memory
|
2024-09-15 09:04:32 +02:00
|
|
|
hex HexView
|
|
|
|
lastErr error
|
2024-09-17 01:10:17 +02:00
|
|
|
statusMsg string
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func (m debugger) Init() tea.Cmd {
|
2024-09-17 01:10:17 +02:00
|
|
|
loads := m.readFullMemoryCmds()
|
|
|
|
ret := make([]tea.Cmd, 0, (len(loads)*2)+1)
|
|
|
|
for i, c := range loads {
|
|
|
|
ret = append(ret, staticMsg(msgUpdateStatus{fmt.Sprintf("Loading block %d/%d", i+1, len(loads))}), c)
|
|
|
|
}
|
|
|
|
ret = append(ret, staticMsg(msgUpdateStatus{"Load complete."}))
|
|
|
|
return tea.Sequence(ret...)
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func (m debugger) readFullMemory() tea.Cmd {
|
2024-09-17 01:10:17 +02:00
|
|
|
return tea.Sequence(m.readFullMemoryCmds()...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m debugger) readFullMemoryCmds() []tea.Cmd {
|
2024-09-17 00:43:36 +02:00
|
|
|
var ret []tea.Cmd
|
2024-09-20 06:25:04 +02:00
|
|
|
for i := 0; i < m.mem.Len(); i += 32 * 1024 {
|
|
|
|
ret = append(ret, m.readMemory(i, 32*1024))
|
2024-09-17 00:43:36 +02:00
|
|
|
}
|
2024-09-17 01:10:17 +02:00
|
|
|
return ret
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func (m debugger) readMemory(start, count int) tea.Cmd {
|
2024-09-15 09:04:32 +02:00
|
|
|
return func() tea.Msg {
|
2024-09-19 06:31:34 +02:00
|
|
|
if err := m.mem.Refresh(start, count); err != nil {
|
2024-09-17 00:43:36 +02:00
|
|
|
return msgErr{fmt.Errorf("loading region 0x%x+%x: %w", start, count, err)}
|
|
|
|
}
|
|
|
|
return msgMemoryChanged{start, count}
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func staticMsg(msg tea.Msg) tea.Cmd {
|
|
|
|
return func() tea.Msg {
|
|
|
|
return msg
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-17 00:43:36 +02:00
|
|
|
func (m debugger) commitWrites() tea.Cmd {
|
2024-09-15 09:04:32 +02:00
|
|
|
return tea.Sequence(
|
|
|
|
staticMsg(msgUpdateStatus{"Writing..."}),
|
|
|
|
func() tea.Msg {
|
2024-09-17 00:43:36 +02:00
|
|
|
if err := m.mem.Commit(); err != nil {
|
2024-09-15 09:04:32 +02:00
|
|
|
return msgUpdateStatus{fmt.Sprintf("Write failed: %v", err)}
|
|
|
|
}
|
2024-09-17 00:43:36 +02:00
|
|
|
return msgUpdateStatus{"Writes complete."}
|
2024-09-15 09:04:32 +02:00
|
|
|
},
|
2024-09-17 00:43:36 +02:00
|
|
|
m.readFullMemory(),
|
2024-09-15 09:04:32 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m debugger) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
|
switch msg := msg.(type) {
|
|
|
|
case tea.WindowSizeMsg:
|
|
|
|
m.width = msg.Width
|
|
|
|
m.height = msg.Height
|
2024-09-17 00:43:36 +02:00
|
|
|
var cmd tea.Cmd
|
2024-09-17 01:10:17 +02:00
|
|
|
m.hex, cmd = m.hex.Update(HexViewSetHeight{m.height - box.GetVerticalFrameSize() - 1})
|
2024-09-17 00:43:36 +02:00
|
|
|
return m, cmd
|
2024-09-15 09:04:32 +02:00
|
|
|
case tea.KeyMsg:
|
|
|
|
switch msg.String() {
|
|
|
|
case "ctrl+c", "q":
|
|
|
|
return m, tea.Quit
|
|
|
|
case "r":
|
2024-09-17 00:43:36 +02:00
|
|
|
start, end := m.hex.VisibleBytes()
|
|
|
|
return m, m.readMemory(start, end-start)
|
2024-09-19 06:31:34 +02:00
|
|
|
case "R":
|
|
|
|
return m, m.readFullMemory()
|
2024-09-20 08:52:45 +02:00
|
|
|
case "p":
|
|
|
|
bs := make([]byte, 19)
|
|
|
|
rand.Read(bs)
|
|
|
|
for i, b := range bs {
|
|
|
|
m.mem.Write(m.hex.SelectedAddr()+i, b)
|
|
|
|
}
|
|
|
|
return m, m.commitWrites()
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
case msgErr:
|
|
|
|
m.lastErr = msg.err
|
|
|
|
return m, nil
|
|
|
|
case msgUpdateStatus:
|
2024-09-17 01:10:17 +02:00
|
|
|
m.statusMsg = msg.status
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|
|
|
|
var cmd tea.Cmd
|
|
|
|
m.hex, cmd = m.hex.Update(msg)
|
|
|
|
return m, cmd
|
|
|
|
}
|
|
|
|
|
2024-09-20 08:52:45 +02:00
|
|
|
func (m debugger) values(addr int) string {
|
|
|
|
var out strings.Builder
|
|
|
|
|
|
|
|
k := func(k string) {
|
|
|
|
out.WriteString(text.Bold(true).Render(k) + text.Render(": "))
|
|
|
|
}
|
|
|
|
v := func(format string, args ...any) {
|
|
|
|
val := fmt.Sprintf(format, args...)
|
|
|
|
out.WriteString(text.Render(val) + "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
b := m.mem.At(addr)
|
|
|
|
k(" addr")
|
|
|
|
v("0x%x", addr)
|
|
|
|
k("binary")
|
|
|
|
v("%s", b.Bin())
|
|
|
|
k(" uint8")
|
|
|
|
v("0x%s (%s)", b.Hex(), b.Dec())
|
|
|
|
var w uint16
|
|
|
|
if m.mem.Decode(addr, &w) {
|
|
|
|
k("uint16")
|
|
|
|
v("0x%04x (%d)", w, w)
|
|
|
|
}
|
|
|
|
var dw uint32
|
|
|
|
if m.mem.Decode(addr, &dw) {
|
|
|
|
k("uint32")
|
|
|
|
v("0x%08x (%d)", dw, dw)
|
|
|
|
}
|
|
|
|
out.WriteByte('\n')
|
|
|
|
|
|
|
|
return strings.TrimSpace(out.String())
|
|
|
|
}
|
2024-09-15 09:04:32 +02:00
|
|
|
|
|
|
|
func (m debugger) View() string {
|
|
|
|
if m.width < 80 || m.height < 20 {
|
|
|
|
return lip.Place(m.width, m.height, lip.Center, lip.Center, "Please embiggen your terminal")
|
|
|
|
}
|
|
|
|
|
2024-09-17 01:10:17 +02:00
|
|
|
hex := box.Render(m.hex.View())
|
2024-09-20 08:52:45 +02:00
|
|
|
decoder := box.Width(m.width - lip.Width(hex) - 2).Render(m.values(m.hex.SelectedAddr()))
|
|
|
|
main := lip.JoinHorizontal(lip.Top, hex, decoder)
|
2024-09-17 01:10:17 +02:00
|
|
|
|
2024-09-20 08:52:45 +02:00
|
|
|
barText := text.Reverse(true)
|
2024-09-17 01:10:17 +02:00
|
|
|
|
|
|
|
topLeft := barText.Bold(true).PaddingLeft(1).Render("Addr: ")
|
|
|
|
topLeft += barText.Render(fmt.Sprintf("0x%x (%d)", m.hex.SelectedAddr(), m.hex.SelectedAddr()))
|
|
|
|
topRight := barText.PaddingRight(1).Render(m.statusMsg)
|
|
|
|
title := barText.Padding(0, 1).Bold(true).Render("GARY Debugger")
|
2024-09-15 09:04:32 +02:00
|
|
|
|
2024-09-17 01:10:17 +02:00
|
|
|
sideWidth := max(lip.Width(topLeft), lip.Width(topRight))
|
|
|
|
titleWidth := m.width - 2*sideWidth
|
|
|
|
topLeft = barText.Width(sideWidth).Align(lip.Left).Render(topLeft)
|
|
|
|
topRight = barText.Width(sideWidth).Align(lip.Right).Render(topRight)
|
|
|
|
title = barText.Width(titleWidth).Align(lip.Center).Render(title)
|
2024-09-15 09:04:32 +02:00
|
|
|
|
2024-09-17 01:10:17 +02:00
|
|
|
status := lip.JoinHorizontal(lip.Top, topLeft, title, topRight)
|
2024-09-15 09:04:32 +02:00
|
|
|
|
2024-09-20 08:52:45 +02:00
|
|
|
all := lip.JoinVertical(lip.Center, status, main)
|
2024-09-17 01:10:17 +02:00
|
|
|
return all
|
2024-09-15 09:04:32 +02:00
|
|
|
}
|