EightBit/M6502/inc/mos6502.h
Adrian Conlon 5c3568aebd Undocumented instruction: SLO added
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
2018-01-04 22:46:44 +00:00

639 lines
12 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <array>
#include <functional>
#include <cassert>
#include <Bus.h>
#include <Processor.h>
#include <Signal.h>
namespace EightBit {
class MOS6502 : public Processor {
public:
struct opcode_decoded_t {
int aaa = 0;
int bbb = 0;
int cc = 0;
opcode_decoded_t() {}
opcode_decoded_t(uint8_t opcode) {
aaa = (opcode & 0b11100000) >> 5; // 0 - 7
bbb = (opcode & 0b00011100) >> 2; // 0 - 7
cc = (opcode & 0b00000011); // 0 - 3
}
};
enum StatusBits {
NF = Bit7, // Negative
VF = Bit6, // Overflow
RF = Bit5, // reserved
BF = Bit4, // Brk
DF = Bit3, // D (use BCD for arithmetic)
IF = Bit2, // I (IRQ disable)
ZF = Bit1, // Zero
CF = Bit0, // Carry
};
MOS6502(Bus& bus);
Signal<MOS6502> ExecutingInstruction;
Signal<MOS6502> ExecutedInstruction;
virtual int execute(uint8_t opcode) final;
virtual int step() final;
uint8_t& X() { return x; }
uint8_t& Y() { return y; }
uint8_t& A() { return a; }
uint8_t& S() { return s; }
uint8_t& P() { return p; }
protected:
virtual void reset() final;
virtual uint8_t SUB(uint8_t operand, uint8_t data, int borrow = 0);
uint8_t SBC(uint8_t operand, uint8_t data);
uint8_t SUB_b(uint8_t operand, uint8_t data, int borrow);
uint8_t SUB_d(uint8_t operand, uint8_t data, int borrow);
virtual uint8_t ADD(uint8_t operand, uint8_t data, int carry = 0);
uint8_t ADC(uint8_t operand, uint8_t data);
uint8_t ADD_b(uint8_t operand, uint8_t data, int carry);
uint8_t ADD_d(uint8_t operand, uint8_t data, int carry);
private:
void interrupt(uint8_t vector);
void adjustZero(uint8_t datum) { clearFlag(P(), ZF, datum); }
void adjustNegative(uint8_t datum) { setFlag(P(), NF, datum & NF); }
void adjustNZ(uint8_t datum) {
adjustZero(datum);
adjustNegative(datum);
}
void getWord(uint8_t page, uint8_t offset, register16_t& output);
virtual void push(uint8_t value) final;
virtual uint8_t pop() final;
// Address resolution
void Address_Absolute() {
fetchWord();
}
void Address_ZeroPage() {
MEMPTR().low = fetchByte();
MEMPTR().high = 0;
}
void Address_ZeroPageIndirect() {
Address_ZeroPage();
getWord(0, MEMPTR().low, MEMPTR());
}
void Address_Indirect() {
Address_Absolute();
getWord(MEMPTR().high, MEMPTR().low, MEMPTR());
}
void Address_ZeroPageX() {
Address_ZeroPage();
MEMPTR().low += X();
}
void Address_ZeroPageY() {
Address_ZeroPage();
MEMPTR().low += Y();
}
bool Address_AbsoluteX() {
Address_Absolute();
const auto page = MEMPTR().high;
MEMPTR().word += X();
return MEMPTR().high != page;
}
bool Address_AbsoluteY() {
Address_Absolute();
const auto page = MEMPTR().high;
MEMPTR().word += Y();
return MEMPTR().high != page;
}
void Address_IndexedIndirectX() {
Address_ZeroPageX();
getWord(0, MEMPTR().low, MEMPTR());
}
bool Address_IndirectIndexedY() {
Address_ZeroPageIndirect();
const auto page = MEMPTR().high;
MEMPTR().word += Y();
return MEMPTR().high != page;
}
// Addressing modes, read
uint8_t AM_A() {
return A();
}
uint8_t AM_Immediate() {
return fetchByte();
}
uint8_t AM_Absolute() {
Address_Absolute();
return getByte(MEMPTR());
}
uint8_t AM_ZeroPage() {
Address_ZeroPage();
return getByte(MEMPTR());
}
uint8_t AM_AbsoluteX() {
if (UNLIKELY(Address_AbsoluteX()))
addCycle();
return getByte(MEMPTR());
}
uint8_t AM_AbsoluteY() {
if (UNLIKELY(Address_AbsoluteY()))
addCycle();
return getByte(MEMPTR());
}
uint8_t AM_ZeroPageX() {
Address_ZeroPageX();
return getByte(MEMPTR());
}
uint8_t AM_ZeroPageY() {
Address_ZeroPageY();
return getByte(MEMPTR());
}
uint8_t AM_IndexedIndirectX() {
Address_IndexedIndirectX();
return getByte(MEMPTR());
}
uint8_t AM_IndirectIndexedY() {
if (UNLIKELY(Address_IndirectIndexedY()))
addCycle();
return getByte(MEMPTR());
}
// Addressing modes, write
void AM_A(uint8_t value) {
A() = value;
}
void AM_Absolute(uint8_t value) {
Address_Absolute();
setByte(MEMPTR(), value);
}
void AM_ZeroPage(uint8_t value) {
Address_ZeroPage();
setByte(MEMPTR(), value);
}
void AM_AbsoluteX(uint8_t value) {
Address_AbsoluteX();
setByte(MEMPTR(), value);
}
void AM_AbsoluteY(uint8_t value) {
Address_AbsoluteY();
setByte(MEMPTR(), value);
}
void AM_ZeroPageX(uint8_t value) {
Address_ZeroPageX();
setByte(MEMPTR(), value);
}
void AM_ZeroPageY(uint8_t value) {
Address_ZeroPageY();
setByte(MEMPTR(), value);
}
void AM_IndexedIndirectX(uint8_t value) {
Address_IndexedIndirectX();
setByte(MEMPTR(), value);
}
void AM_IndirectIndexedY(uint8_t value) {
Address_IndirectIndexedY();
setByte(MEMPTR(), value);
}
// 6502 addressing mode switching
uint8_t AM_00(int bbb) {
switch (bbb) {
case 0b000:
return AM_Immediate();
case 0b001:
return AM_ZeroPage();
case 0b011:
return AM_Absolute();
case 0b101:
return AM_ZeroPageX();
case 0b111:
return AM_AbsoluteX();
case 0b010:
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
void AM_00(int bbb, uint8_t value) {
switch (bbb) {
case 0b001:
AM_ZeroPage(value);
break;
case 0b011:
AM_Absolute(value);
break;
case 0b101:
AM_ZeroPageX(value);
break;
case 0b111:
AM_AbsoluteX(value);
break;
case 0b000:
case 0b010:
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
uint8_t AM_01(int bbb) {
switch (bbb) {
case 0b000:
return AM_IndexedIndirectX();
case 0b001:
return AM_ZeroPage();
case 0b010:
return AM_Immediate();
case 0b011:
return AM_Absolute();
case 0b100:
return AM_IndirectIndexedY();
case 0b101:
return AM_ZeroPageX();
case 0b110:
return AM_AbsoluteY();
case 0b111:
return AM_AbsoluteX();
default:
UNREACHABLE;
}
}
void AM_01(int bbb, uint8_t value) {
switch (bbb) {
case 0b000:
AM_IndexedIndirectX(value);
break;
case 0b001:
AM_ZeroPage(value);
break;
case 0b010:
throw std::domain_error("Illegal addressing mode");
case 0b011:
AM_Absolute(value);
break;
case 0b100:
AM_IndirectIndexedY(value);
break;
case 0b101:
AM_ZeroPageX(value);
break;
case 0b110:
AM_AbsoluteY(value);
break;
case 0b111:
AM_AbsoluteX(value);
break;
default:
UNREACHABLE;
}
}
uint8_t AM_10(int bbb) {
switch (bbb) {
case 0b000:
return AM_Immediate();
case 0b001:
return AM_ZeroPage();
case 0b010:
return AM_A();
case 0b011:
return AM_Absolute();
case 0b101:
return AM_ZeroPageX();
case 0b111:
return AM_AbsoluteX();
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
return 0xff;
}
void AM_10(int bbb, uint8_t value) {
switch (bbb) {
case 0b010:
AM_A(value);
break;
case 0b001:
case 0b011:
case 0b101:
case 0b111:
setByte(MEMPTR(), value);
break;
case 0b000:
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
uint8_t AM_10_x(int bbb) {
switch (bbb) {
case 0b000:
return AM_Immediate();
case 0b001:
return AM_ZeroPage();
case 0b010:
return AM_A();
case 0b011:
return AM_Absolute();
case 0b101:
return AM_ZeroPageY();
case 0b111:
return AM_AbsoluteY();
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
void AM_10_x(int bbb, uint8_t value) {
switch (bbb) {
case 0b001:
AM_ZeroPage(value);
break;
case 0b010:
AM_A(value);
break;
case 0b011:
AM_Absolute(value);
break;
case 0b101:
AM_ZeroPageY(value);
break;
case 0b111:
AM_AbsoluteY(value);
break;
case 0b000:
case 0b100:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
uint8_t AM_11(int bbb) {
switch (bbb) {
case 0b000:
return AM_IndexedIndirectX();
case 0b001:
return AM_ZeroPage();
case 0b010:
return AM_Immediate();
case 0b011:
return AM_Absolute();
case 0b100:
return AM_IndirectIndexedY();
case 0b101:
return AM_ZeroPageY();
case 0b110:
throw std::domain_error("Illegal addressing mode");
case 0b111:
return AM_AbsoluteY();
default:
UNREACHABLE;
}
}
void AM_11(int bbb, uint8_t value) {
switch (bbb) {
case 0b000:
AM_IndexedIndirectX(value);
break;
case 0b001:
AM_ZeroPage(value);
break;
case 0b011:
AM_Absolute(value);
break;
case 0b100:
AM_IndirectIndexedY(value);
break;
case 0b101:
AM_ZeroPageY(value);
break;
case 0b111:
AM_AbsoluteY(value);
break;
case 0b010:
case 0b110:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
uint8_t AM_11_x(int bbb) {
switch (bbb) {
case 0b000:
return AM_IndexedIndirectX();
case 0b001:
return AM_ZeroPage();
case 0b010:
return AM_Immediate();
case 0b011:
return AM_Absolute();
case 0b100:
return AM_IndirectIndexedY();
case 0b101:
return AM_ZeroPageX();
case 0b110:
return AM_AbsoluteY();
case 0b111:
return AM_AbsoluteX();
default:
UNREACHABLE;
}
}
void AM_11_x(int bbb, uint8_t value) {
switch (bbb) {
case 0b000:
AM_IndexedIndirectX(value);
break;
case 0b001:
AM_ZeroPage(value);
break;
case 0b011:
AM_Absolute(value);
break;
case 0b100:
AM_IndirectIndexedY(value);
break;
case 0b101:
AM_ZeroPageX(value);
break;
case 0b110:
AM_AbsoluteY(value);
break;
case 0b111:
AM_AbsoluteX(value);
break;
case 0b010:
throw std::domain_error("Illegal addressing mode");
default:
UNREACHABLE;
}
}
// Operations
void ASL(int bbb) {
auto operand = AM_10(bbb);
ASL(operand);
AM_10(bbb, operand);
}
void ROL(int bbb) {
auto operand = AM_10(bbb);
ROL(operand);
AM_10(bbb, operand);
}
void LSR(int bbb) {
auto operand = AM_10(bbb);
LSR(operand);
AM_10(bbb, operand);
}
void ROR(int bbb) {
auto operand = AM_10(bbb);
ROR(operand);
AM_10(bbb, operand);
}
void DEC(int bbb) {
auto operand = AM_10(bbb);
adjustNZ(--operand);
AM_10(bbb, operand);
}
void INC(int bbb) {
auto operand = AM_10(bbb);
adjustNZ(++operand);
AM_10(bbb, operand);
}
void DCP(int bbb) {
auto operand = AM_11_x(bbb);
setByte(--operand);
CMP(A(), operand);
}
void ISB(int bbb) {
auto operand = AM_01(bbb);
setByte(++operand);
A() = SBC(A(), operand);
}
void ORA(uint8_t value) {
adjustNZ(A() |= value);
}
void SLO(int bbb) {
auto operand = AM_01(bbb);
ASL(operand);
setByte(operand);
ORA(operand);
}
void ROR(uint8_t& output);
void LSR(uint8_t& output);
void BIT(uint8_t data);
void ROL(uint8_t& output);
void ASL(uint8_t& output);
void CMP(uint8_t first, uint8_t second);
void Branch(int8_t displacement);
void Branch(bool flag);
void PHP();
void PLP();
void JSR_abs();
void RTI();
void RTS();
void JMP_abs();
void JMP_ind();
void BRK();
const uint16_t PageOne = 0x100;
// All interrupt vectors are on the 0xFF page
const uint8_t IRQvector = 0xfe;
const uint8_t RSTvector = 0xfc;
const uint8_t NMIvector = 0xfa;
uint8_t x; // index register X
uint8_t y; // index register Y
uint8_t a; // accumulator
uint8_t s; // stack pointer
uint8_t p; // processor status
std::array<int, 0x100> m_timings;
std::array<opcode_decoded_t, 0x100> m_decodedOpcodes;
};
}