gary/debugger/memory/memory.go

166 lines
2.8 KiB
Go

package memory
import (
"encoding/binary"
"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) ByteSlice(start, end int) []byte {
ret := make([]byte, end-start)
for i, b := range m.Slice(start, end) {
if !b.Valid {
return nil
}
ret[i] = b.Value
}
return ret
}
func (m *Memory) Decode(addr int, out any) bool {
bs := make([]byte, binary.Size(out))
mem := m.Slice(addr, addr+len(bs))
for i, b := range mem {
if !b.Valid {
return false
}
bs[i] = b.Value
}
if _, err := binary.Decode(bs, binary.BigEndian, out); err != nil {
return false
}
return true
}
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
}