mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-14 03:37:04 +00:00
Fix PUSH SP, far call. Further simplify FlowController.
This commit is contained in:
parent
acb55aa4e2
commit
e4fdf09149
@ -189,13 +189,13 @@ IntT *resolve(
|
||||
|
||||
namespace 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.
|
||||
// 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 <typename IntT, bool Preauthorised, typename ContextT>
|
||||
void push(IntT value, ContextT &context) {
|
||||
void push(IntT &value, ContextT &context) {
|
||||
context.registers.sp_ -= sizeof(IntT);
|
||||
context.memory.template access<IntT, Preauthorised ? AccessType::PreauthorisedWrite : AccessType::Write>(
|
||||
InstructionSet::x86::Source::SS,
|
||||
Source::SS,
|
||||
context.registers.sp_) = value;
|
||||
context.memory.template write_back<IntT>();
|
||||
}
|
||||
@ -203,7 +203,7 @@ void push(IntT value, ContextT &context) {
|
||||
template <typename IntT, bool Preauthorised, typename ContextT>
|
||||
IntT pop(ContextT &context) {
|
||||
const auto value = context.memory.template access<IntT, Preauthorised ? AccessType::PreauthorisedRead : AccessType::Read>(
|
||||
InstructionSet::x86::Source::SS,
|
||||
Source::SS,
|
||||
context.registers.sp_);
|
||||
context.registers.sp_ += sizeof(IntT);
|
||||
return value;
|
||||
@ -597,7 +597,7 @@ void div(IntT &destination_high, IntT &destination_low, IntT source, ContextT &c
|
||||
The CF, OF, SF, ZF, AF, and PF flags are undefined.
|
||||
*/
|
||||
if(!source) {
|
||||
InstructionSet::x86::interrupt(Interrupt::DivideError, context);
|
||||
interrupt(Interrupt::DivideError, context);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -869,12 +869,14 @@ void call_far(InstructionT &instruction, ContextT &context) {
|
||||
}
|
||||
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
push<uint16_t, true>(context.registers.cs(), context);
|
||||
push<uint16_t, true>(context.registers.ip(), context);
|
||||
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
|
||||
// At least on an 8086, the stack writes occur after the target address read.
|
||||
push<uint16_t, true>(context.registers.cs(), context);
|
||||
push<uint16_t, true>(context.registers.ip(), context);
|
||||
|
||||
context.flow_controller.jump(segment, offset);
|
||||
}
|
||||
|
||||
@ -899,6 +901,7 @@ void jump_far(InstructionT &instruction, ContextT &context) {
|
||||
}
|
||||
|
||||
const Source source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
@ -1919,7 +1922,8 @@ template <
|
||||
const uint16_t ip = context.memory.template access<uint16_t, AccessType::Read>(address);
|
||||
const uint16_t cs = context.memory.template access<uint16_t, AccessType::Read>(address + 2);
|
||||
|
||||
Primitive::push<uint16_t, true>(context.status.get(), context);
|
||||
auto flags = context.status.get();
|
||||
Primitive::push<uint16_t, true>(flags, context);
|
||||
context.status.template set_from<Flag::Interrupt, Flag::Trap>(0);
|
||||
|
||||
// Push CS and IP.
|
||||
|
@ -109,8 +109,6 @@ struct Memory {
|
||||
Seeded,
|
||||
AccessExpected,
|
||||
Accessed,
|
||||
FlagsL,
|
||||
FlagsH
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, Tag> tags;
|
||||
@ -239,25 +237,7 @@ class FlowController {
|
||||
FlowController(Memory &memory, Registers ®isters, Status &status) :
|
||||
memory_(memory), registers_(registers), status_(status) {}
|
||||
|
||||
void interrupt(int index) {
|
||||
// TODO: reauthorise and possibly double fault?
|
||||
const uint16_t address = static_cast<uint16_t>(index) << 2;
|
||||
const uint16_t new_ip = memory_.access<uint16_t, Memory::AccessType::Read>(address, Memory::Tag::Accessed);
|
||||
const uint16_t new_cs = memory_.access<uint16_t, Memory::AccessType::Read>(address + 2, Memory::Tag::Accessed);
|
||||
|
||||
push(status_.get(), true);
|
||||
|
||||
using Flag = InstructionSet::x86::Flag;
|
||||
status_.set_from<Flag::Interrupt, Flag::Trap>(0);
|
||||
|
||||
// Push CS and IP.
|
||||
push(registers_.cs_);
|
||||
push(registers_.ip_);
|
||||
|
||||
registers_.cs_ = new_cs;
|
||||
registers_.ip_ = new_ip;
|
||||
}
|
||||
|
||||
// Requirements for perform.
|
||||
void jump(uint16_t address) {
|
||||
registers_.ip_ = address;
|
||||
}
|
||||
@ -270,12 +250,14 @@ class FlowController {
|
||||
void halt() {}
|
||||
void wait() {}
|
||||
|
||||
void begin_instruction() {
|
||||
should_repeat_ = false;
|
||||
}
|
||||
void repeat_last() {
|
||||
should_repeat_ = true;
|
||||
}
|
||||
|
||||
// Other actions.
|
||||
void begin_instruction() {
|
||||
should_repeat_ = false;
|
||||
}
|
||||
bool should_repeat() const {
|
||||
return should_repeat_;
|
||||
}
|
||||
@ -285,23 +267,6 @@ class FlowController {
|
||||
Registers ®isters_;
|
||||
Status &status_;
|
||||
bool should_repeat_ = false;
|
||||
|
||||
void push(uint16_t value, bool is_flags = false) {
|
||||
// Perform the push in two steps because it's possible for SP to underflow, and so that FlagsL and
|
||||
// FlagsH can be set separately.
|
||||
--registers_.sp_;
|
||||
memory_.access<uint8_t, Memory::AccessType::Write>(
|
||||
InstructionSet::x86::Source::SS,
|
||||
registers_.sp_,
|
||||
is_flags ? Memory::Tag::FlagsH : Memory::Tag::Accessed
|
||||
) = value >> 8;
|
||||
--registers_.sp_;
|
||||
memory_.access<uint8_t, Memory::AccessType::Write>(
|
||||
InstructionSet::x86::Source::SS,
|
||||
registers_.sp_,
|
||||
is_flags ? Memory::Tag::FlagsL : Memory::Tag::Accessed
|
||||
) = value & 0xff;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExecutionSupport {
|
||||
@ -556,21 +521,31 @@ struct FailedExecution {
|
||||
InstructionSet::x86::Status intended_status;
|
||||
|
||||
bool ramEqual = true;
|
||||
int mask_position = 0;
|
||||
for(NSArray<NSNumber *> *ram in final_state[@"ram"]) {
|
||||
const uint32_t address = [ram[0] intValue];
|
||||
|
||||
uint8_t mask = 0xff;
|
||||
if(const auto tag = execution_support.memory.tags.find(address); tag != execution_support.memory.tags.end()) {
|
||||
switch(tag->second) {
|
||||
default: break;
|
||||
case Memory::Tag::FlagsH: mask = flags_mask >> 8; break;
|
||||
case Memory::Tag::FlagsL: mask = flags_mask & 0xff; break;
|
||||
}
|
||||
if((mask_position != 1) && execution_support.memory.memory[address] == [ram[1] intValue]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if((execution_support.memory.memory[address] & mask) != ([ram[1] intValue] & mask)) {
|
||||
ramEqual = false;
|
||||
// Consider whether this apparent mismatch might be because flags have been written to memory;
|
||||
// allow only one use of the [16-bit] mask per test.
|
||||
bool matched_with_mask = false;
|
||||
while(mask_position < 2) {
|
||||
const uint8_t mask = mask_position ? (flags_mask >> 8) : (flags_mask & 0xff);
|
||||
++mask_position;
|
||||
if((execution_support.memory.memory[address] & mask) == ([ram[1] intValue] & mask)) {
|
||||
matched_with_mask = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(matched_with_mask) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ramEqual = false;
|
||||
break;
|
||||
}
|
||||
|
||||
[self populate:intended_registers status:intended_status value:final_state[@"regs"]];
|
||||
|
Loading…
x
Reference in New Issue
Block a user