Skip to content

Instantly share code, notes, and snippets.

@richtw1
Created June 25, 2018 21:03
Show Gist options
  • Save richtw1/77ae851334a0839ac8ee174f8e4fff24 to your computer and use it in GitHub Desktop.
Save richtw1/77ae851334a0839ac8ee174f8e4fff24 to your computer and use it in GitHub Desktop.
/*
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