2015-11-29 20:28:32 +00:00
|
|
|
#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.
|
2015-12-05 03:10:31 +00:00
|
|
|
bool CPU::lastInstructionCrossedPageBoundary = false;
|
|
|
|
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_ora(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x1D:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "ORA M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_ora(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_and(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x3D:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "AND M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_and(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_eor(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x5D:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "EOR M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_eor(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_adc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x7D:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "ADC M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_adc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_lda(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_ldy(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xBD: //LDA - Absolute,X - OK
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "LDA M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_lda(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xBE:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "LDX M,Y $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_ldx(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_cmp(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xDD:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "CMP M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_cmp(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_sbc(fetch_operand_address(ADDRESSING_INDIRECT_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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 $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_Y), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xFD:
|
|
|
|
{
|
|
|
|
cycles += 4;
|
|
|
|
last_executed_opcode = "SBC M,X $";
|
2015-12-05 03:10:31 +00:00
|
|
|
op_sbc(fetch_operand_address(ADDRESSING_ABSOLUTE_X), true);
|
2015-11-29 20:28:32 +00:00
|
|
|
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;
|
|
|
|
}
|