reinette-II-plus/puce6502.c

2047 lines
50 KiB
C

/*
puce6502 - MOS 6502 cpu emulator
Last modified 21st of June 2021
Copyright (c) 2018 Arthur Ferreira
This version has been modified for reinette II plus, a french Apple II plus
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).
Please download the latest version from
https://github.com/ArthurFerreira2/puce6502
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// set to zero for 'normal' use
// or to 1 if you want to run the functionnal tests
#define _FUNCTIONNAL_TESTS 0
#include "puce6502.h"
#define CARRY 0x01
#define ZERO 0x02
#define INTR 0x04
#define DECIM 0x08
#define BREAK 0x10
#define UNDEF 0x20
#define OFLOW 0x40
#define SIGN 0x80
#if _FUNCTIONNAL_TESTS
// for functionnal tests, see main()
uint8_t RAM[65536];
inline uint8_t readMem(uint16_t address) { return RAM[address]; }
inline void writeMem(uint16_t address, uint8_t value) { RAM[address] = value; }
#else
// user provided functions
extern uint8_t readMem(uint16_t address);
extern void writeMem(uint16_t address, uint8_t value);
#endif
unsigned long long int ticks = 0; // accumulated number of clock cycles
static uint16_t PC; // Program Counter
static uint8_t A, X, Y, SP; // Accumulator, X and y indexes and Stack Pointer
static union {
uint8_t byte;
struct {
uint8_t C : 1; // Carry
uint8_t Z : 1; // Zero
uint8_t I : 1; // Interupt-disable
uint8_t D : 1; // Decimal
uint8_t B : 1; // Break
uint8_t U : 1; // Undefined
uint8_t V : 1; // Overflow
uint8_t S : 1; // Sign
};
} P; // Processor Status
void puce6502RST() { // Reset
PC = readMem(0xFFFC) | (readMem(0xFFFD) << 8);
SP = 0xFD;
P.I = 1;
P.U = 1;
ticks += 7;
}
void puce6502IRQ() { // Interupt Request
if (!P.I) return;
P.I = 1;
PC++;
writeMem(0x100 + SP, (PC >> 8) & 0xFF);
SP--;
writeMem(0x100 + SP, PC & 0xFF);
SP--;
writeMem(0x100 + SP, P.byte & ~BREAK);
SP--;
PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8);
ticks += 7;
}
void puce6502NMI() { // Non Maskable Interupt
P.I = 1;
PC++;
writeMem(0x100 + SP, (PC >> 8) & 0xFF);
SP--;
writeMem(0x100 + SP, PC & 0xFF);
SP--;
writeMem(0x100 + SP, P.byte & ~BREAK);
SP--;
PC = readMem(0xFFFA) | (readMem(0xFFFB) << 8);
ticks += 7;
}
/*
Addressing modes abreviations used in the comments down below :
IMP : Implied or Implicit : DEX, RTS, CLC - 25 instructions
ACC : Accumulator : ASL A, ROR A, DEC A - 4 instructions
IMM : Immediate : LDA #$A5 - 11
ZPG : Zero Page : LDA $81 - 21 instructions
ZPX : Zero Page Indexed with X : LDA $55,X - 16 instructions
ZPY : Zero Page Indexed with Y : LDX $55,Y - 2 instructions
REL : Relative : BEQ LABEL12 - 8 instructions
ABS : Absolute : LDA $2000 - 23 instructions
ABX : Absolute Indexed with X : LDA $2000,X - 15 instructions
ABY : Absolute Indexed with Y : LDA $2000,Y - 9 instructions
IND : Indirect : JMP ($1020) - 1 instruction
IZX : ZP Indexed Indirect with X (Preindexed) : LDA ($55,X) - 8 instructions
IZY : ZP Indirect Indexed with Y (Postindexed) : LDA ($55),Y - 8 instructions
*/
uint16_t puce6502Exec(unsigned long long int cycleCount) {
register uint16_t address;
register uint8_t value8;
register uint16_t value16;
cycleCount += ticks; // cycleCount becomes the targeted ticks value
while (ticks < cycleCount) {
switch (readMem(PC++)) { // fetch instruction and increment Program Counter
case 0x00 : // IMP BRK
PC++;
writeMem(0x100 + SP, ((PC) >> 8) & 0xFF);
SP--;
writeMem(0x100 + SP, PC & 0xFF);
SP--;
writeMem(0x100 + SP, P.byte | BREAK);
SP--;
P.I = 1;
P.D = 0;
PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8);
ticks += 7;
break;
case 0x01 : // IZX ORA
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
A |= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0x05 : // ZPG ORA
A |= readMem(readMem(PC));
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0x06 : // ZPG ASL
address = readMem(PC);
PC++;
value16 = readMem(address) << 1;
P.C = value16 > 0xFF;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 5;
break;
case 0x08 : // IMP PHP
writeMem(0x100 + SP, P.byte | BREAK);
SP--;
ticks += 3;
break;
case 0x09 : // IMM ORA
A |= readMem(PC);
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x0A : // ACC ASL
value16 = A << 1;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x0D : // ABS ORA
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
A |= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x0E : // ABS ASL
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value16 = readMem(address) << 1;
P.C = value16 > 0xFF;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 6;
break;
case 0x10 : // REL BPL
address = readMem(PC);
PC++;
if (!P.S) { // jump taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0x11 : // IZY ORA
value8 = readMem(PC);
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing
address += Y;
A |= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x15 : // ZPX ORA
A |= readMem(readMem(PC) + X);
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x16 : // ZPX ASL
address = readMem(PC) + X;
PC++;
value16 = readMem(address) << 1;
writeMem(address, value16 & 0xFF);
P.C = value16 > 0xFF;
P.Z = value16 == 0;
P.S = (value16 & 0xFF) > 0x7F;
ticks += 6;
break;
case 0x18 : // IMP CLC
P.C = 0;
ticks += 2;
break;
case 0x19 : // ABY ORA
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
A |= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x1D : // ABX ORA
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
A |= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x1E : // ABX ASL
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value16 = readMem(address) << 1;
P.C = value16 > 0xFF;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 7;
break;
case 0x20 : // ABS JSR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
writeMem(0x100 + SP, (PC >> 8) & 0xFF);
SP--;
writeMem(0x100 + SP, PC & 0xFF);
SP--;
PC = address;
ticks += 6;
break;
case 0x21 : // IZX AND
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0x24 : // ZPG BIT
address = readMem(PC);
PC++;
value8 = readMem(address);
P.Z = (A & value8) == 0;
P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
ticks += 3;
break;
case 0x25 : // ZPG AND
A &= readMem(readMem(PC));
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0x26 : // ZPG ROL
address = readMem(PC);
PC++;
value16 = (readMem(address) << 1) | P.C;
P.C = (value16 & 0x100) != 0;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 5;
break;
case 0x28 : // IMP PLP
SP++;
P.byte = readMem(0x100 + SP) | UNDEF;
ticks += 4;
break;
case 0x29 : // IMM AND
A &= readMem(PC);
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x2A : // ACC ROL
value16 = (A << 1) | P.C;
P.C = (value16 & 0x100) != 0;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x2C : // ABS BIT
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
P.Z = (A & value8) == 0;
P.byte = (P.byte & 0x3F) | (value8 & 0xC0);
ticks += 4;
break;
case 0x2D : // ABS AND
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x2E : // ABS ROL
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value16 = (readMem(address) << 1) | P.C;
P.C = (value16 & 0x100) != 0;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 6;
break;
case 0x30 : // REL BMI
address = readMem(PC);
PC++;
if (P.S) { // branch taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0x31 : // IZY AND
value8 = readMem(PC);
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing
address += Y;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x35 : // ZPX AND
address = (readMem(PC) + X) & 0xFF;
PC++;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x36 : // ZPX ROL
address = (readMem(PC) + X) & 0xFF;
PC++;
value16 = (readMem(address) << 1) | P.C;
P.C = value16 > 0xFF;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 6;
break;
case 0x38 : // IMP SEC
P.C = 1;
ticks += 2;
break;
case 0x39 : // ABY AND
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x3D : // ABX AND
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
A &= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x3E : // ABX ROL
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value16 = (readMem(address) << 1) | P.C;
P.C = value16 > 0xFF;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 7;
break;
case 0x40 : // IMP RTI
SP++;
P.byte = readMem(0x100 + SP);
SP++;
PC = readMem(0x100 + SP);
SP++;
PC |= readMem(0x100 + SP) << 8;
ticks += 6;
break;
case 0x41 : // IZX EOR
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0x45 : // ZPG EOR
address = readMem(PC);
PC++;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0x46 : // ZPG LSR
address = readMem(PC);
PC++;
value8 = readMem(address);
P.C = (value8 & 1) != 0;
value8 = value8 >> 1;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 5;
break;
case 0x48 : // IMP PHA
writeMem(0x100 + SP, A);
SP--;
ticks += 3;
break;
case 0x49 : // IMM EOR
A ^= readMem(PC);
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x4A : // ACC LSR
P.C = (A & 1) != 0;
A = A >> 1;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x4C : // ABS JMP
PC = readMem(PC) | (readMem(PC + 1) << 8);
ticks += 3;
break;
case 0x4D : // ABS EOR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x4E : // ABS LSR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
P.C = (value8 & 1) != 0;
value8 = value8 >> 1;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 6;
break;
case 0x50 : // REL BVC
address = readMem(PC);
PC++;
if (!P.V) { // branch taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0x51 : // IZY EOR
value8 = readMem(PC);
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing
A ^= readMem(address + Y);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x55 : // ZPX EOR
address = (readMem(PC) + X) & 0xFF;
PC++;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x56 : // ZPX LSR
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
P.C = (value8 & 1) != 0;
value8 = value8 >> 1;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 6;
break;
case 0x58 : // IMP CLI
P.I = 0;
ticks += 2;
break;
case 0x59 : // ABY EOR
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x5D : // ABX EOR
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
A ^= readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0x5E : // ABX LSR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
P.C = (value8 & 1) != 0;
value8 = value8 >> 1;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 7;
break;
case 0x60 : // IMP RTS
SP++;
PC = readMem(0x100 + SP);
SP++;
PC |= readMem(0x100 + SP) << 8;
PC++;
ticks += 6;
break;
case 0x61 : // IZX ADC
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0x65 : // ZPG ADC
address = readMem(PC);
PC++;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0x66 : // ZPG ROR
address = readMem(PC);
PC++;
value8 = readMem(address);
value16 = (value8 >> 1) | (P.C << 7);
P.C = (value8 & 0x1) != 0;
value16 &= 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 5;
break;
case 0x68 : // IMP PLA
SP++;
A = readMem(0x100 + SP);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x69 : // IMM ADC
value8 = readMem(PC);
PC++;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x6A : // ACC ROR
value16 = (A >> 1) | (P.C << 7);
P.C = (A & 0x1) != 0;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x6C : // IND JMP
address = readMem(PC) | readMem(PC + 1) << 8;
PC = readMem(address) | (readMem(address + 1) << 8);
ticks += 5;
break;
case 0x6D : // ABS ADC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x6E : // ABS ROR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
value16 = (value8 >> 1) | (P.C << 7);
P.C = (value8 & 0x1) != 0;
value16 = value16 & 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 6;
break;
case 0x70 : // REL BVS
address = readMem(PC);
PC++;
if (P.V) { // branch taken
ticks++;
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
PC += address;
}
ticks += 2;
break;
case 0x71 : // IZY ADC
value8 = readMem(PC);
PC++;
address = readMem(value8);
if ((address + Y) & 0xFF00) // page crossing
ticks++;
value8++;
address |= readMem(value8) << 8;
address += Y;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 5;
break;
case 0x75 : // ZPX ADC
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x76 : // ZPX ROR
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
value16 = (value8 >> 1) | (P.C << 7);
P.C = (value8 & 0x1) != 0;
value16 = value16 & 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 6;
break;
case 0x78 : // IMP SEI
P.I = 1;
ticks += 2;
break;
case 0x79 : // ABY ADC
if ((readMem(PC) + Y) & 0xFF00)
ticks++;
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += Y;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x7D : // ABX ADC
if ((readMem(PC) + X) & 0xFF00)
ticks++;
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0x7E : // ABX ROR
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
value16 = (value8 >> 1) | (P.C << 7);
P.C = (value8 & 0x1) != 0; // TBR
value16 = value16 & 0xFF;
writeMem(address, value16);
P.Z = value16 == 0;
P.S = value16 > 0x7F;
ticks += 7;
break;
case 0x81 : // IZX STA
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
writeMem(address, A);
ticks += 6;
break;
case 0x84 : // ZPG STY
writeMem(readMem(PC), Y);
PC++;
ticks += 3;
break;
case 0x85 : // ZPG STA
writeMem(readMem(PC), A);
PC++;
ticks += 3;
break;
case 0x86 : // ZPG STX
writeMem(readMem(PC), X);
PC++;
ticks += 3;
break;
case 0x88 : // IMP DEY
Y--;
P.Z = (Y & 0xFF) == 0;
P.S = (Y & SIGN) != 0;
ticks += 2;
break;
case 0x8A : // IMP TXA
A = X;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x8C : // ABS STY
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
writeMem(address, Y);
ticks += 4;
break;
case 0x8D : // ABS STA
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
writeMem(address, A);
ticks += 4;
break;
case 0x8E : // ABS STX
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
writeMem(address, X);
ticks += 4;
break;
case 0x90 : // REL BCC
address = readMem(PC);
PC++;
if (!P.C) { // branch taken
ticks++;
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
PC += address;
}
ticks += 2;
break;
case 0x91 : // IZY STA
value8 = readMem(PC);
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
address += Y;
writeMem(address, A);
ticks += 6;
break;
case 0x94 : // ZPX STY
address = (readMem(PC) + X) & 0xFF;
PC++;
writeMem(address, Y);
ticks += 4;
break;
case 0x95 : // ZPX STA
writeMem((readMem(PC) + X) & 0xFF, A);
PC++;
ticks += 4;
break;
case 0x96 : // ZPY STX
writeMem((readMem(PC) + Y) & 0xFF, X);
PC++;
ticks += 4;
break;
case 0x98 : // IMP TYA
A = Y;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0x99 : // ABY STA
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += Y;
writeMem(address, A);
ticks += 5;
break;
case 0x9A : // IMP TXS
SP = X;
ticks += 2;
break;
case 0x9D : // ABX STA
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
writeMem(address, A);
ticks += 5;
break;
case 0xA0 : // IMM LDY
Y = readMem(PC);
PC++;
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 2;
break;
case 0xA1 : // IZX LDA
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
A = readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0xA2 : // IMM LDX
address = PC;
PC++;
X = readMem(address);
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 2;
break;
case 0xA4 : // ZPG LDY
Y = readMem(readMem(PC));
PC++;
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 3;
break;
case 0xA5 : // ZPG LDA
A = readMem(readMem(PC));
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0xA6 : // ZPG LDX
X = readMem(readMem(PC));
PC++;
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 3;
break;
case 0xA8 : // IMP TAY
Y = A;
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 2;
break;
case 0xA9 : // IMM LDA
A = readMem(PC);
PC++;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0xAA : // IMP TAX
X = A;
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 2;
break;
case 0xAC : // ABS LDY
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
Y = readMem(address);
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 4;
break;
case 0xAD : // ABS LDA
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
A = readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xAE : // ABS LDX
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
X = readMem(address);
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 4;
break;
case 0xB0 : // REL BCS
address = readMem(PC);
PC++;
if (P.C) { // branch taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0xB1 : // IZY LDA
value8 = readMem(PC);
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
A = readMem(address + Y);
ticks += (((address & 0xFF) + Y) & 0xFF00) ? 6 : 5; // page crossing
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0xB4 : // ZPX LDY
address = (readMem(PC) + X) & 0xFF;
PC++;
Y = readMem(address);
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 4;
break;
case 0xB5 : // ZPX LDA
address = (readMem(PC) + X) & 0xFF;
PC++;
A = readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xB6 : // ZPY LDX
address = (readMem(PC) + Y) & 0xFF;
PC++;
X = readMem(address);
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 4;
break;
case 0xB8 : // IMP CLV
P.V = 0;
ticks += 2;
break;
case 0xB9 : // ABY LDA
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
A = readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0xBA : // IMP TSX
X = SP;
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 2;
break;
case 0xBC : // ABX LDY
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
Y = readMem(address);
P.Z = Y == 0;
P.S = Y > 0x7F;
break;
case 0xBD : // ABX LDA
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
A = readMem(address);
P.Z = A == 0;
P.S = A > 0x7F;
break;
case 0xBE : // ABY LDX
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
X = readMem(address);
P.Z = X == 0;
P.S = X > 0x7F;
break;
case 0xC0 : // IMM CPY
value8 = readMem(PC);
PC++;
P.Z = ((Y - value8) & 0xFF) == 0;
P.S = ((Y - value8) & SIGN) != 0;
P.C = (Y >= value8) != 0;
ticks += 2;
break;
case 0xC1 : // IZX CMP
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
ticks += 6;
break;
case 0xC4 : // ZPG CPY
value8 = readMem(readMem(PC));
PC++;
P.Z = ((Y - value8) & 0xFF) == 0;
P.S = ((Y - value8) & SIGN) != 0;
P.C = (Y >= value8) != 0;
ticks += 3;
break;
case 0xC5 : // ZPG CMP
value8 = readMem(readMem(PC));
PC++;
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
ticks += 3;
break;
case 0xC6 : // ZPG DEC
address = readMem(PC);
PC++;
value8 = readMem(address);
--value8;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 5;
break;
case 0xC8 : // IMP INY
Y++;
P.Z = Y == 0;
P.S = Y > 0x7F;
ticks += 2;
break;
case 0xC9 : // IMM CMP
value8 = readMem(PC);
PC++;
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
ticks += 2;
break;
case 0xCA : // IMP DEX
X--;
P.Z = (X & 0xFF) == 0;
P.S = X > 0x7F;
ticks += 2;
break;
case 0xCC : // ABS CPY
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
P.Z = ((Y - value8) & 0xFF) == 0;
P.S = ((Y - value8) & SIGN) != 0;
P.C = (Y >= value8) != 0;
ticks += 4;
break;
case 0xCD : // ABS CMP
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
ticks += 4;
break;
case 0xCE : // ABS DEC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
value8--;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 3;
break;
case 0xD0 : // REL BNE
address = readMem(PC);
PC++;
if (!P.Z) { // branch taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0xD1 : // IZY CMP
value8 = readMem(PC);
PC++;
address = readMem(value8);
ticks += ((address + Y) & 0xFF00) ? 6 : 5; // page crossing
value8++;
address |= readMem(value8) << 8;
address += Y;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
break;
case 0xD5 : // ZPX CMP
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
ticks += 4;
break;
case 0xD6 : // ZPX DEC
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
value8--;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 6;
break;
case 0xD8 : // IMP CLD
P.D = 0;
ticks += 2;
break;
case 0xD9 : // ABY CMP
address = readMem(PC);
PC++;
ticks += ((address + Y) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += Y;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
break;
case 0xDD : // ABX CMP
address = readMem(PC);
PC++;
ticks += ((address + X) & 0xFF00) ? 5 : 4; // page crossing
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
P.Z = ((A - value8) & 0xFF) == 0;
P.S = ((A - value8) & SIGN) != 0;
P.C = (A >= value8) != 0;
break;
case 0xDE : // ABX DEC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
value8--;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = (value8 & SIGN) != 0;
ticks += 7;
break;
case 0xE0 : // IMM CPX
value8 = readMem(PC);
PC++;
P.Z = ((X - value8) & 0xFF) == 0;
P.S = ((X - value8) & SIGN) != 0;
P.C = (X >= value8) != 0;
ticks += 2;
break;
case 0xE1 : // IZX SBC
value8 = readMem(PC) + X;
PC++;
address = readMem(value8);
value8++;
address |= readMem(value8) << 8;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 6;
break;
case 0xE4 : // ZPG CPX
value8 = readMem(readMem(PC));
PC++;
P.Z = ((X - value8) & 0xFF) == 0;
P.S = ((X - value8) & SIGN) != 0;
P.C = (X >= value8) != 0;
ticks += 3;
break;
case 0xE5 : // ZPG SBC
value8 = readMem(readMem(PC));
PC++;
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 3;
break;
case 0xE6 : // ZPG INC
address = readMem(PC);
PC++;
value8 = readMem(address);
value8++;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 5;
break;
case 0xE8 : // IMP INX
X++;
P.Z = X == 0;
P.S = X > 0x7F;
ticks += 2;
break;
case 0xE9 : // IMM SBC
value8 = readMem(PC);
PC++;
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + (P.C);
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 2;
break;
case 0xEA: // IMP NOP
ticks += 2;
break;
case 0xEC : // ABS CPX
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
P.Z = ((X - value8) & 0xFF) == 0;
P.S = ((X - value8) & SIGN) != 0;
P.C = (X >= value8) != 0;
ticks += 4;
break;
case 0xED : // ABS SBC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xEE : // ABS INC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
value8 = readMem(address);
value8++;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 6;
break;
case 0xF0 : // REL BEQ
address = readMem(PC);
PC++;
if (P.Z) { // branch taken
ticks++;
if (address & SIGN)
address |= 0xFF00; // jump backward
if (((PC & 0xFF) + address) & 0xFF00) // page crossing
ticks++;
PC += address;
}
ticks += 2;
break;
case 0xF1 : // IZY SBC
value8 = readMem(PC);
PC++;
address = readMem(value8);
if ((address + Y) & 0xFF00) // page crossing
ticks++;
value8++;
address |= readMem(value8) << 8;
address += Y;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 5;
break;
case 0xF5 : // ZPX SBC
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xF6 : // ZPX INC
address = (readMem(PC) + X) & 0xFF;
PC++;
value8 = readMem(address);
value8++;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 6;
break;
case 0xF8 : // IMP SED
P.D = 1;
ticks += 2;
break;
case 0xF9 : // ABY SBC
address = readMem(PC);
PC++;
if ((address + Y) & 0xFF00) // page crossing
ticks++;
address |= readMem(PC) << 8;
PC++;
address += Y;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = value16 > 0xFF;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xFD : // ABX SBC
address = readMem(PC);
PC++;
if ((address + X) & 0xFF00) // page crossing
ticks++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
value8 ^= 0xFF;
if (P.D)
value8 -= 0x0066;
value16 = A + value8 + P.C;
P.V = ((value16 ^ A) & (value16 ^ value8) & 0x0080) != 0;
if (P.D)
value16 += ((((value16 + 0x66) ^ A ^ value8) >> 3) & 0x22) * 3;
P.C = (value16 & 0xFF00) != 0;
A = value16 & 0xFF;
P.Z = A == 0;
P.S = A > 0x7F;
ticks += 4;
break;
case 0xFE : // ABX INC
address = readMem(PC);
PC++;
address |= readMem(PC) << 8;
PC++;
address += X;
value8 = readMem(address);
value8++;
writeMem(address, value8);
P.Z = value8 == 0;
P.S = value8 > 0x7F;
ticks += 7;
break;
default: // invalid / undocumented opcode
ticks += 2; // as NOP
break;
} // end of switch
}
return PC;
}
// the code below was used during developpment for test and debug
// and is not required for normal operation
#include <stdio.h>
static const char* mn[256] = {
"BRK","ORA","UND","UND","UND","ORA","ASL","UND","PHP","ORA","ASL","UND","UND","ORA","ASL","UND",
"BPL","ORA","UND","UND","UND","ORA","ASL","UND","CLC","ORA","UND","UND","UND","ORA","ASL","UND",
"JSR","AND","UND","UND","BIT","AND","ROL","UND","PLP","AND","ROL","UND","BIT","AND","ROL","UND",
"BMI","AND","UND","UND","UND","AND","ROL","UND","SEC","AND","UND","UND","UND","AND","ROL","UND",
"RTI","EOR","UND","UND","UND","EOR","LSR","UND","PHA","EOR","LSR","UND","JMP","EOR","LSR","UND",
"BVC","EOR","UND","UND","UND","EOR","LSR","UND","CLI","EOR","UND","UND","UND","EOR","LSR","UND",
"RTS","ADC","UND","UND","UND","ADC","ROR","UND","PLA","ADC","ROR","UND","JMP","ADC","ROR","UND",
"BVS","ADC","UND","UND","UND","ADC","ROR","UND","SEI","ADC","UND","UND","UND","ADC","ROR","UND",
"UND","STA","UND","UND","STY","STA","STX","UND","DEY","UND","TXA","UND","STY","STA","STX","UND",
"BCC","STA","UND","UND","STY","STA","STX","UND","TYA","STA","TXS","UND","UND","STA","UND","UND",
"LDY","LDA","LDX","UND","LDY","LDA","LDX","UND","TAY","LDA","TAX","UND","LDY","LDA","LDX","UND",
"BCS","LDA","UND","UND","LDY","LDA","LDX","UND","CLV","LDA","TSX","UND","LDY","LDA","LDX","UND",
"CPY","CMP","UND","UND","CPY","CMP","DEC","UND","INY","CMP","DEX","UND","CPY","CMP","DEC","UND",
"BNE","CMP","UND","UND","UND","CMP","DEC","UND","CLD","CMP","UND","UND","UND","CMP","DEC","UND",
"CPX","SBC","UND","UND","CPX","SBC","INC","UND","INX","SBC","NOP","UND","CPX","SBC","INC","UND",
"BEQ","SBC","UND","UND","UND","SBC","INC","UND","SED","SBC","UND","UND","UND","SBC","INC","UND"
};
static const int am[256] = {
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x0 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x7 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0xA , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x0 , 0x0 ,
0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x8 , 0x8 , 0x9 , 0x0 ,
0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0
};
void dasm(uint16_t address) {
uint8_t op = readMem(address);
uint8_t b1 = readMem((address + 1) & 0xFFFF);
uint8_t b2 = readMem((address + 2) & 0xFFFF);
printf("%04X %02X ", address, op);
switch(am[op]) {
case 0x0: printf(" %s ", mn[op] ); break; // implied
case 0x1: printf(" %s A ", mn[op] ); break; // accumulator
case 0x2: printf("%02X %s #$%02X ", b1, mn[op],b1 ); break; // immediate
case 0x3: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // zero page
case 0x4: printf("%02X %s $%02X,X ", b1, mn[op],b1 ); break; // zero page, X indexed
case 0x5: printf("%02X %s $%02X,Y ", b1, mn[op],b1 ); break; // zero page, Y indexed
case 0x6: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // relative
case 0xC: printf("%02X %s ($%02X,X) ", b1, mn[op],b1 ); break; // X indexed, indirect
case 0xD: printf("%02X %s ($%02X),Y ", b1, mn[op],b1 ); break; // indirect, Y indexed
case 0x7: printf("%02X%02X %s $%02X%02X ",b1,b2,mn[op],b2,b1); break; // absolute
case 0x8: printf("%02X%02X %s $%02X%02X,X ",b1,b2,mn[op],b2,b1); break; // absolute, X indexed
case 0x9: printf("%02X%02X %s $%02X%02X,Y ",b1,b2,mn[op],b2,b1); break; // absolute, Y indexed
case 0xA: printf("%02X%02X %s ($%02X%02X) ",b1,b2,mn[op],b2,b1); break; // indirect
}
}
void printRegs() {
printf("A=%02X X=%02X Y=%02X S=%02X *S=%02X %c%c%c%c%c%c%c%c", \
A, X, Y, SP, readMem(0x100 + SP), \
P.S?'N':'-', P.V?'V':'-', P.U?'U':'.', P.B?'B':'-', \
P.D?'D':'-', P.I?'I':'-', P.Z?'Z':'-', P.C?'C':'-');
}
void setPC(uint16_t address) {
PC = address;
}
uint16_t getPC(){
return PC;
}
#if _FUNCTIONNAL_TESTS
// 6502 functonnal tests
// using Klaus Dormann's functonnal tests published at :
// https://github.com/Klaus2m5/6502_65C02_functional_tests
int main(int argc, char* argv[]){
char *filename = "6502_functional_test.bin";
FILE *f = fopen(filename, "rb");
if (!f || fread(RAM, 1, 65536, f) != 65536) {
printf("ERROR : can't load %s\n", filename);
return(0);
}
fclose(f);
puce6502RST(); // reset the CPU
PC = 0x400; // set Program Counter to start of code
unsigned long long int oldticks = 0;
uint16_t oldPC = PC, newPC = PC; // to detect the BNE $FE when an error occurs
// while(1) {
// dasm(newPC);
// printf(" ");
// newPC = puce6502Exec(1);
// printRegs();
// printf(" Cycles: %llu Total: %llu\n", ticks - oldticks, ticks);
// oldticks = ticks;
//
// if (newPC == 0x3469){ // 6502_functional_test SUCCESS
// printf("\nReached end of 6502_functional_test @ %04X : SUCCESS !\n", newPC);
// break;
// }
//
// if (newPC == oldPC ) {
// printf("\n\nLoop detected @ %04X - Press ENTER to proceed with next test or CTRL<C> to stop\n\n", newPC);
// return(-1);
// getchar();
// PC = newPC + 2;
// }
// oldPC = newPC;
// }
// Benchmark : replace the above while loop by this one
while(puce6502Exec(100) != 0x3469);
printf("%llu\n", ticks);
// and use the time utility to avaluate the speed the emulated 65C02
return(0);
}
#endif
/* test results :
## Using 6502_functional_test.bin :
<...>
3457 69 55 ADC #$55 A=AA X=0E Y=FF S=FF *S=34 NVUB---- Cycles: 2 Total: 96240555
3459 C9 AA CMP #$AA A=AA X=0E Y=FF S=FF *S=34 -VUB--ZC Cycles: 2 Total: 96240557
345B D0 FE BNE $FE A=AA X=0E Y=FF S=FF *S=34 -VUB--ZC Cycles: 2 Total: 96240559
345D AD 0002 LDA $0200 A=2B X=0E Y=FF S=FF *S=34 -VUB---C Cycles: 4 Total: 96240563
3460 C9 2B CMP #$2B A=2B X=0E Y=FF S=FF *S=34 -VUB--ZC Cycles: 2 Total: 96240565
3462 D0 FE BNE $FE A=2B X=0E Y=FF S=FF *S=34 -VUB--ZC Cycles: 2 Total: 96240567
3464 A9 F0 LDA #$F0 A=F0 X=0E Y=FF S=FF *S=34 NVUB---C Cycles: 2 Total: 96240569
3466 8D 0002 STA $0200 A=F0 X=0E Y=FF S=FF *S=34 NVUB---C Cycles: 4 Total: 96240573
Reached end of 6502_functional_test @ 3469 : SUCCESS !
=> which means puce65c02 passes the 6502 functionnal tests :
; S U C C E S S ************************************************
; -------------
success ;if you get here everything went well
3469 : 4c6934 > jmp * ;test passed, no errors
; -------------
; S U C C E S S ************************************************
## Benchmarks
No info printed during execution
Using gcc -O3 option
$ gcc -O3 puce6502.c -o puce6502
$ time ./puce6502
96240630
real 0m0,093s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,107s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,096s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,097s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,110s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,110s
user 0m0,000s
sys 0m0,000s
$ time ./puce6502
96240630
real 0m0,095s
user 0m0,000s
sys 0m0,000s
--> emulated CPU running at around 1 GHz !!!?!???
*/