218 lines
6.4 KiB
C++
218 lines
6.4 KiB
C++
#include "e2e/emulator_stepping_test.h"
|
|
|
|
#include "app/emu/debug/step_controller.h"
|
|
#include "app/emu/snes.h"
|
|
#include "imgui.h"
|
|
#include "imgui_test_engine/imgui_te_context.h"
|
|
#include "test_utils.h"
|
|
|
|
namespace yaze {
|
|
namespace test {
|
|
|
|
/**
|
|
* @brief Test step-over functionality for subroutine calls.
|
|
*
|
|
* This test verifies that StepOver correctly executes entire subroutines
|
|
* as single operations when the PC is at a JSR/JSL instruction.
|
|
*/
|
|
void E2ETest_EmulatorStepOver(ImGuiTestContext* ctx) {
|
|
gui::LoadRomInTest(ctx, "zelda3.sfc");
|
|
|
|
// Open emulator
|
|
ctx->SetRef("Yaze");
|
|
ctx->MenuClick("Emulation/Launch Emulator");
|
|
ctx->Yield(30); // Let emulator initialize
|
|
|
|
// Access the emulator and step controller
|
|
// In production, this would access the actual Snes instance
|
|
// For now, we demonstrate the API usage pattern
|
|
ctx->LogInfo("Step-Over Test: Demonstrating API pattern");
|
|
|
|
// Create a mock SNES for demonstration
|
|
// In real test: auto* snes = GetEmulatorInstance();
|
|
// emu::StepController controller(snes);
|
|
|
|
// Configure stepping behavior
|
|
// emu::StepConfig config;
|
|
// config.max_instructions = 10000;
|
|
// config.track_call_stack = true;
|
|
// config.log_instructions = false;
|
|
|
|
// ctx->LogInfo("StepConfig: max_instructions=%d, track_call_stack=%s",
|
|
// config.max_instructions,
|
|
// config.track_call_stack ? "true" : "false");
|
|
|
|
// In real test:
|
|
// controller.SetConfig(config);
|
|
//
|
|
// // Execute step-over
|
|
// auto result = controller.StepOver();
|
|
// if (result.ok()) {
|
|
// IM_CHECK(result->completed);
|
|
// ctx->LogInfo("Step-over completed: PC=$%06X, cycles=%llu",
|
|
// result->final_pc, result->cycles_executed);
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* @brief Test step-out functionality for returning from subroutines.
|
|
*
|
|
* This test verifies that StepOut correctly runs until the current
|
|
* subroutine returns (RTS/RTL).
|
|
*/
|
|
void E2ETest_EmulatorStepOut(ImGuiTestContext* ctx) {
|
|
gui::LoadRomInTest(ctx, "zelda3.sfc");
|
|
|
|
ctx->SetRef("Yaze");
|
|
ctx->MenuClick("Emulation/Launch Emulator");
|
|
ctx->Yield(30);
|
|
|
|
ctx->LogInfo("Step-Out Test: Demonstrating API pattern");
|
|
|
|
// In real test:
|
|
// emu::StepController controller(snes);
|
|
//
|
|
// // First step into a subroutine
|
|
// controller.StepInstruction(); // Execute JSR
|
|
//
|
|
// // Verify we're in a subroutine
|
|
// IM_CHECK(controller.IsInSubroutine());
|
|
// size_t initial_depth = controller.GetStackDepth();
|
|
//
|
|
// // Step out
|
|
// auto result = controller.StepOut();
|
|
// if (result.ok()) {
|
|
// IM_CHECK(result->completed);
|
|
// IM_CHECK_EQ(result->stop_reason, "return");
|
|
// IM_CHECK_LT(controller.GetStackDepth(), initial_depth);
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* @brief Test call stack tracking during execution.
|
|
*
|
|
* This test verifies that the StepController correctly tracks the call stack
|
|
* across JSR/JSL calls and RTS/RTL returns.
|
|
*/
|
|
void E2ETest_EmulatorCallStackTracking(ImGuiTestContext* ctx) {
|
|
gui::LoadRomInTest(ctx, "zelda3.sfc");
|
|
|
|
ctx->SetRef("Yaze");
|
|
ctx->MenuClick("Emulation/Launch Emulator");
|
|
ctx->Yield(30);
|
|
|
|
ctx->LogInfo("Call Stack Tracking Test: Demonstrating API pattern");
|
|
|
|
// In real test:
|
|
// emu::StepController controller(snes);
|
|
// controller.ClearCallStack();
|
|
//
|
|
// // Set up symbol resolver for better debugging
|
|
// controller.SetSymbolResolver([](uint32_t addr) -> std::string {
|
|
// // Look up symbol from ROM's label map
|
|
// // return rom->GetLabelForAddress(addr);
|
|
// return "";
|
|
// });
|
|
//
|
|
// // Execute several instructions and track calls
|
|
// for (int i = 0; i < 100; ++i) {
|
|
// auto result = controller.StepInstruction();
|
|
// if (!result.ok()) break;
|
|
//
|
|
// // Log call stack changes
|
|
// const auto& stack = controller.GetCallStack();
|
|
// if (!stack.empty()) {
|
|
// ctx->LogInfo("Call stack depth: %zu", stack.size());
|
|
// for (const auto& entry : stack) {
|
|
// ctx->LogInfo(" %06X -> %06X (%s)",
|
|
// entry.call_address, entry.target_address,
|
|
// entry.symbol_name.c_str());
|
|
// }
|
|
// }
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* @brief Test run-to-address functionality.
|
|
*
|
|
* This test verifies running execution until a specific address is reached.
|
|
*/
|
|
void E2ETest_EmulatorRunToAddress(ImGuiTestContext* ctx) {
|
|
gui::LoadRomInTest(ctx, "zelda3.sfc");
|
|
|
|
ctx->SetRef("Yaze");
|
|
ctx->MenuClick("Emulation/Launch Emulator");
|
|
ctx->Yield(30);
|
|
|
|
ctx->LogInfo("Run-To-Address Test: Demonstrating API pattern");
|
|
|
|
// In real test:
|
|
// emu::StepController controller(snes);
|
|
//
|
|
// // Run to the NMI handler
|
|
// uint32_t nmi_handler = 0x008081; // Typical ALTTP NMI
|
|
// auto result = controller.RunToAddress(nmi_handler);
|
|
//
|
|
// if (result.ok()) {
|
|
// IM_CHECK(result->completed);
|
|
// IM_CHECK_EQ(result->final_pc, nmi_handler);
|
|
// ctx->LogInfo("Reached NMI handler at $%06X after %d instructions",
|
|
// result->final_pc, result->instructions_executed);
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* @brief Test instruction callback during stepping.
|
|
*
|
|
* This test demonstrates using callbacks to monitor execution
|
|
* for AI-driven analysis or automation.
|
|
*/
|
|
void E2ETest_EmulatorInstructionCallback(ImGuiTestContext* ctx) {
|
|
gui::LoadRomInTest(ctx, "zelda3.sfc");
|
|
|
|
ctx->SetRef("Yaze");
|
|
ctx->MenuClick("Emulation/Launch Emulator");
|
|
ctx->Yield(30);
|
|
|
|
ctx->LogInfo("Instruction Callback Test: Demonstrating API pattern");
|
|
|
|
// Track interesting events during execution
|
|
struct ExecutionStats {
|
|
int total_instructions = 0;
|
|
int subroutine_calls = 0;
|
|
int branches_taken = 0;
|
|
int memory_writes = 0;
|
|
};
|
|
|
|
ExecutionStats stats;
|
|
|
|
// In real test:
|
|
// emu::StepController controller(snes);
|
|
//
|
|
// controller.SetInstructionCallback(
|
|
// [&stats](uint32_t pc, uint8_t opcode,
|
|
// const emu::StepResult& state) -> bool {
|
|
// stats.total_instructions++;
|
|
//
|
|
// if (emu::opcodes::IsCall(opcode)) {
|
|
// stats.subroutine_calls++;
|
|
// }
|
|
// if (emu::opcodes::IsBranch(opcode)) {
|
|
// stats.branches_taken++;
|
|
// }
|
|
//
|
|
// // Return false to stop execution (e.g., for automation triggers)
|
|
// return true; // Continue
|
|
// });
|
|
//
|
|
// // Run for 1000 instructions
|
|
// auto result = controller.RunInstructions(1000);
|
|
//
|
|
// ctx->LogInfo("Execution stats: %d instructions, %d calls, %d branches",
|
|
// stats.total_instructions, stats.subroutine_calls,
|
|
// stats.branches_taken);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace yaze
|