mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-11 15:30:52 +00:00
Merge pull request #629 from TomHarte/MinorSpeedImprovements
Attempts to improve 68000 (and Macintosh) emulation speed
This commit is contained in:
commit
50e954223a
@ -64,6 +64,7 @@ std::uint8_t z8530::read(int address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pointer_ = 0;
|
pointer_ = 0;
|
||||||
|
update_delegate();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +111,12 @@ void z8530::write(int address, std::uint8_t value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
update_delegate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void z8530::set_dcd(int port, bool level) {
|
void z8530::set_dcd(int port, bool level) {
|
||||||
channels_[port].set_dcd(level);
|
channels_[port].set_dcd(level);
|
||||||
|
update_delegate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Channel implementations
|
// MARK: - Channel implementations
|
||||||
@ -255,3 +258,11 @@ bool z8530::Channel::get_interrupt_line() {
|
|||||||
(interrupt_mask_ & 1) && external_status_interrupt_;
|
(interrupt_mask_ & 1) && external_status_interrupt_;
|
||||||
// TODO: other potential causes of an interrupt.
|
// TODO: other potential causes of an interrupt.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void z8530::update_delegate() {
|
||||||
|
const bool interrupt_line = get_interrupt_line();
|
||||||
|
if(interrupt_line != previous_interrupt_line_) {
|
||||||
|
previous_interrupt_line_ = interrupt_line;
|
||||||
|
if(delegate_) delegate_->did_change_interrupt_status(this, interrupt_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,6 +35,13 @@ class z8530 {
|
|||||||
void reset();
|
void reset();
|
||||||
bool get_interrupt_line();
|
bool get_interrupt_line();
|
||||||
|
|
||||||
|
struct Delegate {
|
||||||
|
virtual void did_change_interrupt_status(z8530 *, bool new_status) = 0;
|
||||||
|
};
|
||||||
|
void set_delegate(Delegate *delegate) {
|
||||||
|
delegate_ = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**Interface for serial port input.**
|
**Interface for serial port input.**
|
||||||
*/
|
*/
|
||||||
@ -79,6 +86,10 @@ class z8530 {
|
|||||||
uint8_t interrupt_vector_ = 0;
|
uint8_t interrupt_vector_ = 0;
|
||||||
|
|
||||||
uint8_t master_interrupt_control_ = 0;
|
uint8_t master_interrupt_control_ = 0;
|
||||||
|
|
||||||
|
bool previous_interrupt_line_ = false;
|
||||||
|
void update_delegate();
|
||||||
|
Delegate *delegate_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public MouseMachine::Machine,
|
public MouseMachine::Machine,
|
||||||
public CPU::MC68000::BusHandler,
|
public CPU::MC68000::BusHandler,
|
||||||
public KeyboardMachine::MappedMachine {
|
public KeyboardMachine::MappedMachine,
|
||||||
|
public Zilog::SCC::z8530::Delegate {
|
||||||
public:
|
public:
|
||||||
using Target = Analyser::Static::Macintosh::Target;
|
using Target = Analyser::Static::Macintosh::Target;
|
||||||
|
|
||||||
@ -112,6 +113,9 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
iwm_.iwm.set_drive(0, &drives_[0]);
|
iwm_.iwm.set_drive(0, &drives_[0]);
|
||||||
iwm_.iwm.set_drive(1, &drives_[1]);
|
iwm_.iwm.set_drive(1, &drives_[1]);
|
||||||
|
|
||||||
|
// Make sure interrupt changes from the SCC are observed.
|
||||||
|
scc_.set_delegate(this);
|
||||||
|
|
||||||
// The Mac runs at 7.8336mHz.
|
// The Mac runs at 7.8336mHz.
|
||||||
set_clock_rate(double(CLOCK_RATE));
|
set_clock_rate(double(CLOCK_RATE));
|
||||||
audio_.speaker.set_input_rate(float(CLOCK_RATE) / 2.0f);
|
audio_.speaker.set_input_rate(float(CLOCK_RATE) / 2.0f);
|
||||||
@ -208,16 +212,6 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, false);
|
via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::Two, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update interrupt input. TODO: move this into a VIA/etc delegate callback?
|
|
||||||
// Double TODO: does this really cascade like this?
|
|
||||||
if(scc_.get_interrupt_line()) {
|
|
||||||
mc68000_.set_interrupt_level(2);
|
|
||||||
} else if(via_.get_interrupt_line()) {
|
|
||||||
mc68000_.set_interrupt_level(1);
|
|
||||||
} else {
|
|
||||||
mc68000_.set_interrupt_level(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A null cycle leaves nothing else to do.
|
// A null cycle leaves nothing else to do.
|
||||||
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return delay;
|
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return delay;
|
||||||
|
|
||||||
@ -319,7 +313,14 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
) {
|
) {
|
||||||
memory_base = ram_;
|
memory_base = ram_;
|
||||||
word_address &= ram_mask_;
|
word_address &= ram_mask_;
|
||||||
update_video();
|
|
||||||
|
// This is coupled with the Macintosh implementation of video; the magic
|
||||||
|
// constant should probably be factored into the Video class.
|
||||||
|
// It embodies knowledge of the fact that video (and audio) will always
|
||||||
|
// be fetched from the final $d900 bytes (i.e. $6c80 words) of memory.
|
||||||
|
// (And that ram_mask_ = ram size - 1).
|
||||||
|
// if(word_address > ram_mask_ - 0x6c80)
|
||||||
|
update_video();
|
||||||
} else {
|
} else {
|
||||||
memory_base = rom_;
|
memory_base = rom_;
|
||||||
word_address &= rom_mask_;
|
word_address &= rom_mask_;
|
||||||
@ -433,6 +434,24 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
|
|
||||||
// TODO: clear all keys.
|
// TODO: clear all keys.
|
||||||
|
|
||||||
|
// MARK: Interrupt updates.
|
||||||
|
|
||||||
|
void did_change_interrupt_status(Zilog::SCC::z8530 *sender, bool new_status) override {
|
||||||
|
update_interrupt_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_interrupt_input() {
|
||||||
|
// Update interrupt input.
|
||||||
|
// TODO: does this really cascade like this?
|
||||||
|
if(scc_.get_interrupt_line()) {
|
||||||
|
mc68000_.set_interrupt_level(2);
|
||||||
|
} else if(via_.get_interrupt_line()) {
|
||||||
|
mc68000_.set_interrupt_level(1);
|
||||||
|
} else {
|
||||||
|
mc68000_.set_interrupt_level(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update_video() {
|
void update_video() {
|
||||||
video_.run_for(time_since_video_update_.flush());
|
video_.run_for(time_since_video_update_.flush());
|
||||||
@ -554,6 +573,10 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
audio_.flush();
|
audio_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_interrupt_status(bool status) {
|
||||||
|
machine_.update_interrupt_input();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConcreteMachine &machine_;
|
ConcreteMachine &machine_;
|
||||||
RealTimeClock &clock_;
|
RealTimeClock &clock_;
|
||||||
|
@ -49,7 +49,7 @@ class CPU::MC68000::ProcessorStorageTests {
|
|||||||
|
|
||||||
if(!strcmp(type, "VA")) {
|
if(!strcmp(type, "VA")) {
|
||||||
// Test for validity.
|
// Test for validity.
|
||||||
if(!storage.instructions[next_opcode].micro_operations) {
|
if(storage.instructions[next_opcode].micro_operations == std::numeric_limits<uint32_t>::max()) {
|
||||||
[false_invalids_ addObject:@(next_opcode)];
|
[false_invalids_ addObject:@(next_opcode)];
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -57,7 +57,7 @@ class CPU::MC68000::ProcessorStorageTests {
|
|||||||
|
|
||||||
if(!strcmp(type, "IN")) {
|
if(!strcmp(type, "IN")) {
|
||||||
// Test for invalidity.
|
// Test for invalidity.
|
||||||
if(storage.instructions[next_opcode].micro_operations) {
|
if(storage.instructions[next_opcode].micro_operations != std::numeric_limits<uint32_t>::max()) {
|
||||||
[false_valids_ addObject:@(next_opcode)];
|
[false_valids_ addObject:@(next_opcode)];
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -104,7 +104,8 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length {
|
- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length {
|
||||||
const auto roms = CSROMFetcher()("AtariST", { image.UTF8String });
|
const std::vector<ROMMachine::ROM> rom_names = {{"AtariST", "", image.UTF8String, 0, 0 }};
|
||||||
|
const auto roms = CSROMFetcher()(rom_names);
|
||||||
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
|
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
|
||||||
_machine.reset(new EmuTOS(*roms[0], traceLocation.UTF8String));
|
_machine.reset(new EmuTOS(*roms[0], traceLocation.UTF8String));
|
||||||
_machine->run_for(HalfCycles(length));
|
_machine->run_for(HalfCycles(length));
|
||||||
|
@ -101,7 +101,8 @@ class QL: public ComparativeBusHandler {
|
|||||||
Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace.
|
Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace.
|
||||||
*/
|
*/
|
||||||
- (void)testStartup {
|
- (void)testStartup {
|
||||||
const auto roms = CSROMFetcher()("SinclairQL", {"js.rom"});
|
const std::vector<ROMMachine::ROM> rom_names = {{"SinclairQL", "", "js.rom", 0, 0 }};
|
||||||
|
const auto roms = CSROMFetcher()(rom_names);
|
||||||
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"];
|
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"];
|
||||||
_machine.reset(new QL(*roms[0], traceLocation.UTF8String));
|
_machine.reset(new QL(*roms[0], traceLocation.UTF8String));
|
||||||
|
|
||||||
|
@ -9,10 +9,12 @@
|
|||||||
#ifndef MC68000_h
|
#ifndef MC68000_h
|
||||||
#define MC68000_h
|
#define MC68000_h
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -473,7 +473,7 @@ struct ProcessorStorageConstructor {
|
|||||||
void replace_write_values(ProcessorBase::MicroOp *start, const std::initializer_list<RegisterPair16 *> &values) {
|
void replace_write_values(ProcessorBase::MicroOp *start, const std::initializer_list<RegisterPair16 *> &values) {
|
||||||
auto value = values.begin();
|
auto value = values.begin();
|
||||||
while(!start->is_terminal()) {
|
while(!start->is_terminal()) {
|
||||||
value = replace_write_values(start->bus_program, value);
|
value = replace_write_values(&storage_.all_bus_steps_[start->bus_program], value);
|
||||||
++start;
|
++start;
|
||||||
}
|
}
|
||||||
assert(value == values.end());
|
assert(value == values.end());
|
||||||
@ -827,10 +827,10 @@ struct ProcessorStorageConstructor {
|
|||||||
// The arbitrary_base is used so that the offsets returned by assemble_program into
|
// The arbitrary_base is used so that the offsets returned by assemble_program into
|
||||||
// storage_.all_bus_steps_ can be retained and mapped into the final version of
|
// storage_.all_bus_steps_ can be retained and mapped into the final version of
|
||||||
// storage_.all_bus_steps_ at the end.
|
// storage_.all_bus_steps_ at the end.
|
||||||
BusStep arbitrary_base;
|
// BusStep arbitrary_base;
|
||||||
|
|
||||||
#define op(...) storage_.all_micro_ops_.emplace_back(__VA_ARGS__)
|
#define op(...) storage_.all_micro_ops_.emplace_back(__VA_ARGS__)
|
||||||
#define seq(...) &arbitrary_base + assemble_program(__VA_ARGS__)
|
#define seq(...) assemble_program(__VA_ARGS__)
|
||||||
#define ea(n) &storage_.effective_address_[n].full
|
#define ea(n) &storage_.effective_address_[n].full
|
||||||
#define a(n) &storage_.address_[n].full
|
#define a(n) &storage_.address_[n].full
|
||||||
|
|
||||||
@ -862,16 +862,12 @@ struct ProcessorStorageConstructor {
|
|||||||
// Temporary storage for the Program fields.
|
// Temporary storage for the Program fields.
|
||||||
ProcessorBase::Program program;
|
ProcessorBase::Program program;
|
||||||
|
|
||||||
// if(instruction == 0x4879) {
|
|
||||||
// printf("");
|
|
||||||
// }
|
|
||||||
|
|
||||||
#define dec(n) decrement_action(is_long_word_access, is_byte_access, n)
|
#define dec(n) decrement_action(is_long_word_access, is_byte_access, n)
|
||||||
#define inc(n) increment_action(is_long_word_access, is_byte_access, n)
|
#define inc(n) increment_action(is_long_word_access, is_byte_access, n)
|
||||||
|
|
||||||
switch(mapping.decoder) {
|
switch(mapping.decoder) {
|
||||||
case Decoder::STOP: {
|
case Decoder::STOP: {
|
||||||
program.requires_supervisor = true;
|
program.set_requires_supervisor(true);
|
||||||
op(Action::None, seq("n"));
|
op(Action::None, seq("n"));
|
||||||
op(Action::PerformOperation);
|
op(Action::PerformOperation);
|
||||||
} break;
|
} break;
|
||||||
@ -977,7 +973,7 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
case Decoder::EORI_ORI_ANDI_SR: {
|
case Decoder::EORI_ORI_ANDI_SR: {
|
||||||
// The source used here is always the high word of the prefetch queue.
|
// 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::None, seq("np nn nn"));
|
||||||
op(Action::PerformOperation, seq("np np"));
|
op(Action::PerformOperation, seq("np np"));
|
||||||
} break;
|
} break;
|
||||||
@ -1015,7 +1011,7 @@ struct ProcessorStorageConstructor {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Decoder::RTE_RTR: {
|
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.
|
// 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] } ));
|
op(Action::PrepareRTE_RTR, seq("nR nr nr", { &storage_.precomputed_addresses_[0], &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] } ));
|
||||||
@ -1269,13 +1265,8 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
// Source is always something cribbed from the instruction stream;
|
// Source is always something cribbed from the instruction stream;
|
||||||
// destination is going to be in the write address unit.
|
// destination is going to be in the write address unit.
|
||||||
program.source = &storage_.source_bus_data_[0];
|
program.set_source(storage_, Imm, 0);
|
||||||
if(mode == Dn) {
|
program.set_destination(storage_, mode, ea_register);
|
||||||
program.destination = &storage_.data_[ea_register];
|
|
||||||
} else {
|
|
||||||
program.destination = &storage_.destination_bus_data_[0];
|
|
||||||
program.destination_address = &storage_.address_[ea_register];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||||||
default: continue;
|
default: continue;
|
||||||
@ -1371,33 +1362,36 @@ struct ProcessorStorageConstructor {
|
|||||||
const int mode = combined_mode(ea_mode, ea_register);
|
const int mode = combined_mode(ea_mode, ea_register);
|
||||||
|
|
||||||
if(reverse_source_destination) {
|
if(reverse_source_destination) {
|
||||||
program.destination = &storage_.data_[data_register];
|
program.set_destination(storage_, Dn, data_register);
|
||||||
program.source = &storage_.source_bus_data_[0];
|
program.set_source(storage_, Imm, ea_register);
|
||||||
program.source_address = &storage_.address_[ea_register];
|
|
||||||
|
|
||||||
// Perform [ADD/SUB].blw <ea>, Dn
|
// Perform [ADD/SUB].blw <ea>, Dn
|
||||||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||||||
default: continue;
|
default: continue;
|
||||||
|
|
||||||
case bw(Dn): // ADD/SUB.bw Dn, Dn
|
case bw(Dn): // ADD/SUB.bw Dn, Dn
|
||||||
program.source = &storage_.data_[ea_register];
|
program.set_source(storage_, Dn, ea_register);
|
||||||
|
// program.source = &storage_.data_[ea_register];
|
||||||
op(Action::PerformOperation, seq("np"));
|
op(Action::PerformOperation, seq("np"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case l(Dn): // ADD/SUB.l Dn, Dn
|
case l(Dn): // ADD/SUB.l Dn, Dn
|
||||||
program.source = &storage_.data_[ea_register];
|
program.set_source(storage_, Dn, ea_register);
|
||||||
|
// program.source = &storage_.data_[ea_register];
|
||||||
op(Action::PerformOperation, seq("np nn"));
|
op(Action::PerformOperation, seq("np nn"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case bw(An): // ADD/SUB.bw An, Dn
|
case bw(An): // ADD/SUB.bw An, Dn
|
||||||
// Address registers can't provide single bytes.
|
// Address registers can't provide single bytes.
|
||||||
if(is_byte_access) continue;
|
if(is_byte_access) continue;
|
||||||
program.source = &storage_.address_[ea_register];
|
program.set_source(storage_, An, ea_register);
|
||||||
|
// program.source = &storage_.address_[ea_register];
|
||||||
op(Action::PerformOperation, seq("np"));
|
op(Action::PerformOperation, seq("np"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case l(An): // ADD/SUB.l An, Dn
|
case l(An): // ADD/SUB.l An, Dn
|
||||||
program.source = &storage_.address_[ea_register];
|
program.set_source(storage_, An, ea_register);
|
||||||
|
// program.source = &storage_.address_[ea_register];
|
||||||
op(Action::PerformOperation, seq("np nn"));
|
op(Action::PerformOperation, seq("np nn"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1469,11 +1463,9 @@ struct ProcessorStorageConstructor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
program.source = &storage_.data_[data_register];
|
|
||||||
|
|
||||||
const auto destination_register = ea_register;
|
const auto destination_register = ea_register;
|
||||||
program.destination = &storage_.destination_bus_data_[0];
|
program.set_destination(storage_, Ind, destination_register);
|
||||||
program.destination_address = &storage_.address_[destination_register];
|
program.set_source(storage_, Dn, data_register);
|
||||||
|
|
||||||
// Perform [ADD/SUB].blw Dn, <ea>
|
// Perform [ADD/SUB].blw Dn, <ea>
|
||||||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||||||
@ -1782,7 +1774,7 @@ struct ProcessorStorageConstructor {
|
|||||||
case Ind: // BTST.b Dn, (An)
|
case Ind: // BTST.b Dn, (An)
|
||||||
case PostInc: // BTST.b Dn, (An)+
|
case PostInc: // BTST.b Dn, (An)+
|
||||||
op(Action::None, seq("nrd np", { a(ea_register) }, false));
|
op(Action::None, seq("nrd np", { a(ea_register) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : MicroOp::NoBusProgram);
|
||||||
if(mode == PostInc) {
|
if(mode == PostInc) {
|
||||||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||||||
}
|
}
|
||||||
@ -1790,7 +1782,7 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
case PreDec: // BTST.b Dn, -(An)
|
case PreDec: // BTST.b Dn, -(An)
|
||||||
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }, false));
|
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : MicroOp::NoBusProgram);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XXXl: // BTST.b Dn, (xxx).l
|
case XXXl: // BTST.b Dn, (xxx).l
|
||||||
@ -1805,7 +1797,7 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||||||
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Imm: // BTST.b Dn, #
|
case Imm: // BTST.b Dn, #
|
||||||
@ -1822,7 +1814,7 @@ struct ProcessorStorageConstructor {
|
|||||||
case Decoder::BTSTIMM: {
|
case Decoder::BTSTIMM: {
|
||||||
const bool is_bclr = mapping.decoder == Decoder::BCLRIMM;
|
const bool is_bclr = mapping.decoder == Decoder::BCLRIMM;
|
||||||
|
|
||||||
program.source = &storage_.source_bus_data_[0];
|
program.set_source(storage_, Imm, 0);
|
||||||
program.set_destination(storage_, ea_mode, ea_register);
|
program.set_destination(storage_, ea_mode, ea_register);
|
||||||
|
|
||||||
const int mode = combined_mode(ea_mode, ea_register);
|
const int mode = combined_mode(ea_mode, ea_register);
|
||||||
@ -1844,7 +1836,7 @@ struct ProcessorStorageConstructor {
|
|||||||
case Ind: // BTST.b #, (An)
|
case Ind: // BTST.b #, (An)
|
||||||
case PostInc: // BTST.b #, (An)+
|
case PostInc: // BTST.b #, (An)+
|
||||||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np nrd np", { a(ea_register) }, false));
|
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np nrd np", { a(ea_register) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : MicroOp::NoBusProgram);
|
||||||
if(mode == PostInc) {
|
if(mode == PostInc) {
|
||||||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||||||
}
|
}
|
||||||
@ -1853,7 +1845,7 @@ struct ProcessorStorageConstructor {
|
|||||||
case PreDec: // BTST.b #, -(An)
|
case PreDec: // BTST.b #, -(An)
|
||||||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||||||
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }, false));
|
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : MicroOp::NoBusProgram);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XXXw: // BTST.b #, (xxx).w
|
case XXXw: // BTST.b #, (xxx).w
|
||||||
@ -1867,14 +1859,14 @@ struct ProcessorStorageConstructor {
|
|||||||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||||||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||||||
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XXXl: // BTST.b #, (xxx).l
|
case XXXl: // BTST.b #, (xxx).l
|
||||||
op( int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
op( int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||||||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||||||
seq("np nrd np", { ea(1) }, false));
|
seq("np nrd np", { ea(1) }, false));
|
||||||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : nullptr);
|
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -2013,7 +2005,7 @@ struct ProcessorStorageConstructor {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Decoder::CMP: {
|
case Decoder::CMP: {
|
||||||
program.destination = &storage_.data_[data_register];
|
program.set_destination(storage_, Dn, data_register);
|
||||||
program.set_source(storage_, ea_mode, ea_register);
|
program.set_source(storage_, ea_mode, ea_register);
|
||||||
|
|
||||||
// Byte accesses are not allowed with address registers.
|
// Byte accesses are not allowed with address registers.
|
||||||
@ -2090,12 +2082,12 @@ struct ProcessorStorageConstructor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case bw(Imm): // CMP.br #, Dn
|
case bw(Imm): // CMP.br #, Dn
|
||||||
program.source = &storage_.prefetch_queue_;
|
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||||
op(Action::PerformOperation, seq("np np"));
|
op(Action::PerformOperation, seq("np np"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case l(Imm): // CMP.l #, Dn
|
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::None, seq("np"));
|
||||||
op(Action::PerformOperation, seq("np np n"));
|
op(Action::PerformOperation, seq("np np n"));
|
||||||
break;
|
break;
|
||||||
@ -2109,7 +2101,7 @@ struct ProcessorStorageConstructor {
|
|||||||
is_long_word_access = op_mode == 7;
|
is_long_word_access = op_mode == 7;
|
||||||
|
|
||||||
program.set_source(storage_, ea_mode, ea_register);
|
program.set_source(storage_, ea_mode, ea_register);
|
||||||
program.destination = &storage_.address_[data_register];
|
program.set_destination(storage_, An, data_register);
|
||||||
|
|
||||||
const int mode = combined_mode(ea_mode, ea_register, true);
|
const int mode = combined_mode(ea_mode, ea_register, true);
|
||||||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||||||
@ -2172,12 +2164,12 @@ struct ProcessorStorageConstructor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case bw(Imm): // CMPA.w #, An
|
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"));
|
op(Action::PerformOperation, seq("np np n"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case l(Imm): // CMPA.l #, An
|
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::None, seq("np"));
|
||||||
op(Action::PerformOperation, seq("np np n"));
|
op(Action::PerformOperation, seq("np np n"));
|
||||||
break;
|
break;
|
||||||
@ -2190,7 +2182,7 @@ struct ProcessorStorageConstructor {
|
|||||||
const auto destination_mode = ea_mode;
|
const auto destination_mode = ea_mode;
|
||||||
const auto destination_register = ea_register;
|
const auto destination_register = ea_register;
|
||||||
|
|
||||||
program.source = &storage_.source_bus_data_[0];
|
program.set_source(storage_, Imm, 0); // i.e. from the fetched data latch.
|
||||||
program.set_destination(storage_, destination_mode, destination_register);
|
program.set_destination(storage_, destination_mode, destination_register);
|
||||||
|
|
||||||
const int mode = combined_mode(destination_mode, destination_register);
|
const int mode = combined_mode(destination_mode, destination_register);
|
||||||
@ -2198,12 +2190,12 @@ struct ProcessorStorageConstructor {
|
|||||||
default: continue;
|
default: continue;
|
||||||
|
|
||||||
case bw(Dn): // CMPI.bw #, Dn
|
case bw(Dn): // CMPI.bw #, Dn
|
||||||
program.source = &storage_.prefetch_queue_;
|
program.set_source(storage_, &storage_.prefetch_queue_);
|
||||||
op(Action::PerformOperation, seq("np np"));
|
op(Action::PerformOperation, seq("np np"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case l(Dn): // CMPI.l #, Dn
|
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::None, seq("np"));
|
||||||
op(Action::PerformOperation, seq("np np n"));
|
op(Action::PerformOperation, seq("np np n"));
|
||||||
break;
|
break;
|
||||||
@ -2314,7 +2306,7 @@ struct ProcessorStorageConstructor {
|
|||||||
if(ea_mode == 1) {
|
if(ea_mode == 1) {
|
||||||
// This is a DBcc. Decode as such.
|
// This is a DBcc. Decode as such.
|
||||||
operation = Operation::DBcc;
|
operation = Operation::DBcc;
|
||||||
program.source = &storage_.data_[ea_register];
|
program.set_source(storage_, Dn, ea_register);
|
||||||
|
|
||||||
// Jump straight into deciding what steps to take next,
|
// Jump straight into deciding what steps to take next,
|
||||||
// which will be selected dynamically.
|
// which will be selected dynamically.
|
||||||
@ -2371,7 +2363,7 @@ struct ProcessorStorageConstructor {
|
|||||||
program.set_source(storage_, ea_mode, ea_register);
|
program.set_source(storage_, ea_mode, ea_register);
|
||||||
|
|
||||||
// ... but otherwise assume that the true source of a destination will be the computed source address.
|
// ... 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);
|
const int mode = combined_mode(ea_mode, ea_register);
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
@ -2445,8 +2437,7 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
case Decoder::PEA: {
|
case Decoder::PEA: {
|
||||||
program.set_source(storage_, An, ea_register);
|
program.set_source(storage_, An, ea_register);
|
||||||
program.destination = &storage_.destination_bus_data_[0];
|
program.set_destination(storage_, Imm, 7); // Immediate destination => store to the destination bus latch.
|
||||||
program.destination_address = &storage_.address_[7];
|
|
||||||
|
|
||||||
const int mode = combined_mode(ea_mode, ea_register);
|
const int mode = combined_mode(ea_mode, ea_register);
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
@ -2461,7 +2452,7 @@ struct ProcessorStorageConstructor {
|
|||||||
|
|
||||||
case XXXl: // PEA (XXX).l
|
case XXXl: // PEA (XXX).l
|
||||||
case XXXw: // PEA (XXX).w
|
case XXXw: // PEA (XXX).w
|
||||||
op(int(Action::Decrement4) | MicroOp::DestinationMask, (mode == XXXl) ? seq("np") : nullptr);
|
op(int(Action::Decrement4) | MicroOp::DestinationMask, (mode == XXXl) ? seq("np") : MicroOp::NoBusProgram);
|
||||||
op(address_assemble_for_mode(mode) | MicroOp::SourceMask);
|
op(address_assemble_for_mode(mode) | MicroOp::SourceMask);
|
||||||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask);
|
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask);
|
||||||
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
||||||
@ -2483,11 +2474,11 @@ struct ProcessorStorageConstructor {
|
|||||||
program.set_destination(storage_, An, data_register);
|
program.set_destination(storage_, An, data_register);
|
||||||
|
|
||||||
const int mode = combined_mode(ea_mode, ea_register);
|
const int mode = combined_mode(ea_mode, ea_register);
|
||||||
program.source_address = &storage_.address_[ea_register];
|
program.set_source_address(storage_, ea_register);
|
||||||
program.source =
|
program.set_source(storage_,
|
||||||
(mode == Ind) ?
|
(mode == Ind) ?
|
||||||
&storage_.address_[ea_register] :
|
&storage_.address_[ea_register] :
|
||||||
&storage_.effective_address_[0];
|
&storage_.effective_address_[0]);
|
||||||
|
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
default: continue;
|
default: continue;
|
||||||
@ -2553,7 +2544,7 @@ struct ProcessorStorageConstructor {
|
|||||||
case Decoder::MOVEtoSRCCR: {
|
case Decoder::MOVEtoSRCCR: {
|
||||||
if(ea_mode == An) continue;
|
if(ea_mode == An) continue;
|
||||||
program.set_source(storage_, ea_mode, ea_register);
|
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;
|
/* 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. */
|
this looks like a mistake so I've padded with nil cycles in the middle. */
|
||||||
@ -2591,14 +2582,14 @@ struct ProcessorStorageConstructor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Imm: // MOVE #, SR
|
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"));
|
op(int(Action::PerformOperation), seq("np nn nn np"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Decoder::MOVEq: {
|
case Decoder::MOVEq: {
|
||||||
program.destination = &storage_.data_[data_register];
|
program.set_destination(storage_, Dn, data_register);
|
||||||
op(Action::PerformOperation, seq("np"));
|
op(Action::PerformOperation, seq("np"));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -2679,18 +2670,18 @@ struct ProcessorStorageConstructor {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Decoder::MOVEUSP: {
|
case Decoder::MOVEUSP: {
|
||||||
program.requires_supervisor = true;
|
program.set_requires_supervisor(true);
|
||||||
|
|
||||||
// Observation here: because this is a privileged instruction, the user stack pointer
|
// Observation here: because this is a privileged instruction, the user stack pointer
|
||||||
// definitely isn't currently [copied into] A7.
|
// definitely isn't currently [copied into] A7.
|
||||||
if(instruction & 0x8) {
|
if(instruction & 0x8) {
|
||||||
// Transfer FROM the USP.
|
// 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);
|
program.set_destination(storage_, An, ea_register);
|
||||||
} else {
|
} else {
|
||||||
// Transfer TO the USP.
|
// Transfer TO the USP.
|
||||||
program.set_source(storage_, An, ea_register);
|
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"));
|
op(Action::PerformOperation, seq("np"));
|
||||||
@ -2878,7 +2869,7 @@ struct ProcessorStorageConstructor {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Decoder::RESET:
|
case Decoder::RESET:
|
||||||
program.requires_supervisor = true;
|
program.set_requires_supervisor(true);
|
||||||
op(Action::None, seq("nn _ np"));
|
op(Action::None, seq("nn _ np"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3088,28 +3079,29 @@ struct ProcessorStorageConstructor {
|
|||||||
Iterates through the micro-sequence beginning at @c start, finalising bus_program
|
Iterates through the micro-sequence beginning at @c start, finalising bus_program
|
||||||
pointers that have been transiently stored as relative to @c arbitrary_base.
|
pointers that have been transiently stored as relative to @c arbitrary_base.
|
||||||
*/
|
*/
|
||||||
const auto link_operations = [this](MicroOp *start, BusStep *arbitrary_base) {
|
// const auto link_operations = [this](MicroOp *start, BusStep *arbitrary_base) {
|
||||||
while(!start->is_terminal()) {
|
// while(!start->is_terminal()) {
|
||||||
const auto offset = size_t(start->bus_program - arbitrary_base);
|
// const auto offset = size_t(start->bus_program - arbitrary_base);
|
||||||
assert(offset >= 0 && offset < storage_.all_bus_steps_.size());
|
// assert(offset >= 0 && offset < storage_.all_bus_steps_.size());
|
||||||
start->bus_program = &storage_.all_bus_steps_[offset];
|
// start->bus_program = &storage_.all_bus_steps_[offset];
|
||||||
++start;
|
// ++start;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Finalise micro-op and program pointers.
|
// Finalise micro-op and program pointers.
|
||||||
for(size_t instruction = 0; instruction < 65536; ++instruction) {
|
for(size_t instruction = 0; instruction < 65536; ++instruction) {
|
||||||
if(micro_op_pointers[instruction] != std::numeric_limits<size_t>::max()) {
|
if(micro_op_pointers[instruction] != std::numeric_limits<size_t>::max()) {
|
||||||
storage_.instructions[instruction].micro_operations = &storage_.all_micro_ops_[micro_op_pointers[instruction]];
|
storage_.instructions[instruction].micro_operations = uint32_t(micro_op_pointers[instruction]);
|
||||||
link_operations(storage_.instructions[instruction].micro_operations, &arbitrary_base);
|
// link_operations(&storage_.all_micro_ops_[micro_op_pointers[instruction]], &arbitrary_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link up the interrupt micro ops.
|
// Link up the interrupt micro ops.
|
||||||
storage_.interrupt_micro_ops_ = &storage_.all_micro_ops_[interrupt_pointer];
|
storage_.interrupt_micro_ops_ = &storage_.all_micro_ops_[interrupt_pointer];
|
||||||
link_operations(storage_.interrupt_micro_ops_, &arbitrary_base);
|
// 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:
|
private:
|
||||||
@ -3257,7 +3249,7 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
|
|||||||
//
|
//
|
||||||
// Assumed order of input: PC.h, SR, PC.l (i.e. the opposite of TRAP's output).
|
// Assumed order of input: PC.h, SR, PC.l (i.e. the opposite of TRAP's output).
|
||||||
for(const int instruction: { 0x4e73, 0x4e77 }) {
|
for(const int instruction: { 0x4e73, 0x4e77 }) {
|
||||||
auto steps = instructions[instruction].micro_operations[0].bus_program;
|
auto steps = &all_bus_steps_[all_micro_ops_[instructions[instruction].micro_operations].bus_program];
|
||||||
steps[0].microcycle.value = steps[1].microcycle.value = &program_counter_.halves.high;
|
steps[0].microcycle.value = steps[1].microcycle.value = &program_counter_.halves.high;
|
||||||
steps[4].microcycle.value = steps[5].microcycle.value = &program_counter_.halves.low;
|
steps[4].microcycle.value = steps[5].microcycle.value = &program_counter_.halves.low;
|
||||||
}
|
}
|
||||||
@ -3267,10 +3259,10 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
|
|||||||
|
|
||||||
// Complete linkage of the exception micro program.
|
// Complete linkage of the exception micro program.
|
||||||
short_exception_micro_ops_ = &all_micro_ops_[short_exception_offset];
|
short_exception_micro_ops_ = &all_micro_ops_[short_exception_offset];
|
||||||
short_exception_micro_ops_->bus_program = trap_steps_;
|
short_exception_micro_ops_->bus_program = trap_offset;
|
||||||
|
|
||||||
long_exception_micro_ops_ = &all_micro_ops_[long_exception_offset];
|
long_exception_micro_ops_ = &all_micro_ops_[long_exception_offset];
|
||||||
long_exception_micro_ops_->bus_program = bus_error_steps_;
|
long_exception_micro_ops_->bus_program = bus_error_offset;
|
||||||
|
|
||||||
// Set initial state.
|
// Set initial state.
|
||||||
active_step_ = reset_bus_steps_;
|
active_step_ = reset_bus_steps_;
|
||||||
|
@ -74,7 +74,7 @@ class ProcessorStorage {
|
|||||||
HalfCycles half_cycles_left_to_run_;
|
HalfCycles half_cycles_left_to_run_;
|
||||||
HalfCycles e_clock_phase_;
|
HalfCycles e_clock_phase_;
|
||||||
|
|
||||||
enum class Operation {
|
enum class Operation: uint8_t {
|
||||||
None,
|
None,
|
||||||
ABCD, SBCD, NBCD,
|
ABCD, SBCD, NBCD,
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ class ProcessorStorage {
|
|||||||
be performed.
|
be performed.
|
||||||
*/
|
*/
|
||||||
struct MicroOp {
|
struct MicroOp {
|
||||||
enum class Action: int {
|
enum class Action: uint8_t {
|
||||||
None,
|
None,
|
||||||
|
|
||||||
/// Does whatever this instruction says is the main operation.
|
/// Does whatever this instruction says is the main operation.
|
||||||
@ -312,21 +312,22 @@ class ProcessorStorage {
|
|||||||
// steps detail appropriately.
|
// steps detail appropriately.
|
||||||
PrepareINTVector,
|
PrepareINTVector,
|
||||||
};
|
};
|
||||||
static const int SourceMask = 1 << 30;
|
static const int SourceMask = 1 << 7;
|
||||||
static const int DestinationMask = 1 << 29;
|
static const int DestinationMask = 1 << 6;
|
||||||
int action = int(Action::None);
|
uint8_t action = uint8_t(Action::None);
|
||||||
|
|
||||||
BusStep *bus_program = nullptr;
|
static const uint16_t NoBusProgram = std::numeric_limits<uint16_t>::max();
|
||||||
|
uint16_t bus_program = NoBusProgram;
|
||||||
|
|
||||||
MicroOp() {}
|
MicroOp() {}
|
||||||
MicroOp(int action) : action(action) {}
|
MicroOp(uint8_t action) : action(action) {}
|
||||||
MicroOp(int action, BusStep *bus_program) : action(action), bus_program(bus_program) {}
|
MicroOp(uint8_t action, uint16_t bus_program) : action(action), bus_program(bus_program) {}
|
||||||
|
|
||||||
MicroOp(Action action) : MicroOp(int(action)) {}
|
MicroOp(Action action) : MicroOp(uint8_t(action)) {}
|
||||||
MicroOp(Action action, BusStep *bus_program) : MicroOp(int(action), bus_program) {}
|
MicroOp(Action action, uint16_t bus_program) : MicroOp(uint8_t(action), bus_program) {}
|
||||||
|
|
||||||
forceinline bool is_terminal() const {
|
forceinline bool is_terminal() const {
|
||||||
return bus_program == nullptr;
|
return bus_program == std::numeric_limits<uint16_t>::max();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -334,31 +335,70 @@ class ProcessorStorage {
|
|||||||
A program represents the implementation of a particular opcode, as a sequence
|
A program represents the implementation of a particular opcode, as a sequence
|
||||||
of micro-ops and, separately, the operation to perform plus whatever other
|
of micro-ops and, separately, the operation to perform plus whatever other
|
||||||
fields the operation requires.
|
fields the operation requires.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 {
|
struct Program {
|
||||||
MicroOp *micro_operations = nullptr;
|
/// The offset into the all_micro_ops_ at which micro-ops for this instruction begin,
|
||||||
RegisterPair32 *source = nullptr;
|
/// or std::numeric_limits<uint32_t>::max() if this is an invalid Program.
|
||||||
RegisterPair32 *destination = nullptr;
|
uint32_t micro_operations = std::numeric_limits<uint32_t>::max();
|
||||||
RegisterPair32 *source_address = nullptr;
|
/// The overarching operation applied by this program when the moment comes.
|
||||||
RegisterPair32 *destination_address = nullptr;
|
|
||||||
Operation operation;
|
Operation operation;
|
||||||
bool requires_supervisor = false;
|
/// 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;
|
||||||
|
|
||||||
|
void set_source_address(ProcessorStorage &storage, int index) {
|
||||||
|
source_dest = uint8_t((source_dest & 0x0f) | (index << 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_destination_address(ProcessorStorage &storage, int index) {
|
||||||
|
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_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_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) {
|
void set_source(ProcessorStorage &storage, int mode, int reg) {
|
||||||
source_address = &storage.address_[reg];
|
set_source_address(storage, reg);
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case 0: source = &storage.data_[reg]; break;
|
case 0: set_source(storage, &storage.data_[reg]); break;
|
||||||
case 1: source = &storage.address_[reg]; break;
|
case 1: set_source(storage, &storage.address_[reg]); break;
|
||||||
default: source = &storage.source_bus_data_[0]; break;
|
default: set_source(storage, &storage.source_bus_data_[0]); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_destination(ProcessorStorage &storage, int mode, int reg) {
|
void set_destination(ProcessorStorage &storage, int mode, int reg) {
|
||||||
destination_address = &storage.address_[reg];
|
set_destination_address(storage, reg);
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case 0: destination = &storage.data_[reg]; break;
|
case 0: set_destination(storage, &storage.data_[reg]); break;
|
||||||
case 1: destination = &storage.address_[reg]; break;
|
case 1: set_destination(storage, &storage.address_[reg]); break;
|
||||||
default: destination = &storage.destination_bus_data_[0]; break;
|
default: set_destination(storage, &storage.destination_bus_data_[0]); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user