From 4a7c7d6a6e20c0a5eae4f94d58cf1cfaf0f643f0 Mon Sep 17 00:00:00 2001 From: lavendersprite Date: Tue, 28 Dec 2021 12:41:58 -0800 Subject: [PATCH] added subscription IDs to hcdebug.h --- include/hcdebug.h | 169 +++++++++++++++++++++++++++++++++++++++------- src/Debugger.cpp | 7 +- src/Debugger.h | 5 +- 3 files changed, 153 insertions(+), 28 deletions(-) diff --git a/include/hcdebug.h b/include/hcdebug.h index a5d6fb4..d54d698 100644 --- a/include/hcdebug.h +++ b/include/hcdebug.h @@ -17,14 +17,32 @@ typedef enum { HC_EVENT_TICK = 0, HC_EVENT_EXECUTION = 1, - HC_EVENT_INTERRUPT = 2, - HC_EVENT_MEMORY = 3, - HC_EVENT_REG = 4, - HC_EVENT_IO = 5, - HC_EVENT_GENERIC = 6 + HC_EVENT_RETURN = 2, + HC_EVENT_INTERRUPT = 3, + HC_EVENT_MEMORY = 4, + HC_EVENT_REG = 5, + HC_EVENT_IO = 6, + HC_EVENT_GENERIC = 7 } hc_EventType; +/* Subscription ID. Helps identify subscriber, and also allows unsubscribing from an event. A negative ID indicates an error. + IDs are not necessarily consecutive, and an ID may be re-used only after unsubscribing. Otherwise, IDs are unique, even + between different event types. (The core might implement this by using some bits of the event ID to indicate the event type.) */ +typedef int64_t hc_SubscriptionID; + +typedef enum { + /* Report all execution events */ + HC_STEP, + + /* As above, but if an interrupt occurs, temporarily disable until returned from interrupt */ + HC_STEP_SKIP_INTERRUPT, + + /* As above, but if a subroutine is invoked, temporarily disable until returned from subroutine */ + HC_STEP_CURRENT_SUBROUTINE, +} +hc_ExecutionType; + typedef struct hc_GenericBreakpoint { struct { /* Breakpoint info */ @@ -106,31 +124,50 @@ typedef struct hc_System { hc_System; /* Informs the front-end that a CPU is about to execute an instruction at the given address */ -typedef struct hc_Execution { +typedef struct hc_ExecutionEvent { hc_Cpu const* cpu; uint64_t address; } -hc_ExecutionBreakpoint; +hc_ExecutionEvent; + +/* Informs the front-end that a CPU has just returned from a subroutine */ +typedef struct hc_ExecutionReturnEvent { + hc_Cpu const* cpu; + + /* address of the return instruction */ + uint64_t previous_address; + + /* address returned to */ + uint64_t return_address; +} +hc_ExecutionReturnEvent; /* Informs the front-end that an interrupt was served */ -typedef struct hc_Interrupt { +typedef struct hc_InterruptEvent { hc_Cpu const* cpu; + + /* Identifies the type of interrupt. Meaning depends on CPU model */ unsigned kind; - uint64_t address; + + /* Address of the next instruction to be executed when returning from interrupt */ + uint64_t return_address; + + /* New value of the program counter (in general, the start of the interrupt vector) */ + uint64_t vector_address; } -hc_InterruptBreakpoint; +hc_InterruptEvent; /* Informs the front-end that a memory location is about to be read from or written to */ -typedef struct hc_MemoryWatchpoint { +typedef struct hc_MemoryWatchpointEvent { hc_Memory const* memory; uint64_t address; - unsigned operation; - uint8_t new_value; + uint8_t operation; + uint8_t value; } hc_MemoryWatchpoint; /* Informs the front-end that a register is about to have its value changed */ -typedef struct hc_RegisterWatchpoint { +typedef struct hc_RegisterWatchpointEvent { hc_Cpu const* cpu; unsigned reg; uint64_t new_value; @@ -138,37 +175,117 @@ typedef struct hc_RegisterWatchpoint { hc_RegisterWatchpoint; /* Informs the front-end that an IO port is about to be read from or written to */ -typedef struct hc_IoWatchpoint { +typedef struct hc_IoWatchpointEvent { hc_Cpu const* cpu; uint64_t address; - unsigned operation; + uint8_t operation; uint64_t value; } hc_IoWatchpoint; /* Informs the front-end that a generic breakpoint was hit */ -typedef struct hc_Breakpoint { +typedef struct hc_GenericBreakpointEvent { hc_GenericBreakpoint const* breakpoint; uint64_t args[4]; } hc_Breakpoint; +/* Tagged union over all hc Event types */ typedef struct hc_Event { hc_EventType type; - void* user_data; + hc_SubscriptionID subscribtion_id; union { - hc_ExecutionBreakpoint execution; - hc_InterruptBreakpoint interrupt; - hc_MemoryWatchpoint memory; - hc_RegisterWatchpoint reg; - hc_IoWatchpoint io; - hc_Breakpoint generic; + hc_ExecutionEvent execution; + hc_ExecutionReturnEvent execution_return; + hc_InterruptEvent interrupt; + hc_MemoryWatchpointEvent memory; + hc_RegisterWatchpointEvent reg; + hc_IoWatchpointEvent io; + hc_GenericBreakpointEvent generic; } event; } hc_Event; +/* Tells the core to report certain execution events. Note that the core should implicitly include the + current stack depth and/or subroutine being executed in the context of this subscription */ +typedef struct hc_ExecutionSubscription { + hc_Cpu const* cpu; + hc_ExecutionType type; + uint64_t address_range_begin; + uint64_t address_range_end; +} +hc_ExecutionSubscription; + +/* Tells the core to report after returning from the current subroutine. + Note: + - The core should implicitly include the current stack depth and/or subroutine + being executed in the context of this subscription. + - This event is only reported once per subscription, so the front-end should + immediately unsubscribe any time this event is reported. +*/ +typedef struct hc_ExecutionReturnSubscription { + hc_Cpu const* cpu; +} +hc_ExecutionReturnSubscription; + +/* Tells the core to report certain interrupt events */ +typedef struct hc_InterruptSubscription { + hc_Cpu const* cpu; + unsigned kind; +} +hc_InterruptSubscription; + +/* Tells the core to report certain memory access events */ +typedef struct hc_MemoryWatchpointSubscription { + hc_Memory const* memory; + uint64_t address_range_begin; + uint64_t address_range_end; + uint8_t operation; +} +hc_MemoryWatchpointSubscription; + +/* Tells the core to report certain register change events */ +typedef struct hc_RegisterWatchpointSubscription { + hc_Cpu const cpu; + unsigned reg; +} +hc_RegisterWatchpointSubscription; + +/* Tells the core to report when an IO port is accessed */ +typedef struct hc_IoWatchpointSubscription { + hc_Cpu const* cpu; + uint64_t address_range_begin; + uint64_t address_range_end; + uint8_t operation; +} +hc_IoWatchpointSubscription; + +/* Tells the core to report a generic breakpoint event */ +typedef struct hc_GenericBreakpointSubscription { + hc_GenericBreakpoint const* breakpoint; +} +hc_GenericBreakpointSubscription; + +/* Informs the core that a particular type of event should be reported (via handle_event()) */ +typedef struct hc_Subscription { + hc_EventType type; + + union { + hc_ExecutionSubscription execution; + hc_ExecutionReturnSubscription execution_return; + hc_InterruptSubscription interrupt; + hc_MemoryWatchpointSubscription memory; + hc_RegisterWatchpointSubscription reg; + hc_IoWatchpointSubscription io; + hc_GenericBreakpointSubscription generic; + } + subscription; +} +hc_Subscription; + +/* Debug interface. Shared between the core and frontend. Members which are const are initialized by the frontend */ typedef struct hc_DebuggerIf { unsigned const frontend_api_version; unsigned core_api_version; @@ -182,6 +299,10 @@ typedef struct hc_DebuggerIf { /* Handles an event from the core */ void (* const handle_event)(void* frontend_user_data, hc_Event const* event); + + /* Tells the core to report certain events. Returns negative value if not supported or if an error occurred */ + hc_SubscriptionID (* subscribe)(hc_Subscription); + void (* unsubscribe)(hc_SubscriptionID); } v1; } diff --git a/src/Debugger.cpp b/src/Debugger.cpp index 85be076..dda76a4 100644 --- a/src/Debugger.cpp +++ b/src/Debugger.cpp @@ -215,9 +215,11 @@ void hc::Debugger::tick() { } } -void hc::Debugger::executionBreakpoint(hc_ExecutionBreakpoint const* event) {} +void hc::Debugger::executionBreakpoint(hc_ExecutionEvent const* event) {} -void hc::Debugger::interruptBreakpoint(hc_InterruptBreakpoint const* event) {} +void hc::Debugger::returnBreakpoint(hc_ExecutionReturnEvent const* event) {} + +void hc::Debugger::interruptBreakpoint(hc_InterruptEvent const* event) {} void hc::Debugger::memoryWatchpoint(hc_MemoryWatchpoint const* event) {} @@ -231,6 +233,7 @@ void hc::Debugger::handleEvent(void* frontend_user_data, hc_Event const* event) switch (event->type) { case HC_EVENT_TICK: static_cast(frontend_user_data)->tick(); break; case HC_EVENT_EXECUTION: static_cast(frontend_user_data)->executionBreakpoint(&event->event.execution); break; + case HC_EVENT_RETURN: static_cast(frontend_user_data)->returnBreakpoint(&event->event.execution_return); break; case HC_EVENT_INTERRUPT: static_cast(frontend_user_data)->interruptBreakpoint(&event->event.interrupt); break; case HC_EVENT_MEMORY: static_cast(frontend_user_data)->memoryWatchpoint(&event->event.memory); break; case HC_EVENT_REG: static_cast(frontend_user_data)->registerWatchpoint(&event->event.reg); break; diff --git a/src/Debugger.h b/src/Debugger.h index 8b52eb0..0c6dcb5 100644 --- a/src/Debugger.h +++ b/src/Debugger.h @@ -54,8 +54,9 @@ namespace hc { protected: void tick(); - void executionBreakpoint(hc_ExecutionBreakpoint const* event); - void interruptBreakpoint(hc_InterruptBreakpoint const* event); + void executionBreakpoint(hc_ExecutionEvent const* event); + void returnBreakpoint(hc_ExecutionReturnEvent const* event); + void interruptBreakpoint(hc_InterruptEvent const* event); void memoryWatchpoint(hc_MemoryWatchpoint const* event); void registerWatchpoint(hc_RegisterWatchpoint const* event); void ioWatchpoint(hc_IoWatchpoint const* event);