mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Slims the Program struct down to 8 bytes total.
This commit is contained in:
parent
827c4e172a
commit
1327de1c82
@ -13,6 +13,7 @@
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
|
@ -316,8 +316,8 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
// should_log = (fetched_pc >= 0x408D66) && (fetched_pc <= 0x408D84);
|
||||
#endif
|
||||
|
||||
if(instructions[decoded_instruction_.full].micro_operations) {
|
||||
if(instructions[decoded_instruction_.full].requires_supervisor && !is_supervisor_) {
|
||||
if(instructions[decoded_instruction_.full].micro_operations != std::numeric_limits<uint32_t>::max()) {
|
||||
if((instructions[decoded_instruction_.full].source_dest & 0x80) && !is_supervisor_) {
|
||||
// A privilege violation has been detected.
|
||||
active_program_ = nullptr;
|
||||
active_micro_op_ = short_exception_micro_ops_;
|
||||
@ -325,7 +325,7 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
} else {
|
||||
// Standard instruction dispatch.
|
||||
active_program_ = &instructions[decoded_instruction_.full];
|
||||
active_micro_op_ = active_program_->micro_operations;
|
||||
active_micro_op_ = &all_micro_ops_[active_program_->micro_operations];
|
||||
}
|
||||
} else {
|
||||
// The opcode fetched isn't valid.
|
||||
@ -364,10 +364,11 @@ template <class T, bool dtack_is_implicit, bool signal_will_perform> void Proces
|
||||
|
||||
case int(MicroOp::Action::None): break;
|
||||
|
||||
#define source() active_program_->source
|
||||
#define source_address() address_[active_program_->source_dest >> 4]
|
||||
#define destination() active_program_->destination
|
||||
#define destination_address() address_[active_program_->source_dest & 0x0f]
|
||||
#define offset_pointer(x) reinterpret_cast<RegisterPair32 *>(&reinterpret_cast<uint8_t *>(static_cast<ProcessorStorage *>(this))[x])
|
||||
#define source() offset_pointer(active_program_->source_offset)
|
||||
#define source_address() address_[(active_program_->source_dest >> 4) & 7]
|
||||
#define destination() offset_pointer(active_program_->destination_offset)
|
||||
#define destination_address() address_[active_program_->source_dest & 7]
|
||||
|
||||
case int(MicroOp::Action::PerformOperation):
|
||||
#define sub_overflow() ((result ^ destination) & (destination ^ source))
|
||||
|
@ -867,7 +867,7 @@ struct ProcessorStorageConstructor {
|
||||
|
||||
switch(mapping.decoder) {
|
||||
case Decoder::STOP: {
|
||||
program.requires_supervisor = true;
|
||||
program.set_requires_supervisor(true);
|
||||
op(Action::None, seq("n"));
|
||||
op(Action::PerformOperation);
|
||||
} break;
|
||||
@ -973,7 +973,7 @@ struct ProcessorStorageConstructor {
|
||||
|
||||
case Decoder::EORI_ORI_ANDI_SR: {
|
||||
// The source used here is always the high word of the prefetch queue.
|
||||
program.requires_supervisor = !!(instruction & 0x40);
|
||||
program.set_requires_supervisor(!!(instruction & 0x40));
|
||||
op(Action::None, seq("np nn nn"));
|
||||
op(Action::PerformOperation, seq("np np"));
|
||||
} break;
|
||||
@ -1011,7 +1011,7 @@ struct ProcessorStorageConstructor {
|
||||
} break;
|
||||
|
||||
case Decoder::RTE_RTR: {
|
||||
program.requires_supervisor = instruction == 0x4e73;
|
||||
program.set_requires_supervisor(instruction == 0x4e73);
|
||||
|
||||
// TODO: something explicit to ensure the nR nr nr is exclusively linked.
|
||||
op(Action::PrepareRTE_RTR, seq("nR nr nr", { &storage_.precomputed_addresses_[0], &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] } ));
|
||||
@ -2082,12 +2082,12 @@ struct ProcessorStorageConstructor {
|
||||
break;
|
||||
|
||||
case bw(Imm): // CMP.br #, Dn
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::PerformOperation, seq("np np"));
|
||||
break;
|
||||
|
||||
case l(Imm): // CMP.l #, Dn
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::None, seq("np"));
|
||||
op(Action::PerformOperation, seq("np np n"));
|
||||
break;
|
||||
@ -2164,12 +2164,12 @@ struct ProcessorStorageConstructor {
|
||||
break;
|
||||
|
||||
case bw(Imm): // CMPA.w #, An
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::PerformOperation, seq("np np n"));
|
||||
break;
|
||||
|
||||
case l(Imm): // CMPA.l #, An
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::None, seq("np"));
|
||||
op(Action::PerformOperation, seq("np np n"));
|
||||
break;
|
||||
@ -2190,12 +2190,12 @@ struct ProcessorStorageConstructor {
|
||||
default: continue;
|
||||
|
||||
case bw(Dn): // CMPI.bw #, Dn
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::PerformOperation, seq("np np"));
|
||||
break;
|
||||
|
||||
case l(Dn): // CMPI.l #, Dn
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(Action::None, seq("np"));
|
||||
op(Action::PerformOperation, seq("np np n"));
|
||||
break;
|
||||
@ -2363,7 +2363,7 @@ struct ProcessorStorageConstructor {
|
||||
program.set_source(storage_, ea_mode, ea_register);
|
||||
|
||||
// ... but otherwise assume that the true source of a destination will be the computed source address.
|
||||
program.source = &storage_.effective_address_[0];
|
||||
program.set_source(storage_, &storage_.effective_address_[0]);
|
||||
|
||||
const int mode = combined_mode(ea_mode, ea_register);
|
||||
switch(mode) {
|
||||
@ -2475,10 +2475,10 @@ struct ProcessorStorageConstructor {
|
||||
|
||||
const int mode = combined_mode(ea_mode, ea_register);
|
||||
program.set_source_address(storage_, ea_register);
|
||||
program.source =
|
||||
program.set_source(storage_,
|
||||
(mode == Ind) ?
|
||||
&storage_.address_[ea_register] :
|
||||
&storage_.effective_address_[0];
|
||||
&storage_.effective_address_[0]);
|
||||
|
||||
switch(mode) {
|
||||
default: continue;
|
||||
@ -2544,7 +2544,7 @@ struct ProcessorStorageConstructor {
|
||||
case Decoder::MOVEtoSRCCR: {
|
||||
if(ea_mode == An) continue;
|
||||
program.set_source(storage_, ea_mode, ea_register);
|
||||
program.requires_supervisor = (operation == Operation::MOVEtoSR);
|
||||
program.set_requires_supervisor(operation == Operation::MOVEtoSR);
|
||||
|
||||
/* DEVIATION FROM YACHT.TXT: it has all of these reading an extra word from the PC;
|
||||
this looks like a mistake so I've padded with nil cycles in the middle. */
|
||||
@ -2582,7 +2582,7 @@ struct ProcessorStorageConstructor {
|
||||
break;
|
||||
|
||||
case Imm: // MOVE #, SR
|
||||
program.source = &storage_.prefetch_queue_;
|
||||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||
op(int(Action::PerformOperation), seq("np nn nn np"));
|
||||
break;
|
||||
}
|
||||
@ -2670,18 +2670,18 @@ struct ProcessorStorageConstructor {
|
||||
} break;
|
||||
|
||||
case Decoder::MOVEUSP: {
|
||||
program.requires_supervisor = true;
|
||||
program.set_requires_supervisor(true);
|
||||
|
||||
// Observation here: because this is a privileged instruction, the user stack pointer
|
||||
// definitely isn't currently [copied into] A7.
|
||||
if(instruction & 0x8) {
|
||||
// Transfer FROM the USP.
|
||||
program.source = &storage_.stack_pointers_[0];
|
||||
program.set_source(storage_, &storage_.stack_pointers_[0]);
|
||||
program.set_destination(storage_, An, ea_register);
|
||||
} else {
|
||||
// Transfer TO the USP.
|
||||
program.set_source(storage_, An, ea_register);
|
||||
program.destination = &storage_.stack_pointers_[0];
|
||||
program.set_destination(storage_, &storage_.stack_pointers_[0]);
|
||||
}
|
||||
|
||||
op(Action::PerformOperation, seq("np"));
|
||||
@ -2869,7 +2869,7 @@ struct ProcessorStorageConstructor {
|
||||
} break;
|
||||
|
||||
case Decoder::RESET:
|
||||
program.requires_supervisor = true;
|
||||
program.set_requires_supervisor(true);
|
||||
op(Action::None, seq("nn _ np"));
|
||||
break;
|
||||
|
||||
@ -3091,8 +3091,8 @@ struct ProcessorStorageConstructor {
|
||||
// Finalise micro-op and program pointers.
|
||||
for(size_t instruction = 0; instruction < 65536; ++instruction) {
|
||||
if(micro_op_pointers[instruction] != std::numeric_limits<size_t>::max()) {
|
||||
storage_.instructions[instruction].micro_operations = &storage_.all_micro_ops_[micro_op_pointers[instruction]];
|
||||
link_operations(storage_.instructions[instruction].micro_operations, &arbitrary_base);
|
||||
storage_.instructions[instruction].micro_operations = uint32_t(micro_op_pointers[instruction]);
|
||||
link_operations(&storage_.all_micro_ops_[micro_op_pointers[instruction]], &arbitrary_base);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3100,7 +3100,8 @@ struct ProcessorStorageConstructor {
|
||||
storage_.interrupt_micro_ops_ = &storage_.all_micro_ops_[interrupt_pointer];
|
||||
link_operations(storage_.interrupt_micro_ops_, &arbitrary_base);
|
||||
|
||||
std::cout << storage_.all_bus_steps_.size() << " total steps" << std::endl;
|
||||
std::cout << storage_.all_bus_steps_.size() << " total bus steps" << std::endl;
|
||||
std::cout << storage_.all_micro_ops_.size() << " total micro ops" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -3248,7 +3249,7 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
|
||||
//
|
||||
// Assumed order of input: PC.h, SR, PC.l (i.e. the opposite of TRAP's output).
|
||||
for(const int instruction: { 0x4e73, 0x4e77 }) {
|
||||
auto steps = instructions[instruction].micro_operations[0].bus_program;
|
||||
auto steps = all_micro_ops_[instructions[instruction].micro_operations].bus_program;
|
||||
steps[0].microcycle.value = steps[1].microcycle.value = &program_counter_.halves.high;
|
||||
steps[4].microcycle.value = steps[5].microcycle.value = &program_counter_.halves.low;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class ProcessorStorage {
|
||||
HalfCycles half_cycles_left_to_run_;
|
||||
HalfCycles e_clock_phase_;
|
||||
|
||||
enum class Operation {
|
||||
enum class Operation: uint8_t {
|
||||
None,
|
||||
ABCD, SBCD, NBCD,
|
||||
|
||||
@ -335,21 +335,30 @@ class ProcessorStorage {
|
||||
of micro-ops and, separately, the operation to perform plus whatever other
|
||||
fields the operation requires.
|
||||
|
||||
TODO: this struct, as currently formed, is 48 bytes large on my 64-bit Intel
|
||||
machine — 8 bytes for each of the pointers, plus 8 bytes for the non-pointer fields.
|
||||
That means that the Program[65536] table is 3mb large. Far too huge for a cache.
|
||||
So slim this, even if it makes things much more painful to dereference.
|
||||
Some of the fields are slightly convoluted in how they identify the information
|
||||
they reference; this is done to keep this struct as small as possible due to
|
||||
concerns about cache size.
|
||||
|
||||
(Aside: the compiler seems to prefer 8-byte alignment so eliminating or slimming the
|
||||
non-pointer fields doesn't seem to be helpful immediately.)
|
||||
On the 64-bit Intel processor this emulator was developed on, the struct below
|
||||
adds up to 8 bytes; four for the initial uint32_t and then one each for the
|
||||
remaining fields, with no additional padding being inserted by the compiler.
|
||||
*/
|
||||
struct Program {
|
||||
/// The offset into the all_micro_ops_ at which micro-ops for this instruction begin.
|
||||
uint32_t micro_operations = std::numeric_limits<uint32_t>::max();
|
||||
/// The overarching operation applied by this program when the moment comes.
|
||||
Operation operation;
|
||||
/// The number of bytes after the beginning of an instance of ProcessorStorage that the RegisterPair32 containing
|
||||
/// a source value for this operation lies at.
|
||||
uint8_t source_offset = 0;
|
||||
/// The number of bytes after the beginning of an instance of ProcessorStorage that the RegisterPair32 containing
|
||||
/// a destination value for this operation lies at.
|
||||
uint8_t destination_offset = 0;
|
||||
/// A bitfield comprised of:
|
||||
/// b7 = set if this program requires supervisor mode;
|
||||
/// b0–b2 = the source address register (for pre-decrement and post-increment actions); and
|
||||
/// b4-b6 = destination address register.
|
||||
uint8_t source_dest = 0;
|
||||
bool requires_supervisor = false;
|
||||
MicroOp *micro_operations = nullptr;
|
||||
RegisterPair32 *source = nullptr;
|
||||
RegisterPair32 *destination = nullptr;
|
||||
|
||||
void set_source_address(ProcessorStorage &storage, int index) {
|
||||
source_dest = uint8_t((source_dest & 0x0f) | (index << 4));
|
||||
@ -359,12 +368,18 @@ class ProcessorStorage {
|
||||
source_dest = uint8_t((source_dest & 0xf0) | index);
|
||||
}
|
||||
|
||||
void set_requires_supervisor(bool requires_supervisor) {
|
||||
source_dest = (source_dest & 0x7f) | (requires_supervisor ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
void set_source(ProcessorStorage &storage, RegisterPair32 *target) {
|
||||
source = target;
|
||||
source_offset = decltype(source_offset)(reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage));
|
||||
assert(source_offset == (reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage)));
|
||||
}
|
||||
|
||||
void set_destination(ProcessorStorage &storage, RegisterPair32 *target) {
|
||||
destination = target;
|
||||
destination_offset = decltype(destination_offset)(reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage));
|
||||
assert(destination_offset == (reinterpret_cast<uint8_t *>(target) - reinterpret_cast<uint8_t *>(&storage)));
|
||||
}
|
||||
|
||||
void set_source(ProcessorStorage &storage, int mode, int reg) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user