Created
June 25, 2018 21:03
-
-
Save richtw1/77ae851334a0839ac8ee174f8e4fff24 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (C) 2018 Rich Talbot-Watkins | |
This file is part of VirtualBeeb. | |
VirtualBeeb is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
VirtualBeeb is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with VirtualBeeb. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include <array> | |
#include <cassert> | |
#include <fstream> | |
#include <iomanip> | |
#include <iostream> | |
#include <string> | |
#include <vector> | |
using uint = unsigned int; | |
/** | |
* This represents the next node in a given opcode's graph. | |
* We do not commit to solid state numbers at this point, everything is relative (so common tails compare equally). | |
* So, possible next states are: | |
* none: a sudden finish (not even going to the common dispatch state) | |
* end: going to the common dispatch state | |
* next: the next state in the list | |
* skip: skipping the next and going straight to the one after (e.g. to optimize page crossing) | |
*/ | |
enum class next_node | |
{ | |
none, | |
end, | |
next, | |
skip | |
}; | |
/** | |
* This represents one node of the graph, i.e. a unique state for the 6502. | |
*/ | |
class node | |
{ | |
public: | |
node(std::string&& in_body, std::string&& in_condition, next_node in_true_state, next_node in_false_state) | |
: body(std::move(in_body)), | |
condition(std::move(in_condition)), | |
next_state_if_true(in_true_state), | |
next_state_if_false(in_false_state) | |
{} | |
/** This is the logic for this state */ | |
const std::string& get_body() const { return body; } | |
/** This is a condition which determines whether the true or false state comes next */ | |
const std::string& get_condition() const { return condition; } | |
/** Determines whether this state has a condition or not */ | |
bool has_condition() const { return next_state_if_true != next_state_if_false; } | |
/** Accessors for next state */ | |
next_node get_next_state_if_true() const { return next_state_if_true; } | |
next_node get_next_state_if_false() const { return next_state_if_false; } | |
next_node get_next_state() const | |
{ | |
assert(!has_condition()); | |
return next_state_if_true; | |
} | |
friend bool operator==(const node& a, const node& b) | |
{ | |
return a.body == b.body && | |
a.condition == b.condition && | |
a.next_state_if_true == b.next_state_if_true && | |
a.next_state_if_false == b.next_state_if_false; | |
} | |
private: | |
std::string body; | |
std::string condition; | |
next_node next_state_if_true; | |
next_node next_state_if_false; | |
}; | |
/** | |
* This is a container for all 6502 states. | |
* | |
* We have 256 start states, corresponding to each opcode. | |
* We should have a common final state, which is the one which dispatches the new opcode just fetched. | |
* | |
* Hence, simplistically, we have 256 graphs which all terminate in a common node. | |
* However, we optimize the graph before rendering it, so that any common graph tails are coalesced, | |
* in order to reduce the total number of states we need to define. | |
*/ | |
class state_machine | |
{ | |
public: | |
state_machine() | |
{} | |
static const uint num_opcodes = 0x100; | |
static const uint end_state = num_opcodes; // Hardwire a special state 0x100 for 'dispatch' | |
static const uint num_start_states = num_opcodes + 1; | |
static const uint first_new_state = num_start_states; | |
// A bunch of different ways of adding a state to the state machine | |
void add(uint start, std::string&& body, next_node next_state) | |
{ | |
graphs[start].emplace_back(std::move(body), "", next_state, next_state); | |
} | |
void add(uint start, std::string&& body, std::string&& cond, next_node next_state_if_true, next_node next_state_if_false) | |
{ | |
graphs[start].emplace_back(std::move(body), std::move(cond), next_state_if_true, next_state_if_false); | |
} | |
void render(std::ofstream& file) const; | |
private: | |
std::array<std::vector<node>, num_start_states> graphs; | |
}; | |
/** | |
* Given a next_node value, an array of concrete state numbers for this subgraph, and the index of the current state within that array, | |
* yield a concrete state number for the next state. | |
*/ | |
static uint calculate_state(const next_node next, const std::vector<uint>& state_numbers, const std::size_t index) | |
{ | |
if (next == next_node::end) | |
{ | |
return state_machine::end_state; | |
} | |
else if (next == next_node::next) | |
{ | |
return state_numbers[index + 1]; | |
} | |
else if (next == next_node::skip) | |
{ | |
return state_numbers[index + 2]; | |
} | |
else | |
{ | |
assert(false); | |
return 0; | |
} | |
} | |
/** | |
* Given two vectors, returns the number of elements at the end which are equal. | |
* @todo This is just std::mismatch, should really use that instead. | |
*/ | |
static std::size_t get_common_tail_length(const std::vector<node>& g1, const std::vector<node>& g2) | |
{ | |
std::size_t common_length = 0; | |
std::size_t i1 = g1.size() - 1; | |
std::size_t i2 = g2.size() - 1; | |
while (i1 > 0 && i2 > 0 && g1[i1] == g2[i2]) | |
{ | |
++common_length; | |
--i1; | |
--i2; | |
} | |
return common_length; | |
} | |
/** | |
* At this point we have an array of graphs, one per opcode, representing their state transitions. | |
* Here we merge the tails of each graph with common endings to reduce the total number of nodes, | |
* then render out the state machine to the given file. | |
*/ | |
void state_machine::render(std::ofstream& file) const | |
{ | |
std::array<std::vector<uint>, num_start_states> state_numbers; | |
uint next_state = first_new_state; | |
for (std::size_t i = 0; i < num_start_states; ++i) | |
{ | |
const std::vector<node>& graph = graphs[i]; | |
std::vector<uint>& graph_state_numbers = state_numbers[i]; | |
// Find largest shared common tail | |
std::size_t best_graph = i; | |
std::size_t best_length = 0; | |
for (std::size_t j = 0; j < i; ++j) | |
{ | |
const std::vector<node>& graph_to_test = graphs[j]; | |
const std::size_t common_length = get_common_tail_length(graph, graphs[j]); | |
if (common_length > best_length) | |
{ | |
best_length = common_length; | |
best_graph = j; | |
} | |
} | |
// Set state numbers for each graph node: | |
// first state is always equal to the start state index | |
graph_state_numbers.push_back(uint(i)); | |
// next add all the new nodes we need to create | |
const std::size_t num_new_nodes = graph.size() - best_length; | |
for (std::size_t j = 1; j < num_new_nodes; ++j) | |
{ | |
graph_state_numbers.push_back(next_state++); | |
} | |
// last add all the nodes we're sharing from a previous graph tail | |
for (std::size_t j = 0; j < best_length; ++j) | |
{ | |
assert(best_graph < i); | |
const std::size_t best_graph_length = graphs[best_graph].size(); | |
graph_state_numbers.push_back(state_numbers[best_graph][best_graph_length - best_length + j]); | |
} | |
assert(graph.size() == graph_state_numbers.size()); | |
// Render the new nodes | |
for (std::size_t j = 0; j < num_new_nodes; ++j) | |
{ | |
const node& graph_node = graph[j]; | |
file << "\t\tcase 0x" << std::setw(2) << graph_state_numbers[j] << ":\t"; | |
file << graph_node.get_body(); | |
if (graph_node.has_condition()) | |
{ | |
const uint next_if_true = calculate_state(graph_node.get_next_state_if_true(), graph_state_numbers, j); | |
const uint next_if_false = calculate_state(graph_node.get_next_state_if_false(), graph_state_numbers, j); | |
file << "state = (" << graph_node.get_condition() << ") ? 0x" << std::setw(2) << next_if_true << " : 0x" << std::setw(2) << next_if_false << "; return;" << std::endl; | |
} | |
else | |
{ | |
if (graph_node.get_next_state() == next_node::none) | |
{ | |
file << "return;" << std::endl; | |
} | |
else | |
{ | |
const uint next = calculate_state(graph_node.get_next_state(), graph_state_numbers, j); | |
file << "state = 0x" << std::setw(2) << next << "; return;" << std::endl; | |
} | |
} | |
} | |
} | |
} | |
/** | |
* This is a dummy definition class with all opcodes set to undefined. | |
* The idea is we can derive from this, and any holes in the opcode set will be caught by this. | |
*/ | |
template <typename Policy> | |
struct opcode_defs_null | |
{ | |
#define DEF(a, b) using opcode##a##b = typename Policy::undefined | |
#define DEFS(a) DEF(a, 0); DEF(a, 1); DEF(a, 2); DEF(a, 3); DEF(a, 4); DEF(a, 5); DEF(a, 6); DEF(a, 7); DEF(a, 8); DEF(a, 9); DEF(a, A); DEF(a, B); DEF(a, C); DEF(a, D); DEF(a, E); DEF(a, F) | |
DEFS(0); DEFS(1); DEFS(2); DEFS(3); DEFS(4); DEFS(5); DEFS(6); DEFS(7); DEFS(8); DEFS(9); DEFS(A); DEFS(B); DEFS(C); DEFS(D); DEFS(E); DEFS(F); | |
#undef DEFS | |
#undef DEF | |
}; | |
/** | |
* This defines the common opcode set for all 6502s, based on the documented NMOS 6502 instructions. | |
*/ | |
template <typename Policy> | |
struct opcode_defs_6502_generic : public opcode_defs_null<Policy> | |
{ | |
// Dispatches the next instruction | |
using dispatch = typename Policy::dispatch; | |
// ADC | |
using opcode69 = typename Policy::template read_imm<typename Policy::op_adc>; | |
using opcode65 = typename Policy::template read_zp<typename Policy::op_adc>; | |
using opcode75 = typename Policy::template read_zp_x<typename Policy::op_adc>; | |
using opcode6D = typename Policy::template read_abs<typename Policy::op_adc>; | |
using opcode7D = typename Policy::template read_abs_x<typename Policy::op_adc>; | |
using opcode79 = typename Policy::template read_abs_y<typename Policy::op_adc>; | |
using opcode61 = typename Policy::template read_ind_x<typename Policy::op_adc>; | |
using opcode71 = typename Policy::template read_ind_y<typename Policy::op_adc>; | |
// AND | |
using opcode29 = typename Policy::template read_imm<typename Policy::op_and>; | |
using opcode25 = typename Policy::template read_zp<typename Policy::op_and>; | |
using opcode35 = typename Policy::template read_zp_x<typename Policy::op_and>; | |
using opcode2D = typename Policy::template read_abs<typename Policy::op_and>; | |
using opcode3D = typename Policy::template read_abs_x<typename Policy::op_and>; | |
using opcode39 = typename Policy::template read_abs_y<typename Policy::op_and>; | |
using opcode21 = typename Policy::template read_ind_x<typename Policy::op_and>; | |
using opcode31 = typename Policy::template read_ind_y<typename Policy::op_and>; | |
// ASL | |
using opcode0A = typename Policy::template acc<typename Policy::op_asl>; | |
using opcode06 = typename Policy::template rmw_zp<typename Policy::op_asl>; | |
using opcode16 = typename Policy::template rmw_zp_x<typename Policy::op_asl>; | |
using opcode0E = typename Policy::template rmw_abs<typename Policy::op_asl>; | |
using opcode1E = typename Policy::template rmw_abs_x<typename Policy::op_asl>; | |
// BIT | |
using opcode24 = typename Policy::template read_zp<typename Policy::op_bit>; | |
using opcode2C = typename Policy::template read_abs<typename Policy::op_bit>; | |
// Branches | |
using opcode10 = typename Policy::template branch<typename Policy::op_bpl>; | |
using opcode30 = typename Policy::template branch<typename Policy::op_bmi>; | |
using opcode50 = typename Policy::template branch<typename Policy::op_bvc>; | |
using opcode70 = typename Policy::template branch<typename Policy::op_bvs>; | |
using opcode90 = typename Policy::template branch<typename Policy::op_bcc>; | |
using opcodeB0 = typename Policy::template branch<typename Policy::op_bcs>; | |
using opcodeD0 = typename Policy::template branch<typename Policy::op_bne>; | |
using opcodeF0 = typename Policy::template branch<typename Policy::op_beq>; | |
// BRK / Interrupts | |
using opcode00 = typename Policy::template brk<typename Policy::op_brk>; | |
// CMP | |
using opcodeC9 = typename Policy::template read_imm<typename Policy::op_cmp>; | |
using opcodeC5 = typename Policy::template read_zp<typename Policy::op_cmp>; | |
using opcodeD5 = typename Policy::template read_zp_x<typename Policy::op_cmp>; | |
using opcodeCD = typename Policy::template read_abs<typename Policy::op_cmp>; | |
using opcodeDD = typename Policy::template read_abs_x<typename Policy::op_cmp>; | |
using opcodeD9 = typename Policy::template read_abs_y<typename Policy::op_cmp>; | |
using opcodeC1 = typename Policy::template read_ind_x<typename Policy::op_cmp>; | |
using opcodeD1 = typename Policy::template read_ind_y<typename Policy::op_cmp>; | |
// CPX | |
using opcodeE0 = typename Policy::template read_imm<typename Policy::op_cpx>; | |
using opcodeE4 = typename Policy::template read_zp<typename Policy::op_cpx>; | |
using opcodeEC = typename Policy::template read_abs<typename Policy::op_cpx>; | |
// CPY | |
using opcodeC0 = typename Policy::template read_imm<typename Policy::op_cpy>; | |
using opcodeC4 = typename Policy::template read_zp<typename Policy::op_cpy>; | |
using opcodeCC = typename Policy::template read_abs<typename Policy::op_cpy>; | |
// DEC | |
using opcodeC6 = typename Policy::template rmw_zp<typename Policy::op_dec>; | |
using opcodeD6 = typename Policy::template rmw_zp_x<typename Policy::op_dec>; | |
using opcodeCE = typename Policy::template rmw_abs<typename Policy::op_dec>; | |
using opcodeDE = typename Policy::template rmw_abs_x<typename Policy::op_dec>; | |
// DEX | |
using opcodeCA = typename Policy::template imp<typename Policy::op_dex>; | |
// DEY | |
using opcode88 = typename Policy::template imp<typename Policy::op_dey>; | |
// EOR | |
using opcode49 = typename Policy::template read_imm<typename Policy::op_eor>; | |
using opcode45 = typename Policy::template read_zp<typename Policy::op_eor>; | |
using opcode55 = typename Policy::template read_zp_x<typename Policy::op_eor>; | |
using opcode4D = typename Policy::template read_abs<typename Policy::op_eor>; | |
using opcode5D = typename Policy::template read_abs_x<typename Policy::op_eor>; | |
using opcode59 = typename Policy::template read_abs_y<typename Policy::op_eor>; | |
using opcode41 = typename Policy::template read_ind_x<typename Policy::op_eor>; | |
using opcode51 = typename Policy::template read_ind_y<typename Policy::op_eor>; | |
// Flags | |
using opcode18 = typename Policy::template imp<typename Policy::op_clc>; | |
using opcode38 = typename Policy::template imp<typename Policy::op_sec>; | |
using opcode58 = typename Policy::template imp<typename Policy::op_cli>; | |
using opcode78 = typename Policy::template imp<typename Policy::op_sei>; | |
using opcodeB8 = typename Policy::template imp<typename Policy::op_clv>; | |
using opcodeD8 = typename Policy::template imp<typename Policy::op_cld>; | |
using opcodeF8 = typename Policy::template imp<typename Policy::op_sed>; | |
// INC | |
using opcodeE6 = typename Policy::template rmw_zp<typename Policy::op_inc>; | |
using opcodeF6 = typename Policy::template rmw_zp_x<typename Policy::op_inc>; | |
using opcodeEE = typename Policy::template rmw_abs<typename Policy::op_inc>; | |
using opcodeFE = typename Policy::template rmw_abs_x<typename Policy::op_inc>; | |
// INX | |
using opcodeE8 = typename Policy::template imp<typename Policy::op_inx>; | |
// INY | |
using opcodeC8 = typename Policy::template imp<typename Policy::op_iny>; | |
// JMP | |
using opcode4C = typename Policy::template jmp_abs<typename Policy::op_jmp>; | |
using opcode6C = typename Policy::template jmp_ind<typename Policy::op_jmp>; | |
// JSR | |
using opcode20 = typename Policy::template jsr_abs<typename Policy::op_jsr>; | |
// LDA | |
using opcodeA9 = typename Policy::template read_imm<typename Policy::op_lda>; | |
using opcodeA5 = typename Policy::template read_zp<typename Policy::op_lda>; | |
using opcodeB5 = typename Policy::template read_zp_x<typename Policy::op_lda>; | |
using opcodeAD = typename Policy::template read_abs<typename Policy::op_lda>; | |
using opcodeBD = typename Policy::template read_abs_x<typename Policy::op_lda>; | |
using opcodeB9 = typename Policy::template read_abs_y<typename Policy::op_lda>; | |
using opcodeA1 = typename Policy::template read_ind_x<typename Policy::op_lda>; | |
using opcodeB1 = typename Policy::template read_ind_y<typename Policy::op_lda>; | |
// LDX | |
using opcodeA2 = typename Policy::template read_imm<typename Policy::op_ldx>; | |
using opcodeA6 = typename Policy::template read_zp<typename Policy::op_ldx>; | |
using opcodeB6 = typename Policy::template read_zp_y<typename Policy::op_ldx>; | |
using opcodeAE = typename Policy::template read_abs<typename Policy::op_ldx>; | |
using opcodeBE = typename Policy::template read_abs_y<typename Policy::op_ldx>; | |
// LDY | |
using opcodeA0 = typename Policy::template read_imm<typename Policy::op_ldy>; | |
using opcodeA4 = typename Policy::template read_zp<typename Policy::op_ldy>; | |
using opcodeB4 = typename Policy::template read_zp_x<typename Policy::op_ldy>; | |
using opcodeAC = typename Policy::template read_abs<typename Policy::op_ldy>; | |
using opcodeBC = typename Policy::template read_abs_x<typename Policy::op_ldy>; | |
// LSR | |
using opcode4A = typename Policy::template acc<typename Policy::op_lsr>; | |
using opcode46 = typename Policy::template rmw_zp<typename Policy::op_lsr>; | |
using opcode56 = typename Policy::template rmw_zp_x<typename Policy::op_lsr>; | |
using opcode4E = typename Policy::template rmw_abs<typename Policy::op_lsr>; | |
using opcode5E = typename Policy::template rmw_abs_x<typename Policy::op_lsr>; | |
// NOP | |
using opcodeEA = typename Policy::template imp<typename Policy::op_nop>; | |
// ORA | |
using opcode09 = typename Policy::template read_imm<typename Policy::op_ora>; | |
using opcode05 = typename Policy::template read_zp<typename Policy::op_ora>; | |
using opcode15 = typename Policy::template read_zp_x<typename Policy::op_ora>; | |
using opcode0D = typename Policy::template read_abs<typename Policy::op_ora>; | |
using opcode1D = typename Policy::template read_abs_x<typename Policy::op_ora>; | |
using opcode19 = typename Policy::template read_abs_y<typename Policy::op_ora>; | |
using opcode01 = typename Policy::template read_ind_x<typename Policy::op_ora>; | |
using opcode11 = typename Policy::template read_ind_y<typename Policy::op_ora>; | |
// Push | |
using opcode48 = typename Policy::template push<typename Policy::op_pha>; | |
using opcode08 = typename Policy::template push<typename Policy::op_php>; | |
// Pull | |
using opcode68 = typename Policy::template pull<typename Policy::op_pla>; | |
using opcode28 = typename Policy::template pull<typename Policy::op_plp>; | |
// ROL | |
using opcode2A = typename Policy::template acc<typename Policy::op_rol>; | |
using opcode26 = typename Policy::template rmw_zp<typename Policy::op_rol>; | |
using opcode36 = typename Policy::template rmw_zp_x<typename Policy::op_rol>; | |
using opcode2E = typename Policy::template rmw_abs<typename Policy::op_rol>; | |
using opcode3E = typename Policy::template rmw_abs_x<typename Policy::op_rol>; | |
// ROR | |
using opcode6A = typename Policy::template acc<typename Policy::op_ror>; | |
using opcode66 = typename Policy::template rmw_zp<typename Policy::op_ror>; | |
using opcode76 = typename Policy::template rmw_zp_x<typename Policy::op_ror>; | |
using opcode6E = typename Policy::template rmw_abs<typename Policy::op_ror>; | |
using opcode7E = typename Policy::template rmw_abs_x<typename Policy::op_ror>; | |
// RTI | |
using opcode40 = typename Policy::template rti<typename Policy::op_rti>; | |
// RTS | |
using opcode60 = typename Policy::template rts<typename Policy::op_rts>; | |
// SBC | |
using opcodeE9 = typename Policy::template read_imm<typename Policy::op_sbc>; | |
using opcodeE5 = typename Policy::template read_zp<typename Policy::op_sbc>; | |
using opcodeF5 = typename Policy::template read_zp_x<typename Policy::op_sbc>; | |
using opcodeED = typename Policy::template read_abs<typename Policy::op_sbc>; | |
using opcodeFD = typename Policy::template read_abs_x<typename Policy::op_sbc>; | |
using opcodeF9 = typename Policy::template read_abs_y<typename Policy::op_sbc>; | |
using opcodeE1 = typename Policy::template read_ind_x<typename Policy::op_sbc>; | |
using opcodeF1 = typename Policy::template read_ind_y<typename Policy::op_sbc>; | |
// STA | |
using opcode85 = typename Policy::template write_zp<typename Policy::op_sta>; | |
using opcode95 = typename Policy::template write_zp_x<typename Policy::op_sta>; | |
using opcode8D = typename Policy::template write_abs<typename Policy::op_sta>; | |
using opcode9D = typename Policy::template write_abs_x<typename Policy::op_sta>; | |
using opcode99 = typename Policy::template write_abs_y<typename Policy::op_sta>; | |
using opcode81 = typename Policy::template write_ind_x<typename Policy::op_sta>; | |
using opcode91 = typename Policy::template write_ind_y<typename Policy::op_sta>; | |
// STX | |
using opcode86 = typename Policy::template write_zp<typename Policy::op_stx>; | |
using opcode96 = typename Policy::template write_zp_y<typename Policy::op_stx>; | |
using opcode8E = typename Policy::template write_abs<typename Policy::op_stx>; | |
// STY | |
using opcode84 = typename Policy::template write_zp<typename Policy::op_sty>; | |
using opcode94 = typename Policy::template write_zp_x<typename Policy::op_sty>; | |
using opcode8C = typename Policy::template write_abs<typename Policy::op_sty>; | |
// Transfer | |
using opcode8A = typename Policy::template imp<typename Policy::op_txa>; | |
using opcode98 = typename Policy::template imp<typename Policy::op_tya>; | |
using opcode9A = typename Policy::template imp<typename Policy::op_txs>; | |
using opcodeA8 = typename Policy::template imp<typename Policy::op_tay>; | |
using opcodeAA = typename Policy::template imp<typename Policy::op_tax>; | |
using opcodeBA = typename Policy::template imp<typename Policy::op_tsx>; | |
}; | |
using namespace std::string_literals; | |
/** | |
* This defines a generic policy for 6502 opcodes, based on the NMOS logic. | |
*/ | |
struct opcode_policy_6502_generic | |
{ | |
struct undefined | |
{ | |
static const char* opcode_name() { return "???"; } | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "assert(false); "s, next_node::none); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct imp : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "check_irq(); host.read(ab); "s + op(), next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct read_imm : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "check_irq(); "s + op() + "ab = ++pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "#b"; } | |
}; | |
template <typename Op> | |
struct read_zp : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "cached_irq = irq; ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b"; } | |
}; | |
template <typename Op> | |
struct read_zp_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b,X"; } | |
}; | |
template <typename Op> | |
struct read_zp_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read_zp(ab); ab = (ab + y) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b,Y"; } | |
}; | |
template <typename Op> | |
struct read_abs : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read(ab) << 8) | temp; ++pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w"; } | |
}; | |
template <typename Op> | |
struct read_abs_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read(ab) << 8) | ((temp + x) & 0xFFU); ++pc; "s, "temp + x < 0x100U"s, next_node::skip, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); ab += 0x100U; ", next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,X"; } | |
}; | |
template <typename Op> | |
struct read_abs_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read(ab) << 8) | ((temp + y) & 0xFFU); ++pc; "s, "temp + y < 0x100U"s, next_node::skip, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); ab += 0x100U; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,Y"; } | |
}; | |
template <typename Op> | |
struct read_ind_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read_zp(ab) << 8) | temp; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b,X)"; } | |
}; | |
template <typename Op> | |
struct read_ind_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1U) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read_zp(ab) << 8) | ((temp + y) & 0xFFU); "s, "temp + y < 0x100U"s, next_node::skip, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); ab += 0x100U; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b),Y"; } | |
}; | |
template <typename Op> | |
struct acc : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "check_irq(); host.read(ab); "s + op(), next_node::end); | |
} | |
static const char* operand_text() { return "A"; } | |
}; | |
template <typename Op> | |
struct rmw_zp : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b"; } | |
}; | |
template <typename Op> | |
struct rmw_zp_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b,X"; } | |
}; | |
template <typename Op> | |
struct rmw_abs : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | temp; ++pc; "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w"; } | |
}; | |
template <typename Op> | |
struct rmw_abs_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | ((temp + x) & 0xFFU); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read(ab); if (temp + x >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,X"; } | |
}; | |
template <typename Op> | |
struct write_zp : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "cached_irq = irq; ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b"; } | |
}; | |
template <typename Op> | |
struct write_zp_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b,X"; } | |
}; | |
template <typename Op> | |
struct write_zp_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read_zp(ab); ab = (ab + y) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "b,Y"; } | |
}; | |
template <typename Op> | |
struct write_abs : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read(ab) << 8) | temp; ++pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w"; } | |
}; | |
template <typename Op> | |
struct write_abs_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | ((temp + x) & 0xFFU); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); if (temp + x >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,X"; } | |
}; | |
template <typename Op> | |
struct write_abs_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | ((temp + y) & 0xFFU); ++pc; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); if (temp + y >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,Y"; } | |
}; | |
template <typename Op> | |
struct write_ind_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; ab = (host.read_zp(ab) << 8) | temp; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b,X)"; } | |
}; | |
template <typename Op> | |
struct write_ind_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1U) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "ab = (host.read_zp(ab) << 8) | ((temp + y) & 0xFFU); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read(ab); if (temp + y >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b),Y"; } | |
}; | |
template <typename Op> | |
struct branch : Op | |
{ | |
using Op::opcode_name; | |
using Op::cond; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "check_irq(); temp = host.read(ab); ab = ++pc; "s, cond(), next_node::next, next_node::end); | |
states.add(opcode, "cached_irq = irq; host.read(ab); take_branch(); ab = pc; "s, "temp", next_node::next, next_node::end); | |
states.add(opcode, "check_irq(); host.read(ab); pc += (temp << 8); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "r"; } | |
}; | |
template <typename Op> | |
struct push : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "cached_irq = irq; host.read(ab); ab = s | 0x100U; --s; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct pull : Op | |
{ | |
using Op::opcode_name; | |
using Op::op; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "host.read(ab); ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.read_stack(ab); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "check_irq(); "s + op() + "ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct jmp_abs : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "cached_irq = irq; temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); pc = (host.read(ab) << 8) | temp; ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w"; } | |
}; | |
template <typename Op> | |
struct jmp_ind : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | temp; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; temp = host.read(ab); ab = (ab & 0xFF00U) | ((ab + 1) & 0xFFU); "s, next_node::next); | |
states.add(opcode, "check_irq(); pc = (host.read(ab) << 8) | temp; ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(w)"; } | |
}; | |
template <typename Op> | |
struct jsr_abs : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ++pc; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "host.read_stack(ab); --s; "s, next_node::next); | |
states.add(opcode, "host.write_stack(ab, pc >> 8); ab = s | 0x100U; --s; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write_stack(ab, pc & 0xFFU); ab = pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); pc = (host.read(ab) << 8) | temp; ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w"; } | |
}; | |
template <typename Op> | |
struct rts : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "host.read(ab); ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "host.read_stack(ab); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "temp = host.read_stack(ab); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; pc = (host.read_stack(ab) << 8) | temp; ab = pc; "s, next_node::next); | |
states.add(opcode, "check_irq(); host.read(ab); ab = ++pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct rti : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "host.read(ab); ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "host.read_stack(ab); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "p_to_flags(host.read_stack(ab)); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; temp = host.read_stack(ab); ++s; ab = s | 0x100U; "s, next_node::next); | |
states.add(opcode, "check_irq(); pc = (host.read_stack(ab) << 8) | temp; ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct brk : Op | |
{ | |
using Op::opcode_name; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "host.read(ab); pc += !interrupt; ab = s | 0x100U; --s; "s, next_node::next); | |
states.add(opcode, "if (!(interrupt & pending_reset)) { host.write_stack(ab, pc >> 8); } ab = s | 0x100U; --s; "s, next_node::next); | |
states.add(opcode, "if (!(interrupt & pending_reset)) { host.write_stack(ab, pc & 0xFFU); } ab = s | 0x100U; --s; "s, next_node::next); | |
states.add(opcode, "if (!(interrupt & pending_reset)) { host.write_stack(ab, flags_to_p(!interrupt)); } ab = get_interrupt_addr(); interrupt = 0U; "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); ab++; i = 1; "s, next_node::next); | |
states.add(opcode, "pc = (host.read(ab) << 8) | temp; ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
struct dispatch | |
{ | |
static void generate(state_machine& states, uint opcode) | |
{ | |
// Try to do this branchlessly: if there's a pending hardware interrupt, interrupt != 0. | |
// So either set state to the opcode just read, or to 0. Likewise, only inc pc if not an interrupt. | |
// Cache the current irq state here, for 2 cycle instructions which need to read it in their first t-state. | |
states.add(opcode, "cached_irq = irq; state = host.read(ab) * !interrupt; pc += !interrupt; ab = pc; ", next_node::none); | |
} | |
}; | |
struct op_adc | |
{ | |
static const char* opcode_name() { return "ADC"; } | |
static const char* op() { return "do_adc(host.read(ab)); "; } | |
}; | |
struct op_and | |
{ | |
static const char* opcode_name() { return "AND"; } | |
static const char* op() { return "do_and(host.read(ab)); "; } | |
}; | |
struct op_asl | |
{ | |
static const char* opcode_name() { return "ASL"; } | |
static const char* op() { return "do_asl(a); "; } | |
static const char* op_rmw() { return "do_asl(temp); "; } | |
}; | |
struct op_bit | |
{ | |
static const char* opcode_name() { return "BIT"; } | |
static const char* op() { return "do_bit(host.read(ab)); "; } | |
}; | |
struct op_bpl | |
{ | |
static const char* opcode_name() { return "BPL"; } | |
static const char* cond() { return "!n"; } | |
}; | |
struct op_bmi | |
{ | |
static const char* opcode_name() { return "BMI"; } | |
static const char* cond() { return "n"; } | |
}; | |
struct op_bvc | |
{ | |
static const char* opcode_name() { return "BVC"; } | |
static const char* cond() { return "!v"; } | |
}; | |
struct op_bvs | |
{ | |
static const char* opcode_name() { return "BVS"; } | |
static const char* cond() { return "v"; } | |
}; | |
struct op_bcc | |
{ | |
static const char* opcode_name() { return "BCC"; } | |
static const char* cond() { return "!c"; } | |
}; | |
struct op_bcs | |
{ | |
static const char* opcode_name() { return "BCS"; } | |
static const char* cond() { return "c"; } | |
}; | |
struct op_bne | |
{ | |
static const char* opcode_name() { return "BNE"; } | |
static const char* cond() { return "!z"; } | |
}; | |
struct op_beq | |
{ | |
static const char* opcode_name() { return "BEQ"; } | |
static const char* cond() { return "z"; } | |
}; | |
struct op_brk | |
{ | |
static const char* opcode_name() { return "BRK"; } | |
}; | |
struct op_clc | |
{ | |
static const char* opcode_name() { return "CLC"; } | |
static const char* op() { return "c = 0; "; } | |
}; | |
struct op_cli | |
{ | |
static const char* opcode_name() { return "CLI"; } | |
static const char* op() { return "i = 0; "; } | |
}; | |
struct op_clv | |
{ | |
static const char* opcode_name() { return "CLV"; } | |
static const char* op() { return "v = 0; "; } | |
}; | |
struct op_cld | |
{ | |
static const char* opcode_name() { return "CLD"; } | |
static const char* op() { return "d = 0; "; } | |
}; | |
struct op_cmp | |
{ | |
static const char* opcode_name() { return "CMP"; } | |
static const char* op() { return "do_compare(a, host.read(ab)); "; } | |
}; | |
struct op_cpx | |
{ | |
static const char* opcode_name() { return "CPX"; } | |
static const char* op() { return "do_compare(x, host.read(ab)); "; } | |
}; | |
struct op_cpy | |
{ | |
static const char* opcode_name() { return "CPY"; } | |
static const char* op() { return "do_compare(y, host.read(ab)); "; } | |
}; | |
struct op_dec | |
{ | |
static const char* opcode_name() { return "DEC"; } | |
static const char* op() { return "do_dec(a); "; } | |
static const char* op_rmw() { return "do_dec(temp); "; } | |
}; | |
struct op_dex | |
{ | |
static const char* opcode_name() { return "DEX"; } | |
static const char* op() { return "do_dec(x); "; } | |
}; | |
struct op_dey | |
{ | |
static const char* opcode_name() { return "DEY"; } | |
static const char* op() { return "do_dec(y); "; } | |
}; | |
struct op_eor | |
{ | |
static const char* opcode_name() { return "EOR"; } | |
static const char* op() { return "do_eor(host.read(ab)); "; } | |
}; | |
struct op_inc | |
{ | |
static const char* opcode_name() { return "INC"; } | |
static const char* op() { return "do_inc(a); "; } | |
static const char* op_rmw() { return "do_inc(temp); "; } | |
}; | |
struct op_inx | |
{ | |
static const char* opcode_name() { return "INX"; } | |
static const char* op() { return "do_inc(x); "; } | |
}; | |
struct op_iny | |
{ | |
static const char* opcode_name() { return "INY"; } | |
static const char* op() { return "do_inc(y); "; } | |
}; | |
struct op_jmp | |
{ | |
static const char* opcode_name() { return "JMP"; } | |
}; | |
struct op_jsr | |
{ | |
static const char* opcode_name() { return "JSR"; } | |
}; | |
struct op_lda | |
{ | |
static const char* opcode_name() { return "LDA"; } | |
static const char* op() { return "do_load(a, host.read(ab)); "; } | |
}; | |
struct op_ldx | |
{ | |
static const char* opcode_name() { return "LDX"; } | |
static const char* op() { return "do_load(x, host.read(ab)); "; } | |
}; | |
struct op_ldy | |
{ | |
static const char* opcode_name() { return "LDY"; } | |
static const char* op() { return "do_load(y, host.read(ab)); "; } | |
}; | |
struct op_lsr | |
{ | |
static const char* opcode_name() { return "LSR"; } | |
static const char* op() { return "do_lsr(a); "; } | |
static const char* op_rmw() { return "do_lsr(temp); "; } | |
}; | |
struct op_nop | |
{ | |
static const char* opcode_name() { return "NOP"; } | |
static const char* op() { return ""; } | |
}; | |
struct op_ora | |
{ | |
static const char* opcode_name() { return "ORA"; } | |
static const char* op() { return "do_ora(host.read(ab)); "; } | |
}; | |
struct op_pha | |
{ | |
static const char* opcode_name() { return "PHA"; } | |
static const char* op() { return "host.write_stack(ab, a); "; } | |
}; | |
struct op_php | |
{ | |
static const char* opcode_name() { return "PHP"; } | |
static const char* op() { return "host.write_stack(ab, flags_to_p(true)); "; } | |
}; | |
struct op_pla | |
{ | |
static const char* opcode_name() { return "PLA"; } | |
static const char* op() { return "do_load(a, host.read_stack(ab)); "; } | |
}; | |
struct op_plp | |
{ | |
static const char* opcode_name() { return "PLP"; } | |
static const char* op() { return "p_to_flags(host.read_stack(ab)); "; } | |
}; | |
struct op_rol | |
{ | |
static const char* opcode_name() { return "ROL"; } | |
static const char* op() { return "do_rol(a); "; } | |
static const char* op_rmw() { return "do_rol(temp); "; } | |
}; | |
struct op_ror | |
{ | |
static const char* opcode_name() { return "ROR"; } | |
static const char* op() { return "do_ror(a); "; } | |
static const char* op_rmw() { return "do_ror(temp); "; } | |
}; | |
struct op_rti | |
{ | |
static const char* opcode_name() { return "RTI"; } | |
}; | |
struct op_rts | |
{ | |
static const char* opcode_name() { return "RTS"; } | |
}; | |
struct op_sbc | |
{ | |
static const char* opcode_name() { return "SBC"; } | |
static const char* op() { return "do_sbc(host.read(ab)); "; } | |
}; | |
struct op_sec | |
{ | |
static const char* opcode_name() { return "SEC"; } | |
static const char* op() { return "c = 1; "; } | |
}; | |
struct op_sei | |
{ | |
static const char* opcode_name() { return "SEI"; } | |
static const char* op() { return "i = 1; "; } | |
}; | |
struct op_sed | |
{ | |
static const char* opcode_name() { return "SED"; } | |
static const char* op() { return "d = 1; "; } | |
}; | |
struct op_sta | |
{ | |
static const char* opcode_name() { return "STA"; } | |
static const char* op() { return "host.write(ab, a); "; } | |
}; | |
struct op_stx | |
{ | |
static const char* opcode_name() { return "STX"; } | |
static const char* op() { return "host.write(ab, x); "; } | |
}; | |
struct op_sty | |
{ | |
static const char* opcode_name() { return "STY"; } | |
static const char* op() { return "host.write(ab, y); "; } | |
}; | |
struct op_txa | |
{ | |
static const char* opcode_name() { return "TXA"; } | |
static const char* op() { return "do_transfer(x, a); "; } | |
}; | |
struct op_tya | |
{ | |
static const char* opcode_name() { return "TYA"; } | |
static const char* op() { return "do_transfer(y, a); "; } | |
}; | |
struct op_txs | |
{ | |
static const char* opcode_name() { return "TXS"; } | |
static const char* op() { return "s = x; "; } | |
}; | |
struct op_tay | |
{ | |
static const char* opcode_name() { return "TAY"; } | |
static const char* op() { return "do_transfer(a, y); "; } | |
}; | |
struct op_tax | |
{ | |
static const char* opcode_name() { return "TAX"; } | |
static const char* op() { return "do_transfer(a, x); "; } | |
}; | |
struct op_tsx | |
{ | |
static const char* opcode_name() { return "TSX"; } | |
static const char* op() { return "do_transfer(s, x); "; } | |
}; | |
}; | |
/** | |
* This defines all instructions for the NMOS 6502, both documented and undocumented. | |
* It inherits all definitions from the generic opcode defs, and just adds undocumented opcodes. | |
*/ | |
template <typename Policy> | |
struct opcode_defs_nmos6502 : public opcode_defs_6502_generic<Policy> | |
{ | |
// Exotic NOPs | |
using opcode04 = typename Policy::template read_zp<typename Policy::op_nop>; | |
using opcode44 = typename Policy::template read_zp<typename Policy::op_nop>; | |
using opcode64 = typename Policy::template read_zp<typename Policy::op_nop>; | |
using opcode14 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcode34 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcode54 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcode74 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcodeD4 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcodeF4 = typename Policy::template read_zp_x<typename Policy::op_nop>; | |
using opcode0C = typename Policy::template read_abs<typename Policy::op_nop>; | |
using opcode1C = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcode3C = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcode5C = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcode7C = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcodeDC = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcodeFC = typename Policy::template read_abs_x<typename Policy::op_nop>; | |
using opcode1A = typename Policy::template imp<typename Policy::op_nop>; | |
using opcode3A = typename Policy::template imp<typename Policy::op_nop>; | |
using opcode5A = typename Policy::template imp<typename Policy::op_nop>; | |
using opcode7A = typename Policy::template imp<typename Policy::op_nop>; | |
using opcodeDA = typename Policy::template imp<typename Policy::op_nop>; | |
using opcodeFA = typename Policy::template imp<typename Policy::op_nop>; | |
using opcode80 = typename Policy::template read_imm<typename Policy::op_nop>; | |
using opcode82 = typename Policy::template read_imm<typename Policy::op_nop>; | |
using opcode89 = typename Policy::template read_imm<typename Policy::op_nop>; | |
using opcodeC2 = typename Policy::template read_imm<typename Policy::op_nop>; | |
using opcodeE2 = typename Policy::template read_imm<typename Policy::op_nop>; | |
// KILs | |
using opcode02 = typename Policy::kil; | |
using opcode12 = typename Policy::kil; | |
using opcode22 = typename Policy::kil; | |
using opcode32 = typename Policy::kil; | |
using opcode42 = typename Policy::kil; | |
using opcode52 = typename Policy::kil; | |
using opcode62 = typename Policy::kil; | |
using opcode72 = typename Policy::kil; | |
using opcode92 = typename Policy::kil; | |
using opcodeB2 = typename Policy::kil; | |
using opcodeD2 = typename Policy::kil; | |
using opcodeF2 = typename Policy::kil; | |
// SLO | |
using opcode07 = typename Policy::template rmw_zp<typename Policy::op_slo>; | |
using opcode17 = typename Policy::template rmw_zp_x<typename Policy::op_slo>; | |
using opcode03 = typename Policy::template rmw_ind_x<typename Policy::op_slo>; | |
using opcode13 = typename Policy::template rmw_ind_y<typename Policy::op_slo>; | |
using opcode0F = typename Policy::template rmw_abs<typename Policy::op_slo>; | |
using opcode1F = typename Policy::template rmw_abs_x<typename Policy::op_slo>; | |
using opcode1B = typename Policy::template rmw_abs_y<typename Policy::op_slo>; | |
// RLA | |
using opcode27 = typename Policy::template rmw_zp<typename Policy::op_rla>; | |
using opcode37 = typename Policy::template rmw_zp_x<typename Policy::op_rla>; | |
using opcode23 = typename Policy::template rmw_ind_x<typename Policy::op_rla>; | |
using opcode33 = typename Policy::template rmw_ind_y<typename Policy::op_rla>; | |
using opcode2F = typename Policy::template rmw_abs<typename Policy::op_rla>; | |
using opcode3F = typename Policy::template rmw_abs_x<typename Policy::op_rla>; | |
using opcode3B = typename Policy::template rmw_abs_y<typename Policy::op_rla>; | |
// SRE | |
using opcode47 = typename Policy::template rmw_zp<typename Policy::op_sre>; | |
using opcode57 = typename Policy::template rmw_zp_x<typename Policy::op_sre>; | |
using opcode43 = typename Policy::template rmw_ind_x<typename Policy::op_sre>; | |
using opcode53 = typename Policy::template rmw_ind_y<typename Policy::op_sre>; | |
using opcode4F = typename Policy::template rmw_abs<typename Policy::op_sre>; | |
using opcode5F = typename Policy::template rmw_abs_x<typename Policy::op_sre>; | |
using opcode5B = typename Policy::template rmw_abs_y<typename Policy::op_sre>; | |
// RRA | |
using opcode67 = typename Policy::template rmw_zp<typename Policy::op_rra>; | |
using opcode77 = typename Policy::template rmw_zp_x<typename Policy::op_rra>; | |
using opcode63 = typename Policy::template rmw_ind_x<typename Policy::op_rra>; | |
using opcode73 = typename Policy::template rmw_ind_y<typename Policy::op_rra>; | |
using opcode6F = typename Policy::template rmw_abs<typename Policy::op_rra>; | |
using opcode7F = typename Policy::template rmw_abs_x<typename Policy::op_rra>; | |
using opcode7B = typename Policy::template rmw_abs_y<typename Policy::op_rra>; | |
// DCP | |
using opcodeC7 = typename Policy::template rmw_zp<typename Policy::op_dcp>; | |
using opcodeD7 = typename Policy::template rmw_zp_x<typename Policy::op_dcp>; | |
using opcodeC3 = typename Policy::template rmw_ind_x<typename Policy::op_dcp>; | |
using opcodeD3 = typename Policy::template rmw_ind_y<typename Policy::op_dcp>; | |
using opcodeCF = typename Policy::template rmw_abs<typename Policy::op_dcp>; | |
using opcodeDF = typename Policy::template rmw_abs_x<typename Policy::op_dcp>; | |
using opcodeDB = typename Policy::template rmw_abs_y<typename Policy::op_dcp>; | |
// ISC | |
using opcodeE7 = typename Policy::template rmw_zp<typename Policy::op_isc>; | |
using opcodeF7 = typename Policy::template rmw_zp_x<typename Policy::op_isc>; | |
using opcodeE3 = typename Policy::template rmw_ind_x<typename Policy::op_isc>; | |
using opcodeF3 = typename Policy::template rmw_ind_y<typename Policy::op_isc>; | |
using opcodeEF = typename Policy::template rmw_abs<typename Policy::op_isc>; | |
using opcodeFF = typename Policy::template rmw_abs_x<typename Policy::op_isc>; | |
using opcodeFB = typename Policy::template rmw_abs_y<typename Policy::op_isc>; | |
// LAX | |
using opcodeA7 = typename Policy::template read_zp<typename Policy::op_lax>; | |
using opcodeB7 = typename Policy::template read_zp_y<typename Policy::op_lax>; | |
using opcodeA3 = typename Policy::template read_ind_x<typename Policy::op_lax>; | |
using opcodeB3 = typename Policy::template read_ind_y<typename Policy::op_lax>; | |
using opcodeAF = typename Policy::template read_abs<typename Policy::op_lax>; | |
using opcodeBF = typename Policy::template read_abs_y<typename Policy::op_lax>; | |
// SAX | |
using opcode87 = typename Policy::template write_zp<typename Policy::op_sax>; | |
using opcode97 = typename Policy::template write_zp_y<typename Policy::op_sax>; | |
using opcode83 = typename Policy::template write_ind_x<typename Policy::op_sax>; | |
using opcode8F = typename Policy::template write_abs<typename Policy::op_sax>; | |
// AND# + op | |
using opcode0B = typename Policy::template read_imm<typename Policy::op_anc>; | |
using opcode2B = typename Policy::template read_imm<typename Policy::op_anc>; | |
using opcode4B = typename Policy::template read_imm<typename Policy::op_alr>; | |
using opcode6B = typename Policy::template read_imm<typename Policy::op_arr>; | |
using opcode8B = typename Policy::template read_imm<typename Policy::op_xaa>; | |
using opcodeAB = typename Policy::template read_imm<typename Policy::op_lxa>; | |
using opcodeCB = typename Policy::template read_imm<typename Policy::op_axs>; | |
using opcodeEB = typename Policy::template read_imm<typename Policy::op_sbc>; | |
// Misc | |
using opcode93 = typename Policy::template write_ind_y<typename Policy::op_ahx>; | |
using opcode9F = typename Policy::template write_abs_y<typename Policy::op_ahx>; | |
using opcode9C = typename Policy::template write_abs_x<typename Policy::op_shy>; | |
using opcode9E = typename Policy::template write_abs_y<typename Policy::op_shx>; | |
using opcode9B = typename Policy::template write_abs_y<typename Policy::op_tas>; | |
using opcodeBB = typename Policy::template write_abs_y<typename Policy::op_las>; | |
}; | |
struct opcode_policy_nmos6502 : public opcode_policy_6502_generic | |
{ | |
struct kil | |
{ | |
static const char* opcode_name() { return "KIL"; } | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "host.read(ab); "s, next_node::none); | |
} | |
static const char* operand_text() { return ""; } | |
}; | |
template <typename Op> | |
struct rmw_abs_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "temp = host.read(ab); ab = ++pc; "s, next_node::next); | |
states.add(opcode, "ab = (host.read(ab) << 8) | ((temp + y) & 0xFFU); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read(ab); if (temp + y >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "w,Y"; } | |
}; | |
template <typename Op> | |
struct rmw_ind_x : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "host.read_zp(ab); ab = (ab + x) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "ab = (host.read_zp(ab) << 8) | temp; "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b,X)"; } | |
}; | |
template <typename Op> | |
struct rmw_ind_y : Op | |
{ | |
using Op::opcode_name; | |
using Op::op_rmw; | |
static void generate(state_machine& states, uint opcode) | |
{ | |
states.add(opcode, "ab = host.read(ab); ++pc; "s, next_node::next); | |
states.add(opcode, "temp = host.read_zp(ab); ab = (ab + 1U) & 0xFFU; "s, next_node::next); | |
states.add(opcode, "ab = (host.read_zp(ab) << 8) | ((temp + y) & 0xFFU); "s, next_node::next); | |
states.add(opcode, "host.read(ab); if (temp + y >= 0x100U) { ab += 0x100; } "s, next_node::next); | |
states.add(opcode, "temp = host.read(ab); "s, next_node::next); | |
states.add(opcode, "cached_irq = irq; host.write(ab, temp); "s + op_rmw(), next_node::next); | |
states.add(opcode, "check_irq(); host.write(ab, temp); ab = pc; "s, next_node::end); | |
} | |
static const char* operand_text() { return "(b),Y"; } | |
}; | |
struct op_slo | |
{ | |
static const char* opcode_name() { return "SLO"; } | |
static const char* op_rmw() { return "do_asl(temp); do_ora(temp); "; } | |
}; | |
struct op_rla | |
{ | |
static const char* opcode_name() { return "RLA"; } | |
static const char* op_rmw() { return "do_rol(temp); do_and(temp); "; } | |
}; | |
struct op_sre | |
{ | |
static const char* opcode_name() { return "SRE"; } | |
static const char* op_rmw() { return "do_lsr(temp); do_eor(temp); "; } | |
}; | |
struct op_rra | |
{ | |
static const char* opcode_name() { return "RRA"; } | |
static const char* op_rmw() { return "do_ror(temp); do_adc(temp); "; } | |
}; | |
struct op_dcp | |
{ | |
static const char* opcode_name() { return "DCP"; } | |
static const char* op_rmw() { return "do_dec(temp); do_compare(a, temp); "; } | |
}; | |
struct op_isc | |
{ | |
static const char* opcode_name() { return "ISC"; } | |
static const char* op_rmw() { return "do_inc(temp); do_sbc(temp); "; } | |
}; | |
struct op_lax | |
{ | |
static const char* opcode_name() { return "LAX"; } | |
static const char* op() { return "do_load(a, host.read(ab)); x = a; "; } | |
}; | |
struct op_sax | |
{ | |
static const char* opcode_name() { return "SAX"; } | |
static const char* op() { return "host.write(ab, a & x); "; } | |
}; | |
struct op_anc | |
{ | |
static const char* opcode_name() { return "ANC"; } | |
static const char* op() { return "do_and(host.read(ab)); c = n; "; } | |
}; | |
struct op_alr | |
{ | |
static const char* opcode_name() { return "ALR"; } | |
static const char* op() { return "do_and(host.read(ab)); do_lsr(a); "; } | |
}; | |
struct op_arr | |
{ | |
static const char* opcode_name() { return "ARR"; } | |
static const char* op() { return "do_arr(); "; } | |
}; | |
struct op_xaa | |
{ | |
static const char* opcode_name() { return "XAA"; } | |
static const char* op() { return "do_xaa(); "; } | |
}; | |
struct op_lxa | |
{ | |
static const char* opcode_name() { return "LXA"; } | |
static const char* op() { return "do_lxa(); "; } | |
}; | |
struct op_axs | |
{ | |
static const char* opcode_name() { return "AXS"; } | |
static const char* op() { return "do_axs(); "; } | |
}; | |
struct op_ahx | |
{ | |
static const char* opcode_name() { return "AHX"; } | |
static const char* op() { return "do_ahx(); "; } | |
}; | |
struct op_shy | |
{ | |
static const char* opcode_name() { return "SHY"; } | |
static const char* op() { return "do_shy(); "; } | |
}; | |
struct op_shx | |
{ | |
static const char* opcode_name() { return "SHX"; } | |
static const char* op() { return "do_shx(); "; } | |
}; | |
struct op_tas | |
{ | |
static const char* opcode_name() { return "TAS"; } | |
static const char* op() { return "do_tas(); "; } | |
}; | |
struct op_las | |
{ | |
static const char* opcode_name() { return "LAS"; } | |
static const char* op() { return "do_las(); "; } | |
}; | |
}; | |
template <typename OpcodeDefs> | |
void generate_class(const char* classname, const char* header_guard, const char* filename) | |
{ | |
// Open file | |
auto file = std::ofstream{filename}; | |
// Write file header | |
file << "// Auto-generated file, please do not edit" << std::endl; | |
file << std::endl; | |
file << "#ifndef " << header_guard << std::endl; | |
file << "#define " << header_guard << std::endl; | |
file << std::endl; | |
// Write tick method | |
file << "template <typename Host>" << std::endl; | |
file << "void " << classname << "::tick(Host& host)" << std::endl; | |
file << "{" << std::endl; | |
file << "\tswitch (state)" << std::endl; | |
file << "\t{" << std::endl; | |
file << std::hex << std::uppercase << std::setfill('0'); | |
state_machine sm; | |
#define DEF(a, b) OpcodeDefs::opcode##a##b::generate(sm, 0x##a##b) | |
#define DEFS(a) DEF(a, 0); DEF(a, 1); DEF(a, 2); DEF(a, 3); DEF(a, 4); DEF(a, 5); DEF(a, 6); DEF(a, 7); DEF(a, 8); DEF(a, 9); DEF(a, A); DEF(a, B); DEF(a, C); DEF(a, D); DEF(a, E); DEF(a, F) | |
DEFS(0); DEFS(1); DEFS(2); DEFS(3); DEFS(4); DEFS(5); DEFS(6); DEFS(7); DEFS(8); DEFS(9); DEFS(A); DEFS(B); DEFS(C); DEFS(D); DEFS(E); DEFS(F); | |
#undef DEFS | |
#undef DEF | |
OpcodeDefs::dispatch::generate(sm, state_machine::end_state); | |
sm.render(file); | |
file << "\t}" << std::endl; | |
file << "}" << std::endl; | |
file << std::endl; | |
// Write get_name method | |
file << "inline const char* " << classname << "::get_name(const byte opcode)" << std::endl; | |
file << "{" << std::endl; | |
file << "\tstatic const char* names[] =" << std::endl; | |
file << "\t{" << std::endl; | |
#define DEF(a, b) << '\"' << OpcodeDefs::opcode##a##b::opcode_name() << (*OpcodeDefs::opcode##a##b::operand_text() ? " " : "") << OpcodeDefs::opcode##a##b::operand_text() << "\", " | |
#define DEFS(a) file << "\t\t" DEF(a, 0) DEF(a, 1) DEF(a, 2) DEF(a, 3) DEF(a, 4) DEF(a, 5) DEF(a, 6) DEF(a, 7) DEF(a, 8) DEF(a, 9) DEF(a, A) DEF(a, B) DEF(a, C) DEF(a, D) DEF(a, E) DEF(a, F) << std::endl | |
DEFS(0); DEFS(1); DEFS(2); DEFS(3); DEFS(4); DEFS(5); DEFS(6); DEFS(7); DEFS(8); DEFS(9); DEFS(A); DEFS(B); DEFS(C); DEFS(D); DEFS(E); DEFS(F); | |
#undef DEFS | |
#undef DEF | |
file << "\t};" << std::endl; | |
file << std::endl; | |
file << "\treturn names[opcode];" << std::endl; | |
file << "}" << std::endl; | |
file << std::endl; | |
// Write footer | |
file << "#endif" << std::endl; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
// Check filename parameter supplied | |
if (argc != 2) | |
{ | |
std::cerr << "No filename provided" << std::endl; | |
return 1; | |
} | |
const char* filename = argv[1]; | |
generate_class<opcode_defs_nmos6502<opcode_policy_nmos6502>>("cpu_6502a", "CPU_6502A_GENERATED_H_", filename); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment