// // Stack.hpp // Clock Signal // // Created by Thomas Harte on 08/11/2023. // Copyright © 2023 Thomas Harte. All rights reserved. // #pragma once #include "../AccessType.hpp" #include namespace InstructionSet::x86::Primitive { // The below takes a reference in order properly to handle PUSH SP, // which should place the value of SP after the push onto the stack. template void push( IntT &value, ContextT &context ) { context.registers.sp() -= sizeof(IntT); if constexpr (preauthorised) { context.memory.template preauthorised_write(Source::SS, context.registers.sp(), value); } else { context.memory.template access( Source::SS, context.registers.sp()) = value; } context.memory.template write_back(); } template IntT pop( ContextT &context ) { const auto value = context.memory.template access( Source::SS, context.registers.sp()); context.registers.sp() += sizeof(IntT); return value; } template void sahf( uint8_t &ah, ContextT &context ) { /* EFLAGS(SF:ZF:0:AF:0:PF:1:CF) ← AH; */ context.flags.template set_from(ah); context.flags.template set_from(!(ah & 0x40)); context.flags.template set_from(ah & 0x10); context.flags.template set_from(!(ah & 0x04)); context.flags.template set_from(ah & 0x01); } template void lahf( uint8_t &ah, ContextT &context ) { /* AH ← EFLAGS(SF:ZF:0:AF:0:PF:1:CF); */ ah = (context.flags.template flag() ? 0x80 : 0x00) | (context.flags.template flag() ? 0x40 : 0x00) | (context.flags.template flag() ? 0x10 : 0x00) | (context.flags.template flag() ? 0x00 : 0x04) | 0x02 | (context.flags.template flag() ? 0x01 : 0x00); } template void popf( ContextT &context ) { context.flags.set(pop(context)); } template void pushf( ContextT &context ) { uint16_t value = context.flags.get(); push(value, context); } template void popa( ContextT &context ) { context.memory.preauthorise_stack_read(sizeof(IntT) * 8); if constexpr (std::is_same_v) { context.registers.edi() = pop(context); context.registers.esi() = pop(context); context.registers.ebp() = pop(context); context.registers.esp() += 4; context.registers.ebx() = pop(context); context.registers.edx() = pop(context); context.registers.ecx() = pop(context); context.registers.eax() = pop(context); } else { context.registers.di() = pop(context); context.registers.si() = pop(context); context.registers.bp() = pop(context); context.registers.sp() += 2; context.registers.bx() = pop(context); context.registers.dx() = pop(context); context.registers.cx() = pop(context); context.registers.ax() = pop(context); } } template void pusha( ContextT &context ) { context.memory.preauthorise_stack_read(sizeof(IntT) * 8); IntT initial_sp = context.registers.sp(); if constexpr (std::is_same_v) { push(context.registers.eax(), context); push(context.registers.ecx(), context); push(context.registers.edx(), context); push(context.registers.ebx(), context); push(initial_sp, context); push(context.registers.ebp(), context); push(context.registers.esi(), context); push(context.registers.esi(), context); } else { push(context.registers.ax(), context); push(context.registers.cx(), context); push(context.registers.dx(), context); push(context.registers.bx(), context); push(initial_sp, context); push(context.registers.bp(), context); push(context.registers.si(), context); push(context.registers.si(), context); } } 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. if constexpr (std::is_same_v) { context.registers.esp() = context.registers.ebp(); context.registers.ebp() = pop(context); } else { context.registers.sp() = context.registers.bp(); context.registers.bp() = pop(context); } } }