2021-10-20 20:39:29 +00:00
|
|
|
#include "cpu.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
#include "log.h"
|
2021-10-20 20:39:29 +00:00
|
|
|
#include "bus.h"
|
|
|
|
|
2021-10-29 20:17:01 +00:00
|
|
|
static inline void Push(struct Bus* bus, Byte val)
|
2021-10-22 22:47:33 +00:00
|
|
|
{
|
|
|
|
writeBus(bus, 0x0100 + (bus->cpu->sp--), val);
|
|
|
|
}
|
|
|
|
|
2021-10-29 20:17:01 +00:00
|
|
|
static inline Byte Pop(struct Bus* bus)
|
2021-10-22 22:47:33 +00:00
|
|
|
{
|
2021-10-30 17:49:02 +00:00
|
|
|
return readBus(bus, 0x0100 + (++bus->cpu->sp));
|
2021-10-22 22:47:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 20:39:29 +00:00
|
|
|
struct CPU* createCPU(struct Bus* parent)
|
|
|
|
{
|
|
|
|
struct CPU* cpu = (struct CPU*)malloc(sizeof(struct CPU));
|
|
|
|
if (cpu == NULL)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to create CPU, aborting.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-10-21 17:24:53 +00:00
|
|
|
// TODO: THIS IS JUST FOR THE TEST ROM
|
2021-10-23 18:36:38 +00:00
|
|
|
cpu->pc.word = 0xC000;
|
2021-10-30 17:49:02 +00:00
|
|
|
cpu->pc.word = ((Word)readBus(parent, 0xFFFD) << 8) | readBus(parent, 0xFFFC);
|
2021-10-21 17:24:53 +00:00
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
cpu->status.raw = 0x34;
|
|
|
|
cpu->acc = 0;
|
|
|
|
cpu->x = 0;
|
|
|
|
cpu->y = 0;
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->sp = 0xFD;
|
2021-10-22 15:49:20 +00:00
|
|
|
|
|
|
|
cpu->remainingCycles = 7;
|
|
|
|
cpu->totalCycles = 0;
|
|
|
|
|
2021-10-24 14:52:15 +00:00
|
|
|
cpu->irq = 0;
|
|
|
|
cpu->nmi = 0;
|
|
|
|
|
2021-10-20 20:39:29 +00:00
|
|
|
cpu->bus = parent;
|
|
|
|
return cpu;
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroyCPU(struct CPU* cpu)
|
|
|
|
{
|
|
|
|
free(cpu);
|
|
|
|
}
|
2021-10-21 17:24:53 +00:00
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
int tickCPU(struct CPU* cpu)
|
2021-10-21 17:24:53 +00:00
|
|
|
{
|
2021-10-22 23:29:15 +00:00
|
|
|
if (cpu->remainingCycles == -1) // Jammed
|
|
|
|
return 1;
|
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
cpu->remainingCycles--;
|
|
|
|
cpu->totalCycles += 1;
|
|
|
|
|
2021-10-24 14:52:15 +00:00
|
|
|
if (cpu->remainingCycles == 1)
|
|
|
|
{
|
|
|
|
// Handle interrupts
|
|
|
|
if (cpu->nmi)
|
|
|
|
{
|
|
|
|
Push(cpu->bus, cpu->pc.hi);
|
|
|
|
Push(cpu->bus, cpu->pc.lo);
|
|
|
|
Push(cpu->bus, cpu->status.raw & 0b11101111);
|
|
|
|
|
|
|
|
cpu->nmi = 0;
|
|
|
|
|
2021-10-30 19:01:27 +00:00
|
|
|
// printf("NMI TRIGGERED FROM $%04x\n", cpu->pc.word);
|
2021-10-24 14:52:15 +00:00
|
|
|
cpu->pc.lo = readBus(cpu->bus, 0xFFFA);
|
|
|
|
cpu->pc.hi = readBus(cpu->bus, 0xFFFB);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cpu->status.id)
|
|
|
|
{
|
|
|
|
if (cpu->irq)
|
|
|
|
{
|
|
|
|
Push(cpu->bus, cpu->pc.hi);
|
|
|
|
Push(cpu->bus, cpu->pc.lo);
|
|
|
|
Push(cpu->bus, cpu->status.raw & 0b11101111);
|
|
|
|
|
|
|
|
cpu->status.id = 1;
|
|
|
|
cpu->pc.lo = readBus(cpu->bus, 0xFFFE);
|
|
|
|
cpu->pc.hi = readBus(cpu->bus, 0xFFFF);
|
|
|
|
|
|
|
|
cpu->irq = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
if (cpu->remainingCycles == 0)
|
2021-10-21 17:24:53 +00:00
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
prepareFetch(cpu);
|
2021-10-22 15:49:20 +00:00
|
|
|
execute(cpu);
|
|
|
|
return 1;
|
2021-10-21 17:24:53 +00:00
|
|
|
}
|
|
|
|
|
2021-10-22 15:49:20 +00:00
|
|
|
return 0;
|
2021-10-21 17:24:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tickInstr(struct CPU* cpu)
|
|
|
|
{
|
2021-10-22 15:49:20 +00:00
|
|
|
while (!tickCPU(cpu));
|
2021-10-21 17:24:53 +00:00
|
|
|
}
|
|
|
|
|
2021-10-30 17:36:32 +00:00
|
|
|
void prepareFetch(struct CPU* cpu)
|
2021-10-21 17:24:53 +00:00
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte opcodeVal = readBus(cpu->bus, cpu->pc.word);
|
2021-10-21 17:24:53 +00:00
|
|
|
cpu->currentOpcode = OPCODE_TABLE + opcodeVal;
|
|
|
|
|
2021-10-22 14:39:48 +00:00
|
|
|
if (cpu->currentOpcode->op == XXX)
|
2021-10-21 17:24:53 +00:00
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
fprintf(stderr, "Unknown opcode: %02X", opcodeVal);
|
2021-10-21 17:24:53 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->pc.word++;
|
2021-10-22 14:39:48 +00:00
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
2021-10-21 17:24:53 +00:00
|
|
|
|
2021-10-22 14:39:48 +00:00
|
|
|
switch (cpu->currentOpcode->addr)
|
|
|
|
{
|
|
|
|
case ACC:
|
|
|
|
cpu->fetchedVal = cpu->acc;
|
2021-10-22 22:47:33 +00:00
|
|
|
return;
|
2021-10-22 14:39:48 +00:00
|
|
|
|
|
|
|
case ABS:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte lo = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Byte hi = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
cpu->fetchedAddress = ((Word)hi << 8) | lo;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ABX:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte lo = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Byte hi = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
Word addr = ((Word)hi << 8) | lo;
|
|
|
|
|
|
|
|
cpu->fetchedAddress = addr + cpu->x;
|
|
|
|
|
|
|
|
if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ABY:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte lo = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Byte hi = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
Word addr = ((Word)hi << 8) | lo;
|
|
|
|
|
|
|
|
cpu->fetchedAddress = addr + cpu->y;
|
|
|
|
|
|
|
|
if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case IMM:
|
2021-10-30 17:36:32 +00:00
|
|
|
cpu->fetchedAddress = cpu->pc.word++;
|
2021-10-22 22:47:33 +00:00
|
|
|
return;
|
2021-10-22 14:39:48 +00:00
|
|
|
|
|
|
|
case IMP:
|
2021-10-22 22:47:33 +00:00
|
|
|
return;
|
2021-10-22 14:39:48 +00:00
|
|
|
|
|
|
|
case IND:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte lo = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Byte hi = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Word addr = ((Word)hi << 8);
|
2021-10-22 14:39:48 +00:00
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedAddress = ((Word)readBus(cpu->bus, addr | ((lo + 1) & 0xFF)) << 8) | readBus(cpu->bus, addr | lo);
|
2021-10-22 14:39:48 +00:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case INDX:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte op = readBus(cpu->bus, cpu->pc.word++);
|
|
|
|
Byte lo = readBus(cpu->bus, (op + cpu->x) & 0xFF);
|
|
|
|
Byte hi = readBus(cpu->bus, (op + cpu->x + 1) & 0xFF);
|
2021-10-22 14:39:48 +00:00
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedAddress = ((Word)hi << 8) | lo;
|
2021-10-22 14:39:48 +00:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case INDY:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte op = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
Byte lo = readBus(cpu->bus, op);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte hi = readBus(cpu->bus, (op + 1) & 0xFF);
|
2021-10-22 14:39:48 +00:00
|
|
|
Word addr = ((Word)hi << 8) | lo;
|
|
|
|
|
|
|
|
cpu->fetchedAddress = addr + cpu->y;
|
|
|
|
|
|
|
|
if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case REL:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedRelAddress = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case ZPG:
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedAddress = readBus(cpu->bus, cpu->pc.word++);
|
2021-10-22 14:39:48 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ZPX:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->x) & 0xFF;
|
2021-10-22 14:39:48 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case ZPY:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->y) & 0xFF;
|
2021-10-22 14:39:48 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
}
|
2021-10-30 17:36:32 +00:00
|
|
|
}
|
2021-10-22 22:47:33 +00:00
|
|
|
|
2021-10-30 17:36:32 +00:00
|
|
|
void fetch(struct CPU* cpu)
|
|
|
|
{
|
2021-10-30 19:01:27 +00:00
|
|
|
if(cpu->currentOpcode->addr != IMP && cpu->currentOpcode->addr != ACC)
|
2021-10-30 17:36:32 +00:00
|
|
|
cpu->fetchedVal = readBus(cpu->bus, cpu->fetchedAddress);
|
2021-10-21 17:24:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void execute(struct CPU* cpu)
|
|
|
|
{
|
2021-10-24 14:52:15 +00:00
|
|
|
// LOG_BUS(cpu->bus);
|
2021-10-22 15:49:20 +00:00
|
|
|
|
2021-10-22 14:39:48 +00:00
|
|
|
switch (cpu->currentOpcode->op)
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
case ADC:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Word result = cpu->acc + cpu->fetchedVal + cpu->status.carry;
|
|
|
|
|
|
|
|
cpu->status.carry = (result > 0xFF);
|
|
|
|
cpu->status.overflow = ((~(cpu->acc ^ cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80);
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = ((result & 0xFF) == 0x00);
|
|
|
|
|
|
|
|
cpu->acc = result & 0xFF;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case AND:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->acc &= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->acc >> 7);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ASL:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
|
|
|
|
cpu->fetchedVal <<= 1;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
if (!cpu->currentOpcode->addr == ACC)
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
else
|
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BCC:
|
|
|
|
{
|
|
|
|
if (!cpu->status.carry)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BCS:
|
|
|
|
{
|
|
|
|
if (cpu->status.carry)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BEQ:
|
|
|
|
{
|
|
|
|
if (cpu->status.zero)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BMI:
|
|
|
|
{
|
|
|
|
if (cpu->status.negative)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BPL:
|
|
|
|
{
|
|
|
|
if (!cpu->status.negative)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
2021-10-28 14:57:37 +00:00
|
|
|
case BRK:
|
|
|
|
{
|
|
|
|
cpu->pc.word++;
|
|
|
|
Push(cpu->bus, cpu->pc.hi);
|
|
|
|
Push(cpu->bus, cpu->pc.lo);
|
|
|
|
Push(cpu->bus, cpu->status.raw | 0b00110000);
|
|
|
|
|
|
|
|
cpu->status.id = 1;
|
|
|
|
cpu->pc.hi = readBus(cpu->bus, 0xFFFF);
|
|
|
|
cpu->pc.lo = readBus(cpu->bus, 0xFFFE);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case BVC:
|
|
|
|
{
|
|
|
|
if (!cpu->status.overflow)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BVS:
|
|
|
|
{
|
|
|
|
if (cpu->status.overflow)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BIT:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->status.negative = (cpu->fetchedVal >> 7);
|
|
|
|
cpu->status.overflow = (cpu->fetchedVal >> 6);
|
|
|
|
cpu->status.zero = ((cpu->acc & cpu->fetchedVal) == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case BNE:
|
|
|
|
{
|
|
|
|
if (!cpu->status.zero)
|
|
|
|
{
|
|
|
|
Word target = cpu->pc.word + cpu->fetchedRelAddress;
|
|
|
|
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
if ((target & 0xFF00) != (cpu->pc.word & 0xFF00))
|
|
|
|
cpu->remainingCycles++;
|
|
|
|
|
|
|
|
cpu->pc.word = target;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CLC:
|
|
|
|
{
|
|
|
|
cpu->status.carry = 0;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CLD:
|
|
|
|
{
|
|
|
|
cpu->status.decimal = 0;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CLV:
|
|
|
|
{
|
|
|
|
cpu->status.overflow = 0;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CMP:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte result = cpu->acc - cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = (result == 0x00);
|
|
|
|
cpu->status.carry = (cpu->fetchedVal <= cpu->acc);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CPX:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte result = cpu->x - cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = (result == 0x00);
|
|
|
|
cpu->status.carry = (cpu->fetchedVal <= cpu->x);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CPY:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte result = cpu->y - cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = (result == 0x00);
|
|
|
|
cpu->status.carry = (cpu->fetchedVal <= cpu->y);
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case DCP:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
cpu->fetchedVal--;
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
Byte result = cpu->acc - cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = (result == 0x00);
|
|
|
|
cpu->status.carry = (cpu->fetchedVal <= cpu->acc);
|
|
|
|
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case DEC:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedVal--;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case DEX:
|
|
|
|
{
|
|
|
|
cpu->x--;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->x & 0x80) == 0x80);
|
2021-10-23 13:55:54 +00:00
|
|
|
cpu->status.zero = (cpu->x == 0x00);
|
2021-10-22 22:47:33 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case DEY:
|
|
|
|
{
|
|
|
|
cpu->y--;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->y & 0x80) == 0x80);
|
2021-10-24 11:47:11 +00:00
|
|
|
cpu->status.zero = (cpu->y == 0x00);
|
2021-10-22 22:47:33 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case EOR:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->acc ^= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->acc >> 7);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case INC:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->fetchedVal++;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case INX:
|
|
|
|
{
|
|
|
|
cpu->x++;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->x & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->x == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case INY:
|
|
|
|
{
|
|
|
|
cpu->y++;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->y & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->y == 0x00);
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case ISC:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
cpu->fetchedVal++;
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
Word result = cpu->acc + ~cpu->fetchedVal + cpu->status.carry;
|
|
|
|
|
|
|
|
cpu->status.carry = ((result & 0x8000) != 0x8000);
|
|
|
|
cpu->status.overflow = ((~(cpu->acc ^ ~cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80);
|
|
|
|
cpu->status.negative = ((result & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = ((result & 0xFF) == 0x00);
|
|
|
|
|
|
|
|
cpu->acc = result & 0xFF;
|
|
|
|
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 14:39:48 +00:00
|
|
|
case JMP:
|
|
|
|
{
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->pc.word = cpu->fetchedAddress;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case JSR:
|
|
|
|
{
|
|
|
|
cpu->pc.word--;
|
|
|
|
Push(cpu->bus, cpu->pc.hi);
|
|
|
|
Push(cpu->bus, cpu->pc.lo);
|
|
|
|
|
|
|
|
cpu->pc.word = cpu->fetchedAddress;
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 14:00:40 +00:00
|
|
|
case LAX:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 14:00:40 +00:00
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
cpu->x = cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case LDA:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->acc >> 7);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case LDX:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->x = cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->x >> 7);
|
|
|
|
cpu->status.zero = (cpu->x == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case LDY:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->y = cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->y >> 7);
|
|
|
|
cpu->status.zero = (cpu->y == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case LSR:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->status.negative = 0;
|
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01);
|
|
|
|
|
|
|
|
cpu->fetchedVal >>= 1;
|
|
|
|
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
if (!cpu->currentOpcode->addr == ACC)
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
else
|
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case NOP:
|
|
|
|
{
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ORA:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
cpu->acc |= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->acc >> 7);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case PHA:
|
|
|
|
{
|
|
|
|
Push(cpu->bus, cpu->acc);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case PHP:
|
|
|
|
{
|
|
|
|
Push(cpu->bus, cpu->status.raw | 0b00110000);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case PLA:
|
|
|
|
{
|
|
|
|
cpu->acc = Pop(cpu->bus);
|
|
|
|
|
|
|
|
cpu->status.negative = (cpu->acc >> 7);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case PLP:
|
|
|
|
{
|
|
|
|
cpu->status.raw = Pop(cpu->bus) | 0b00110000;
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case RLA:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
Byte oldCarry = cpu->status.carry;
|
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
|
|
|
|
cpu->fetchedVal <<= 1;
|
|
|
|
cpu->fetchedVal |= oldCarry;
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
cpu->acc &= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case ROL:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte oldCarry = cpu->status.carry;
|
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
|
|
|
|
cpu->fetchedVal <<= 1;
|
|
|
|
cpu->fetchedVal |= oldCarry;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
if (!cpu->currentOpcode->addr == ACC)
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
else
|
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ROR:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Byte oldCarry = cpu->status.carry;
|
|
|
|
cpu->status.negative = oldCarry;
|
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01);
|
|
|
|
|
|
|
|
cpu->fetchedVal >>= 1;
|
|
|
|
cpu->fetchedVal |= (oldCarry << 7);
|
|
|
|
|
|
|
|
cpu->status.zero = (cpu->fetchedVal == 0x00);
|
|
|
|
|
|
|
|
if (!cpu->currentOpcode->addr == ACC)
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
else
|
|
|
|
cpu->acc = cpu->fetchedVal;
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case RRA:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
Byte oldCarry = cpu->status.carry;
|
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01);
|
|
|
|
|
|
|
|
cpu->fetchedVal >>= 1;
|
|
|
|
cpu->fetchedVal |= (oldCarry << 7);
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
|
|
|
|
Word result = cpu->acc + cpu->fetchedVal + cpu->status.carry;
|
|
|
|
|
|
|
|
cpu->status.carry = (result > 0xFF);
|
|
|
|
cpu->status.overflow = ((~(cpu->acc ^ cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80);
|
|
|
|
cpu->status.negative = (result >> 7);
|
|
|
|
cpu->status.zero = ((result & 0xFF) == 0x00);
|
|
|
|
|
|
|
|
cpu->acc = result & 0xFF;
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case RTI:
|
|
|
|
{
|
|
|
|
cpu->status.raw = Pop(cpu->bus) | 0b00110000;
|
|
|
|
|
|
|
|
cpu->pc.lo = Pop(cpu->bus);
|
|
|
|
cpu->pc.hi = Pop(cpu->bus);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case RTS:
|
|
|
|
{
|
|
|
|
cpu->pc.lo = Pop(cpu->bus);
|
|
|
|
cpu->pc.hi = Pop(cpu->bus);
|
|
|
|
cpu->pc.word++;
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case SAX:
|
|
|
|
{
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->acc & cpu->x);
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case SBC:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-22 22:47:33 +00:00
|
|
|
Word result = cpu->acc + ~cpu->fetchedVal + cpu->status.carry;
|
|
|
|
|
|
|
|
cpu->status.carry = ((result & 0x8000) != 0x8000);
|
|
|
|
cpu->status.overflow = ((~(cpu->acc ^ ~cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80);
|
|
|
|
cpu->status.negative = ((result & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = ((result & 0xFF) == 0x00);
|
|
|
|
|
|
|
|
cpu->acc = result & 0xFF;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case SEC:
|
|
|
|
{
|
|
|
|
cpu->status.carry = 1;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case SED:
|
|
|
|
{
|
|
|
|
cpu->status.decimal = 1;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case SEI:
|
|
|
|
{
|
|
|
|
cpu->status.id = 1;
|
|
|
|
} break;
|
|
|
|
|
2021-10-23 16:52:09 +00:00
|
|
|
case SLO:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80);
|
|
|
|
|
|
|
|
cpu->fetchedVal <<= 1;
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
cpu->acc |= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case SRE:
|
|
|
|
{
|
2021-10-30 17:36:32 +00:00
|
|
|
fetch(cpu);
|
2021-10-23 16:52:09 +00:00
|
|
|
cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01);
|
|
|
|
|
|
|
|
cpu->fetchedVal >>= 1;
|
|
|
|
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal);
|
|
|
|
cpu->acc ^= cpu->fetchedVal;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles;
|
|
|
|
} break;
|
|
|
|
|
2021-10-22 22:47:33 +00:00
|
|
|
case STA:
|
|
|
|
{
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->acc);
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it)
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case STX:
|
|
|
|
{
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->x);
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it)
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case STY:
|
|
|
|
{
|
|
|
|
writeBus(cpu->bus, cpu->fetchedAddress, cpu->y);
|
|
|
|
cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it)
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TAX:
|
|
|
|
{
|
|
|
|
cpu->x = cpu->acc;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->x & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->x == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TAY:
|
|
|
|
{
|
|
|
|
cpu->y = cpu->acc;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->y & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->y == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TSX:
|
|
|
|
{
|
|
|
|
cpu->x = cpu->sp;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->x & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->x == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TXA:
|
|
|
|
{
|
|
|
|
cpu->acc = cpu->x;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TXS:
|
|
|
|
{
|
|
|
|
cpu->sp = cpu->x;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case TYA:
|
|
|
|
{
|
|
|
|
cpu->acc = cpu->y;
|
|
|
|
|
|
|
|
cpu->status.negative = ((cpu->acc & 0x80) == 0x80);
|
|
|
|
cpu->status.zero = (cpu->acc == 0x00);
|
2021-10-22 14:39:48 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unknown instruction: %s", cpu->currentOpcode->str);
|
|
|
|
exit(1);
|
|
|
|
break;
|
|
|
|
}
|
2021-10-21 17:24:53 +00:00
|
|
|
}
|
2021-10-24 14:52:15 +00:00
|
|
|
|
|
|
|
void IRQ(struct CPU* cpu)
|
|
|
|
{
|
|
|
|
cpu->irq = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NMI(struct CPU* cpu)
|
|
|
|
{
|
|
|
|
cpu->nmi = 1;
|
|
|
|
}
|