Scenario: Label / page calls share a single call stack

This allows the debugger to present a linear backtrace.
It's still possible to return from the other call type.
This commit is contained in:
kichikuou
2024-12-27 16:08:58 +09:00
parent a13719d84c
commit d6e3cb8415
2 changed files with 86 additions and 18 deletions
+68 -10
View File
@@ -1,4 +1,5 @@
#include "scenario.h"
#include <algorithm>
#include <string.h>
#include "common.h"
#include "encoding.h"
@@ -44,35 +45,92 @@ void Scenario::get_syseng_string(char* buf, int size, Encoding *enc, uint8_t ter
void Scenario::label_call(int label)
{
if (label == 0) {
if (label_stack.empty()) {
// Return
auto it = std::find_if(call_stack.rbegin(), call_stack.rend(),
[](const StackFrame& frame) { return !frame.is_page_call; });
if (it == call_stack.rend()) {
WARNING("Label stack underflow");
return;
}
jump_to(label_stack.back());
label_stack.pop_back();
if (it != call_stack.rbegin()) {
WARNING("Return from non-top label call");
}
if (it->page != page_) {
sys_error("Illegal label return at %d:%04x", page_, cmd_addr_);
}
jump_to(it->addr);
call_stack.erase(std::next(it).base());
} else {
label_stack.push_back(addr_);
call_stack.emplace_back(false, page_, addr_);
jump_to(label);
}
}
size_t Scenario::label_stack_size() const
{
return std::count_if(call_stack.begin(), call_stack.end(),
[](const StackFrame& frame) { return !frame.is_page_call; });
}
void Scenario::label_stack_pop()
{
auto it = std::find_if(call_stack.rbegin(), call_stack.rend(),
[](const StackFrame& frame) { return !frame.is_page_call; });
if (it == call_stack.rend())
return;
call_stack.erase(std::next(it).base());
}
void Scenario::label_stack_clear()
{
call_stack.erase(std::remove_if(call_stack.begin(), call_stack.end(),
[](const StackFrame& frame) { return !frame.is_page_call; }), call_stack.end());
}
void Scenario::page_call(int target_page)
{
if (target_page == 0) {
if (page_stack.empty()) {
// Return
auto it = std::find_if(call_stack.rbegin(), call_stack.rend(),
[](const StackFrame& frame) { return frame.is_page_call; });
if (it == call_stack.rend()) {
WARNING("Page stack underflow");
return;
}
auto& pair = page_stack.back();
page_jump(pair.first, pair.second);
page_stack.pop_back();
if (it != call_stack.rbegin()) {
WARNING("Return from non-top page call");
}
page_jump(it->page, it->addr);
call_stack.erase(std::next(it).base());
} else {
page_stack.emplace_back(page_, addr_);
call_stack.emplace_back(true, page_, addr_);
page_jump(target_page, 2);
}
}
[[noreturn]] void Scenario::unknown_command(uint8_t cmd) {
size_t Scenario::page_stack_size() const
{
return std::count_if(call_stack.begin(), call_stack.end(),
[](const StackFrame& frame) { return frame.is_page_call; });
}
void Scenario::page_stack_pop()
{
auto it = std::find_if(call_stack.rbegin(), call_stack.rend(),
[](const StackFrame& frame) { return frame.is_page_call; });
if (it == call_stack.rend())
return;
call_stack.erase(std::next(it).base());
}
void Scenario::page_stack_clear()
{
call_stack.erase(std::remove_if(call_stack.begin(), call_stack.end(),
[](const StackFrame& frame) { return frame.is_page_call; }), call_stack.end());
}
[[noreturn]] void Scenario::unknown_command(uint8_t cmd)
{
if (cmd >= 0x20 && cmd < 0x7f) {
sys_error("Unknown Command: '%c' at %d:%04x", cmd, page_, cmd_addr_);
} else {