// Copyright (c) 2008, Michał Cichoń
// All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading;
using Tiny8051.Mcu8051;
namespace Tiny8051
{
public class Debugger
{
public enum DebugerState
{
Stop,
StepInto,
StepOver,
StepOut,
RunWithBreakpoints,
RunWithoutBreakpoints
}
// Zdarzenie wywoływane po wykonaniu cyklu
public delegate void OnTickEvent(object sender, DebugerState state);
// Zdarzenie cyklu
public event OnTickEvent OnTick;
// Watek wykonujący
private Thread m_thread = null;
// Zdarzenie zakończenia wątku
private EventWaitHandle m_exit_event;
// Zdarzenie uśpienia wątku
private EventWaitHandle m_sleep_event;
// Zdarzenie uśpienia wątku
private EventWaitHandle m_suspend_event;
// Rodzaj wykonywanej operacji
private DebugerState m_debug_state = DebugerState.Stop;
// Procesor
private Mcu8051.Mcu8051 m_processor = null;
// Częstotliwość
private decimal m_frequency = 11.0592e6m;
// Prezycja
private bool m_high_precision = true;
// Breakpoints
private System.Collections.ArrayList m_breakpoints = new System.Collections.ArrayList();
// Specjalny punkt zatrzymania
private int m_special_breakpoint = -1;
// Procesor
public Mcu8051.Mcu8051 Processor
{
set { m_processor = value; }
get { return m_processor; }
}
// Częstotliwość
public decimal Frequency
{
set { m_frequency = value; }
get { return m_frequency; }
}
// Precyzja
public bool HighPrecision
{
set { m_high_precision = value; }
get { return m_high_precision; }
}
// Stan debuger
public DebugerState State
{
get { return m_debug_state; }
}
// Breakpoints
public System.Collections.ArrayList Breakpoints
{
get { return m_breakpoints; }
}
// Suspend
public bool Suspend
{
set
{
if (value)
m_suspend_event.Reset();
else
m_suspend_event.Set();
}
}
// Konstruktor
public Debugger()
{
m_exit_event = new EventWaitHandle(false, EventResetMode.ManualReset);
m_sleep_event = new EventWaitHandle(false, EventResetMode.ManualReset);
m_suspend_event = new EventWaitHandle(true, EventResetMode.ManualReset);
m_thread = new Thread(new ParameterizedThreadStart(HandleThreadProc));
m_thread.Priority = ThreadPriority.Highest;
m_thread.Start();
}
// Zamknięcie debugera
public void Close()
{
m_exit_event.Set();
m_sleep_event.Set();
m_suspend_event.Set();
//m_thread.Join();
m_thread.Abort();
}
// Krok procesora
private void Tick(
bool ignore_interrupts)
{
if (m_processor == null)
return;
lock (m_processor)
{
if (m_high_precision)
{
bool entering = m_processor.EnteringToInterrupt;
while (!m_processor.Cycle())
;
if (entering && ignore_interrupts)
{
while (m_processor.InInterrupt)
while (!m_processor.Cycle())
;
while (!m_processor.Cycle())
;
}
}
else
{
bool entering = m_processor.EnteringToInterrupt;
m_processor.Step();
if (entering && ignore_interrupts)
{
while (m_processor.InInterrupt)
m_processor.Step();
m_processor.Step();
}
}
}
}
// Obsługa wątku debugera
private void HandleThreadProc(
Object obj)
{
double interval = 0.15;
double last = Utilities.QueryPerfCounter.Now;
double time = last;
int call_depth = 0;
while (!m_exit_event.WaitOne(0, false))
{
m_suspend_event.WaitOne();
if (m_processor == null)
{
m_sleep_event.Reset();
m_debug_state = DebugerState.Stop;
}
if (m_debug_state == DebugerState.Stop)
{
m_sleep_event.WaitOne();
if (OnTick != null)
OnTick(this, m_debug_state);
last = Utilities.QueryPerfCounter.Now;
time = last;
}
switch (m_debug_state)
{
case DebugerState.StepInto:
Tick(false);
m_sleep_event.Reset();
m_debug_state = DebugerState.Stop;
if (OnTick != null)
OnTick(this, m_debug_state);
break;
case DebugerState.StepOver:
byte opcode = m_processor.Code[m_processor.PC];
if ((Dasm8051.Flags[opcode] & OpcodeFlags.CallTransfer) != 0)
{
ushort address = (ushort)(m_processor.PC + Dasm8051.GetOpcodeSize(opcode));
while (m_processor.PC != address)
if (m_debug_state == DebugerState.StepOver)
Tick(true);
else
break;
}
else
Tick(true);
m_sleep_event.Reset();
m_debug_state = DebugerState.Stop;
if (OnTick != null)
OnTick(this, m_debug_state);
break;
case DebugerState.StepOut:
opcode = m_processor.Code[m_processor.PC];
Tick(false);
if ((Dasm8051.Flags[opcode] & OpcodeFlags.ReturnTransfer) != 0)
{
if (call_depth == 0)
{
m_sleep_event.Reset();
m_debug_state = DebugerState.Stop;
call_depth = 0;
if (OnTick != null)
OnTick(this, m_debug_state);
}
else
call_depth--;
}
else
{
if (((Dasm8051.Flags[opcode] & OpcodeFlags.CallTransfer) != 0) ||
m_processor.EnteringToInterrupt)
call_depth++;
}
break;
case DebugerState.RunWithoutBreakpoints:
Tick(false);
break;
case DebugerState.RunWithBreakpoints:
Tick(false);
if (m_breakpoints.IndexOf(m_processor.PC) >= 0 || m_processor.PC == m_special_breakpoint)
{
m_sleep_event.Reset();
m_debug_state = DebugerState.Stop;
if (OnTick != null)
OnTick(this, m_debug_state);
}
break;
}
time = Utilities.QueryPerfCounter.Now;
if ((time - last) >= interval)
{
if (OnTick != null)
OnTick(this, m_debug_state);
last = time;
}
}
}
public void ToggleBreakpoint(
ushort address)
{
if (m_breakpoints.IndexOf(address) < 0)
m_breakpoints.Add(address);
else
m_breakpoints.Remove(address);
}
public void DeleteAllBreakpoints()
{
m_breakpoints.Clear();
}
public void RunWithDebugging()
{
m_debug_state = DebugerState.RunWithBreakpoints;
m_special_breakpoint = -1;
m_sleep_event.Set();
}
// Wykonaj bez debugu
public void RunWithoutDebugging()
{
m_debug_state = DebugerState.RunWithoutBreakpoints;
m_sleep_event.Set();
}
// Wykonije do momentu znalezienia adresu
public void RunToAddress(
ushort address)
{
m_special_breakpoint = address;
m_debug_state = DebugerState.RunWithBreakpoints;
m_sleep_event.Set();
}
// Pojedyńczy krok
public void StepInto()
{
m_debug_state = DebugerState.StepInto;
m_sleep_event.Set();
}
// Pojedyńczy krok
public void StepOver()
{
m_debug_state = DebugerState.StepOver;
m_sleep_event.Set();
}
// Syjście z podprocedury
public void StepOut()
{
m_debug_state = DebugerState.StepOut;
m_sleep_event.Set();
}
// Zatrzymuje wykonanie
public void Stop()
{
m_debug_state = DebugerState.Stop;
m_sleep_event.Reset();
}
// Restart
public void Restart()
{
m_debug_state = DebugerState.Stop;
m_sleep_event.Reset();
m_processor.Reset();
OnTick(this, m_debug_state);
}
}
}