diff --git a/src/sys/scenario.cpp b/src/sys/scenario.cpp index f91eb3b..c78d1da 100644 --- a/src/sys/scenario.cpp +++ b/src/sys/scenario.cpp @@ -1,4 +1,5 @@ #include "scenario.h" +#include #include #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 { diff --git a/src/sys/scenario.h b/src/sys/scenario.h index 6d85a3b..f73e5ce 100644 --- a/src/sys/scenario.h +++ b/src/sys/scenario.h @@ -40,25 +40,35 @@ public: void get_syseng_string(char* buf, int size, Encoding *enc, uint8_t terminator); void label_call(int label); - size_t label_stack_size() const { return label_stack.size(); } - void label_stack_pop() { label_stack.pop_back(); } - void label_stack_clear() { label_stack.clear(); } + size_t label_stack_size() const; + void label_stack_pop(); + void label_stack_clear(); void page_call(int page); - size_t page_stack_size() const { return page_stack.size(); } - void page_stack_pop() { page_stack.pop_back(); } - void page_stack_clear() { page_stack.clear(); } + size_t page_stack_size() const; + void page_stack_pop(); + void page_stack_clear(); [[noreturn]] void unknown_command(uint8_t cmd); + struct StackFrame { + bool is_page_call; + uint8_t page; + uint16_t addr; + + StackFrame(bool is_page_call, uint8_t page, uint16_t addr) + : is_page_call(is_page_call), page(page), addr(addr) {} + }; + const std::vector& get_call_stack() const { return call_stack; } + int write_instruction(int page, int addr, uint8_t op); + private: Dri adisk; std::vector data_; int page_; int addr_; int cmd_addr_; - std::vector label_stack; - std::vector> page_stack; + std::vector call_stack; }; #endif // _SCENARIO_H_