fruitmachine/M6502EmulatorDll/M6502EmulatorDll/CPU.cpp

1493 lines
26 KiB
C++

#include "stdafx.h"
#include "CPU.h"
#include <iostream>
/*
MOS 6502 CPU instruction set simulator.
Passes the functional test ROMs I can find.
TODO: Inaccurate cycle counts (cycle counter isn't incremented when zero-page access wraps, maybe some other things)
TODO: Cycle-accurate timing instead of per-instruction timing (see above)
*/
//The master processor of instructions.
bool CPU::lastInstructionCrossedPageBoundary = false;
void CPU::process_instruction() {
last_executed_opcode = "";
last_operand = 0x00;
old_program_counter = program_counter;
uint8_t opcode = fetch_memory_byte(program_counter);
execute_opcode(opcode);
if (opcode == 0x4C) {
loopCount++;
if (loopCount == 10) {
std::cout << "Infinite loop detected, aborting.";
std::exit(3);
}
}
else {
loopCount = 0;
}
}
void CPU::print_status(uint8_t opcode) {
std::cout << "[EXE] Opcode " << std::hex << unsigned(opcode) << " (" << last_executed_opcode.c_str() << unsigned(last_operand) << "):" << std::endl;
std::cout << "\tA=" << unsigned(accumulator) << std::setw(2) << " X=" << unsigned(index_x) << std::setw(2) << " Y=" << unsigned(index_y) << std::setw(2) << " SP=" << unsigned(stack_pointer) << " PC=" << unsigned(program_counter) << " ";
if (FLAG_SIGN) {
std::cout << "N";
}
else {
std::cout << "-";
}
if (FLAG_OVERFLOW) {
std::cout << "V";
}
else {
std::cout << "-";
}
std::cout << "-";
if (FLAG_BREAKPOINT) {
std::cout << "B";
}
else {
std::cout << "-";
}
if (FLAG_DECIMAL) {
std::cout << "D";
}
else {
std::cout << "-";
}
if (FLAG_INTERRUPT) {
std::cout << "I";
}
else {
std::cout << "-";
}
if (FLAG_ZERO) {
std::cout << "Z";
}
else {
std::cout << "-";
}
if (FLAG_CARRY) {
std::cout << "C";
}
else {
std::cout << "-";
}
std::cout << std::endl;
}
uint16_t CPU::getCycleCount() {
return cycles;
}
void CPU::execute_opcode(uint8_t opcode)
{
switch (opcode) {
/* Addressing modes
- ABC # - Immediate - the operand is the next byte. advances PC by 2
- ABC #$ - Accumulator. advances PC by 1
- ABC ZP - Zero-Page - the operand refers to a location in zero-page memory. advances PC by 2
- ABC ZP,X - Zero-Page, X/Y indexed. advances PC by 2. If ZP+X > $FF, wrap to $00 of zero-page memory.
- ABC M - Absolute - the operand is a 16-bit memory location. advances PC by 3
- ABC M,X - Absolute, X/Y indexed. advances PC by 2. if Abs+X or Abs+Y > $FF, do not wrap to the beginning of the page.
- ABC (M) - Indirect - the operand is a 16-bit pointer. advances PC by 3
- ABC (M,X) - Indirect X-indexed. Calculate address from the pointer located at ZP+X. If ZP+X > $FF, wrap to $00 of zero-page memory.
- ABC (M),Y - Indirect Y-indexed. The pointer is at ZP. Add Y to the dereferenced destination address. Do not wrap.
*/
case 0x00: //BRK
{
cycles += 7;
last_executed_opcode = "BRK";
program_counter++; //waste an instruction
op_brk();
break;
}
case 0x01:
{
cycles += 6;
last_executed_opcode = "ORA (ZP,X) $";
op_ora(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0x05:
{
cycles += 2;
last_executed_opcode = "ORA $";
op_ora(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x06:
{
cycles += 5;
last_executed_opcode = "ASL $";
op_asl(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x08: //PHP
{
cycles += 3;
last_executed_opcode = "PHP";
op_php();
break;
}
case 0x09:
{
cycles += 2;
last_executed_opcode = "ORA #$";
op_ora(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x0A:
{
cycles += 2;
last_executed_opcode = "ASL";
op_asl();
break;
}
case 0x0D:
{
cycles += 4;
last_executed_opcode = "ORA $";
uint8_t operand = fetch_operand(ADDRESSING_ABSOLUTE);
accumulator = accumulator | operand;
set_zero_and_sign_flags(accumulator);
break;
}
case 0x0E:
{
cycles += 6;
last_executed_opcode = "ASL $";
op_asl(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x10:
{
cycles += 2; //2; +1 if dest on same page; +2 if on dest on diff page
last_executed_opcode = "BPL $";
op_bpl(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x11:
{
cycles += 5;
last_executed_opcode = "ORA (M),Y $";
op_ora(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0x15:
{
cycles += 4;
last_executed_opcode = "ORA ZP,X";
op_ora(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x16:
{
cycles += 6;
last_executed_opcode = "ASL ZP,X $";
op_asl(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x18: //CLC
{
cycles += 2;
last_executed_opcode = "CLC";
op_clc();
break;
}
case 0x19:
{
cycles += 4;
last_executed_opcode = "ORA M,Y $";
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0x1D:
{
cycles += 4;
last_executed_opcode = "ORA M,X $";
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0x1E:
{
cycles += 7;
last_executed_opcode = "ASL M,X $";
op_asl(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0x20:
{
cycles += 6;
last_executed_opcode = "JSR $";
op_jsr(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x21: //AND (M, X)
{
cycles += 6;
last_executed_opcode = "AND (M,X) $";
op_and(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0x24:
{
cycles += 3;
last_executed_opcode = "BIT $";
op_bit(fetch_operand(ADDRESSING_ZEROPAGE));
break;
}
case 0x25: //AND - AND zeropage
{
cycles += 2;
last_executed_opcode = "AND $";
op_and(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x26:
{
cycles += 5;
last_executed_opcode = "ROL $";
op_rol(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x28: //PLP
{
cycles += 4;
last_executed_opcode = "PLP";
op_plp();
break;
}
case 0x29: //AND - AND(M, immediate)
{
cycles += 2;
last_executed_opcode = "AND #$";
op_and(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x2A:
{
cycles += 2;
last_executed_opcode = "ROL A";
op_rol();
break;
}
case 0x2C:
{
cycles += 4;
last_executed_opcode = "BIT $";
op_bit(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x2D:
{
cycles += 4;
last_executed_opcode = "AND $";
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x2E:
{
cycles += 6;
last_executed_opcode = "ROL $";
op_rol(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x30:
{
cycles += 2;
last_executed_opcode = "BMI $";
op_bmi(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x31: //AND (M),Y
{
cycles += 5;
last_executed_opcode = "AND (M),Y $";
op_and(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0x35: //AND,X - AND(A, zeropage+X)
{
cycles += 3;
last_executed_opcode = "AND ZP,X";
op_and(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x36:
{
cycles += 6;
last_executed_opcode = "ROL ZP,X $";
op_rol(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x38: //SEC - set carry flag
{
cycles += 2;
last_executed_opcode = "SEC";
op_sec();
break;
}
case 0x39: //AND M,Y
{
cycles += 4;
last_executed_opcode = "AND M,Y $";
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0x3D:
{
cycles += 4;
last_executed_opcode = "AND M,X $";
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0x3E:
{
cycles += 7;
last_executed_opcode = "ROL M,X $";
op_rol(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0x40:
{
cycles += 6;
last_executed_opcode = "RTI";
op_rti();
break;
}
case 0x41:
{
cycles += 6;
last_executed_opcode = "EOR (ZP,X) $";
op_eor(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0x45:
{
cycles += 3;
last_executed_opcode = "EOR $";
op_eor(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x46:
{
cycles += 5;
last_executed_opcode = "LSR $";
op_lsr(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x48:
{
cycles += 3;
last_executed_opcode = "PHA";
op_pha();
break;
}
case 0x49:
{
cycles += 2;
last_executed_opcode = "EOR #$";
op_eor(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x4A:
{
cycles += 2;
last_executed_opcode = "LSR";
op_lsr();
break;
}
case 0x4C: //JMP - unconditional branch
{
cycles += 3;
last_executed_opcode = "JMP $";
op_jmp(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x4D:
{
cycles += 4;
last_executed_opcode = "EOR $";
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x4E:
{
cycles += 6;
last_executed_opcode = "LSR $";
op_lsr(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x50:
{
cycles += 2;
last_executed_opcode = "BVC";
op_bvc(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x51:
{
cycles += 5;
last_executed_opcode = "EOR (ZP),Y $";
op_eor(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0x55:
{
cycles += 4;
last_executed_opcode = "EOR ZP,X";
op_eor(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x56:
{
cycles += 6;
last_executed_opcode = "LSR ZP,X $";
op_lsr(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x58: //CLI
{
cycles += 2;
last_executed_opcode = "CLI";
op_cli();
break;
}
case 0x59:
{
cycles += 4;
last_executed_opcode = "EOR M,Y $";
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0x5D:
{
cycles += 4;
last_executed_opcode = "EOR M,X $";
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0x5E:
{
cycles += 7;
last_executed_opcode = "LSR M,X $";
op_lsr(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0x60:
{
cycles += 6;
last_executed_opcode = "RTS";
op_rts();
break;
}
case 0x61:
{
cycles += 6;
last_executed_opcode = "ADC (ZP,X) $";
op_adc(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0x65:
{
cycles += 3;
last_executed_opcode = "ADC ZP $";
op_adc(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x66:
{
cycles += 5;
last_executed_opcode = "ROR $";
op_ror(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x68: //PLA - pop accumulator from stack
{
cycles += 4;
last_executed_opcode = "PLA";
op_pla();
break;
}
case 0x69:
{
cycles += 2;
last_executed_opcode = "ADC #$";
op_adc(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x6A:
{
cycles += 2;
last_executed_opcode = "ROR A";
op_ror();
break;
}
case 0x6C: //JMP (M)
{
cycles += 5;
last_executed_opcode = "JMP (M) $";
WideAddress pointer = fetch_operand_address(ADDRESSING_ABSOLUTE);
WideAddress destination = dereference_indirect_address(pointer);
op_jmp(destination);
break;
}
case 0x6D:
{
cycles += 4;
last_executed_opcode = "ADC M $";
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x6E:
{
cycles += 6;
last_executed_opcode = "ROR $";
op_ror(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x70:
{
cycles += 2;
last_executed_opcode = "BVS $";
op_bvs(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x71:
{
cycles += 5;
last_executed_opcode = "ADC (ZP),Y $";
op_adc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0x75:
{
cycles += 4;
last_executed_opcode = "ADC ZP,X $";
op_adc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x76:
{
cycles += 6;
last_executed_opcode = "ROR ZP,X $";
op_ror(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x78: //SEI - set interrupt flag
{
cycles += 2;
last_executed_opcode = "SEI";
op_sei();
break;
}
case 0x7E:
{
cycles += 7;
last_executed_opcode = "ROR $,X $";
op_ror(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0x79:
{
cycles += 4;
last_executed_opcode = "ADC M,Y $";
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0x7D:
{
cycles += 4;
last_executed_opcode = "ADC M,X $";
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0x81:
{
cycles += 6;
last_executed_opcode = "STA (ZP,X)";
op_sta(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0x84: //STY - store Y into zeropage memory - OK
{
cycles += 3;
last_executed_opcode = "STY $";
op_sty(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x85: //STA - store A into zeropage memory - OK
{
cycles += 3;
last_executed_opcode = "STA $";
op_sta(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x86: //STX - store X into zeropage memory - OK
{
cycles += 3;
last_executed_opcode = "STX $";
op_stx(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0x88: //DEY - OK
{
cycles += 2;
last_executed_opcode = "DEY";
op_dey();
break;
}
case 0x8A: //TXA - X -> A - OK
{
cycles += 2;
last_executed_opcode = "TXA";
op_txa();
break;
}
case 0x8C:
{
cycles += 4;
last_executed_opcode = "STY $";
op_sty(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x8D:
{
cycles += 4;
last_executed_opcode = "STA $";
op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x8E:
{
cycles += 4;
last_executed_opcode = "STX $";
op_stx(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0x90:
{
cycles += 2;
last_executed_opcode = "BCC $";
op_bcc(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0x91:
{
cycles += 6;
last_executed_opcode = "STA (ZP),Y $";
op_sta(fetch_operand_address(ADDRESSING_INDIRECT_Y));
break;
}
case 0x94:
{
cycles += 4;
last_executed_opcode = "STY ZP,X $"; //OK
op_sty(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x95:
{
cycles += 4;
last_executed_opcode = "STA ZP,X $"; //OK
op_sta(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0x96:
{
cycles += 4;
last_executed_opcode = "STX ZP,Y $"; //OK
op_stx(fetch_operand_address(ADDRESSING_ZEROPAGE_Y));
break;
}
case 0x98: //TYA - Y -> A - OK
{
cycles += 2;
last_executed_opcode = "TYA";
op_tya();
break;
}
case 0x99:
{
cycles += 5;
last_executed_opcode = "STA M,Y $";
op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE_Y));
break;
}
case 0x9A: //TXS - X -> SP - OK
{
cycles += 2;
last_executed_opcode = "TXS";
op_txs();
break;
}
case 0x9D:
{
cycles += 5;
last_executed_opcode = "STA M,X $";
op_sta(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0xA0: //LDY - immediate - OK
{
cycles += 2;
last_executed_opcode = "LDY #$";
op_ldy(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xA1:
{
cycles += 6;
last_executed_opcode = "LDA (ZP,X)";
op_lda(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0xA2: //LDX - immediate - OK
{
cycles += 2;
last_executed_opcode = "LDX #$";
op_ldx(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xA4:
{
cycles += 3;
last_executed_opcode = "LDY $";
op_ldy(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xA5: //LDA - Zeropage - Load A from zeropage operand.
{
cycles += 3;
last_executed_opcode = "LDA $";
op_lda(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xA6:
{
cycles += 3;
last_executed_opcode = "LDX $";
op_ldx(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xA8: //TAY - OK
{
cycles += 2;
last_executed_opcode = "TAY";
op_tay();
break;
}
case 0xA9: //LDA - Immediate - Load A from immediate value - OK
{
cycles += 2;
last_executed_opcode = "LDA #$";
op_lda(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xAA: //TAX - A -> X - OK
{
cycles += 2;
last_executed_opcode = "TAX";
op_tax();
break;
}
case 0xAC:
{
cycles += 4;
last_executed_opcode = "LDY $";
op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xAD:
{
cycles += 4;
last_executed_opcode = "LDA $";
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xAE:
{
cycles += 4;
last_executed_opcode = "LDX $";
op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xB0:
{
cycles += 2;
last_executed_opcode = "BCS $";
op_bcs(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xB1:
{
cycles += 5;
last_executed_opcode = "LDA (ZP),Y $";
op_lda(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0xB4:
{
cycles += 4;
last_executed_opcode = "LDY ZP,X $";
op_ldy(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xB5: //LDA,X - Load A from Zeropage+X value
{
cycles += 4;
last_executed_opcode = "LDA ZP,X $";
op_lda(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xB6:
{
cycles += 4;
last_executed_opcode = "LDX ZP,Y $";
op_ldx(fetch_operand_address(ADDRESSING_ZEROPAGE_Y));
break;
}
case 0xB8: //CLV - OK
{
cycles += 2;
last_executed_opcode = "CLV";
op_clv();
break;
}
case 0xB9: //LDA Absolute,Y - OK
{
cycles += 4;
last_executed_opcode = "LDA M,Y $";
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0xBA: //TSX - SP -> X - OK
{
cycles += 2;
last_executed_opcode = "TSX";
op_tsx();
break;
}
case 0xBC:
{
cycles += 4;
last_executed_opcode = "LDY M,X $";
op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0xBD: //LDA - Absolute,X - OK
{
cycles += 4;
last_executed_opcode = "LDA M,X $";
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0xBE:
{
cycles += 4;
last_executed_opcode = "LDX M,Y $";
op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0xC0:
{
cycles += 2;
last_executed_opcode = "CPY #$";
op_cpy(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xC1:
{
cycles += 6;
last_executed_opcode = "CMP (ZP,X)";
op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0xC4:
{
cycles += 3;
last_executed_opcode = "CPY $";
op_cpy(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xC5:
{
cycles += 3;
last_executed_opcode = "CMP $";
op_cmp(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xC6:
{
cycles += 5;
last_executed_opcode = "DEC $";
op_dec(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xC8: //INY - increment Y - OK
{
cycles += 2;
last_executed_opcode = "INY";
op_iny();
break;
}
case 0xC9:
{
cycles += 2;
last_executed_opcode = "CMP #$";
op_cmp(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xCA: //DEX - OK
{
cycles += 2;
last_executed_opcode = "DEX";
op_dex();
break;
}
case 0xCC:
{
cycles += 4;
last_executed_opcode = "CPY $";
op_cpy(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xCD:
{
cycles += 4;
last_executed_opcode = "CMP $";
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xCE:
{
cycles += 6;
last_executed_opcode = "DEC $";
op_dec(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xD0: //BNE
{
cycles += 2;
last_executed_opcode = "BNE $";
op_bne(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xD1:
{
cycles += 5;
last_executed_opcode = "CMP (ZP),Y";
op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0xD5:
{
cycles += 4;
last_executed_opcode = "CMP ZP,X $";
op_cmp(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xD6:
{
cycles += 6;
last_executed_opcode = "DEC ZP,X $";
op_dec(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xD8: //CLD - OK
{
cycles += 2;
last_executed_opcode = "CLD";
op_cld();
break;
}
case 0xD9:
{
cycles += 4;
last_executed_opcode = "CMP M,Y $";
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0xDD:
{
cycles += 4;
last_executed_opcode = "CMP M,X $";
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0xDE:
{
cycles += 7;
last_executed_opcode = "DEC M,X $";
op_dec(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
case 0xE0:
{
cycles += 2;
last_executed_opcode = "CPX #$";
op_cpx(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xE1:
{
cycles += 6;
last_executed_opcode = "SBC (ZP,X) $";
op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_X));
break;
}
case 0xE4:
{
cycles += 3;
last_executed_opcode = "CPX $";
op_cpx(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xE5:
{
cycles += 3;
last_executed_opcode = "SBC $";
op_sbc(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xE6: //INC, increment zero-page memory address
{
cycles += 5;
last_executed_opcode = "INC $";
op_inc(fetch_operand_address(ADDRESSING_ZEROPAGE));
break;
}
case 0xE8: //INX - increment X - OK
{
cycles += 2;
last_executed_opcode = "INX";
op_inx();
break;
}
case 0xE9:
{
cycles += 2;
last_executed_opcode = "SBC #$";
op_sbc(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xEA: //NOP - OK
{
cycles += 2;
last_executed_opcode = "NOP";
break;
}
case 0xEC:
{
cycles += 4;
last_executed_opcode = "CPX $";
op_cpx(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xED:
{
cycles += 4;
last_executed_opcode = "SBC $";
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xEE:
{
cycles += 6;
last_executed_opcode = "INC $";
op_inc(fetch_operand_address(ADDRESSING_ABSOLUTE));
break;
}
case 0xF0:
{
cycles += 2;
last_executed_opcode = "BEQ $";
op_beq(fetch_operand(ADDRESSING_IMMEDIATE));
break;
}
case 0xF1:
{
cycles += 5;
last_executed_opcode = "SBC (ZP),Y $";
op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
break;
}
case 0xF5:
{
cycles += 4;
last_executed_opcode = "SBC ZP,X $";
op_sbc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xF6:
{
cycles += 6;
last_executed_opcode = "INC ZP,X $";
op_inc(fetch_operand_address(ADDRESSING_ZEROPAGE_X));
break;
}
case 0xF8: //SED - set decimal flag - OK
{
cycles += 2;
last_executed_opcode = "SED";
op_sed();
break;
}
case 0xF9:
{
cycles += 4;
last_executed_opcode = "SBC M,Y $";
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
break;
}
case 0xFD:
{
cycles += 4;
last_executed_opcode = "SBC M,X $";
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
break;
}
case 0xFE:
{
cycles += 7;
last_executed_opcode = "INC M,X $";
op_inc(fetch_operand_address(ADDRESSING_ABSOLUTE_X));
break;
}
default:
printf("Illegal instruction %x! Aborting execution.", opcode);
std::exit(2);
}
}
//check some flags
void CPU::set_zero_and_sign_flags(uint8_t data) {
if (data == 0) {
FLAG_ZERO = true;
}
else {
FLAG_ZERO = false;
}
if (data & 0x80) {
FLAG_SIGN = true;
}
else {
FLAG_SIGN = false;
}
}
/* Stack */
//Push a value onto the stack and decrement SP.
void CPU::stack_push(uint8_t data) {
MemoryMap::Instance()->write_byte(0x0100 + stack_pointer, data);
stack_pointer--;
}
//Increment SP and pop the value we find.
uint8_t CPU::stack_pop() {
stack_pointer++;
uint8_t data = MemoryMap::Instance()->read_byte(0x0100 + stack_pointer);
return data;
}
/* Memory read/write */
uint8_t CPU::fetch_memory_byte(WideAddress address, bool incrementPc) {
if (incrementPc) {
program_counter++;
}
return MemoryMap::Instance()->read_byte(address);
}
uint8_t CPU::fetch_memory_byte(uint16_t address, bool incrementPc) {
return fetch_memory_byte(uintAddressToWideAddress(address), incrementPc);
}
uint8_t CPU::fetch_zero_page_byte(uint8_t address) {
return MemoryMap::Instance()->read_zero_page_byte(address);
}
void CPU::write_memory_byte(uint16_t address, uint8_t data) {
MemoryMap::Instance()->write_byte(address, data);
}
void CPU::write_memory_byte(WideAddress address, uint8_t data) {
MemoryMap::Instance()->write_byte(address, data);
}
void CPU::write_zero_page_byte(uint8_t address, uint8_t data) {
MemoryMap::Instance()->write_zero_page_byte(address, data);
}
/* PSW */
uint8_t CPU::getProgramStatusWord() {
uint8_t psw = 0x20;
if (FLAG_SIGN) {
psw += 0x80;
}
if (FLAG_OVERFLOW) {
psw += 0x40;
}
if (FLAG_BREAKPOINT) {
psw += 0x10;
}
if (FLAG_DECIMAL) {
psw += 0x08;
}
if (FLAG_INTERRUPT) {
psw += 0x04;
}
if (FLAG_ZERO) {
psw += 0x02;
}
if (FLAG_CARRY) {
psw += 0x01;
}
return psw;
}
void CPU::setProgramStatusWord(uint8_t psw) {
FLAG_SIGN = ((0x80 & psw) == 0x80);
FLAG_OVERFLOW = ((0x40 & psw) == 0x40);
FLAG_BREAKPOINT = ((0x10 & psw) == 0x10);
FLAG_DECIMAL = ((0x08 & psw) == 0x08);
FLAG_INTERRUPT = ((0x04 & psw) == 0x04);
FLAG_ZERO = ((0x02 & psw) == 0x02);
FLAG_CARRY = ((0x01 & psw) == 0x01);
}
/* Debug */
uint8_t CPU::getAccumulator() {
return accumulator;
}
bool CPU::frameInstructionsComplete() {
return cycles >= maxCycles;
}
void CPU::resetCycleCounter() {
cycles = 0;
}
/* Interop */
bool CPU::getSignFlag() {
return FLAG_SIGN;
}
bool CPU::getOverflowFlag() {
return FLAG_OVERFLOW;
}
bool CPU::getBreakpointFlag() {
return FLAG_BREAKPOINT;
}
bool CPU::getDecimalFlag() {
return FLAG_DECIMAL;
}
bool CPU::getInterruptFlag() {
return FLAG_INTERRUPT;
}
bool CPU::getZeroFlag() {
return FLAG_ZERO;
}
bool CPU::getCarryFlag() {
return FLAG_CARRY;
}
uint8_t CPU::getIndexX() {
return index_x;
}
uint8_t CPU::getIndexY() {
return index_y;
}
uint8_t CPU::getStackPointer() {
return stack_pointer;
}
uint16_t CPU::getProgramCounter() {
return program_counter;
}
uint16_t CPU::getOldProgramCounter() {
return old_program_counter;
}