diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index e1ff68393..1d1422fe5 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -317,8 +317,8 @@ template < Primitive::setmo(destination_w(), context); break; } else { - // TODO: perform ENTER as of the 80186. static_assert(int(Operation::SETMO) == int(Operation::ENTER)); + Primitive::enter(instruction, context); } return; case Operation::SETMOC: diff --git a/InstructionSets/x86/Implementation/Stack.hpp b/InstructionSets/x86/Implementation/Stack.hpp index 892b2f4e3..dfefb18d2 100644 --- a/InstructionSets/x86/Implementation/Stack.hpp +++ b/InstructionSets/x86/Implementation/Stack.hpp @@ -22,13 +22,13 @@ void push( IntT &value, ContextT &context ) { - context.registers.sp_ -= sizeof(IntT); + context.registers.sp() -= sizeof(IntT); if constexpr (preauthorised) { - context.memory.template preauthorised_write(Source::SS, context.registers.sp_, value); + context.memory.template preauthorised_write(Source::SS, context.registers.sp(), value); } else { context.memory.template access( Source::SS, - context.registers.sp_) = value; + context.registers.sp()) = value; } context.memory.template write_back(); } @@ -39,8 +39,8 @@ IntT pop( ) { const auto value = context.memory.template access( Source::SS, - context.registers.sp_); - context.registers.sp_ += sizeof(IntT); + context.registers.sp()); + context.registers.sp() += sizeof(IntT); return value; } @@ -144,12 +144,49 @@ void pusha( } } +template +void enter( + const InstructionT &instruction, + ContextT &context +) { + // TODO: all non-16bit address sizes. + const auto alloc_size = instruction.dynamic_storage_size(); + const auto nesting_level = instruction.nesting_level() & 0x1f; + + // Preauthorse contents that'll be fetched via BP. + const auto copied_pointers = nesting_level - 2; + if(copied_pointers > 0) { + context.memory.preauthorise_read( + Source::SS, + context.registers.bp() - copied_pointers * sizeof(uint16_t), + copied_pointers * sizeof(uint16_t) + ); + } + + // Preauthorse stack activity. + context.memory.preauthorise_stack_write((1 + copied_pointers) * sizeof(uint16_t)); + + // Push BP and grab the end of frame. + push(context.registers.bp(), context); + const auto frame = context.registers.sp(); + + // Copy data as per the nesting level. + for(int c = 1; c < nesting_level; c++) { + context.registers.bp() -= 2; + + const auto value = context.memory.template preauthorised_read(Source::SS, context.registers.bp()); + push(value); + } + + // Set final BP. + context.registers.bp() = frame; +} + template void leave( ContextT &context ) { // TODO: should use StackAddressSize to determine assignment of bp to sp. - // Probably make that a static constexpr on registers. if constexpr (std::is_same_v) { context.registers.esp() = context.registers.ebp(); context.registers.ebp() = pop(context); diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index da62be4d1..baaf3ff6f 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -245,8 +245,8 @@ enum class Operation: uint8_t { /// Raises a bounds exception if not. BOUND = SETMOC, - /// Create stack frame. See operand() for the nesting level and offset() - /// for the dynamic storage size. + /// Create stack frame. See the Instruction getters `nesting_level()` + /// and `dynamic_storage_size()`. ENTER, /// Procedure exit; copies BP to SP, then pops a new BP from the stack. LEAVE, @@ -815,6 +815,11 @@ template class Instruction { return ops[has_operand()]; } + /// @returns The nesting level argument supplied to an ENTER. + constexpr ImmediateT nesting_level() const { + return operand(); + } + /// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will /// have a segment value of `1234h`. constexpr uint16_t segment() const { @@ -833,6 +838,11 @@ template class Instruction { return DisplacementT(offset()); } + /// @returns The dynamic storage size argument supplied to an ENTER. + constexpr ImmediateT dynamic_storage_size() const { + return displacement(); + } + // Standard comparison operator. constexpr bool operator ==(const Instruction &rhs) const { if( operation_ != rhs.operation_ ||