diff --git a/toolbox/toolbox.cpp b/toolbox/toolbox.cpp index 35b1392..212fbca 100644 --- a/toolbox/toolbox.cpp +++ b/toolbox/toolbox.cpp @@ -26,8 +26,11 @@ #include #include +#include + #include #include +#include #include #include @@ -41,18 +44,251 @@ #include "sane.h" #include "utility.h" #include "loader.h" +#include "stackframe.h" #include "macos/traps.h" + // yuck. TST.W d0 extern "C" void cpuSetFlagsNZ00NewW(UWO res); + +template +void Push__(uint32_t sp) +{ + static_assert(Bytes == 0, "Invalid Stack Size"); +} + +template +void Push__(uint32_t sp, uint16_t value, Args&&... args); + +template +void Push__(uint32_t sp, uint32_t value, Args&&... args); + + +template +void Push__(uint32_t sp, uint16_t value, Args&&... args) +{ + memoryWriteWord(value, sp); + Push__(sp + 2, std::forward(args)...); +} + +template +void Push__(uint32_t sp, uint32_t value, Args&&... args) +{ + memoryWriteLong(value, sp); + Push__(sp + 4, std::forward(args)...); +} + +template +void Push(Args&&... args) +{ + uint32_t sp = cpuGetAReg(7) - Bytes; + cpuSetAReg(7, sp); + + Push__(sp, std::forward(args)...); + +} + + namespace ToolBox { bool Trace = false; + uint32_t trap_address[1024]; + uint32_t os_address[256]; + + namespace { + inline constexpr bool is_tool_trap(uint16_t trap) + { + // %1010 | x . . . | . . . . . . . . | + // + // x = 1 if this is a toolbox call, x = 0 if this is an os call. + + return (trap & 0x0800) == 0x0800; + } + + inline constexpr bool auto_pop(uint16_t trap) + { + // %1010 | 1 x . . | . . . . . . . . | + // + // if x = 1, an extra rts is pushed, + return (trap & 0x0c00) == 0x0c00; + } + + inline constexpr bool save_a0(uint16_t trap) + { + // %1010 | 0 . . x | . . . . . . . . | + // + // if x = 0, a0 is saved and restored. + // if x = 1, a0 is not saved and restored. + return (trap & 0x0900) == 0x0000; + } + + inline constexpr unsigned os_flags(uint16_t trap) + { + // %1010 | 0 x x . | . . . . . . . . | + return trap & 0x0600; + } + + } + + void pre_dispatch(uint16_t trap) + { + uint32_t returnPC = 0; + + bool saveA0 = false; + uint32_t a0 = cpuGetAReg(0); + + if (trap == 0xafff) + { + // called from the trap address stub. + + // uint16_t 0xafff + // uint16_t trap + // return address, etc, is on stack. + + + uint32_t pc = cpuGetPC(); + trap = memoryReadWord(pc); + + if (is_tool_trap(trap)) + { + StackFrame<4>(returnPC); + } + + #if 0 + + /* + * os stubs: + * os_return_a0 + * pop registers a0, a1, d0, d1, d2 + * pop long word + * tst.w d0 + * rts + * + * os_return: + * pop registers a0, a1, d0, d1, d2 + * pop long word + * tst.w d0 + * rts + * + * os_entry_a0: + * + */ + else + { + // os calls push the trap dispatcher pc as well. + uint32_t tmp; + uint32_t a0, a1, d0, d1, d2; + if (save_a0(trap)) + { + StackFrame<4*8>(returnPC, tmp, a0, a1, d0, d1, d2, tmp); + } + else + { + StackFrame<4*7>(returnPC, tmp, a1, d0, d1, d2, tmp); + } + trap = d1; // trap w/ flag bits. + } + #endif + + dispatch(trap); + cpuInitializeFromNewPC(returnPC); + return; + } + + + + /* + * The auto-pop bit is bit 10 in an A-line instruction for a + * Toolbox routine. Some language systems prefer to generate + * jump-subroutine calls (JSR) to intermediate routines, called + * glue routines, which then call Toolbox routines instead of + * executing the Toolbox routine directly. This glue method + * would normally interfere with Toolbox traps because the + * return address of the glue subroutine is placed on the stack + * between the Toolbox routine's parameters and the address of + * the place where the glue routine was called from (where + * control returns once the Toolbox routine has completed + * execution). + * + * The auto-pop bit forces the trap dispatcher to remove the + * top 4 bytes from the stack before dispatching to the Toolbox + * routine. After the Toolbox routine completes execution, + * control is transferred back to the place where the glue + * routine was called from, not back to the glue routine. + * + * Most development environments, including MPW, do not use + * this feature. + * + * (Trap Manager, 8-20) + */ + + if (auto_pop(trap)) + { + /* + * | args | + * | returnPC | + * ------------- + */ + + StackFrame<4>(returnPC); + trap &= ~0x0400; + } + + + if (is_tool_trap(trap)) + { + uint16_t tt = trap & 0x03ff; + uint32_t address = trap_address[tt]; + if (address) + { + /* + * parameters were pushed on the stack but + * the a-line instruction was intercepted + * before an exception occurred. Therefore, + * we need to push the return address on the + * stack and set the new pc to the trap address. + * + * returnPC may have been previously set from the + * auto pop bit. + */ + + Push<4>(returnPC == 0 ? cpuGetPC() : returnPC); + cpuInitializeFromNewPC(address); + return; + } + } + else + { + // os calls - check the return/save a0 bit. + // if bit 8 is 0, a0 is saved and restored. + // if bit 8 is 1, a0 is modified. + // Trap Manager 8-12. + + // not actually an issue yet, will only matter + // if os address is overridden. + + if (save_a0(trap)) { + saveA0 = true; + } + uint16_t tt = trap & 0x00ff; + + uint32_t address = os_address[tt]; + + if (address) { + assert("OS trap overrides are not yet supported."); + } + } + + + dispatch(trap); + + if (saveA0) cpuSetAReg(0, a0); + if (returnPC) cpuInitializeFromNewPC(returnPC); + } void dispatch(uint16_t trap) { - // todo -- check/remove extra bits for save a0, toolglue, etc. uint32_t d0 = 0; switch (trap) @@ -534,6 +770,7 @@ namespace ToolBox { cpuSetDReg(0, d0); cpuSetFlagsNZ00NewW(d0); + } std::string ReadCString(uint32_t address, bool fname) @@ -611,4 +848,4 @@ namespace ToolBox { return std::string(tmp); } -} \ No newline at end of file +}