mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-16 16:28:59 +00:00
3349 lines
134 KiB
C++
3349 lines
134 KiB
C++
//
|
||
// 68000Storage.cpp
|
||
// Clock Signal
|
||
//
|
||
// Created by Thomas Harte on 08/03/2019.
|
||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||
//
|
||
|
||
#include "../68000.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <cassert>
|
||
#include <ctime>
|
||
#include <map>
|
||
#include <vector>
|
||
#include <sstream>
|
||
|
||
namespace CPU {
|
||
namespace MC68000 {
|
||
|
||
#define Dn 0x00
|
||
#define An 0x01
|
||
#define Ind 0x02
|
||
#define PostInc 0x03
|
||
#define PreDec 0x04
|
||
#define d16An 0x05
|
||
#define d8AnXn 0x06
|
||
#define XXXw 0x10
|
||
#define XXXl 0x11
|
||
#define d16PC 0x12
|
||
#define d8PCXn 0x13
|
||
#define Imm 0x14
|
||
|
||
struct ProcessorStorageConstructor {
|
||
ProcessorStorageConstructor(ProcessorStorage &storage) : storage_(storage) {}
|
||
|
||
using BusStep = ProcessorStorage::BusStep;
|
||
|
||
/*!
|
||
*/
|
||
int calc_action_for_mode(int mode) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
switch(mode & 0xff) {
|
||
default: assert(false);
|
||
case d16PC: return int(Action::CalcD16PC);
|
||
case d8PCXn: return int(Action::CalcD8PCXn);
|
||
case d16An: return int(Action::CalcD16An);
|
||
case d8AnXn: return int(Action::CalcD8AnXn);
|
||
}
|
||
}
|
||
|
||
int address_assemble_for_mode(int mode) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
assert((mode & 0xff) == XXXw || (mode & 0xff) == XXXl);
|
||
return int(((mode & 0xff) == XXXw) ? Action::AssembleWordAddressFromPrefetch : Action::AssembleLongWordAddressFromPrefetch);
|
||
}
|
||
|
||
int address_action_for_mode(int mode) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
switch(mode & 0xff) {
|
||
default: assert(false);
|
||
case d16PC: return int(Action::CalcD16PC);
|
||
case d8PCXn: return int(Action::CalcD8PCXn);
|
||
case d16An: return int(Action::CalcD16An);
|
||
case d8AnXn: return int(Action::CalcD8AnXn);
|
||
case XXXw: return int(Action::AssembleWordAddressFromPrefetch);
|
||
case XXXl: return int(Action::AssembleLongWordAddressFromPrefetch);
|
||
}
|
||
}
|
||
|
||
int combined_mode(int mode, int reg, bool collapse_an_dn = false, bool collapse_postinc = false) {
|
||
if(collapse_an_dn && mode == An) mode = Dn;
|
||
if(collapse_postinc && mode == PostInc) mode = Ind;
|
||
return (mode == 7) ? (0x10 | reg) : mode;
|
||
}
|
||
|
||
int data_assemble_for_mode(int mode) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
assert((mode & 0xff) == XXXw || (mode & 0xff) == XXXl);
|
||
return int(((mode & 0xff) == XXXw) ? Action::AssembleWordDataFromPrefetch : Action::AssembleLongWordDataFromPrefetch);
|
||
}
|
||
|
||
int byte_inc(int reg) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
// Special case: stack pointer byte accesses adjust by two.
|
||
return int((reg == 7) ? Action::Increment2 : Action::Increment1);
|
||
}
|
||
|
||
int byte_dec(int reg) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
// Special case: stack pointer byte accesses adjust by two.
|
||
return int((reg == 7) ? Action::Decrement2 : Action::Decrement1);
|
||
}
|
||
|
||
int increment_action(bool is_long_word_access, bool is_byte_access, int reg) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
if(is_long_word_access) return int(Action::Increment4);
|
||
if(is_byte_access) return byte_inc(reg);
|
||
return int(Action::Increment2);
|
||
}
|
||
|
||
int decrement_action(bool is_long_word_access, bool is_byte_access, int reg) const {
|
||
using Action = ProcessorBase::MicroOp::Action;
|
||
if(is_long_word_access) return int(Action::Decrement4);
|
||
if(is_byte_access) return byte_dec(reg);
|
||
return int(Action::Decrement2);
|
||
}
|
||
|
||
#define pseq(x, m) ((((m)&0xff) == d8AnXn) || (((m)&0xff) == d8PCXn) ? "n " x : x)
|
||
|
||
/*!
|
||
Installs BusSteps that implement the described program into the relevant
|
||
instance storage, returning the offset within @c all_bus_steps_ at which
|
||
the generated steps begin.
|
||
|
||
@param access_pattern A string describing the bus activity that occurs
|
||
during this program. This should follow the same general pattern as
|
||
those in yacht.txt; full description below.
|
||
|
||
@param addresses A vector of the addresses to place on the bus coincident
|
||
with those acess steps that require them.
|
||
|
||
@param read_full_words @c true to indicate that read and write operations are
|
||
selecting a full word; @c false to signal byte accesses only.
|
||
|
||
@discussion
|
||
The access pattern is defined to correlate closely to that in yacht.txt; it is
|
||
a space-separated sequence of the following actions:
|
||
|
||
* n: no operation for four cycles; data bus is not used;
|
||
* nn: no operation for eight cycles; data bus is not used;
|
||
* r: a 'replaceable'-length no operation; data bus is not used and no guarantees are
|
||
made about the length of the cycle other than that when it reaches the interpreter,
|
||
it is safe to alter the length and leave it altered;
|
||
* np: program fetch; reads from the PC and adds two to it, advancing the prefetch queue;
|
||
* nW: write MSW of something onto the bus;
|
||
* nw: write LSW of something onto the bus;
|
||
* nR: read MSW of something from the bus into the source latch;
|
||
* nr: read LSW of soemthing from the bus into the source latch;
|
||
* nRd: read MSW of something from the bus into the destination latch;
|
||
* nrd: read LSW of soemthing from the bus into the destination latch;
|
||
* nS: push the MSW of something onto the stack **and then** decrement the pointer;
|
||
* ns: push the LSW of something onto the stack **and then** decrement the pointer;
|
||
* nU: pop the MSW of something from the stack;
|
||
* nu: pop the LSW of something from the stack;
|
||
* nV: fetch a vector's MSW;
|
||
* nv: fetch a vector's LSW;
|
||
* i: acquire interrupt vector in an IACK cycle;
|
||
* nF: fetch the current SPs MSW;
|
||
* nf: fetch the current SP's LSW;
|
||
* _: hold the reset line active for the usual period.
|
||
* tas: perform the final 6 cycles of a TAS: like an n nw but with the address strobe active for the entire period.
|
||
* int: the interrupt acknowledge cycle.
|
||
|
||
Quite a lot of that is duplicative, implying both something about internal
|
||
state and something about what's observable on the bus, but it's helpful to
|
||
stick to that document's coding exactly for easier debugging.
|
||
|
||
np fetches will fill the prefetch queue, attaching an action to both the
|
||
step that precedes them and to themselves. The SP fetches will go
|
||
to address_[7], whichever stack pointer that may currently be.
|
||
|
||
Other actions will by default act via effective_address_ and bus_data_.
|
||
The user should fill in the steps necessary to get data into or extract
|
||
data from those.
|
||
|
||
nr/nw-type operations may have a + or - suffix; if such a suffix is attached
|
||
then the corresponding effective address will be incremented or decremented
|
||
by two after the cycle has completed.
|
||
*/
|
||
size_t assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses = {}, bool read_full_words = true) {
|
||
auto address_iterator = addresses.begin();
|
||
using Action = BusStep::Action;
|
||
|
||
std::vector<BusStep> steps;
|
||
|
||
// Tokenise the access pattern by splitting on spaces.
|
||
const char *next_access_pattern = access_pattern;
|
||
while(true) {
|
||
/*
|
||
Ugly C-style string parsing here:
|
||
|
||
next_access_pattern is the end of the previous access pattern, i.e.
|
||
it is where parsing should begin to find the next one.
|
||
|
||
end_of_pattern will be where the current pattern ends, after any
|
||
modifier suffixes have been removed.
|
||
|
||
access_pattern is the beginning of the current pattern.
|
||
|
||
Obiter: this replaces a std::stringstream >> std::string implementation,
|
||
that was a lot cleaner but implied a lot of std::string constructions
|
||
that made this section of code measureable slower. Which, inter alia,
|
||
had a bit impact on the rate at which unit tests would run.
|
||
|
||
So this ugliness is a net project improvement, I promise!
|
||
*/
|
||
while(*next_access_pattern == ' ') ++next_access_pattern;
|
||
access_pattern = next_access_pattern;
|
||
while(*next_access_pattern != ' ' && *next_access_pattern != '\0') ++next_access_pattern;
|
||
if(next_access_pattern == access_pattern) break;
|
||
const char *end_of_pattern = next_access_pattern;
|
||
|
||
ProcessorBase::BusStep step;
|
||
|
||
// Check for a plus-or-minus suffix.
|
||
int post_adjustment = 0;
|
||
if(end_of_pattern[-1] == '-' || end_of_pattern[-1] == '+') {
|
||
if(end_of_pattern[-1] == '-') {
|
||
post_adjustment = -1;
|
||
}
|
||
|
||
if(end_of_pattern[-1] == '+') {
|
||
post_adjustment = 1;
|
||
}
|
||
|
||
--end_of_pattern;
|
||
}
|
||
|
||
const auto token_length = end_of_pattern - access_pattern;
|
||
|
||
// Do nothing (possibly twice).
|
||
if(
|
||
access_pattern[0] == 'n' &&
|
||
(
|
||
token_length == 1 ||
|
||
(token_length == 2 && access_pattern[1] == 'n')
|
||
)
|
||
) {
|
||
if(token_length == 2) {
|
||
step.microcycle.length = HalfCycles(8);
|
||
}
|
||
steps.push_back(step);
|
||
continue;
|
||
}
|
||
|
||
// Do nothing, but with a length that definitely won't map it to the other do-nothings.
|
||
if(
|
||
access_pattern[0] == 'r' &&
|
||
token_length == 1
|
||
) {
|
||
#ifndef NDEBUG
|
||
// If this is a debug build, not where the resizeable microcycle is
|
||
// (and double check that there's only the one).
|
||
step.microcycle.is_resizeable = true;
|
||
#endif
|
||
step.microcycle.length = HalfCycles(0);
|
||
steps.push_back(step);
|
||
continue;
|
||
}
|
||
|
||
if(
|
||
token_length == 2 &&
|
||
access_pattern[0] == 'n'
|
||
) {
|
||
// Fetch SSP.
|
||
if(tolower(access_pattern[1]) == 'f') {
|
||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
|
||
step.microcycle.address = &storage_.effective_address_[0].full;
|
||
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.address_[7].halves.high : &storage_.address_[7].halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
|
||
step.action = Action::IncrementEffectiveAddress0;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
|
||
// Fetch exception vector.
|
||
if(tolower(access_pattern[1]) == 'v') {
|
||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
|
||
step.microcycle.address = &storage_.effective_address_[0].full;
|
||
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.program_counter_.halves.high : &storage_.program_counter_.halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
|
||
step.action = Action::IncrementEffectiveAddress0;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
|
||
// Fetch from the program counter into the prefetch queue.
|
||
if(access_pattern[1] == 'p') {
|
||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram;
|
||
step.microcycle.address = &storage_.program_counter_.full;
|
||
step.microcycle.value = &storage_.prefetch_queue_.halves.low;
|
||
step.action = Action::AdvancePrefetch;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
|
||
step.action = Action::IncrementProgramCounter;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
|
||
// A stack write.
|
||
if(tolower(access_pattern[1]) == 's') {
|
||
step.microcycle.operation = Microcycle::NewAddress;
|
||
step.microcycle.address = &storage_.effective_address_[1].full;
|
||
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_.halves.high : &storage_.destination_bus_data_.halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord;
|
||
step.action = Action::DecrementEffectiveAddress1;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
|
||
// A stack read.
|
||
if(tolower(access_pattern[1]) == 'u') {
|
||
RegisterPair32 *const scratch_data = &storage_.source_bus_data_;
|
||
|
||
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read;
|
||
step.microcycle.address = &storage_.effective_address_[0].full;
|
||
step.microcycle.value = isupper(access_pattern[1]) ? &scratch_data->halves.high : &scratch_data->halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::SelectWord;
|
||
step.action = Action::IncrementEffectiveAddress0;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// The reset cycle.
|
||
if(token_length == 1 && access_pattern[0] == '_') {
|
||
step.microcycle.length = HalfCycles(248);
|
||
step.microcycle.operation = Microcycle::Reset;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
|
||
// A standard read or write.
|
||
if(
|
||
access_pattern[0] == 'n' &&
|
||
(tolower(access_pattern[1]) == 'r' || tolower(access_pattern[1]) == 'w') &&
|
||
(
|
||
token_length == 2 ||
|
||
(
|
||
token_length == 3 &&
|
||
(access_pattern[2] == 'd' || access_pattern[2] == 'r')
|
||
)
|
||
)
|
||
) {
|
||
const bool is_read = tolower(access_pattern[1]) == 'r';
|
||
const bool use_source_storage = tolower(end_of_pattern[-1]) == 'r';
|
||
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_ : &storage_.destination_bus_data_;
|
||
|
||
assert(address_iterator != addresses.end());
|
||
|
||
step.microcycle.operation = Microcycle::NewAddress | (is_read ? Microcycle::Read : 0);
|
||
step.microcycle.address = *address_iterator;
|
||
step.microcycle.value = isupper(access_pattern[1]) ? &scratch_data->halves.high : &scratch_data->halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::SameAddress | (is_read ? Microcycle::Read : 0) | (read_full_words ? Microcycle::SelectWord : Microcycle::SelectByte);
|
||
if(post_adjustment) {
|
||
assert((*address_iterator == &storage_.effective_address_[0].full) || (*address_iterator == &storage_.effective_address_[1].full));
|
||
if(*address_iterator == &storage_.effective_address_[0].full) {
|
||
step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress0 : Action::DecrementEffectiveAddress0;
|
||
} else {
|
||
step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress1 : Action::DecrementEffectiveAddress1;
|
||
}
|
||
}
|
||
steps.push_back(step);
|
||
++address_iterator;
|
||
|
||
continue;
|
||
}
|
||
|
||
if(token_length == 3) {
|
||
// The completing part of a TAS.
|
||
if(access_pattern[0] == 't' && access_pattern[1] == 'a' && access_pattern[2] == 's') {
|
||
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_;
|
||
|
||
assert(address_iterator != addresses.end());
|
||
|
||
step.microcycle.length = HalfCycles(9);
|
||
step.microcycle.operation = Microcycle::SameAddress;
|
||
step.microcycle.address = *address_iterator;
|
||
step.microcycle.value = &scratch_data->halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.length = HalfCycles(3);
|
||
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectByte;
|
||
steps.push_back(step);
|
||
++address_iterator;
|
||
|
||
continue;
|
||
}
|
||
|
||
// Interrupt acknowledge.
|
||
if(access_pattern[0] == 'i' && access_pattern[1] == 'n' && access_pattern[2] == 't') {
|
||
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::NewAddress;
|
||
step.microcycle.address = &storage_.effective_address_[0].full; // The selected interrupt should be in bits 1–3; but 0 should be set.
|
||
step.microcycle.value = &storage_.source_bus_data_.halves.low;
|
||
steps.push_back(step);
|
||
|
||
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte;
|
||
steps.push_back(step);
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
std::cerr << "MC68000 program builder; Unknown access token " << std::string(access_pattern, end_of_pattern) << std::endl;
|
||
assert(false);
|
||
}
|
||
|
||
// Add a final 'ScheduleNextProgram' sentinel.
|
||
BusStep end_program;
|
||
end_program.action = Action::ScheduleNextProgram;
|
||
steps.push_back(end_program);
|
||
|
||
// If the new steps already exist, just return the existing index to them;
|
||
// otherwise insert them.
|
||
/*const auto position = std::search(storage_.all_bus_steps_.begin(), storage_.all_bus_steps_.end(), steps.begin(), steps.end());
|
||
if(position != storage_.all_bus_steps_.end()) {
|
||
return size_t(position - storage_.all_bus_steps_.begin());
|
||
}
|
||
|
||
const auto start = storage_.all_bus_steps_.size();
|
||
std::copy(steps.begin(), steps.end(), std::back_inserter(storage_.all_bus_steps_));
|
||
return start;*/
|
||
|
||
// If the new steps already exist, just return the existing index to them;
|
||
// otherwise insert them. A lookup table of steps to start positions within
|
||
// all_bus_steps_ is maintained to shorten setup time here
|
||
auto potential_locations = locations_by_bus_step_[steps.front()];
|
||
for(auto index: potential_locations) {
|
||
if(index + steps.size() > storage_.all_bus_steps_.size()) continue;
|
||
|
||
if(std::equal(
|
||
storage_.all_bus_steps_.begin() + ssize_t(index),
|
||
storage_.all_bus_steps_.begin() + ssize_t(index + steps.size()),
|
||
steps.begin())) {
|
||
return index;
|
||
}
|
||
}
|
||
|
||
// Copy to the end, and update potential_locations.
|
||
const auto start = storage_.all_bus_steps_.size();
|
||
std::copy(steps.begin(), steps.end(), std::back_inserter(storage_.all_bus_steps_));
|
||
auto index = start;
|
||
for(const auto &step: steps) {
|
||
locations_by_bus_step_[step].push_back(index);
|
||
++index;
|
||
}
|
||
|
||
return start;
|
||
}
|
||
|
||
/*!
|
||
Walks through the sequence of bus steps beginning at @c start, replacing the value supplied for each write
|
||
encountered with the respective value from @c values.
|
||
*/
|
||
void replace_write_values(BusStep *start, const std::initializer_list<RegisterPair16 *> &values) {
|
||
const auto end = replace_write_values(start, values.begin());
|
||
assert(end == values.end());
|
||
(void)end;
|
||
}
|
||
|
||
/*!
|
||
Walks through the sequence of micro-ops beginning at @c start, replacing the value supplied for each write
|
||
encountered in each micro-op's bus steps with the respective value from @c values.
|
||
*/
|
||
void replace_write_values(ProcessorBase::MicroOp *start, const std::initializer_list<RegisterPair16 *> &values) {
|
||
auto value = values.begin();
|
||
while(!start->is_terminal()) {
|
||
value = replace_write_values(&storage_.all_bus_steps_[start->bus_program], value);
|
||
++start;
|
||
}
|
||
assert(value == values.end());
|
||
}
|
||
|
||
/*!
|
||
Disassembles the instruction @c instruction and inserts it into the
|
||
appropriate lookup tables.
|
||
|
||
install_instruction acts, in effect, in the manner of a disassembler. So this class is
|
||
formulated to run through all potential 65536 instuction encodings and attempt to
|
||
disassemble each, rather than going in the opposite direction.
|
||
|
||
This has two benefits:
|
||
|
||
(i) which addressing modes go with which instructions falls out automatically;
|
||
(ii) it is a lot easier during the manual verification stage of development to work
|
||
from known instructions to their disassembly rather than vice versa; especially
|
||
(iii) given that there are plentiful disassemblers against which to test work in progress.
|
||
*/
|
||
void install_instructions() {
|
||
enum class Decoder {
|
||
ABCD_SBCD, // Maps source and desintation registers and a register/memory selection bit to an ABCD or SBCD.
|
||
ADD_SUB, // Maps a register and a register and mode to an ADD or SUB.
|
||
ADDA_SUBA, // Maps a destination register and a source mode and register to an ADDA or SUBA.
|
||
ADDQ_SUBQ, // Maps a register and a mode to an ADDQ or SUBQ.
|
||
ADDX_SUBX, // Maps source and destination registers, and register/memory mode to an ADDX or SUBX.
|
||
|
||
AND_OR_EOR, // Maps a source register, operation mode and destination register and mode to an AND, OR or EOR.
|
||
|
||
BRA, // Maps to a BRA. All fields are decoded at runtime.
|
||
Bcc_BSR, // Maps to a Bcc or BSR. Other than determining the type of operation, fields are decoded at runtime.
|
||
|
||
BTST, // Maps a source register and a destination register and mode to a BTST.
|
||
BTSTIMM, // Maps a destination mode and register to a BTST #.
|
||
|
||
BCLR, // Maps a source register and a destination register and mode to a BCLR.
|
||
BCLRIMM, // Maps a destination mode and register to a BCLR #.
|
||
|
||
CLR_NEG_NEGX_NOT, // Maps a destination mode and register to a CLR, NEG, NEGX or NOT.
|
||
|
||
CMP, // Maps a destination register and a source mode and register to a CMP.
|
||
CMPI, // Maps a destination mode and register to a CMPI.
|
||
CMPA, // Maps a destination register and a source mode and register to a CMPA.
|
||
CMPM, // Maps to a CMPM.
|
||
|
||
EORI_ORI_ANDI_SUBI_ADDI, // Maps a mode and register to one of EORI, ORI, ANDI, SUBI or ADDI.
|
||
|
||
JMP, // Maps a mode and register to a JMP.
|
||
JSR, // Maps a mode and register to a JSR.
|
||
|
||
LEA, // Maps a destination register and a source mode and register to an LEA.
|
||
|
||
MOVE, // Maps a source mode and register and a destination mode and register to a MOVE.
|
||
MOVEtoSRCCR, // Maps a source mode and register to a MOVE to SR or MOVE to CCR.
|
||
MOVEfromSR_NBCD, // Maps a source mode and register to a MOVE fom SR.
|
||
MOVEq, // Maps a destination register to a MOVEQ.
|
||
|
||
MULU_MULS, // Maps a destination register and a source mode and register to a MULU or MULS.
|
||
DIVU_DIVS, // Maps a destination register and a source mode and register to a DIVU or DIVS.
|
||
|
||
RESET, // Maps to a RESET.
|
||
|
||
ASLR_LSLR_ROLR_ROXLRr, // Maps a destination register to a AS[L/R], LS[L/R], RO[L/R], ROX[L/R]; shift quantities are
|
||
// decoded at runtime.
|
||
ASLR_LSLR_ROLR_ROXLRm, // Maps a destination mode and register to a memory-based AS[L/R], LS[L/R], RO[L/R], ROX[L/R].
|
||
|
||
MOVEM, // Maps a mode and register as if they were a 'destination' and sets up bus steps with a suitable
|
||
// hole for the runtime part to install proper MOVEM activity.
|
||
MOVEP, // Maps a data register, address register and operation mode to a MOVEP.
|
||
|
||
RTE_RTR, // Maps to an RTE/RTR.
|
||
|
||
Scc_DBcc, // Maps a mode and destination register to either a DBcc or Scc.
|
||
|
||
TST, // Maps a mode and register to a TST.
|
||
|
||
RTS, // Maps to an RST.
|
||
|
||
MOVEUSP, // Maps a direction and register to a MOVE [to/from] USP.
|
||
|
||
TRAP, // Maps to a TRAP.
|
||
TRAPV, // Maps to a TRAPV.
|
||
CHK, // Maps to a CHK.
|
||
|
||
NOP, // Maps to a NOP.
|
||
|
||
EXG, // Maps source and destination registers and an operation mode to an EXG.
|
||
EXT_SWAP, // Maps a source register to a SWAP or EXT.
|
||
|
||
EORI_ORI_ANDI_SR, // Maps to an EORI, ORI or ANDI to SR/CCR.
|
||
|
||
BCHG_BSET, // Maps a mode and register, and possibly a source register, to a BCHG or BSET.
|
||
|
||
TAS, // Maps a mode and register to a TAS.
|
||
|
||
PEA, // Maps a mode and register to a PEA.
|
||
|
||
LINK, // Maps a register to a LINK.
|
||
UNLINK, // Maps a register to an UNLINK.
|
||
|
||
STOP, // Maps to a STOP.
|
||
};
|
||
|
||
using Operation = ProcessorStorage::Operation;
|
||
using Action = ProcessorStorage::MicroOp::Action;
|
||
using MicroOp = ProcessorBase::MicroOp;
|
||
struct PatternMapping {
|
||
uint16_t mask, value;
|
||
Operation operation;
|
||
Decoder decoder;
|
||
};
|
||
|
||
/*
|
||
Inspired partly by 'wrm' (https://github.com/wrm-za I assume); the following
|
||
table draws from the M68000 Programmer's Reference Manual, currently available at
|
||
https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf
|
||
|
||
After each line is the internal page number on which documentation of that
|
||
instruction mapping can be found, followed by the page number within the PDF
|
||
linked above.
|
||
|
||
NB: a vector is used to allow easy iteration.
|
||
*/
|
||
const std::initializer_list<PatternMapping> mappings = {
|
||
{0xf1f0, 0xc100, Operation::ABCD, Decoder::ABCD_SBCD}, // 4-3 (p107)
|
||
{0xf1f0, 0x8100, Operation::SBCD, Decoder::ABCD_SBCD}, // 4-171 (p275)
|
||
{0xffc0, 0x4800, Operation::NBCD, Decoder::MOVEfromSR_NBCD}, // 4-142 (p246)
|
||
|
||
{0xf0c0, 0xc000, Operation::ANDb, Decoder::AND_OR_EOR}, // 4-15 (p119)
|
||
{0xf0c0, 0xc040, Operation::ANDw, Decoder::AND_OR_EOR}, // 4-15 (p119)
|
||
{0xf0c0, 0xc080, Operation::ANDl, Decoder::AND_OR_EOR}, // 4-15 (p119)
|
||
|
||
{0xf0c0, 0x8000, Operation::ORb, Decoder::AND_OR_EOR}, // 4-150 (p254)
|
||
{0xf0c0, 0x8040, Operation::ORw, Decoder::AND_OR_EOR}, // 4-150 (p254)
|
||
{0xf0c0, 0x8080, Operation::ORl, Decoder::AND_OR_EOR}, // 4-150 (p254)
|
||
|
||
{0xf0c0, 0xb000, Operation::EORb, Decoder::AND_OR_EOR}, // 4-100 (p204)
|
||
{0xf0c0, 0xb040, Operation::EORw, Decoder::AND_OR_EOR}, // 4-100 (p204)
|
||
{0xf0c0, 0xb080, Operation::EORl, Decoder::AND_OR_EOR}, // 4-100 (p204)
|
||
|
||
{0xffc0, 0x0600, Operation::ADDb, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-9 (p113)
|
||
{0xffc0, 0x0640, Operation::ADDw, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-9 (p113)
|
||
{0xffc0, 0x0680, Operation::ADDl, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-9 (p113)
|
||
|
||
{0xffc0, 0x0200, Operation::ANDb, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-18 (p122)
|
||
{0xffc0, 0x0240, Operation::ANDw, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-18 (p122)
|
||
{0xffc0, 0x0280, Operation::ANDl, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-18 (p122)
|
||
|
||
{0xffc0, 0x0000, Operation::ORb, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-153 (p257)
|
||
{0xffc0, 0x0040, Operation::ORw, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-153 (p257)
|
||
{0xffc0, 0x0080, Operation::ORl, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-153 (p257)
|
||
|
||
{0xffc0, 0x0a00, Operation::EORb, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-102 (p206)
|
||
{0xffc0, 0x0a40, Operation::EORw, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-102 (p206)
|
||
{0xffc0, 0x0a80, Operation::EORl, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-102 (p206)
|
||
|
||
{0xffc0, 0x0400, Operation::SUBb, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-179 (p283)
|
||
{0xffc0, 0x0440, Operation::SUBw, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-179 (p283)
|
||
{0xffc0, 0x0480, Operation::SUBl, Decoder::EORI_ORI_ANDI_SUBI_ADDI}, // 4-179 (p283)
|
||
|
||
{0xf000, 0x1000, Operation::MOVEb, Decoder::MOVE}, // 4-116 (p220)
|
||
{0xf000, 0x2000, Operation::MOVEl, Decoder::MOVE}, // 4-116 (p220)
|
||
{0xf000, 0x3000, Operation::MOVEw, Decoder::MOVE}, // 4-116 (p220)
|
||
|
||
{0xffc0, 0x46c0, Operation::MOVEtoSR, Decoder::MOVEtoSRCCR}, // 6-19 (p473)
|
||
{0xffc0, 0x44c0, Operation::MOVEtoCCR, Decoder::MOVEtoSRCCR}, // 4-123 (p227)
|
||
{0xffc0, 0x40c0, Operation::MOVEfromSR, Decoder::MOVEfromSR_NBCD}, // 6-17 (p471)
|
||
|
||
{0xf1c0, 0xb000, Operation::CMPb, Decoder::CMP}, // 4-75 (p179)
|
||
{0xf1c0, 0xb040, Operation::CMPw, Decoder::CMP}, // 4-75 (p179)
|
||
{0xf1c0, 0xb080, Operation::CMPl, Decoder::CMP}, // 4-75 (p179)
|
||
|
||
{0xf1c0, 0xb0c0, Operation::CMPAw, Decoder::CMPA}, // 4-77 (p181)
|
||
{0xf1c0, 0xb1c0, Operation::CMPl, Decoder::CMPA}, // 4-77 (p181)
|
||
|
||
{0xffc0, 0x0c00, Operation::CMPb, Decoder::CMPI}, // 4-79 (p183)
|
||
{0xffc0, 0x0c40, Operation::CMPw, Decoder::CMPI}, // 4-79 (p183)
|
||
{0xffc0, 0x0c80, Operation::CMPl, Decoder::CMPI}, // 4-79 (p183)
|
||
|
||
{0xf1f8, 0xb108, Operation::CMPb, Decoder::CMPM}, // 4-81 (p185)
|
||
{0xf1f8, 0xb148, Operation::CMPw, Decoder::CMPM}, // 4-81 (p185)
|
||
{0xf1f8, 0xb188, Operation::CMPl, Decoder::CMPM}, // 4-81 (p185)
|
||
|
||
// {0xff00, 0x6000, Operation::BRA, Decoder::BRA}, // 4-55 (p159) TODO: confirm that this really, really is just a special case of Bcc.
|
||
{0xf000, 0x6000, Operation::Bcc, Decoder::Bcc_BSR}, // 4-25 (p129) and 4-59 (p163)
|
||
|
||
{0xf1c0, 0x41c0, Operation::MOVEAl, Decoder::LEA}, // 4-110 (p214)
|
||
{0xffc0, 0x4840, Operation::PEA, Decoder::PEA}, // 4-159 (p263)
|
||
|
||
{0xf100, 0x7000, Operation::MOVEq, Decoder::MOVEq}, // 4-134 (p238)
|
||
|
||
{0xffff, 0x4e70, Operation::None, Decoder::RESET}, // 6-83 (p537)
|
||
|
||
{0xffc0, 0x4ec0, Operation::JMP, Decoder::JMP}, // 4-108 (p212)
|
||
{0xffc0, 0x4e80, Operation::JMP, Decoder::JSR}, // 4-109 (p213)
|
||
{0xffff, 0x4e75, Operation::RTS, Decoder::RTS}, // 4-169 (p273)
|
||
|
||
{0xf0c0, 0x9000, Operation::SUBb, Decoder::ADD_SUB}, // 4-174 (p278)
|
||
{0xf0c0, 0x9040, Operation::SUBw, Decoder::ADD_SUB}, // 4-174 (p278)
|
||
{0xf0c0, 0x9080, Operation::SUBl, Decoder::ADD_SUB}, // 4-174 (p278)
|
||
|
||
{0xf0c0, 0xd000, Operation::ADDb, Decoder::ADD_SUB}, // 4-4 (p108)
|
||
{0xf0c0, 0xd040, Operation::ADDw, Decoder::ADD_SUB}, // 4-4 (p108)
|
||
{0xf0c0, 0xd080, Operation::ADDl, Decoder::ADD_SUB}, // 4-4 (p108)
|
||
|
||
{0xf1c0, 0xd0c0, Operation::ADDAw, Decoder::ADDA_SUBA}, // 4-7 (p111)
|
||
{0xf1c0, 0xd1c0, Operation::ADDAl, Decoder::ADDA_SUBA}, // 4-7 (p111)
|
||
{0xf1c0, 0x90c0, Operation::SUBAw, Decoder::ADDA_SUBA}, // 4-177 (p281)
|
||
{0xf1c0, 0x91c0, Operation::SUBAl, Decoder::ADDA_SUBA}, // 4-177 (p281)
|
||
|
||
{0xf1c0, 0x5000, Operation::ADDQb, Decoder::ADDQ_SUBQ}, // 4-11 (p115)
|
||
{0xf1c0, 0x5040, Operation::ADDQw, Decoder::ADDQ_SUBQ}, // 4-11 (p115)
|
||
{0xf1c0, 0x5080, Operation::ADDQl, Decoder::ADDQ_SUBQ}, // 4-11 (p115)
|
||
|
||
{0xf1c0, 0x5100, Operation::SUBQb, Decoder::ADDQ_SUBQ}, // 4-181 (p285)
|
||
{0xf1c0, 0x5140, Operation::SUBQw, Decoder::ADDQ_SUBQ}, // 4-181 (p285)
|
||
{0xf1c0, 0x5180, Operation::SUBQl, Decoder::ADDQ_SUBQ}, // 4-181 (p285)
|
||
|
||
{0xf1f0, 0xd100, Operation::ADDXb, Decoder::ADDX_SUBX}, // 4-14 (p118)
|
||
{0xf1f0, 0xd140, Operation::ADDXw, Decoder::ADDX_SUBX}, // 4-14 (p118)
|
||
{0xf1f0, 0xd180, Operation::ADDXl, Decoder::ADDX_SUBX}, // 4-14 (p118)
|
||
|
||
{0xf1f0, 0x9100, Operation::SUBXb, Decoder::ADDX_SUBX}, // 4-184 (p288)
|
||
{0xf1f0, 0x9140, Operation::SUBXw, Decoder::ADDX_SUBX}, // 4-184 (p288)
|
||
{0xf1f0, 0x9180, Operation::SUBXl, Decoder::ADDX_SUBX}, // 4-184 (p288)
|
||
|
||
{0xf1c0, 0x0100, Operation::BTSTb, Decoder::BTST}, // 4-62 (p166)
|
||
{0xffc0, 0x0800, Operation::BTSTb, Decoder::BTSTIMM}, // 4-63 (p167)
|
||
|
||
{0xf1c0, 0x0180, Operation::BCLRb, Decoder::BCLR}, // 4-31 (p135)
|
||
{0xffc0, 0x0880, Operation::BCLRb, Decoder::BCLRIMM}, // 4-32 (p136)
|
||
|
||
{0xf0c0, 0x50c0, Operation::Scc, Decoder::Scc_DBcc}, // Scc: 4-173 (p276); DBcc: 4-91 (p195)
|
||
|
||
{0xffc0, 0x4200, Operation::CLRb, Decoder::CLR_NEG_NEGX_NOT}, // 4-73 (p177)
|
||
{0xffc0, 0x4240, Operation::CLRw, Decoder::CLR_NEG_NEGX_NOT}, // 4-73 (p177)
|
||
{0xffc0, 0x4280, Operation::CLRl, Decoder::CLR_NEG_NEGX_NOT}, // 4-73 (p177)
|
||
{0xffc0, 0x4400, Operation::NEGb, Decoder::CLR_NEG_NEGX_NOT}, // 4-144 (p248)
|
||
{0xffc0, 0x4440, Operation::NEGw, Decoder::CLR_NEG_NEGX_NOT}, // 4-144 (p248)
|
||
{0xffc0, 0x4480, Operation::NEGl, Decoder::CLR_NEG_NEGX_NOT}, // 4-144 (p248)
|
||
{0xffc0, 0x4000, Operation::NEGXb, Decoder::CLR_NEG_NEGX_NOT}, // 4-146 (p250)
|
||
{0xffc0, 0x4040, Operation::NEGXw, Decoder::CLR_NEG_NEGX_NOT}, // 4-146 (p250)
|
||
{0xffc0, 0x4080, Operation::NEGXl, Decoder::CLR_NEG_NEGX_NOT}, // 4-146 (p250)
|
||
{0xffc0, 0x4600, Operation::NOTb, Decoder::CLR_NEG_NEGX_NOT}, // 4-148 (p250)
|
||
{0xffc0, 0x4640, Operation::NOTw, Decoder::CLR_NEG_NEGX_NOT}, // 4-148 (p250)
|
||
{0xffc0, 0x4680, Operation::NOTl, Decoder::CLR_NEG_NEGX_NOT}, // 4-148 (p250)
|
||
|
||
{0xf1d8, 0xe100, Operation::ASLb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xf1d8, 0xe140, Operation::ASLw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xf1d8, 0xe180, Operation::ASLl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xffc0, 0xe1c0, Operation::ASLm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-22 (p126)
|
||
|
||
{0xf1d8, 0xe000, Operation::ASRb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xf1d8, 0xe040, Operation::ASRw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xf1d8, 0xe080, Operation::ASRl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-22 (p126)
|
||
{0xffc0, 0xe0c0, Operation::ASRm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-22 (p126)
|
||
|
||
{0xf1d8, 0xe108, Operation::LSLb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xf1d8, 0xe148, Operation::LSLw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xf1d8, 0xe188, Operation::LSLl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xffc0, 0xe3c0, Operation::LSLm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-113 (p217)
|
||
|
||
{0xf1d8, 0xe008, Operation::LSRb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xf1d8, 0xe048, Operation::LSRw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xf1d8, 0xe088, Operation::LSRl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-113 (p217)
|
||
{0xffc0, 0xe2c0, Operation::LSRm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-113 (p217)
|
||
|
||
{0xf1d8, 0xe118, Operation::ROLb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xf1d8, 0xe158, Operation::ROLw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xf1d8, 0xe198, Operation::ROLl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xffc0, 0xe7c0, Operation::ROLm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-160 (p264)
|
||
|
||
{0xf1d8, 0xe018, Operation::RORb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xf1d8, 0xe058, Operation::RORw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xf1d8, 0xe098, Operation::RORl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-160 (p264)
|
||
{0xffc0, 0xe6c0, Operation::RORm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-160 (p264)
|
||
|
||
{0xf1d8, 0xe110, Operation::ROXLb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xf1d8, 0xe150, Operation::ROXLw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xf1d8, 0xe190, Operation::ROXLl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xffc0, 0xe5c0, Operation::ROXLm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-163 (p267)
|
||
|
||
{0xf1d8, 0xe010, Operation::ROXRb, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xf1d8, 0xe050, Operation::ROXRw, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xf1d8, 0xe090, Operation::ROXRl, Decoder::ASLR_LSLR_ROLR_ROXLRr}, // 4-163 (p267)
|
||
{0xffc0, 0xe4c0, Operation::ROXRm, Decoder::ASLR_LSLR_ROLR_ROXLRm}, // 4-163 (p267)
|
||
|
||
{0xffc0, 0x48c0, Operation::MOVEMtoMl, Decoder::MOVEM}, // 4-128 (p232)
|
||
{0xffc0, 0x4880, Operation::MOVEMtoMw, Decoder::MOVEM}, // 4-128 (p232)
|
||
{0xffc0, 0x4cc0, Operation::MOVEMtoRl, Decoder::MOVEM}, // 4-128 (p232)
|
||
{0xffc0, 0x4c80, Operation::MOVEMtoRw, Decoder::MOVEM}, // 4-128 (p232)
|
||
|
||
{0xf1f8, 0x0108, Operation::MOVEPtoRw, Decoder::MOVEP}, // 4-133 (p237)
|
||
{0xf1f8, 0x0148, Operation::MOVEPtoRl, Decoder::MOVEP}, // 4-133 (p237)
|
||
{0xf1f8, 0x0188, Operation::MOVEPtoMw, Decoder::MOVEP}, // 4-133 (p237)
|
||
{0xf1f8, 0x01c8, Operation::MOVEPtoMl, Decoder::MOVEP}, // 4-133 (p237)
|
||
|
||
{0xffc0, 0x4a00, Operation::TSTb, Decoder::TST}, // 4-192 (p296)
|
||
{0xffc0, 0x4a40, Operation::TSTw, Decoder::TST}, // 4-192 (p296)
|
||
{0xffc0, 0x4a80, Operation::TSTl, Decoder::TST}, // 4-192 (p296)
|
||
|
||
{0xf1c0, 0xc0c0, Operation::MULU, Decoder::MULU_MULS}, // 4-139 (p243)
|
||
{0xf1c0, 0xc1c0, Operation::MULS, Decoder::MULU_MULS}, // 4-136 (p240)
|
||
|
||
{0xf1c0, 0x80c0, Operation::DIVU, Decoder::DIVU_DIVS}, // 4-97 (p201)
|
||
{0xf1c0, 0x81c0, Operation::DIVS, Decoder::DIVU_DIVS}, // 4-93 (p197)
|
||
|
||
{0xfff0, 0x4e60, Operation::MOVEAl, Decoder::MOVEUSP}, // 6-21 (p475)
|
||
|
||
{0xfff0, 0x4e40, Operation::TRAP, Decoder::TRAP}, // 4-188 (p292)
|
||
{0xffff, 0x4e76, Operation::TRAPV, Decoder::TRAPV}, // 4-191 (p295)
|
||
{0xf1c0, 0x4180, Operation::CHK, Decoder::CHK}, // 4-69 (p173)
|
||
|
||
{0xffff, 0x4e77, Operation::RTE_RTR, Decoder::RTE_RTR}, // 4-168 (p272) [RTR]
|
||
{0xffff, 0x4e73, Operation::RTE_RTR, Decoder::RTE_RTR}, // 6-84 (p538) [RTE]
|
||
|
||
{0xffff, 0x4e71, Operation::None, Decoder::NOP}, // 8-13 (p469)
|
||
|
||
{0xf1f8, 0xc140, Operation::EXG, Decoder::EXG}, // 4-105 (p209)
|
||
{0xf1f8, 0xc148, Operation::EXG, Decoder::EXG}, // 4-105 (p209)
|
||
{0xf1f8, 0xc188, Operation::EXG, Decoder::EXG}, // 4-105 (p209)
|
||
|
||
{0xfff8, 0x4840, Operation::SWAP, Decoder::EXT_SWAP}, // 4-185 (p289)
|
||
|
||
{0xffff, 0x027c, Operation::ANDItoSR, Decoder::EORI_ORI_ANDI_SR},
|
||
{0xffff, 0x023c, Operation::ANDItoCCR, Decoder::EORI_ORI_ANDI_SR},
|
||
{0xffff, 0x0a7c, Operation::EORItoSR, Decoder::EORI_ORI_ANDI_SR},
|
||
{0xffff, 0x0a3c, Operation::EORItoCCR, Decoder::EORI_ORI_ANDI_SR},
|
||
{0xffff, 0x007c, Operation::ORItoSR, Decoder::EORI_ORI_ANDI_SR},
|
||
{0xffff, 0x003c, Operation::ORItoCCR, Decoder::EORI_ORI_ANDI_SR},
|
||
|
||
{0xf1c0, 0x0140, Operation::BCHGb, Decoder::BCHG_BSET}, // 4-28 (p132)
|
||
{0xffc0, 0x0840, Operation::BCHGb, Decoder::BCHG_BSET}, // 4-29 (p133)
|
||
{0xf1c0, 0x01c0, Operation::BSETb, Decoder::BCHG_BSET}, // 4-57 (p161)
|
||
{0xffc0, 0x08c0, Operation::BSETb, Decoder::BCHG_BSET}, // 4-58 (p162)
|
||
|
||
{0xffc0, 0x4ac0, Operation::TAS, Decoder::TAS}, // 4-186 (p290)
|
||
|
||
{0xfff8, 0x4880, Operation::EXTbtow, Decoder::EXT_SWAP}, // 4-106 (p210)
|
||
{0xfff8, 0x48c0, Operation::EXTwtol, Decoder::EXT_SWAP}, // 4-106 (p210)
|
||
|
||
{0xfff8, 0x4e50, Operation::LINK, Decoder::LINK}, // 4-111 (p215)
|
||
{0xfff8, 0x4e58, Operation::UNLINK, Decoder::UNLINK}, // 4-194 (p298)
|
||
|
||
{0xffff, 0x4e72, Operation::STOP, Decoder::STOP}, // 6-85 (p539)
|
||
};
|
||
|
||
std::vector<size_t> micro_op_pointers(65536, std::numeric_limits<size_t>::max());
|
||
|
||
// 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_ at the end.
|
||
// BusStep arbitrary_base;
|
||
|
||
#define op(...) storage_.all_micro_ops_.emplace_back(__VA_ARGS__)
|
||
#define seq(...) assemble_program(__VA_ARGS__)
|
||
#define ea(n) &storage_.effective_address_[n].full
|
||
#define a(n) &storage_.address_[n].full
|
||
|
||
#define bw(x) (x)
|
||
#define l(x) (0x10000 | (x))
|
||
|
||
// Perform a linear search of the mappings above for this instruction.
|
||
for(ssize_t instruction = 65535; instruction >= 0; --instruction) {
|
||
#ifndef NDEBUG
|
||
int hits = 0;
|
||
#endif
|
||
for(const auto &mapping: mappings) {
|
||
if((instruction & mapping.mask) == mapping.value) {
|
||
auto operation = mapping.operation;
|
||
const auto micro_op_start = storage_.all_micro_ops_.size();
|
||
|
||
// The following fields are used commonly enough to be worth pulling out here.
|
||
const int ea_register = instruction & 7;
|
||
const int ea_mode = (instruction >> 3) & 7;
|
||
const int data_register = (instruction >> 9) & 7;
|
||
const int op_mode = (instruction >> 6)&7;
|
||
const bool op_mode_high_bit = !!(op_mode&4);
|
||
|
||
// These are almost always true; they're non-const so that they can be corrected
|
||
// by the few deviations.
|
||
bool is_byte_access = (op_mode&3) == 0;
|
||
bool is_long_word_access = (op_mode&3) == 2;
|
||
|
||
// Temporary storage for the Program fields.
|
||
ProcessorBase::Program program;
|
||
|
||
#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)
|
||
|
||
switch(mapping.decoder) {
|
||
case Decoder::STOP: {
|
||
program.set_requires_supervisor(true);
|
||
op(Action::None, seq("n"));
|
||
op(Action::PerformOperation);
|
||
} break;
|
||
|
||
case Decoder::LINK: {
|
||
program.set_source(storage_, An, ea_register);
|
||
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
||
} break;
|
||
|
||
case Decoder::UNLINK: {
|
||
program.set_destination(storage_, An, ea_register);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation);
|
||
} break;
|
||
|
||
case Decoder::TAS: {
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // TAS Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case Ind: // TAS (An)
|
||
case PostInc: // TAS (An)+
|
||
op(Action::None, seq("nrd", { a(ea_register) }, false));
|
||
op(Action::PerformOperation, seq("tas np", { a(ea_register) }, false));
|
||
if(mode == PostInc) {
|
||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // TAS -(An)
|
||
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd", { a(ea_register) }, false));
|
||
op(Action::PerformOperation, seq("tas np", { a(ea_register) }, false));
|
||
break;
|
||
|
||
case XXXl: // TAS (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // TAS (xxx).w
|
||
case d16An: // TAS (d16, An)
|
||
case d8AnXn: // TAS (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq("np nrd", { ea(1) }, false));
|
||
op(Action::PerformOperation, seq("tas np", { ea(1) }, false));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::BCHG_BSET: {
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
|
||
// Operations on a register are .l; all others are the default .b.
|
||
if(ea_mode == Dn) {
|
||
operation = (operation == Operation::BSETb) ? Operation::BSETl : Operation::BCHGl;
|
||
}
|
||
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
if(instruction & 0x100) {
|
||
// The bit is nominated by a register.
|
||
program.set_source(storage_, Dn, data_register);
|
||
} else {
|
||
// The bit is nominated by a constant, that will be obtained right here.
|
||
program.set_source(storage_, Imm, 0);
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
}
|
||
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // [BCHG/BSET].l Dn, Dn
|
||
// Execution length depends on the selected bit, so allow flexible time for that.
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("r"));
|
||
break;
|
||
|
||
case Ind: // [BCHG/BSET].b Dn, (An)
|
||
case PostInc: // [BCHG/BSET].b Dn, (An)+
|
||
op(Action::None, seq("nrd np", { a(ea_register) }, false));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, false));
|
||
if(mode == PostInc) {
|
||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // [BCHG/BSET].b Dn, -(An)
|
||
op(byte_dec(ea_register) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }, false));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, false));
|
||
break;
|
||
|
||
case XXXl: // [BCHG/BSET].b Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // [BCHG/BSET].b Dn, (xxx).w
|
||
case d16An: // [BCHG/BSET].b Dn, (d16, An)
|
||
case d8AnXn: // [BCHG/BSET].b Dn, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nrd np", mode), { ea(1) }, false));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, false));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::EORI_ORI_ANDI_SR: {
|
||
// The source used here is always the high word of the prefetch queue.
|
||
program.set_requires_supervisor(!!(instruction & 0x40));
|
||
op(Action::None, seq("np nn nn"));
|
||
op(Action::PerformOperation, seq("np np"));
|
||
} break;
|
||
|
||
case Decoder::EXT_SWAP: {
|
||
program.set_destination(storage_, Dn, ea_register);
|
||
op(Action::PerformOperation, seq("np"));
|
||
} break;
|
||
|
||
case Decoder::EXG: {
|
||
switch((instruction >> 3)&31) {
|
||
default: continue;
|
||
|
||
case 0x08:
|
||
program.set_source(storage_, Dn, data_register);
|
||
program.set_destination(storage_, Dn, ea_register);
|
||
break;
|
||
|
||
case 0x09:
|
||
program.set_source(storage_, An, data_register);
|
||
program.set_destination(storage_, An, ea_register);
|
||
break;
|
||
|
||
case 0x11:
|
||
program.set_source(storage_, Dn, data_register);
|
||
program.set_destination(storage_, An, ea_register);
|
||
break;
|
||
}
|
||
|
||
op(Action::PerformOperation, seq("np n"));
|
||
} break;
|
||
|
||
case Decoder::NOP: {
|
||
op(Action::None, seq("np"));
|
||
} break;
|
||
|
||
case Decoder::RTE_RTR: {
|
||
program.set_requires_supervisor(instruction == 0x4e73);
|
||
|
||
// The targets of the nR nr nr below are reset to the program counter elsewhere;
|
||
// look for the comment "relink the RTE and RTR bus steps". It is currently not
|
||
// explicitly tested that these bus steps are not shared with a non-RTE/RTR operation,
|
||
// just assumed because the repetition of nr is fairly silly. A more explicit soution
|
||
// might be preferable in the future.
|
||
op(Action::PrepareRTE_RTR, seq("nR nr nr", { &storage_.precomputed_addresses_[0], &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] } ));
|
||
op(Action::PerformOperation, seq("np np"));
|
||
op();
|
||
} break;
|
||
|
||
case Decoder::AND_OR_EOR: {
|
||
const bool to_ea = op_mode_high_bit;
|
||
const bool is_eor = (instruction >> 12) == 0xb;
|
||
|
||
// Weed out illegal operation modes.
|
||
if(op_mode == 7) continue;
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
|
||
if(to_ea) {
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
program.set_source(storage_, Dn, data_register);
|
||
|
||
// Only EOR takes Dn as a destination effective address.
|
||
if(!is_eor && mode == Dn) continue;
|
||
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // EOR.bw Dn, Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // EOR.l Dn, Dn
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(Ind): // [AND/OR/EOR].bw Dn, (An)
|
||
case bw(PostInc): // [AND/OR/EOR].bw Dn, (An)+
|
||
op(Action::None, seq("nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // [AND/OR/EOR].bw Dn, -(An)
|
||
op(dec(ea_register) | MicroOp::DestinationMask, seq("n nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(PreDec): // [AND/OR/EOR].l Dn, -(An)
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask, seq("n"));
|
||
[[fallthrough]];
|
||
case l(Ind): // [AND/OR/EOR].l Dn, (An)
|
||
case l(PostInc): // [AND/OR/EOR].l Dn, (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("np nw- nW", { ea(1), ea(1) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(XXXl): // [AND/OR/EOR].bw Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // [AND/OR/EOR].bw Dn, (xxx).w
|
||
case bw(d16An): // [AND/OR/EOR].bw Dn, (d16, An)
|
||
case bw(d8AnXn): // [AND/OR/EOR].bw Dn, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nrd", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // [AND/OR/EOR].l Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // [AND/OR/EOR].l Dn, (xxx).w
|
||
case l(d16An): // [AND/OR/EOR].l Dn, (d16, An)
|
||
case l(d8AnXn): // [AND/OR/EOR].l Dn, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nRd+ nrd", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("np nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} else {
|
||
// EORs can be to EA only.
|
||
if(is_eor) continue;
|
||
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, Dn, data_register);
|
||
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // [AND/OR].bw Dn, Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // [AND/OR].l Dn, Dn
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(Ind): // [AND/OR].bw (An), Dn
|
||
case bw(PostInc): // [AND/OR].bw (An)+, Dn
|
||
op(Action::None, seq("nr", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np"));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // [AND/OR].bw -(An), Dn
|
||
op(dec(ea_register) | MicroOp::SourceMask, seq("n nr", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(PreDec): // [AND/OR].l -(An), Dn
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask, seq("n"));
|
||
[[fallthrough]];
|
||
case l(Ind): // [AND/OR].l (An), Dn,
|
||
case l(PostInc): // [AND/OR].l (An)+, Dn
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case bw(XXXl): // [AND/OR].bw (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // [AND/OR].bw (xxx).w, Dn
|
||
case bw(d16An): // [AND/OR].bw (d16, An), Dn
|
||
case bw(d16PC): // [AND/OR].bw (d16, PC), Dn
|
||
case bw(d8AnXn): // [AND/OR].bw (d8, An, Xn), Dn
|
||
case bw(d8PCXn): // [AND/OR].bw (d8, PX, Xn), Dn
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nr", mode), { ea(0) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(XXXl): // [AND/OR].bw (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // [AND/OR].bw (xxx).w, Dn
|
||
case l(d16An): // [AND/OR].l (d16, An), Dn
|
||
case l(d16PC): // [AND/OR].l (d16, PC), Dn
|
||
case l(d8AnXn): // [AND/OR].l (d8, An, Xn), Dn
|
||
case l(d8PCXn): // [AND/OR].l (d8, PX, Xn), Dn
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nR+ nr", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(Imm): // [AND/OR].bw #, Dn
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Imm): // [AND/OR].l #, Dn
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
}
|
||
}
|
||
} break;
|
||
|
||
case Decoder::DIVU_DIVS: {
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, Dn, data_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // [DIVU/DIVS] Dn, Dn
|
||
op(Action::PerformOperation, seq("r"));
|
||
op(Action::None, seq("np"));
|
||
break;
|
||
|
||
case Ind: // [DIVU/DIVS] (An), Dn
|
||
case PostInc: // [DIVU/DIVS] (An)+, Dn
|
||
op(Action::None, seq("nr", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("r np"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // [DIVU/DIVS] -(An), Dn
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("nr", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("r np"));
|
||
break;
|
||
|
||
case XXXl: // [DIVU/DIVS] (XXX).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // [DIVU/DIVS] (XXX).w, Dn
|
||
case d16An: // [DIVU/DIVS] (d16, An), Dn
|
||
case d16PC: // [DIVU/DIVS] (d16, PC), Dn
|
||
case d8AnXn: // [DIVU/DIVS] (d8, An, Xn), Dn
|
||
case d8PCXn: // [DIVU/DIVS] (d8, PC, Xn), Dn
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq("np nr", { ea(0) }));
|
||
op(Action::PerformOperation, seq("r np"));
|
||
break;
|
||
|
||
case Imm: // [DIVU/DIVS] #, Dn
|
||
// DEVIATION FROM YACHT.TXT. It shows an additional np, which is incorrect.
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op(Action::PerformOperation, seq("r np"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::MULU_MULS: {
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, Dn, data_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // [MULU/MULS] Dn, Dn
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("r"));
|
||
break;
|
||
|
||
case Ind: // [MULU/MULS] (An), Dn
|
||
case PostInc: // [MULU/MULS] (An)+, Dn
|
||
op(Action::None, seq("nr np", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("r"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // [MULU/MULS] -(An), Dn
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("n nr np", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("r"));
|
||
break;
|
||
|
||
case XXXl: // [MULU/MULS] (XXX).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // [MULU/MULS] (XXX).w, Dn
|
||
case d16An: // [MULU/MULS] (d16, An), Dn
|
||
case d16PC: // [MULU/MULS] (d16, PC), Dn
|
||
case d8AnXn: // [MULU/MULS] (d8, An, Xn), Dn
|
||
case d8PCXn: // [MULU/MULS] (d8, PX, Xn), Dn
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("n np nr np", mode), { ea(0) }));
|
||
op(Action::PerformOperation, seq("r"));
|
||
break;
|
||
|
||
case Imm: // [MULU/MULS] #, Dn
|
||
// DEVIATION FROM YACHT.TXT. It shows an additional np, which is incorrect.
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(Action::PerformOperation, seq("r"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::EORI_ORI_ANDI_SUBI_ADDI: {
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
|
||
// Source is always something cribbed from the instruction stream;
|
||
// destination is going to be in the write address unit.
|
||
program.set_source(storage_, Imm, 0);
|
||
program.set_destination(storage_, mode, ea_register);
|
||
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, Dn
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Dn): // [EORI/ORI/ANDI/SUBI/ADDI].l #, Dn
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np nn"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(Ind): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (An)
|
||
case bw(PostInc): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (An)+
|
||
op( int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask,
|
||
seq("np nrd np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case l(Ind): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (An)
|
||
case l(PostInc): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("np"));
|
||
op( int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask,
|
||
seq("np nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, -(An)
|
||
op(dec(ea_register) | MicroOp::DestinationMask);
|
||
op( int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask,
|
||
seq("np n nrd np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(PreDec): // [EORI/ORI/ANDI/SUBI/ADDI].l #, -(An)
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("np"));
|
||
op( int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask,
|
||
seq("np n nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case bw(XXXw): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (xxx).w
|
||
case bw(d8AnXn): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (d8, An, Xn)
|
||
case bw(d16An): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (d16, An)
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXw): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (xxx).w
|
||
case l(d8AnXn): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (d8, An, Xn)
|
||
case l(d16An): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (d16, An)
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nRd+ nrd np", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case bw(XXXl): // [EORI/ORI/ANDI/SUBI/ADDI].bw #, (xxx).l
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("np nrd np", { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // [EORI/ORI/ANDI/SUBI/ADDI].l #, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("np nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::ADD_SUB: {
|
||
// ADD and SUB definitely always involve a data register and an arbitrary addressing mode;
|
||
// which direction they operate in depends on bit 8.
|
||
const bool reverse_source_destination = !(instruction & 256);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
|
||
if(reverse_source_destination) {
|
||
program.set_destination(storage_, Dn, data_register);
|
||
program.set_source(storage_, Imm, ea_register);
|
||
|
||
// Perform [ADD/SUB].blw <ea>, Dn
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // ADD/SUB.bw Dn, Dn
|
||
program.set_source(storage_, Dn, ea_register);
|
||
// program.source = &storage_.data_[ea_register];
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // ADD/SUB.l Dn, Dn
|
||
program.set_source(storage_, Dn, ea_register);
|
||
// program.source = &storage_.data_[ea_register];
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(An): // ADD/SUB.bw An, Dn
|
||
// Address registers can't provide single bytes.
|
||
if(is_byte_access) continue;
|
||
program.set_source(storage_, An, ea_register);
|
||
// program.source = &storage_.address_[ea_register];
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(An): // ADD/SUB.l An, Dn
|
||
program.set_source(storage_, An, ea_register);
|
||
// program.source = &storage_.address_[ea_register];
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(Ind): // ADD/SUB.bw (An), Dn
|
||
case bw(PostInc): // ADD/SUB.bw (An)+, Dn
|
||
op(Action::None, seq("nr np", { a(ea_register) }, !is_byte_access));
|
||
if(ea_mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Ind): // ADD/SUB.l (An), Dn
|
||
case l(PostInc): // ADD/SUB.l (An)+, Dn
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("nR+ nr np n", { ea(0), ea(0) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(PreDec): // ADD/SUB.bw -(An), Dn
|
||
op( dec(ea_register) | MicroOp::SourceMask,
|
||
seq("n nr np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(PreDec): // ADD/SUB.l -(An), Dn
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("n nR+ nr np n", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(XXXl): // ADD/SUB.bw (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // ADD/SUB.bw (xxx).w, Dn
|
||
case bw(d16PC): // ADD/SUB.bw (d16, PC), Dn
|
||
case bw(d8PCXn): // ADD/SUB.bw (d8, PC, Xn), Dn
|
||
case bw(d16An): // ADD/SUB.bw (d16, An), Dn
|
||
case bw(d8AnXn): // ADD/SUB.bw (d8, An, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nr np", mode), { ea(0) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(XXXl): // ADD/SUB.l (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // ADD/SUB.l (xxx).w, Dn
|
||
case l(d16PC): // ADD/SUB.l (d16, PC), Dn
|
||
case l(d8PCXn): // ADD/SUB.l (d8, PC, Xn), Dn
|
||
case l(d16An): // ADD/SUB.l (d16, An), Dn
|
||
case l(d8AnXn): // ADD/SUB.l (d8, An, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nR+ nr np n", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(Imm): // ADD/SUB.bw #, Dn
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Imm): // ADD/SUB.l #, Dn
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np nn"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} else {
|
||
const auto destination_register = ea_register;
|
||
program.set_destination(storage_, Ind, destination_register);
|
||
program.set_source(storage_, Dn, data_register);
|
||
|
||
// Perform [ADD/SUB].blw Dn, <ea>
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Ind): // ADD/SUB.bw Dn, (An)
|
||
case bw(PostInc): // ADD/SUB.bw Dn, (An)+
|
||
op(Action::None, seq("nrd np", { a(destination_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(destination_register) }, !is_byte_access));
|
||
if(ea_mode == PostInc) {
|
||
op(inc(destination_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case l(Ind): // ADD/SUB.l Dn, (An)
|
||
case l(PostInc): // ADD/SUB.l Dn, (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
if(ea_mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // ADD/SUB.bw Dn, -(An)
|
||
op( dec(destination_register) | MicroOp::DestinationMask,
|
||
seq("n nrd np", { a(destination_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(destination_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(PreDec): // ADD/SUB.l Dn, -(An)
|
||
op( int(Action::Decrement4) | MicroOp::DestinationMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("n nRd+ nrd np", { ea(1), ea(1) }));
|
||
op( Action::PerformOperation,
|
||
seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case bw(XXXl): // ADD/SUB.bw Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // ADD/SUB.bw Dn, (xxx).w
|
||
case bw(d16An): // ADD/SUB.bw (d16, An), Dn
|
||
case bw(d8AnXn): // ADD/SUB.bw (d8, An, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // ADD/SUB.l Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // ADD/SUB.l Dn, (xxx).w
|
||
case l(d16An): // ADD/SUB.l (d16, An), Dn
|
||
case l(d8AnXn): // ADD/SUB.l (d8, An, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nRd+ nrd np", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
}
|
||
} break;
|
||
|
||
case Decoder::ADDA_SUBA: {
|
||
program.set_destination(storage_, 1, data_register);
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
is_long_word_access = op_mode_high_bit;
|
||
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // ADDA/SUBA.w Dn, An
|
||
case bw(An): // ADDA/SUBA.w An, An
|
||
case l(Dn): // ADDA/SUBA.l Dn, An
|
||
case l(An): // ADDA/SUBA.l An, An
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(Ind): // ADDA/SUBA.w (An), An
|
||
case bw(PostInc): // ADDA/SUBA.w (An)+, An
|
||
op(Action::None, seq("nr np nn", { a(ea_register) }));
|
||
if(ea_mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Ind): // ADDA/SUBA.l (An), An
|
||
case l(PostInc): // ADDA/SUBA.l (An)+, An
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr np n", { ea(0), ea(0) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(PreDec): // ADDA/SUBA.w -(An), An
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask);
|
||
op(Action::None, seq("n nr np nn", { a(ea_register) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(PreDec): // ADDA/SUBA.l -(An), An
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("n nR+ nr np n", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(XXXl): // ADDA/SUBA.w (xxx).l, An
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // ADDA/SUBA.w (xxx).w, An
|
||
case bw(d16An): // ADDA/SUBA.w (d16, An), An
|
||
case bw(d8AnXn): // ADDA/SUBA.w (d8, An, Xn), An
|
||
case bw(d16PC): // ADDA/SUBA.w (d16, PC), An
|
||
case bw(d8PCXn): // ADDA/SUBA.w (d8, PC, Xn), An
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nr np nn", mode), { ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(XXXl): // ADDA/SUBA.l (xxx).l, An
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // ADDA/SUBA.l (xxx).w, An
|
||
case l(d16An): // ADDA/SUBA.l (d16, An), An
|
||
case l(d8AnXn): // ADDA/SUBA.l (d8, An, Xn), An
|
||
case l(d16PC): // ADDA/SUBA.l (d16, PC), An
|
||
case l(d8PCXn): // ADDA/SUBA.l (d8, PC, Xn), An
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nR+ nr np n", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(Imm): // ADDA/SUBA.w #, An
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np nn"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Imm): // ADDA/SUBA.l #, Dn
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np nn"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::ADDQ_SUBQ: {
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
|
||
// If the destination is an address register then byte mode isn't allowed, and
|
||
// flags shouldn't be affected (so, a different operation is used).
|
||
if(mode == An) {
|
||
if(is_byte_access) continue;
|
||
switch(operation) {
|
||
default: break;
|
||
case Operation::ADDQl: // TODO: should the adds be distinguished? If so, how?
|
||
case Operation::ADDQw: operation = Operation::ADDQAl; break;
|
||
case Operation::SUBQl:
|
||
case Operation::SUBQw: operation = Operation::SUBQAl; break;
|
||
}
|
||
}
|
||
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // [ADD/SUB]Q.bw #, Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // [ADD/SUB]Q.l #, Dn
|
||
case l(An): // [ADD/SUB]Q.l #, An
|
||
case bw(An): // [ADD/SUB]Q.bw #, An
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
break;
|
||
|
||
case bw(Ind): // [ADD/SUB]Q.bw #, (An)
|
||
case bw(PostInc): // [ADD/SUB]Q.bw #, (An)+
|
||
op(Action::None, seq("nrd np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case l(PreDec): // [ADD/SUB]Q.l #, -(An)
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask, seq("n"));
|
||
[[fallthrough]];
|
||
case l(Ind): // [ADD/SUB]Q.l #, (An)
|
||
case l(PostInc): // [ADD/SUB]Q.l #, (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // [ADD/SUB]Q.bw #, -(An)
|
||
op( dec(ea_register) | MicroOp::DestinationMask,
|
||
seq("n nrd np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case bw(XXXl): // [ADD/SUB]Q.bw #, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // [ADD/SUB]Q.bw #, (xxx).w
|
||
case bw(d16An): // [ADD/SUB]Q.bw #, (d16, An)
|
||
case bw(d8AnXn): // [ADD/SUB]Q.bw #, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nrd np", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // [ADD/SUB]Q.l #, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // [ADD/SUB]Q.l #, (xxx).w
|
||
case l(d16An): // [ADD/SUB]Q.l #, (d16, An)
|
||
case l(d8AnXn): // [ADD/SUB]Q.l #, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nRd+ nrd np", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::ADDX_SUBX: {
|
||
if(instruction & 0x8) {
|
||
// Use predecrementing address registers.
|
||
program.set_source(storage_, Ind, ea_register);
|
||
program.set_destination(storage_, Ind, data_register);
|
||
|
||
if(is_long_word_access) {
|
||
// Access order is very atypical here: it's lower parts each for both words,
|
||
// and then also a lower-part-first write.
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("n nr- nR", { ea(0), ea(0) }));
|
||
op(int(Action::Decrement2) | MicroOp::DestinationMask | MicroOp::SourceMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("nrd- nRd+", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("nw- np nW", { ea(1), ea(1) }));
|
||
op(int(Action::Decrement2) | MicroOp::DestinationMask);
|
||
} else {
|
||
op(dec(ea_register) | MicroOp::SourceMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("n nr", { ea(0) }, !is_byte_access));
|
||
op(dec(data_register) | MicroOp::DestinationMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("nrd np", { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }, !is_byte_access));
|
||
}
|
||
} else {
|
||
// Use data registers.
|
||
program.set_source(storage_, Dn, ea_register);
|
||
program.set_destination(storage_, Dn, data_register);
|
||
|
||
if(is_long_word_access) {
|
||
op(Action::PerformOperation, seq("np nn"));
|
||
} else {
|
||
op(Action::PerformOperation, seq("np"));
|
||
}
|
||
}
|
||
|
||
} break;
|
||
|
||
// This decoder actually decodes nothing; it just schedules a PerformOperation followed by an empty step.
|
||
case Decoder::Bcc_BSR: {
|
||
const int condition = (instruction >> 8) & 0xf;
|
||
if(condition == 1) {
|
||
// This is BSR, which is unconditional and means pushing a return address to the stack first.
|
||
|
||
// Push the return address to the stack.
|
||
op(Action::PrepareBSR, seq("n nW+ nw", { ea(1), ea(1) }));
|
||
}
|
||
|
||
// This is Bcc.
|
||
op(Action::PerformOperation);
|
||
op(); // The above looks terminal, but will be dynamically reprogrammed.
|
||
} break;
|
||
|
||
// A little artificial, there's nothing really to decode for BRA.
|
||
case Decoder::BRA: {
|
||
op(Action::PerformOperation, seq("n np np"));
|
||
} break;
|
||
|
||
// Decodes a BTST, potential mutating the operation into a BTSTl,
|
||
// or a BCLR.
|
||
case Decoder::BCLR:
|
||
case Decoder::BTST: {
|
||
const bool is_bclr = mapping.decoder == Decoder::BCLR;
|
||
|
||
program.set_source(storage_, 0, data_register);
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // BTST.l Dn, Dn
|
||
if(is_bclr) {
|
||
operation = Operation::BCLRl;
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("r"));
|
||
} else {
|
||
operation = Operation::BTSTl;
|
||
op(Action::PerformOperation, seq("np n"));
|
||
}
|
||
break;
|
||
|
||
case Ind: // BTST.b Dn, (An)
|
||
case PostInc: // BTST.b Dn, (An)+
|
||
op(Action::None, seq("nrd np", { a(ea_register) }, false));
|
||
op(Action::PerformOperation, is_bclr ? seq("nw", { a(ea_register) }, false) : MicroOp::NoBusProgram);
|
||
if(mode == PostInc) {
|
||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // BTST.b Dn, -(An)
|
||
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) : MicroOp::NoBusProgram);
|
||
break;
|
||
|
||
case XXXl: // BTST.b Dn, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // BTST.b Dn, (xxx).w
|
||
case d16An: // BTST.b Dn, (d16, An)
|
||
case d8AnXn: // BTST.b Dn, (d8, An, Xn)
|
||
case d16PC: // BTST.b Dn, (d16, PC)
|
||
case d8PCXn: // BTST.b Dn, (d8, PC, Xn)
|
||
// PC-relative addressing isn't support for BCLR.
|
||
if((mode == d16PC || mode == d8PCXn) && is_bclr) continue;
|
||
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||
break;
|
||
|
||
case Imm: // BTST.b Dn, #
|
||
if(is_bclr) continue;
|
||
|
||
/* Yacht.txt doesn't cover this; this is a guess. */
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::DestinationMask, seq("np"));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::BCLRIMM:
|
||
case Decoder::BTSTIMM: {
|
||
const bool is_bclr = mapping.decoder == Decoder::BCLRIMM;
|
||
|
||
program.set_source(storage_, Imm, 0);
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // BTST.l #, Dn
|
||
if(is_bclr) {
|
||
operation = Operation::BCLRl;
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(Action::PerformOperation, seq("r"));
|
||
} else {
|
||
operation = Operation::BTSTl;
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np n"));
|
||
op(Action::PerformOperation);
|
||
}
|
||
break;
|
||
|
||
case Ind: // BTST.b #, (An)
|
||
case PostInc: // BTST.b #, (An)+
|
||
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) : MicroOp::NoBusProgram);
|
||
if(mode == PostInc) {
|
||
op(byte_inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // BTST.b #, -(An)
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
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) : MicroOp::NoBusProgram);
|
||
break;
|
||
|
||
case XXXw: // BTST.b #, (xxx).w
|
||
case d16An: // BTST.b #, (d16, An)
|
||
case d8AnXn: // BTST.b #, (d8, An, Xn)
|
||
case d16PC: // BTST.b #, (d16, PC)
|
||
case d8PCXn: // BTST.b #, (d8, PC, Xn)
|
||
// PC-relative addressing isn't support for BCLR.
|
||
if((mode == d16PC || mode == d8PCXn) && is_bclr) continue;
|
||
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }, false));
|
||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||
break;
|
||
|
||
case XXXl: // BTST.b #, (xxx).l
|
||
op( int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("np nrd np", { ea(1) }, false));
|
||
op(Action::PerformOperation, is_bclr ? seq("nw", { ea(1) }, false) : MicroOp::NoBusProgram);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
// Decodes the format used by ABCD and SBCD.
|
||
case Decoder::ABCD_SBCD: {
|
||
if(instruction & 8) {
|
||
// [A/S]BCD -(An), -(An)
|
||
program.set_source(storage_, Ind, ea_register);
|
||
program.set_destination(storage_, Ind, data_register);
|
||
|
||
op(MicroOp::SourceMask | dec(ea_register), seq("n nr", { a(ea_register) }, false ));
|
||
op(MicroOp::DestinationMask | dec(data_register), seq("nrd np", { a(data_register) }, false ));
|
||
op(Action::PerformOperation, seq("nw", { a(data_register) }, false));
|
||
} else {
|
||
// [A/S]BCD Dn, Dn
|
||
program.set_source(storage_, Dn, ea_register);
|
||
program.set_destination(storage_, Dn, data_register);
|
||
|
||
op(Action::PerformOperation, seq("np n"));
|
||
}
|
||
} break;
|
||
|
||
case Decoder::ASLR_LSLR_ROLR_ROXLRr: {
|
||
program.set_destination(storage_, 0, ea_register);
|
||
|
||
// All further decoding occurs at runtime; that's also when the proper number of
|
||
// no-op cycles will be scheduled.
|
||
if(((instruction >> 6) & 3) == 2) {
|
||
op(Action::None, seq("np nn")); // Long-word rotates take an extra two cycles.
|
||
} else {
|
||
op(Action::None, seq("np n"));
|
||
}
|
||
|
||
// Use a no-op bus cycle that can have its length filled in later.
|
||
op(Action::PerformOperation, seq("r"));
|
||
} break;
|
||
|
||
case Decoder::ASLR_LSLR_ROLR_ROXLRm: {
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Ind: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (An)
|
||
case PostInc: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (An)+
|
||
op(Action::None, seq("nrd np", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }));
|
||
if(ea_mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w -(An)
|
||
op(int(Action::Decrement2) | MicroOp::DestinationMask, seq("n nrd np", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("nw", { a(ea_register) }));
|
||
break;
|
||
|
||
case XXXl: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (xxx).w
|
||
case d16An: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (d16, An)
|
||
case d8AnXn: // AS(L/R)/LS(L/R)/RO(L/R)/ROX(L/R).w (d8, An, Xn)
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }));
|
||
op(Action::PerformOperation, seq("nw", { ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::CLR_NEG_NEGX_NOT: {
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // [CLR/NEG/NEGX/NOT].bw Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // [CLR/NEG/NEGX/NOT].l Dn
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(Ind): // [CLR/NEG/NEGX/NOT].bw (An)
|
||
case bw(PostInc): // [CLR/NEG/NEGX/NOT].bw (An)+
|
||
op(Action::None, seq("nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
if(ea_mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case l(Ind): // [CLR/NEG/NEGX/NOT].l (An)
|
||
case l(PostInc): // [CLR/NEG/NEGX/NOT].l (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("np nw- nW", { ea(1), ea(1) }));
|
||
if(ea_mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // [CLR/NEG/NEGX/NOT].bw -(An)
|
||
op( dec(ea_register) | MicroOp::DestinationMask,
|
||
seq("nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(PreDec): // [CLR/NEG/NEGX/NOT].l -(An)
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("n nRd+ nrd", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("np nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case bw(XXXl): // [CLR/NEG/NEGX/NOT].bw (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // [CLR/NEG/NEGX/NOT].bw (xxx).w
|
||
case bw(d16An): // [CLR/NEG/NEGX/NOT].bw (d16, An)
|
||
case bw(d8AnXn): // [CLR/NEG/NEGX/NOT].bw (d8, An, Xn)
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // [CLR/NEG/NEGX/NOT].l (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // [CLR/NEG/NEGX/NOT].l (xxx).w
|
||
case l(d16An): // [CLR/NEG/NEGX/NOT].l (d16, An)
|
||
case l(d8AnXn): // [CLR/NEG/NEGX/NOT].l (d8, An, Xn)
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nRd+ nrd", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation, seq("np nw- nW", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::CMP: {
|
||
program.set_destination(storage_, Dn, data_register);
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
|
||
// Byte accesses are not allowed with address registers.
|
||
if(is_byte_access && ea_mode == An) {
|
||
continue;
|
||
}
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // CMP.bw Dn, Dn
|
||
case bw(An): // CMP.w An, Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(Dn): // CMP.l Dn, Dn
|
||
case l(An): // CMP.l An, Dn
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(Ind): // CMP.bw (An), Dn
|
||
case bw(PostInc): // CMP.bw (An)+, Dn
|
||
op(Action::None, seq("nr np", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Ind): // CMP.l (An), Dn
|
||
case l(PostInc): // CMP.l (An)+, Dn
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr np n", { ea(0), ea(0) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(PreDec): // CMP.bw -(An), Dn
|
||
op( dec(ea_register) | MicroOp::SourceMask,
|
||
seq("n nr np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(PreDec): // CMP.l -(An), Dn
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("n nR+ nr np n", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(XXXl): // CMP.bw (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // CMP.bw (xxx).w, Dn
|
||
case bw(d16An): // CMP.bw (d16, An), Dn
|
||
case bw(d8AnXn): // CMP.bw (d8, An, Xn), Dn
|
||
case bw(d16PC): // CMP.bw (d16, PC), Dn
|
||
case bw(d8PCXn): // CMP.bw (d8, PC, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nr np", mode), { ea(0) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(XXXl): // CMP.l (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // CMP.l (xxx).w, Dn
|
||
case l(d16An): // CMP.l (d16, An), Dn
|
||
case l(d8AnXn): // CMP.l (d8, An, Xn), Dn
|
||
case l(d16PC): // CMP.l (d16, PC), Dn
|
||
case l(d8PCXn): // CMP.l (d8, PC, Xn), Dn
|
||
op( address_action_for_mode(mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nR+ nr np n", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(Imm): // CMP.br #, Dn
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::PerformOperation, seq("np np"));
|
||
break;
|
||
|
||
case l(Imm): // CMP.l #, Dn
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("np np n"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::CMPA: {
|
||
// Only operation modes 011 and 111 are accepted, and long words are selected
|
||
// by the top bit.
|
||
if(((op_mode)&3) != 3) continue;
|
||
is_long_word_access = op_mode == 7;
|
||
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, An, data_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register, true);
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // CMPA.w [An/Dn], An
|
||
case l(Dn): // CMPA.l [An/Dn], An
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(Ind): // CMPA.w (An), An
|
||
case bw(PostInc): // CMPA.w (An)+, An
|
||
op(Action::None, seq("nr", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case l(Ind): // CMPA.l (An), An
|
||
case l(PostInc): // CMPA.l (An)+, An
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case bw(PreDec): // CMPA.w -(An), An
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("n nr", { a(ea_register) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case l(PreDec): // CMPA.l -(An), An
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(XXXl): // CMPA.w (xxx).l, An
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // CMPA.w (xxx).w, An
|
||
case bw(d16PC): // CMPA.w (d16, PC), An
|
||
case bw(d8PCXn): // CMPA.w (d8, PC, Xn), An
|
||
case bw(d16An): // CMPA.w (d16, An), An
|
||
case bw(d8AnXn): // CMPA.w (d8, An, Xn), An
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nr", mode), { ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case l(XXXl): // CMPA.l (xxx).l, An
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // CMPA.l (xxx).w, An
|
||
case l(d16PC): // CMPA.l (d16, PC), An
|
||
case l(d8PCXn): // CMPA.l (d8, PC, Xn), An
|
||
case l(d16An): // CMPA.l (d16, An), An
|
||
case l(d8AnXn): // CMPA.l (d8, An, Xn), An
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nR+ nr", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case bw(Imm): // CMPA.w #, An
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::PerformOperation, seq("np np n"));
|
||
break;
|
||
|
||
case l(Imm): // CMPA.l #, An
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("np np n"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::CMPI: {
|
||
if(ea_mode == An) continue;
|
||
|
||
const auto destination_mode = ea_mode;
|
||
const auto destination_register = ea_register;
|
||
|
||
program.set_source(storage_, Imm, 0); // i.e. from the fetched data latch.
|
||
program.set_destination(storage_, destination_mode, destination_register);
|
||
|
||
const int mode = combined_mode(destination_mode, destination_register);
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // CMPI.bw #, Dn
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::PerformOperation, seq("np np"));
|
||
break;
|
||
|
||
case l(Dn): // CMPI.l #, Dn
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation, seq("np np n"));
|
||
break;
|
||
|
||
case bw(Ind): // CMPI.bw #, (An)
|
||
case bw(PostInc): // CMPI.bw #, (An)+
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np nrd np", { a(destination_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(destination_register) | MicroOp::DestinationMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(Ind): // CMPI.l #, (An)
|
||
case l(PostInc): // CMPI.l #, (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np nRd+ nrd np", { ea(1), ea(1) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(PreDec): // CMPI.bw #, -(An)
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np n"));
|
||
op(dec(destination_register) | MicroOp::DestinationMask, seq("nrd np", { a(destination_register) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(PreDec): // CMPI.l #, -(An)
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np n"));
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(XXXw): // CMPI.bw #, (xxx).w
|
||
case bw(d16An): // CMPI.bw #, (d16, An)
|
||
case bw(d8AnXn): // CMPI.bw #, (d8, An, Xn)
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op( address_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nrd np", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(d16An): // CMPI.l #, (d16, An)
|
||
case l(d8AnXn): // CMPI.l #, (d8, An, Xn)
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op( calc_action_for_mode(mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nRd+ nrd np", mode), { ea(1), ea(1) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(XXXw): // CMPI.l #, (xxx).w
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
op(int(Action::AssembleWordAddressFromPrefetch) | MicroOp::DestinationMask, seq("np nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case bw(XXXl): // CMPI.bw #, (xxx).l
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask, seq("np nrd np", { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case l(XXXl): // CMPI.l #, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
op(int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask, seq("np nRd+ nrd np", { ea(1), ea(1) }));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::CMPM: {
|
||
program.set_source(storage_, PostInc, ea_register);
|
||
program.set_destination(storage_, PostInc, data_register);
|
||
|
||
const bool is_byte_operation = operation == Operation::CMPb;
|
||
|
||
switch(operation) {
|
||
default: continue;
|
||
|
||
case Operation::CMPb: // CMPM.b, (An)+, (An)+
|
||
case Operation::CMPw: // CMPM.w, (An)+, (An)+
|
||
op(Action::None, seq("nr", { a(ea_register) }, !is_byte_operation));
|
||
op( inc(ea_register) | MicroOp::SourceMask,
|
||
seq("nrd np", { a(data_register) }, !is_byte_operation));
|
||
op(inc(data_register) | MicroOp::DestinationMask);
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case Operation::CMPl: // CMPM.l, (An)+, (An)+
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("nR+ nr", {ea(0), ea(0)}));
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("nRd+ nrd np", {ea(1), ea(1)}));
|
||
op(int(Action::Increment4) | MicroOp::DestinationMask);
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::Scc_DBcc: {
|
||
if(ea_mode == 1) {
|
||
// This is a DBcc. Decode as such.
|
||
operation = Operation::DBcc;
|
||
program.set_source(storage_, Dn, ea_register);
|
||
|
||
// Jump straight into deciding what steps to take next,
|
||
// which will be selected dynamically.
|
||
op(Action::PerformOperation);
|
||
op();
|
||
} else {
|
||
// This is an Scc.
|
||
|
||
// Scc is implemented on the 68000 a read-modify-write operation.
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
// Scc is always a byte operation.
|
||
is_byte_access = true;
|
||
is_long_word_access = false;
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn:
|
||
op(Action::PerformOperation, seq("np"));
|
||
// TODO: if condition true, an extra n.
|
||
break;
|
||
|
||
case Ind:
|
||
case PostInc:
|
||
op(Action::PerformOperation, seq("nr np nw", { a(ea_register), a(ea_register) }, false));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec:
|
||
op(dec(ea_register) | MicroOp::DestinationMask);
|
||
op(Action::PerformOperation, seq("n nr np nw", { a(ea_register), a(ea_register) }, false));
|
||
break;
|
||
|
||
case XXXl:
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw:
|
||
case d16An:
|
||
case d8AnXn:
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nrd", mode), { ea(1) } , false));
|
||
op(Action::PerformOperation, seq("np nw", { ea(1) } , false));
|
||
break;
|
||
}
|
||
}
|
||
|
||
} break;
|
||
|
||
case Decoder::JSR: {
|
||
// Ensure a proper source register is connected up for (d16, An) and (d8, An, Xn)-type addressing.
|
||
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.set_source(storage_, &storage_.effective_address_[0]);
|
||
|
||
// Beware below: PrepareJSR will pre-emptively subtract four from A7 in order
|
||
// to facilitate the peculiar stack write order of JSR. Therefore any address
|
||
// calculation that might be a function of A7 needs to be done before PrepareJSR.
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
case Ind: // JSR (An)
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask);
|
||
op(Action::PrepareJSR);
|
||
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case XXXl: // JSR (xxx).L
|
||
op(Action::None, seq("np"));
|
||
op(Action::PrepareJSR); // TODO: improve PrepareJSR to be able to compute alternative
|
||
// offsets from the current PC, and thereby move this one slot earlier.
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case XXXw: // JSR (xxx).W
|
||
case d16PC: // JSR (d16, PC)
|
||
case d16An: // JSR (d16, An)
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PrepareJSR);
|
||
op(Action::PerformOperation, seq("n np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case d8PCXn: // JSR (d8, PC, Xn)
|
||
case d8AnXn: // JSR (d8, An, Xn)
|
||
op(calc_action_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PrepareJSR);
|
||
op(Action::PerformOperation, seq("n nn np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::RTS: {
|
||
op(Action::PrepareRTS, seq("nU nu"));
|
||
op(Action::PerformOperation, seq("np np"));
|
||
} break;
|
||
|
||
case Decoder::JMP: {
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
case Ind: // JMP (An)
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask);
|
||
op(Action::PerformOperation, seq("np np"));
|
||
break;
|
||
|
||
case XXXw: // JMP (xxx).W
|
||
case d16PC: // JMP (d16, PC)
|
||
case d16An: // JMP (d16, An)
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PerformOperation, seq("n np np"));
|
||
break;
|
||
|
||
case d8PCXn: // JMP (d8, PC, Xn)
|
||
case d8AnXn: // JMP (d8, An, Xn)
|
||
op(calc_action_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PerformOperation, seq("n nn np np"));
|
||
break;
|
||
|
||
case XXXl: // JMP (xxx).L
|
||
op(Action::None, seq("np"));
|
||
op(address_assemble_for_mode(mode) | MicroOp::SourceMask);
|
||
op(Action::PerformOperation, seq("np np"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::PEA: {
|
||
program.set_source(storage_, An, ea_register);
|
||
program.set_destination(storage_, Imm, 7); // Immediate destination => store to the destination bus latch.
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Ind: // PEA (An)
|
||
operation = Operation::MOVEAl;
|
||
op(Action::PerformOperation);
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask, seq("np nW+ nw", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case XXXl: // PEA (XXX).l
|
||
case XXXw: // PEA (XXX).w
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask, (mode == XXXl) ? seq("np") : MicroOp::NoBusProgram);
|
||
op(address_assemble_for_mode(mode) | MicroOp::SourceMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask);
|
||
op(Action::PerformOperation, seq("np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case d16An: // PEA (d16, An)
|
||
case d16PC: // PEA (d16, PC)
|
||
case d8AnXn: // PEA (d8, An, Xn)
|
||
case d8PCXn: // PEA (d8, PC, Xn)
|
||
op(calc_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np", mode)));
|
||
op(int(Action::Decrement4) | MicroOp::DestinationMask);
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask);
|
||
op(Action::PerformOperation, seq(pseq("np nW+ nw", mode), { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::LEA: {
|
||
program.set_destination(storage_, An, data_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
program.set_source_address(storage_, ea_register);
|
||
program.set_source(storage_,
|
||
(mode == Ind) ?
|
||
&storage_.address_[ea_register] :
|
||
&storage_.effective_address_[0]);
|
||
|
||
switch(mode) {
|
||
default: continue;
|
||
case Ind: // LEA (An), An (i.e. MOVEA)
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case XXXl: // LEA (xxx).L, An
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // LEA (xxx).W, An
|
||
case d16An: // LEA (d16, An), An
|
||
case d16PC: // LEA (d16, PC), An
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq("np np"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case d8AnXn: // LEA (d8, An, Xn), An
|
||
case d8PCXn: // LEA (d8, PC, Xn), An
|
||
op(calc_action_for_mode(mode) | MicroOp::SourceMask, seq("n np n np"));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::MOVEfromSR_NBCD: {
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
is_byte_access = operation == Operation::NBCD;
|
||
is_long_word_access = false;
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // MOVE SR, Dn
|
||
op(Action::PerformOperation, seq("np n"));
|
||
break;
|
||
|
||
case Ind: // MOVE SR, (An)
|
||
case PostInc: // MOVE SR, (An)+
|
||
op(Action::None, seq("nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // MOVE SR, -(An)
|
||
op(dec(ea_register) | MicroOp::DestinationMask, seq("n nrd", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case XXXl: // MOVE SR, (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // MOVE SR, (xxx).w
|
||
case d16An: // MOVE SR, (d16, An)
|
||
case d8AnXn: // MOVE SR, (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np nrd", mode), { ea(1) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np nw", { ea(1) }, !is_byte_access));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::MOVEtoSRCCR: {
|
||
if(ea_mode == An) continue;
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_requires_supervisor(operation == Operation::MOVEtoSR);
|
||
|
||
is_long_word_access = false;
|
||
is_byte_access = false; // Even MOVE, CCR is a .w.
|
||
|
||
/* 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. */
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // MOVE Dn, SR/CCR
|
||
op(Action::PerformOperation, seq("nn np"));
|
||
break;
|
||
|
||
case Ind: // MOVE (An), SR/CCR
|
||
case PostInc: // MOVE (An)+, SR/CCR
|
||
op(Action::None, seq("nr nn nn np", { a(ea_register) }, !is_byte_access));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case PreDec: // MOVE -(An), SR/CCR
|
||
op(dec(ea_register) | MicroOp::SourceMask, seq("n nr nn nn np", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case XXXl: // MOVE (xxx).L, SR/CCR
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // MOVE (xxx).W, SR/CCR
|
||
case d16PC: // MOVE (d16, PC), SR/CCR
|
||
case d8PCXn: // MOVE (d8, PC, Xn), SR/CCR
|
||
case d16An: // MOVE (d16, An), SR/CCR
|
||
case d8AnXn: // MOVE (d8, An, Xn), SR/CCR
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nr nn nn np", mode), { ea(0) }, !is_byte_access));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case Imm: // MOVE #, SR/CCR
|
||
program.set_source(storage_, &storage_.prefetch_queue_);
|
||
op(int(Action::PerformOperation), seq("np nn nn np"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::MOVEq: {
|
||
program.set_destination(storage_, Dn, data_register);
|
||
op(Action::PerformOperation, seq("np"));
|
||
} break;
|
||
|
||
case Decoder::MOVEP: {
|
||
program.set_destination(storage_, An, ea_register);
|
||
program.set_source(storage_, Dn, data_register);
|
||
|
||
switch(operation) {
|
||
default: continue;
|
||
|
||
// Both of the MOVEP to memory instructions perform their operation first — it will
|
||
// break up the source value into 8-bit chunks for the write section.
|
||
case Operation::MOVEPtoMw:
|
||
op(Action::PerformOperation);
|
||
op(int(Action::CalcD16An) | MicroOp::DestinationMask, seq("np nW+ nw np", { ea(1), ea(1) }, false));
|
||
break;
|
||
|
||
case Operation::MOVEPtoMl:
|
||
op(Action::PerformOperation);
|
||
op(int(Action::CalcD16An) | MicroOp::DestinationMask, seq("np nW+ nWr+ nw+ nwr np", { ea(1), ea(1), ea(1), ea(1) }, false));
|
||
break;
|
||
|
||
case Operation::MOVEPtoRw:
|
||
op(int(Action::CalcD16An) | MicroOp::DestinationMask, seq("np nRd+ nrd np", { ea(1), ea(1) }, false));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
|
||
case Operation::MOVEPtoRl:
|
||
// TODO: nR+ increments EA(0), not EA(1). Fix.
|
||
op(int(Action::CalcD16An) | MicroOp::DestinationMask, seq("np nRd+ nR+ nrd+ nr np", { ea(1), ea(1), ea(1), ea(1) }, false));
|
||
op(Action::PerformOperation);
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::MOVEM: {
|
||
// For the sake of commonality, both to R and to M will evaluate their addresses
|
||
// as if they were destinations.
|
||
program.set_destination(storage_, ea_mode, ea_register);
|
||
|
||
// Standard prefix: acquire the register selection flags and fetch the next program
|
||
// word to replace them.
|
||
op(Action::CopyNextWord, seq("np"));
|
||
|
||
// Do whatever is necessary to calculate the proper start address.
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
const bool is_to_m = (operation == Operation::MOVEMtoMl || operation == Operation::MOVEMtoMw);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Ind:
|
||
case PreDec:
|
||
case PostInc: {
|
||
// Deal with the illegal combinations.
|
||
if(mode == PostInc && is_to_m) continue;
|
||
if(mode == PreDec && !is_to_m) continue;
|
||
} break;
|
||
|
||
case XXXl:
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw:
|
||
case d16An:
|
||
case d8AnXn:
|
||
case d16PC:
|
||
case d8PCXn:
|
||
// PC-relative addressing is permitted for moving to registers only.
|
||
if((mode == d16PC || mode == d8PCXn) && is_to_m) continue;
|
||
op(address_action_for_mode(mode) | MicroOp::DestinationMask, seq(pseq("np", mode)));
|
||
break;
|
||
}
|
||
|
||
// Standard suffix: perform the MOVEM, which will mean evaluating the
|
||
// register selection flags and substituting the necessary reads or writes.
|
||
op(Action::PerformOperation);
|
||
|
||
// A final program fetch will cue up the next instruction.
|
||
op(is_to_m ? Action::MOVEMtoMComplete : Action::MOVEMtoRComplete, seq("np"));
|
||
} break;
|
||
|
||
case Decoder::MOVEUSP: {
|
||
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.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.set_destination(storage_, &storage_.stack_pointers_[0]);
|
||
}
|
||
|
||
op(Action::PerformOperation, seq("np"));
|
||
} break;
|
||
|
||
// Decodes the format used by most MOVEs and all MOVEAs.
|
||
case Decoder::MOVE: {
|
||
const int destination_mode = (instruction >> 6) & 7;
|
||
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
program.set_destination(storage_, destination_mode, data_register);
|
||
|
||
// These don't come from the usual place.
|
||
is_byte_access = mapping.operation == Operation::MOVEb;
|
||
is_long_word_access = mapping.operation == Operation::MOVEl;
|
||
|
||
// If the move is to an address register, switch the MOVE to a MOVEA.
|
||
// Also: there are no byte moves to address registers.
|
||
if(destination_mode == An) {
|
||
if(is_byte_access) {
|
||
continue;
|
||
}
|
||
operation = is_long_word_access ? Operation::MOVEAl : Operation::MOVEAw;
|
||
}
|
||
|
||
// ... there are also no byte moves from address registers.
|
||
if(ea_mode == An && is_byte_access) continue;
|
||
|
||
// Perform the MOVE[A]'s fetch..
|
||
const int combined_source_mode = combined_mode(ea_mode, ea_register, true);
|
||
switch(is_long_word_access ? l(combined_source_mode) : bw(combined_source_mode)) {
|
||
default: continue;
|
||
|
||
case l(Dn): // MOVE[A].l [An/Dn], <ea>
|
||
case bw(Dn): // MOVE[A].bw [An/Dn], <ea>
|
||
break;
|
||
|
||
case bw(PreDec): // MOVE[A].bw -(An), <ea>
|
||
op(dec(ea_register) | MicroOp::SourceMask, seq("n nr", { a(ea_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case bw(Ind): // MOVE[A].bw (An), <ea>
|
||
case bw(PostInc): // MOVE[A].bw (An)+, <ea>
|
||
op(Action::None, seq("nr", { a(ea_register) }, !is_byte_access));
|
||
if(combined_source_mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case l(PreDec): // MOVE[A].l -(An), <ea>
|
||
op(dec(ea_register) | MicroOp::SourceMask, seq("n"));
|
||
[[fallthrough]];
|
||
case l(Ind): // MOVE[A].l (An), <ea>
|
||
case l(PostInc): // MOVE[A].l (An)+, <ea>
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask,
|
||
seq("nR+ nr", { ea(0), ea(0) }));
|
||
if(combined_source_mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case bw(XXXl): // MOVE[A].bw (xxx).L, <ea>
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // MOVE[A].bw (xxx).W, <ea>
|
||
case bw(d16An): // MOVE[A].bw (d16, An), <ea>
|
||
case bw(d8AnXn): // MOVE[A].bw (d8, An, Xn), <ea>
|
||
case bw(d16PC): // MOVE[A].bw (d16, PC), <ea>
|
||
case bw(d8PCXn): // MOVE[A].bw (d8, PC, Xn), <ea>
|
||
op( address_action_for_mode(combined_source_mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nr", combined_source_mode), { ea(0) },
|
||
!is_byte_access));
|
||
break;
|
||
|
||
case l(XXXl): // MOVE[A].l (xxx).L, <ea>
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // MOVE[A].l (xxx).W, <ea>
|
||
case l(d16An): // MOVE[A].l (d16, An), <ea>
|
||
case l(d8AnXn): // MOVE[A].l (d8, An, Xn), <ea>
|
||
case l(d16PC): // MOVE[A].l (d16, PC), <ea>
|
||
case l(d8PCXn): // MOVE[A].l (d8, PC, Xn), <ea>
|
||
op( address_action_for_mode(combined_source_mode) | MicroOp::SourceMask,
|
||
seq(pseq("np nR+ nr", combined_source_mode), { ea(0), ea(0) }));
|
||
break;
|
||
|
||
case l(Imm): // MOVE[A].l #, <ea>
|
||
op(Action::None, seq("np"));
|
||
op(int(Action::AssembleLongWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
break;
|
||
|
||
case bw(Imm): // MOVE[A].bw #, <ea>
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np"));
|
||
break;
|
||
}
|
||
|
||
// Perform the MOVE[A]'s store.
|
||
const int combined_destination_mode = combined_mode(destination_mode, data_register, true);
|
||
switch(is_long_word_access ? l(combined_destination_mode) : bw(combined_destination_mode)) {
|
||
default: continue;
|
||
|
||
case l(Dn): // MOVE[A].l <ea>, [An/Dn]
|
||
case bw(Dn): // MOVE[A].bw <ea>, [An/Dn]
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case bw(PreDec): // MOVE[A].bw <ea>, -(An)
|
||
op(Action::PerformOperation);
|
||
op( dec(data_register) | MicroOp::DestinationMask,
|
||
seq("np nw", { a(data_register) }, !is_byte_access));
|
||
break;
|
||
|
||
case bw(Ind): // MOVE[A].bw <ea>, (An)
|
||
case bw(PostInc): // MOVE[A].bw <ea>, (An)+
|
||
op(Action::PerformOperation, seq("nw np", { a(data_register) }, !is_byte_access));
|
||
if(combined_destination_mode == PostInc) {
|
||
op(inc(data_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case l(PreDec): // MOVE[A].l <ea>, -(An)
|
||
op(Action::PerformOperation);
|
||
op(int(Action::Decrement2) | MicroOp::DestinationMask);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("np nw- nW", { ea(1), ea(1) }));
|
||
op(int(Action::Decrement2) | MicroOp::DestinationMask);
|
||
break;
|
||
|
||
case l(Ind): // MOVE[A].l <ea>, (An)
|
||
case l(PostInc): // MOVE[A].l <ea>, (An)+
|
||
op(Action::PerformOperation);
|
||
op( int(Action::CopyToEffectiveAddress) | MicroOp::DestinationMask,
|
||
seq("nW+ nw np", { ea(1), ea(1) }));
|
||
if(combined_destination_mode == PostInc) {
|
||
op(inc(data_register) | MicroOp::DestinationMask);
|
||
}
|
||
break;
|
||
|
||
case bw(XXXw): // MOVE[A].bw <ea>, (xxx).W
|
||
case bw(d16An): // MOVE[A].bw <ea>, (d16, An)
|
||
case bw(d8AnXn): // MOVE[A].bw <ea>, (d8, An, Xn)
|
||
op(Action::PerformOperation);
|
||
op( address_action_for_mode(combined_destination_mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nw np", combined_destination_mode), { ea(1) },
|
||
!is_byte_access));
|
||
break;
|
||
|
||
case l(XXXw): // MOVE[A].l <ea>, (xxx).W
|
||
case l(d16An): // MOVE[A].l <ea>, (d16, An)
|
||
case l(d8AnXn): // MOVE[A].l <ea>, (d8, An, Xn)
|
||
op(Action::PerformOperation);
|
||
op( address_action_for_mode(combined_destination_mode) | MicroOp::DestinationMask,
|
||
seq(pseq("np nW+ nw np", combined_destination_mode), { ea(1), ea(1) }));
|
||
break;
|
||
|
||
case bw(XXXl): // MOVE[A].bw <ea>, (xxx).L
|
||
op(Action::PerformOperation, seq("np"));
|
||
switch(combined_source_mode) { // The pattern here is a function of source and destination.
|
||
case Dn:
|
||
case Imm:
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("np nw np", { ea(1) }, !is_byte_access));
|
||
break;
|
||
|
||
default:
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("nw np np", { ea(1) }, !is_byte_access));
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case l(XXXl): // MOVE[A].l <ea>, (xxx).L
|
||
op(Action::PerformOperation, seq("np"));
|
||
switch(combined_source_mode) { // The pattern here is a function of source and destination.
|
||
case Dn:
|
||
case Imm:
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("np nW+ nw np", { ea(1), ea(1) }));
|
||
break;
|
||
|
||
default:
|
||
op( int(Action::AssembleLongWordAddressFromPrefetch) | MicroOp::DestinationMask,
|
||
seq("nW+ nw np np", { ea(1), ea(1) }));
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
case Decoder::RESET:
|
||
program.set_requires_supervisor(true);
|
||
op(Action::None, seq("nn _ np"));
|
||
break;
|
||
|
||
case Decoder::TRAP: {
|
||
// TRAP involves some oddly-sequenced stack writes, so is calculated
|
||
// at runtime; also the same sequence is used for illegal instructions.
|
||
// So the entirety is scheduled at runtime.
|
||
op(Action::PerformOperation);
|
||
op();
|
||
} break;
|
||
|
||
case Decoder::TRAPV: {
|
||
op(Action::None, seq("np"));
|
||
op(Action::PerformOperation);
|
||
op();
|
||
} break;
|
||
|
||
case Decoder::CHK: {
|
||
program.set_destination(storage_, Dn, data_register);
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(mode) {
|
||
default: continue;
|
||
|
||
case Dn: // CHK Dn, Dn
|
||
op(Action::None, seq("np"));
|
||
break;
|
||
|
||
case Ind: // CHK (An), Dn
|
||
case PostInc: // CHK (An)+, Dn
|
||
op(Action::None, seq("nr np", { a(ea_register) }));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment2) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case PreDec: // CHK -(An), Dn
|
||
op(int(Action::Decrement2) | MicroOp::SourceMask, seq("n nr np", { a(ea_register) }));
|
||
break;
|
||
|
||
case XXXl: // CHK (xxx).l, Dn
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case XXXw: // CHK (xxx).w, Dn
|
||
case d16An: // CHK (d16, An), Dn
|
||
case d16PC: // CHK (d16, PC), Dn
|
||
case d8AnXn: // CHK (d8, An, Xn), Dn
|
||
case d8PCXn: // CHK (d8, PC, Xn), Dn
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nr np", mode), { ea(0) }));
|
||
break;
|
||
|
||
case Imm: // CHK #, Dn
|
||
op(int(Action::AssembleWordDataFromPrefetch) | MicroOp::SourceMask, seq("np np"));
|
||
break;
|
||
}
|
||
|
||
// The nn n here is correct if no exception is issued; otherwise this sequence will
|
||
// be replaced.
|
||
op(Action::PerformOperation, seq("nn n"));
|
||
} break;
|
||
|
||
case Decoder::TST: {
|
||
program.set_source(storage_, ea_mode, ea_register);
|
||
|
||
const int mode = combined_mode(ea_mode, ea_register);
|
||
switch(is_long_word_access ? l(mode) : bw(mode)) {
|
||
default: continue;
|
||
|
||
case bw(Dn): // TST.bw Dn
|
||
case l(Dn): // TST.l Dn
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case bw(PreDec): // TST.bw -(An)
|
||
op(dec(ea_register) | MicroOp::SourceMask, seq("n"));
|
||
[[fallthrough]];
|
||
case bw(Ind): // TST.bw (An)
|
||
case bw(PostInc): // TST.bw (An)+
|
||
op(Action::None, seq("nr", { a(ea_register) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np"));
|
||
if(mode == PostInc) {
|
||
op(inc(ea_register) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case l(PreDec): // TST.l -(An)
|
||
op(int(Action::Decrement4) | MicroOp::SourceMask, seq("n"));
|
||
[[fallthrough]];
|
||
case l(Ind): // TST.l (An)
|
||
case l(PostInc): // TST.l (An)+
|
||
op(int(Action::CopyToEffectiveAddress) | MicroOp::SourceMask, seq("nR+ nr", { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np"));
|
||
if(mode == PostInc) {
|
||
op(int(Action::Increment4) | MicroOp::SourceMask);
|
||
}
|
||
break;
|
||
|
||
case bw(XXXl): // TST.bw (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case bw(XXXw): // TST.bw (xxx).w
|
||
case bw(d16An): // TST.bw (d16, An)
|
||
case bw(d8AnXn): // TST.bw (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nr", mode), { ea(0) }, !is_byte_access));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
|
||
case l(XXXl): // TST.l (xxx).l
|
||
op(Action::None, seq("np"));
|
||
[[fallthrough]];
|
||
case l(XXXw): // TST.l (xxx).w
|
||
case l(d16An): // TST.l (d16, An)
|
||
case l(d8AnXn): // TST.l (d8, An, Xn)
|
||
op(address_action_for_mode(mode) | MicroOp::SourceMask, seq(pseq("np nR+ nr", mode), { ea(0), ea(0) }));
|
||
op(Action::PerformOperation, seq("np"));
|
||
break;
|
||
}
|
||
} break;
|
||
|
||
default:
|
||
std::cerr << "Unhandled decoder " << int(mapping.decoder) << std::endl;
|
||
continue;
|
||
}
|
||
|
||
// Add a terminating micro operation if necessary.
|
||
if(!storage_.all_micro_ops_.back().is_terminal()) {
|
||
storage_.all_micro_ops_.emplace_back();
|
||
}
|
||
|
||
// Ensure that steps that weren't meant to look terminal aren't terminal; also check
|
||
// for improperly encoded address calculation-type actions.
|
||
for(auto index = micro_op_start; index < storage_.all_micro_ops_.size() - 1; ++index) {
|
||
|
||
#ifdef DEBUG
|
||
// All of the actions below must also nominate a source and/or destination.
|
||
switch(storage_.all_micro_ops_[index].action) {
|
||
default: break;
|
||
case int(Action::CalcD16PC):
|
||
case int(Action::CalcD8PCXn):
|
||
case int(Action::CalcD16An):
|
||
case int(Action::CalcD8AnXn):
|
||
case int(Action::AssembleWordAddressFromPrefetch):
|
||
case int(Action::AssembleLongWordAddressFromPrefetch):
|
||
case int(Action::CopyToEffectiveAddress):
|
||
case int(Action::Increment1):
|
||
case int(Action::Increment2):
|
||
case int(Action::Increment4):
|
||
case int(Action::Decrement1):
|
||
case int(Action::Decrement2):
|
||
case int(Action::Decrement4):
|
||
assert(false);
|
||
}
|
||
#endif
|
||
|
||
if(storage_.all_micro_ops_[index].is_terminal()) {
|
||
storage_.all_micro_ops_[index].bus_program = uint16_t(seq(""));
|
||
}
|
||
}
|
||
|
||
// Install the operation and make a note of where micro-ops begin.
|
||
program.operation = operation;
|
||
storage_.instructions[instruction] = program;
|
||
micro_op_pointers[size_t(instruction)] = size_t(micro_op_start);
|
||
|
||
// Don't search further through the list of possibilities, unless this is a debugging build,
|
||
// in which case verify there are no double mappings.
|
||
#ifndef NDEBUG
|
||
++hits;
|
||
assert(hits == 1);
|
||
#else
|
||
break;
|
||
#endif
|
||
}
|
||
}
|
||
|
||
#undef inc
|
||
#undef dec
|
||
}
|
||
|
||
// Throw in the interrupt program.
|
||
const auto interrupt_pointer = storage_.all_micro_ops_.size();
|
||
|
||
// WORKAROUND FOR THE 68000 MAIN LOOP. Hopefully temporary.
|
||
op(Action::None, seq(""));
|
||
|
||
// Perform a single write and then a cycle that will obtain an interrupt vector, or else dictate an autovector or a spurious interrupt.
|
||
op(Action::PrepareINT, seq("n nn nw int", { &storage_.precomputed_addresses_[0] }));
|
||
|
||
// The reset of the standard trap steps occur here; PrepareINT will set them up according to the vector received.
|
||
op(Action::PrepareINTVector, seq("nn n nw nW nV nv np np", { &storage_.precomputed_addresses_[1], &storage_.precomputed_addresses_[2] }));
|
||
|
||
// Terminate the sequence.
|
||
op();
|
||
|
||
#undef Dn
|
||
#undef An
|
||
#undef Ind
|
||
#undef PostDec
|
||
#undef PreDec
|
||
#undef d16An
|
||
#undef d8AnXn
|
||
#undef XXXw
|
||
#undef XXXl
|
||
#undef d16PC
|
||
#undef d8PCXn
|
||
#undef Imm
|
||
|
||
#undef bw
|
||
#undef l
|
||
#undef source_dest
|
||
|
||
#undef ea
|
||
#undef a
|
||
#undef seq
|
||
#undef op
|
||
#undef pseq
|
||
|
||
/*!
|
||
Iterates through the micro-sequence beginning at @c start, finalising bus_program
|
||
pointers that have been transiently stored as relative to @c arbitrary_base.
|
||
*/
|
||
// const auto link_operations = [this](MicroOp *start, BusStep *arbitrary_base) {
|
||
// while(!start->is_terminal()) {
|
||
// const auto offset = size_t(start->bus_program - arbitrary_base);
|
||
// assert(offset >= 0 && offset < storage_.all_bus_steps_.size());
|
||
// start->bus_program = &storage_.all_bus_steps_[offset];
|
||
// ++start;
|
||
// }
|
||
// };
|
||
|
||
// 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 = uint32_t(micro_op_pointers[instruction]);
|
||
// link_operations(&storage_.all_micro_ops_[micro_op_pointers[instruction]], &arbitrary_base);
|
||
}
|
||
}
|
||
|
||
// Link up the interrupt micro ops.
|
||
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 bus steps" << std::endl;
|
||
std::cout << storage_.all_micro_ops_.size() << " total micro ops" << std::endl;
|
||
}
|
||
|
||
private:
|
||
ProcessorStorage &storage_;
|
||
|
||
std::initializer_list<RegisterPair16 *>::const_iterator replace_write_values(BusStep *start, std::initializer_list<RegisterPair16 *>::const_iterator value) {
|
||
while(!start->is_terminal()) {
|
||
// Look for any bus step that writes. Then replace its value, and that of the cycle before it.
|
||
if(start->microcycle.data_select_active() && !(start->microcycle.operation & Microcycle::Read) && !(start->microcycle.operation & Microcycle::InterruptAcknowledge)) {
|
||
start[0].microcycle.value = start[-1].microcycle.value = *value;
|
||
++value;
|
||
}
|
||
|
||
++start;
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
struct BusStepOrderer {
|
||
bool operator()( BusStep const& lhs, BusStep const& rhs ) const {
|
||
int action_diff = int(lhs.action) - int(rhs.action);
|
||
if(action_diff < 0) {
|
||
return true;
|
||
}
|
||
if(action_diff > 0) {
|
||
return false;
|
||
}
|
||
|
||
return
|
||
std::make_tuple(lhs.microcycle.value, lhs.microcycle.address, lhs.microcycle.length, lhs.microcycle.operation) <
|
||
std::make_tuple(rhs.microcycle.value, rhs.microcycle.address, rhs.microcycle.length, rhs.microcycle.operation);
|
||
}
|
||
};
|
||
std::map<BusStep, std::vector<size_t>, BusStepOrderer> locations_by_bus_step_;
|
||
};
|
||
|
||
}
|
||
}
|
||
|
||
CPU::MC68000::ProcessorStorage::ProcessorStorage() {
|
||
ProcessorStorageConstructor constructor(*this);
|
||
|
||
// Create the special programs.
|
||
const size_t reset_offset = constructor.assemble_program("n n n n n nn nF nf nV nv np np");
|
||
|
||
const size_t branch_taken_offset = constructor.assemble_program("n np np");
|
||
const size_t branch_byte_not_taken_offset = constructor.assemble_program("nn np");
|
||
const size_t branch_word_not_taken_offset = constructor.assemble_program("nn np np");
|
||
const size_t bsr_offset = constructor.assemble_program("np np");
|
||
|
||
const size_t dbcc_condition_true_offset = constructor.assemble_program("nn np np");
|
||
const size_t dbcc_condition_false_no_branch_offset = constructor.assemble_program("n nr np np", { &dbcc_false_address_ });
|
||
const size_t dbcc_condition_false_branch_offset = constructor.assemble_program("n np np");
|
||
// That nr in dbcc_condition_false_no_branch_offset is to look like an np from the wrong address.
|
||
|
||
// The reads steps needs to be 32 long-word reads plus an overflow word; the writes just the long words.
|
||
// Addresses and data sources/targets will be filled in at runtime, so anything will do here.
|
||
std::string movem_reads_pattern, movem_writes_pattern;
|
||
std::vector<uint32_t *> addresses;
|
||
for(auto c = 0; c < 64; ++c) {
|
||
movem_reads_pattern += "nr ";
|
||
movem_writes_pattern += "nw ";
|
||
addresses.push_back(nullptr);
|
||
}
|
||
movem_reads_pattern += "nr";
|
||
addresses.push_back(nullptr);
|
||
const size_t movem_read_offset = constructor.assemble_program(movem_reads_pattern.c_str(), addresses);
|
||
const size_t movem_write_offset = constructor.assemble_program(movem_writes_pattern.c_str(), addresses);
|
||
|
||
// Target addresses and values will be filled in by TRAP/illegal too.
|
||
const size_t trap_offset = constructor.assemble_program("r nw nw nW nV nv np np", { &precomputed_addresses_[0], &precomputed_addresses_[1], &precomputed_addresses_[2] });
|
||
const size_t bus_error_offset =
|
||
constructor.assemble_program(
|
||
"nn nw nw nW nw nw nw nW nV nv np np",
|
||
{
|
||
&precomputed_addresses_[0],
|
||
&precomputed_addresses_[1],
|
||
&precomputed_addresses_[2],
|
||
&precomputed_addresses_[3],
|
||
&precomputed_addresses_[4],
|
||
&precomputed_addresses_[5],
|
||
&precomputed_addresses_[6]
|
||
}
|
||
);
|
||
|
||
// Chuck in the proper micro-ops for handling an exception.
|
||
const auto short_exception_offset = all_micro_ops_.size();
|
||
all_micro_ops_.emplace_back(ProcessorBase::MicroOp::Action::None);
|
||
all_micro_ops_.emplace_back();
|
||
|
||
const auto long_exception_offset = all_micro_ops_.size();
|
||
all_micro_ops_.emplace_back(ProcessorBase::MicroOp::Action::None);
|
||
all_micro_ops_.emplace_back();
|
||
|
||
// Install operations.
|
||
//#ifndef NDEBUG
|
||
const std::clock_t start = std::clock();
|
||
//#endif
|
||
constructor.install_instructions();
|
||
//#ifndef NDEBUG
|
||
std::cout << "Construction took " << double(std::clock() - start) / double(CLOCKS_PER_SEC / 1000) << "ms" << std::endl;
|
||
//#endif
|
||
|
||
// Realise the special programs as direct pointers.
|
||
reset_bus_steps_ = &all_bus_steps_[reset_offset];
|
||
|
||
branch_taken_bus_steps_ = &all_bus_steps_[branch_taken_offset];
|
||
branch_byte_not_taken_bus_steps_ = &all_bus_steps_[branch_byte_not_taken_offset];
|
||
branch_word_not_taken_bus_steps_ = &all_bus_steps_[branch_word_not_taken_offset];
|
||
bsr_bus_steps_ = &all_bus_steps_[bsr_offset];
|
||
|
||
dbcc_condition_true_steps_ = &all_bus_steps_[dbcc_condition_true_offset];
|
||
dbcc_condition_false_no_branch_steps_ = &all_bus_steps_[dbcc_condition_false_no_branch_offset];
|
||
dbcc_condition_false_no_branch_steps_[1].microcycle.operation |= Microcycle::IsProgram;
|
||
dbcc_condition_false_no_branch_steps_[2].microcycle.operation |= Microcycle::IsProgram;
|
||
dbcc_condition_false_branch_steps_ = &all_bus_steps_[dbcc_condition_false_branch_offset];
|
||
|
||
movem_read_steps_ = &all_bus_steps_[movem_read_offset];
|
||
movem_write_steps_ = &all_bus_steps_[movem_write_offset];
|
||
|
||
// Link the trap steps but also fill in the program counter as the source
|
||
// for its parts, and use the computed addresses.
|
||
//
|
||
// Order of output is: PC.l, SR, PC.h.
|
||
trap_steps_ = &all_bus_steps_[trap_offset];
|
||
constructor.replace_write_values(trap_steps_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high });
|
||
|
||
// Fill in the same order of writes for the interrupt micro-ops, though it divides the work differently.
|
||
constructor.replace_write_values(interrupt_micro_ops_, { &program_counter_.halves.low, &destination_bus_data_.halves.low, &program_counter_.halves.high });
|
||
|
||
// Link the bus error exception steps and fill in the proper sources.
|
||
bus_error_steps_ = &all_bus_steps_[bus_error_offset];
|
||
constructor.replace_write_values(bus_error_steps_, {
|
||
&program_counter_.halves.low,
|
||
&destination_bus_data_.halves.low,
|
||
&program_counter_.halves.high,
|
||
&decoded_instruction_,
|
||
&effective_address_[1].halves.low,
|
||
&destination_bus_data_.halves.high,
|
||
&effective_address_[1].halves.high
|
||
});
|
||
|
||
// Also relink the RTE and RTR bus steps to collect the program counter.
|
||
//
|
||
// 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 = &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[4].microcycle.value = steps[5].microcycle.value = &program_counter_.halves.low;
|
||
}
|
||
|
||
// Setup the stop cycle.
|
||
stop_cycle_.length = HalfCycles(2);
|
||
|
||
// Complete linkage of the exception micro program.
|
||
short_exception_micro_ops_ = &all_micro_ops_[short_exception_offset];
|
||
short_exception_micro_ops_->bus_program = uint16_t(trap_offset);
|
||
|
||
long_exception_micro_ops_ = &all_micro_ops_[long_exception_offset];
|
||
long_exception_micro_ops_->bus_program = uint16_t(bus_error_offset);
|
||
|
||
// Set initial state.
|
||
active_step_ = reset_bus_steps_;
|
||
effective_address_[0] = 0;
|
||
is_supervisor_ = 1;
|
||
interrupt_level_ = 7;
|
||
address_[7] = 0x00030000;
|
||
}
|
||
|
||
void CPU::MC68000::ProcessorStorage::write_back_stack_pointer() {
|
||
stack_pointers_[is_supervisor_] = address_[7];
|
||
}
|
||
|
||
void CPU::MC68000::ProcessorStorage::set_is_supervisor(bool is_supervisor) {
|
||
const int new_is_supervisor = is_supervisor ? 1 : 0;
|
||
if(new_is_supervisor != is_supervisor_) {
|
||
stack_pointers_[is_supervisor_] = address_[7];
|
||
is_supervisor_ = new_is_supervisor;
|
||
address_[7] = stack_pointers_[is_supervisor_];
|
||
}
|
||
}
|