mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
265 lines
5.6 KiB
Go
265 lines
5.6 KiB
Go
package debug
|
|
|
|
import (
|
|
"okemu/debug/breakpoint"
|
|
"okemu/z80"
|
|
"okemu/z80/dis"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const BPMemAccess = 65535
|
|
|
|
type Debugger struct {
|
|
stepMode bool
|
|
doStep bool
|
|
runMode bool
|
|
runInst uint64
|
|
breakpointsEnabled bool
|
|
breakpoints map[uint16]*breakpoint.Breakpoint
|
|
cpuFrequency uint32
|
|
disassembler *dis.Disassembler
|
|
cpuHistoryEnabled bool
|
|
cpuHistoryStarted bool
|
|
cpuHistoryMaxSize int
|
|
cpuHistory []*z80.CPU
|
|
memBreakpoints [65536]byte
|
|
}
|
|
|
|
func NewDebugger() *Debugger {
|
|
d := Debugger{
|
|
stepMode: false,
|
|
doStep: false,
|
|
runMode: false,
|
|
runInst: 0,
|
|
breakpointsEnabled: false,
|
|
breakpoints: map[uint16]*breakpoint.Breakpoint{},
|
|
cpuHistoryEnabled: false,
|
|
cpuHistoryStarted: false,
|
|
cpuHistoryMaxSize: 0,
|
|
cpuHistory: []*z80.CPU{},
|
|
}
|
|
return &d
|
|
}
|
|
|
|
func (d *Debugger) SetStepMode(step bool) {
|
|
d.SetRunMode(false)
|
|
d.stepMode = step
|
|
}
|
|
|
|
func (d *Debugger) SetRunMode(run bool) {
|
|
if run {
|
|
d.runInst = 0
|
|
}
|
|
d.runMode = run
|
|
}
|
|
|
|
func (d *Debugger) RunMode() bool {
|
|
return d.runMode
|
|
}
|
|
|
|
func (d *Debugger) DoStep() bool {
|
|
if d.doStep {
|
|
d.doStep = false
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *Debugger) SetCpuHistoryEnabled(enable bool) {
|
|
d.cpuHistoryEnabled = enable
|
|
}
|
|
|
|
func (d *Debugger) SetCpuHistoryMaxSize(size int) {
|
|
if size < 0 || size > 1_000_000 {
|
|
log.Error("CPU history max size must be positive and up to 1M")
|
|
} else {
|
|
d.cpuHistoryMaxSize = size
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) CpuHistoryClear() {
|
|
d.cpuHistory = make([]*z80.CPU, 0)
|
|
}
|
|
|
|
func (d *Debugger) CpuHistorySize() int {
|
|
return len(d.cpuHistory)
|
|
}
|
|
|
|
func (d *Debugger) CpuHistory(index int) *z80.CPU {
|
|
if index >= 0 && index < len(d.cpuHistory) {
|
|
return d.cpuHistory[index]
|
|
}
|
|
if len(d.cpuHistory) > 0 {
|
|
log.Warnf("CPU history index %d out of range [0:%d]", index, len(d.cpuHistory)-1)
|
|
} else {
|
|
log.Warn("CPU history is empty")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Debugger) SetCpuHistoryStarted(started bool) {
|
|
d.cpuHistoryStarted = started
|
|
}
|
|
|
|
func (d *Debugger) SaveHistory(state *z80.CPU) {
|
|
if d.cpuHistoryEnabled && d.cpuHistoryMaxSize > 0 && d.cpuHistoryStarted {
|
|
d.cpuHistory = append([]*z80.CPU{state}, d.cpuHistory...)
|
|
if len(d.cpuHistory) > d.cpuHistoryMaxSize {
|
|
d.cpuHistory = d.cpuHistory[0 : d.cpuHistoryMaxSize-1]
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) CheckBreakpoints(ctx map[string]interface{}) (bool, uint16) {
|
|
if d.breakpointsEnabled && d.runMode {
|
|
for n, bp := range d.breakpoints {
|
|
if bp != nil && bp.Hit(ctx) {
|
|
// breakpoint hit
|
|
if bp.Pass() >= bp.PassCount() {
|
|
bp.SetPass(0)
|
|
d.runMode = false
|
|
return true, n
|
|
}
|
|
// increment breakpoint pass count
|
|
bp.IncPass()
|
|
}
|
|
}
|
|
}
|
|
return false, 0
|
|
}
|
|
|
|
func (d *Debugger) SetBreakpointsEnabled(enabled bool) {
|
|
d.breakpointsEnabled = enabled
|
|
}
|
|
|
|
func (d *Debugger) BreakpointsEnabled() bool {
|
|
return d.breakpointsEnabled
|
|
}
|
|
|
|
// SetBreakpoint Create new breakpoint with specified number
|
|
func (d *Debugger) SetBreakpoint(number uint16, exp string, mBank uint8) error {
|
|
var err error
|
|
bp, err := breakpoint.NewBreakpoint(exp, mBank)
|
|
if err == nil && bp != nil {
|
|
d.breakpoints[number] = bp
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *Debugger) AddBreakpoint(exp string, mBank uint8) (uint16, error) {
|
|
var err error
|
|
bpNo := d.GetBreakpointNum()
|
|
if bpNo < breakpoint.MaxBreakpoints {
|
|
bp, err := breakpoint.NewBreakpoint(exp, mBank)
|
|
if err == nil && bp != nil {
|
|
bp.SetEnabled(true)
|
|
d.breakpoints[bpNo] = bp
|
|
}
|
|
}
|
|
return bpNo, err
|
|
}
|
|
|
|
func (d *Debugger) GetBreakpointNum() uint16 {
|
|
num := uint16(1)
|
|
for no, bp := range d.breakpoints {
|
|
if bp != nil && no < breakpoint.MaxBreakpoints && num <= no {
|
|
num = no + 1
|
|
}
|
|
}
|
|
return num
|
|
}
|
|
|
|
func (d *Debugger) SetBreakpointPassCount(number uint16, count uint16) {
|
|
bp, ok := d.breakpoints[number]
|
|
if ok && bp != nil {
|
|
bp.SetPass(0)
|
|
bp.SetPassCount(count)
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) SetBreakpointEnabled(number uint16, enabled bool) {
|
|
bp, ok := d.breakpoints[number]
|
|
if ok && bp != nil {
|
|
bp.SetEnabled(enabled)
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) BreakpointEnabled(number uint16) bool {
|
|
bp, ok := d.breakpoints[number]
|
|
if ok && bp != nil {
|
|
return bp.Enabled()
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (d *Debugger) BreakpointMBank(number uint16) uint8 {
|
|
bp, ok := d.breakpoints[number]
|
|
if ok && bp != nil {
|
|
return bp.MBank()
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func (d *Debugger) ClearMemBreakpoints() {
|
|
for c := 0; c < 65536; c++ {
|
|
d.memBreakpoints[c] = 0
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) StepMode() bool {
|
|
return d.stepMode
|
|
}
|
|
|
|
func (d *Debugger) SetDoStep(on bool) {
|
|
d.doStep = on
|
|
}
|
|
|
|
// BPExpression Return requested breakpoint
|
|
func (d *Debugger) BPExpression(number uint16) string {
|
|
bp, ok := d.breakpoints[number]
|
|
if ok && bp != nil {
|
|
return bp.Expression()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// RunInst return and increment count of instructions executed
|
|
func (d *Debugger) RunInst() uint64 {
|
|
v := d.runInst
|
|
d.runInst++
|
|
return v
|
|
}
|
|
|
|
func (d *Debugger) SetMemBreakpoint(address uint16, typ byte, size uint16) {
|
|
var offset uint16
|
|
for offset = address; offset < address+size; offset++ {
|
|
d.memBreakpoints[offset] = typ
|
|
}
|
|
}
|
|
|
|
func (d *Debugger) RemoveBreakpoint(number uint16) {
|
|
delete(d.breakpoints, number)
|
|
}
|
|
|
|
func (d *Debugger) CheckMemBreakpoints(accessMap *map[uint16]byte) (bool, uint16, byte) {
|
|
if !d.breakpointsEnabled {
|
|
return false, 0, 0
|
|
}
|
|
for addr, typ := range *accessMap {
|
|
bp := d.memBreakpoints[addr]
|
|
if bp == 0 {
|
|
return false, addr, 0
|
|
}
|
|
if (bp == 3) || bp == typ {
|
|
d.SetRunMode(false)
|
|
return true, addr, typ
|
|
}
|
|
}
|
|
return false, 0, 0
|
|
}
|
|
|
|
func (d *Debugger) ClearBreakpoints() {
|
|
clear(d.breakpoints)
|
|
}
|