Add JML, AND AbsoluteLong, ADC AbsoluteLong
This commit is contained in:
@@ -66,6 +66,9 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ExecuteInstruction(uint8_t opcode) {
|
void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||||
|
// Update the PC based on the Program Bank Register
|
||||||
|
PC |= (static_cast<uint16_t>(PB) << 16);
|
||||||
|
|
||||||
// uint8_t opcode = FetchByte();
|
// uint8_t opcode = FetchByte();
|
||||||
uint8_t operand = -1;
|
uint8_t operand = -1;
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
@@ -94,7 +97,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
ADC(operand);
|
ADC(operand);
|
||||||
break;
|
break;
|
||||||
case 0x6F: // ADC Absolute Long
|
case 0x6F: // ADC Absolute Long
|
||||||
operand = memory.ReadByte(AbsoluteLong());
|
operand = memory.ReadWord(AbsoluteLong());
|
||||||
ADC(operand);
|
ADC(operand);
|
||||||
break;
|
break;
|
||||||
case 0x71: // ADC DP Indirect Indexed, Y
|
case 0x71: // ADC DP Indirect Indexed, Y
|
||||||
@@ -153,8 +156,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
AND(Absolute());
|
AND(Absolute());
|
||||||
break;
|
break;
|
||||||
case 0x2F: // AND Absolute Long
|
case 0x2F: // AND Absolute Long
|
||||||
operand = memory.ReadByte(AbsoluteLong());
|
ANDAbsoluteLong(AbsoluteLong());
|
||||||
AND(operand);
|
|
||||||
break;
|
break;
|
||||||
case 0x31: // AND DP Indirect Indexed, Y
|
case 0x31: // AND DP Indirect Indexed, Y
|
||||||
operand = memory.ReadByte(DirectPageIndirectIndexedY());
|
operand = memory.ReadByte(DirectPageIndirectIndexedY());
|
||||||
@@ -177,16 +179,13 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
AND(operand);
|
AND(operand);
|
||||||
break;
|
break;
|
||||||
case 0x39: // AND Absolute Indexed, Y
|
case 0x39: // AND Absolute Indexed, Y
|
||||||
operand = memory.ReadByte(AbsoluteIndexedY());
|
AND(AbsoluteIndexedY());
|
||||||
AND(operand);
|
|
||||||
break;
|
break;
|
||||||
case 0x3D: // AND Absolute Indexed, X
|
case 0x3D: // AND Absolute Indexed, X
|
||||||
operand = memory.ReadByte(AbsoluteIndexedX());
|
AND(AbsoluteIndexedX());
|
||||||
AND(operand);
|
|
||||||
break;
|
break;
|
||||||
case 0x3F: // AND Absolute Long Indexed, X
|
case 0x3F: // AND Absolute Long Indexed, X
|
||||||
operand = memory.ReadByte(AbsoluteLongIndexedX());
|
AND(AbsoluteLongIndexedX());
|
||||||
AND(operand);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x06: // ASL Direct Page
|
case 0x06: // ASL Direct Page
|
||||||
@@ -451,7 +450,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
JMP(Absolute());
|
JMP(Absolute());
|
||||||
break;
|
break;
|
||||||
case 0x5C: // JMP Absolute Long
|
case 0x5C: // JMP Absolute Long
|
||||||
// JMP();
|
JML(AbsoluteLong());
|
||||||
break;
|
break;
|
||||||
case 0x6C: // JMP Absolute Indirect
|
case 0x6C: // JMP Absolute Indirect
|
||||||
JMP(AbsoluteIndirect());
|
JMP(AbsoluteIndirect());
|
||||||
@@ -574,7 +573,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xEA: // NOP No Operation
|
case 0xEA: // NOP No Operation
|
||||||
// NOP();
|
NOP();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x01: // ORA DP Indexed Indirect, X
|
case 0x01: // ORA DP Indexed Indirect, X
|
||||||
@@ -1010,6 +1009,14 @@ void CPU::AND(uint16_t address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New function for absolute long addressing mode
|
||||||
|
void CPU::ANDAbsoluteLong(uint32_t address) {
|
||||||
|
uint32_t operand32 = memory.ReadWordLong(address);
|
||||||
|
A &= operand32;
|
||||||
|
SetZeroFlag(A == 0);
|
||||||
|
SetNegativeFlag(A & 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -105,7 +105,7 @@ class CPU : public Memory {
|
|||||||
// Low: First operand byte
|
// Low: First operand byte
|
||||||
//
|
//
|
||||||
// LDA long
|
// LDA long
|
||||||
uint16_t AbsoluteLong() { return FetchLong(); }
|
uint32_t AbsoluteLong() { return FetchLong(); }
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
// The 24-bit operand is added to X based on the emulation mode
|
// The 24-bit operand is added to X based on the emulation mode
|
||||||
@@ -243,11 +243,14 @@ class CPU : public Memory {
|
|||||||
// Registers
|
// Registers
|
||||||
|
|
||||||
uint8_t A = 0; // Accumulator
|
uint8_t A = 0; // Accumulator
|
||||||
|
uint8_t B = 0; // Accumulator (High)
|
||||||
uint8_t X = 0; // X index register
|
uint8_t X = 0; // X index register
|
||||||
|
uint8_t X2 = 0; // X index register (High)
|
||||||
uint8_t Y = 0; // Y index register
|
uint8_t Y = 0; // Y index register
|
||||||
|
uint8_t Y2 = 0; // Y index register (High)
|
||||||
uint16_t D = 0; // Direct Page register
|
uint16_t D = 0; // Direct Page register
|
||||||
uint16_t DB = 0; // Data Bank register
|
uint16_t DB = 0; // Data Bank register
|
||||||
uint16_t PB = 0; // Program Bank register
|
uint8_t PB = 0; // Program Bank register
|
||||||
uint16_t PC = 0; // Program Counter
|
uint16_t PC = 0; // Program Counter
|
||||||
uint8_t E = 1; // Emulation mode flag
|
uint8_t E = 1; // Emulation mode flag
|
||||||
uint8_t status; // Processor Status (P)
|
uint8_t status; // Processor Status (P)
|
||||||
@@ -288,91 +291,47 @@ class CPU : public Memory {
|
|||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Instructions
|
// Instructions
|
||||||
|
/// ``` Unimplemented
|
||||||
|
|
||||||
// Left to implement
|
// ADC: Add with carry
|
||||||
// * = in progress
|
|
||||||
|
|
||||||
// ADC: Add with carry *
|
|
||||||
// AND: Logical AND *
|
|
||||||
// ASL: Arithmetic shift left
|
|
||||||
// BCC: Branch if carry clear *
|
|
||||||
// BCS: Branch if carry set *
|
|
||||||
// BEQ: Branch if equal (zero set) *
|
|
||||||
// BIT: Bit test
|
|
||||||
// BMI: Branch if minus (negative set)
|
|
||||||
// BNE: Branch if not equal (zero clear)
|
|
||||||
// BPL: Branch if plus (negative clear)
|
|
||||||
// BRA: Branch always
|
|
||||||
// BRK: Break
|
|
||||||
// BRL: Branch always long
|
|
||||||
// BVC: Branch if overflow clear
|
|
||||||
// BVS: Branch if overflow set
|
|
||||||
// CMP: Compare
|
|
||||||
// COP: Coprocessor
|
|
||||||
// CPX: Compare X register
|
|
||||||
// CPY: Compare Y register
|
|
||||||
// DEC: Decrement
|
|
||||||
// EOR: Exclusive OR
|
|
||||||
// JMP: Jump
|
|
||||||
// JML: Jump long
|
|
||||||
// JSR: Jump to subroutine
|
|
||||||
// JSL: Jump to subroutine long
|
|
||||||
// LDA: Load accumulator
|
|
||||||
// LDX: Load X register
|
|
||||||
// LDY: Load Y register
|
|
||||||
// LSR: Logical shift right
|
|
||||||
// MVN: Move negative
|
|
||||||
// MVP: Move positive
|
|
||||||
// NOP: No operation
|
|
||||||
// ORA: Logical OR
|
|
||||||
// PEA: Push effective address
|
|
||||||
// PEI: Push effective indirect address
|
|
||||||
// PER: Push effective PC-relative address
|
|
||||||
// ROL: Rotate left
|
|
||||||
// ROR: Rotate right
|
|
||||||
// RTI: Return from interrupt
|
|
||||||
// RTL: Return from subroutine long
|
|
||||||
// RTS: Return from subroutine
|
|
||||||
// SBC: Subtract with carry
|
|
||||||
// STA: Store accumulator
|
|
||||||
// STP: Stop the clock
|
|
||||||
// STX: Store X register
|
|
||||||
// STY: Store Y register
|
|
||||||
// STZ: Store zero
|
|
||||||
// TDC: Transfer direct page register to accumulator
|
|
||||||
// TRB: Test and reset bits
|
|
||||||
// TSB: Test and set bits
|
|
||||||
// WAI: Wait for interrupt
|
|
||||||
// XBA: Exchange B and A accumulator
|
|
||||||
|
|
||||||
void ADC(uint8_t operand);
|
void ADC(uint8_t operand);
|
||||||
|
void ANDAbsoluteLong(uint32_t address);
|
||||||
|
|
||||||
|
// AND: Logical AND
|
||||||
void AND(uint16_t address);
|
void AND(uint16_t address);
|
||||||
|
|
||||||
void BEQ(int8_t offset) {
|
// ASL: Arithmetic shift left ```
|
||||||
if (GetZeroFlag()) { // If the zero flag is set
|
|
||||||
PC += offset; // Add the offset to the program counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// BCC: Branch if carry clear
|
||||||
void BCC(int8_t offset) {
|
void BCC(int8_t offset) {
|
||||||
if (!GetCarryFlag()) { // If the carry flag is clear
|
if (!GetCarryFlag()) { // If the carry flag is clear
|
||||||
PC += offset; // Add the offset to the program counter
|
PC += offset; // Add the offset to the program counter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BCS: Branch if carry set ```
|
||||||
|
|
||||||
|
// BEQ: Branch if equal (zero set)
|
||||||
|
void BEQ(int8_t offset) {
|
||||||
|
if (GetZeroFlag()) { // If the zero flag is set
|
||||||
|
PC += offset; // Add the offset to the program counter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BIT: Bit test ```
|
||||||
|
// BMI: Branch if minus (negative set) ```
|
||||||
|
// BNE: Branch if not equal (zero clear) ```
|
||||||
|
// BPL: Branch if plus (negative clear) ```
|
||||||
|
// BRA: Branch always ```
|
||||||
|
// BRK: Break ```
|
||||||
|
|
||||||
|
// BRL: Branch always long
|
||||||
void BRL(int16_t offset) {
|
void BRL(int16_t offset) {
|
||||||
PC += offset; // Add the offset to the program counter
|
PC += offset; // Add the offset to the program counter
|
||||||
}
|
}
|
||||||
|
|
||||||
void LDA() {
|
// BVC: Branch if overflow clear ```
|
||||||
A = memory[PC];
|
// BVS: Branch if overflow set ```
|
||||||
SetZeroFlag(A == 0);
|
|
||||||
SetNegativeFlag(A & 0x80);
|
|
||||||
PC++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SEC: Set carry flag
|
|
||||||
void SEC() { status |= 0x01; }
|
|
||||||
|
|
||||||
// CLC: Clear carry flag
|
// CLC: Clear carry flag
|
||||||
void CLC() { status &= ~0x01; }
|
void CLC() { status &= ~0x01; }
|
||||||
@@ -386,20 +345,25 @@ class CPU : public Memory {
|
|||||||
// CLV: Clear overflow flag
|
// CLV: Clear overflow flag
|
||||||
void CLV() { status &= ~0x40; }
|
void CLV() { status &= ~0x40; }
|
||||||
|
|
||||||
bool emulation_mode = false;
|
// CMP: Compare ```
|
||||||
|
// COP: Coprocessor ```
|
||||||
|
|
||||||
|
// CPX: Compare X register
|
||||||
void CPX(uint16_t address) {
|
void CPX(uint16_t address) {
|
||||||
uint16_t memory_value =
|
uint16_t memory_value =
|
||||||
E ? memory.ReadByte(address) : memory.ReadWord(address);
|
E ? memory.ReadByte(address) : memory.ReadWord(address);
|
||||||
compare(X, memory_value);
|
compare(X, memory_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CPY: Compare Y register
|
||||||
void CPY(uint16_t address) {
|
void CPY(uint16_t address) {
|
||||||
uint16_t memory_value =
|
uint16_t memory_value =
|
||||||
E ? memory.ReadByte(address) : memory.ReadWord(address);
|
E ? memory.ReadByte(address) : memory.ReadWord(address);
|
||||||
compare(Y, memory_value);
|
compare(Y, memory_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEC: Decrement ```
|
||||||
|
|
||||||
// DEX: Decrement X register
|
// DEX: Decrement X register
|
||||||
void DEX() {
|
void DEX() {
|
||||||
X--;
|
X--;
|
||||||
@@ -414,21 +378,10 @@ class CPU : public Memory {
|
|||||||
SetNegativeFlag(Y & 0x80);
|
SetNegativeFlag(Y & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
// INX: Increment X register
|
// EOR: Exclusive OR ```
|
||||||
void INX() {
|
|
||||||
X++;
|
|
||||||
SetNegativeFlag(X & 0x80);
|
|
||||||
SetZeroFlag(X == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// INY: Increment Y register
|
// INC: Increment
|
||||||
void INY() {
|
// TODO: Check if this is correct
|
||||||
Y++;
|
|
||||||
SetNegativeFlag(Y & 0x80);
|
|
||||||
SetZeroFlag(Y == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// INC: Increment memory
|
|
||||||
void INC(uint16_t address) {
|
void INC(uint16_t address) {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
uint8_t value = ReadByte(address);
|
uint8_t value = ReadByte(address);
|
||||||
@@ -451,11 +404,33 @@ class CPU : public Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// JMP: Jump to new address
|
// INX: Increment X register
|
||||||
|
void INX() {
|
||||||
|
X++;
|
||||||
|
SetNegativeFlag(X & 0x80);
|
||||||
|
SetZeroFlag(X == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INY: Increment Y register
|
||||||
|
void INY() {
|
||||||
|
Y++;
|
||||||
|
SetNegativeFlag(Y & 0x80);
|
||||||
|
SetZeroFlag(Y == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JMP: Jump
|
||||||
void JMP(uint16_t address) {
|
void JMP(uint16_t address) {
|
||||||
PC = address; // Set program counter to the new address
|
PC = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JML: Jump long
|
||||||
|
void JML(uint32_t address) {
|
||||||
|
// Set the lower 16 bits of PC to the lower 16 bits of the address
|
||||||
|
PC = static_cast<uint8_t>(address & 0xFFFF);
|
||||||
|
// Set the PBR to the upper 8 bits of the address
|
||||||
|
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
// JSR: Jump to subroutine
|
// JSR: Jump to subroutine
|
||||||
void JSR(uint16_t address) {
|
void JSR(uint16_t address) {
|
||||||
PC -= 1; // Subtract 1 from program counter
|
PC -= 1; // Subtract 1 from program counter
|
||||||
@@ -471,51 +446,65 @@ class CPU : public Memory {
|
|||||||
PC = address; // Set program counter to the new address
|
PC = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push Accumulator on Stack
|
// LDA: Load accumulator
|
||||||
|
void LDA() {
|
||||||
|
A = memory[PC];
|
||||||
|
SetZeroFlag(A == 0);
|
||||||
|
SetNegativeFlag(A & 0x80);
|
||||||
|
PC++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LDX: Load X register ```
|
||||||
|
// LDY: Load Y register ```
|
||||||
|
// LSR: Logical shift right ```
|
||||||
|
// MVN: Move negative ```
|
||||||
|
// MVP: Move positive ```
|
||||||
|
|
||||||
|
// NOP: No operation
|
||||||
|
void NOP() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// ORA: Logical OR ```
|
||||||
|
// PEA: Push effective address ```
|
||||||
|
// PEI: Push effective indirect address ```
|
||||||
|
// PER: Push effective PC-relative address ```
|
||||||
|
|
||||||
|
// PHA: Push Accumulator on Stack
|
||||||
void PHA() { memory.PushByte(A); }
|
void PHA() { memory.PushByte(A); }
|
||||||
|
|
||||||
// Pull Accumulator from Stack
|
// PHB: Push Data Bank Register on Stack
|
||||||
|
void PHB() { memory.PushByte(DB); }
|
||||||
|
|
||||||
|
// PHD: Push Program Bank Register on Stack
|
||||||
|
void PHD() { memory.PushWord(D); }
|
||||||
|
|
||||||
|
// PHK: Push Program Bank Register on Stack
|
||||||
|
void PHK() { memory.PushByte(PB); }
|
||||||
|
|
||||||
|
// PHP: Push Processor Status Register on Stack
|
||||||
|
void PHP() { memory.PushByte(status); }
|
||||||
|
|
||||||
|
// PHX: Push X Index Register on Stack
|
||||||
|
void PHX() { memory.PushByte(X); }
|
||||||
|
|
||||||
|
// PHY: Push Y Index Register on Stack
|
||||||
|
void PHY() { memory.PushByte(Y); }
|
||||||
|
|
||||||
|
// PLA: Pull Accumulator from Stack
|
||||||
void PLA() {
|
void PLA() {
|
||||||
A = memory.PopByte();
|
A = memory.PopByte();
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push Processor Status Register on Stack
|
// PLB: Pull data bank register
|
||||||
void PHP() { memory.PushByte(status); }
|
|
||||||
|
|
||||||
// Pull Processor Status Register from Stack
|
|
||||||
void PLP() { status = memory.PopByte(); }
|
|
||||||
|
|
||||||
void PHX() { memory.PushByte(X); }
|
|
||||||
|
|
||||||
void PLX() {
|
|
||||||
X = memory.PopByte();
|
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
|
||||||
SetZeroFlag(X == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PHY() { memory.PushByte(Y); }
|
|
||||||
|
|
||||||
void PLY() {
|
|
||||||
Y = memory.PopByte();
|
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
|
||||||
SetZeroFlag(Y == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push Data Bank Register on Stack
|
|
||||||
void PHB() { memory.PushByte(DB); }
|
|
||||||
|
|
||||||
// Pull Data Bank Register from Stack
|
|
||||||
void PLB() {
|
void PLB() {
|
||||||
DB = memory.PopByte();
|
DB = memory.PopByte();
|
||||||
SetNegativeFlag((DB & 0x80) != 0);
|
SetNegativeFlag((DB & 0x80) != 0);
|
||||||
SetZeroFlag(DB == 0);
|
SetZeroFlag(DB == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push Program Bank Register on Stack
|
|
||||||
void PHD() { memory.PushWord(D); }
|
|
||||||
|
|
||||||
// Pull Direct Page Register from Stack
|
// Pull Direct Page Register from Stack
|
||||||
void PLD() {
|
void PLD() {
|
||||||
D = memory.PopWord();
|
D = memory.PopWord();
|
||||||
@@ -523,89 +512,141 @@ class CPU : public Memory {
|
|||||||
SetZeroFlag(D == 0);
|
SetZeroFlag(D == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push Program Bank Register on Stack
|
// Pull Processor Status Register from Stack
|
||||||
void PHK() { memory.PushByte(PB); }
|
void PLP() { status = memory.PopByte(); }
|
||||||
|
|
||||||
void SEI() { status |= 0x04; }
|
// PLX: Pull X Index Register from Stack
|
||||||
|
void PLX() {
|
||||||
void SED() { status |= 0x08; }
|
X = memory.PopByte();
|
||||||
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
void SEP() {
|
SetZeroFlag(X == 0);
|
||||||
PC++;
|
|
||||||
auto byte = FetchByte();
|
|
||||||
status |= byte;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PHY: Pull Y Index Register from Stack
|
||||||
|
void PLY() {
|
||||||
|
Y = memory.PopByte();
|
||||||
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
|
SetZeroFlag(Y == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// REP: Reset status bits
|
||||||
void REP() {
|
void REP() {
|
||||||
PC++;
|
PC++;
|
||||||
auto byte = FetchByte();
|
auto byte = FetchByte();
|
||||||
status &= ~byte;
|
status &= ~byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCD() {
|
// ROL: Rotate left ```
|
||||||
D = A;
|
// ROR: Rotate right ```
|
||||||
SetZeroFlag(D == 0);
|
// RTI: Return from interrupt ```
|
||||||
SetNegativeFlag(D & 0x80);
|
// RTL: Return from subroutine long ```
|
||||||
|
// RTS: Return from subroutine ```
|
||||||
|
// SBC: Subtract with carry ```
|
||||||
|
|
||||||
|
// SEC: Set carry flag
|
||||||
|
void SEC() { status |= 0x01; }
|
||||||
|
|
||||||
|
// SED: Set decimal mode
|
||||||
|
void SED() { status |= 0x08; }
|
||||||
|
|
||||||
|
// SEI: Set interrupt disable flag
|
||||||
|
void SEI() { status |= 0x04; }
|
||||||
|
|
||||||
|
// SEP: Set status bits
|
||||||
|
void SEP() {
|
||||||
|
PC++;
|
||||||
|
auto byte = FetchByte();
|
||||||
|
status |= byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TDC() {
|
// STA: Store accumulator ```
|
||||||
A = D;
|
// STP: Stop the clock ```
|
||||||
SetZeroFlag(A == 0);
|
// STX: Store X register ```
|
||||||
SetNegativeFlag(A & 0x80);
|
// STY: Store Y register ```
|
||||||
}
|
// STZ: Store zero ```
|
||||||
|
|
||||||
void TCS() { memory.SetSP(A); }
|
|
||||||
|
|
||||||
|
// TAX: Transfer accumulator to X
|
||||||
void TAX() {
|
void TAX() {
|
||||||
X = A;
|
X = A;
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
SetNegativeFlag(X & 0x80);
|
SetNegativeFlag(X & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TAY: Transfer accumulator to Y
|
||||||
void TAY() {
|
void TAY() {
|
||||||
Y = A;
|
Y = A;
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
SetNegativeFlag(Y & 0x80);
|
SetNegativeFlag(Y & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TYA() {
|
// TCD: Transfer accumulator to direct page register
|
||||||
A = Y;
|
void TCD() {
|
||||||
|
D = A;
|
||||||
|
SetZeroFlag(D == 0);
|
||||||
|
SetNegativeFlag(D & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCS: Transfer accumulator to stack pointer
|
||||||
|
void TCS() { memory.SetSP(A); }
|
||||||
|
|
||||||
|
// TDC: Transfer direct page register to accumulator
|
||||||
|
void TDC() {
|
||||||
|
A = D;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TXA() {
|
// TRB: Test and reset bits ```
|
||||||
A = X;
|
// TSB: Test and set bits ```
|
||||||
|
|
||||||
|
// TSC: Transfer stack pointer to accumulator
|
||||||
|
void TSC() {
|
||||||
|
A = SP();
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TXY() {
|
// TSX: Transfer stack pointer to X
|
||||||
X = Y;
|
|
||||||
SetZeroFlag(X == 0);
|
|
||||||
SetNegativeFlag(X & 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TYX() {
|
|
||||||
Y = X;
|
|
||||||
SetZeroFlag(Y == 0);
|
|
||||||
SetNegativeFlag(Y & 0x80);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TSX() {
|
void TSX() {
|
||||||
X = SP();
|
X = SP();
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
SetNegativeFlag(X & 0x80);
|
SetNegativeFlag(X & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TXS() { memory.SetSP(X); }
|
// TXA: Transfer X to accumulator
|
||||||
|
void TXA() {
|
||||||
void TSC() {
|
A = X;
|
||||||
A = SP();
|
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TXS: Transfer X to stack pointer
|
||||||
|
void TXS() { memory.SetSP(X); }
|
||||||
|
|
||||||
|
// TXY: Transfer X to Y
|
||||||
|
void TXY() {
|
||||||
|
X = Y;
|
||||||
|
SetZeroFlag(X == 0);
|
||||||
|
SetNegativeFlag(X & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TYA: Transfer Y to accumulator
|
||||||
|
void TYA() {
|
||||||
|
A = Y;
|
||||||
|
SetZeroFlag(A == 0);
|
||||||
|
SetNegativeFlag(A & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TYX: Transfer Y to X
|
||||||
|
void TYX() {
|
||||||
|
Y = X;
|
||||||
|
SetZeroFlag(Y == 0);
|
||||||
|
SetNegativeFlag(Y & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WAI: Wait for interrupt ```
|
||||||
|
// XBA: Exchange B and A accumulator ```
|
||||||
|
|
||||||
// XCE: Exchange Carry and Emulation Flags
|
// XCE: Exchange Carry and Emulation Flags
|
||||||
void XCE() {
|
void XCE() {
|
||||||
uint8_t carry = status & 0x01;
|
uint8_t carry = status & 0x01;
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ class MemoryImpl : public Memory {
|
|||||||
if (address < dp_memory_.size()) {
|
if (address < dp_memory_.size()) {
|
||||||
return dp_memory_.ReadByte(static_cast<uint8_t>(address));
|
return dp_memory_.ReadByte(static_cast<uint8_t>(address));
|
||||||
}
|
}
|
||||||
// uint32_t mapped_address = GetMappedAddress(address);
|
uint32_t mapped_address = GetMappedAddress(address);
|
||||||
return memory_.at(address);
|
return memory_.at(mapped_address);
|
||||||
}
|
}
|
||||||
uint16_t ReadWord(uint16_t address) const override {
|
uint16_t ReadWord(uint16_t address) const override {
|
||||||
if (address < dp_memory_.size()) {
|
if (address < dp_memory_.size()) {
|
||||||
@@ -77,8 +77,8 @@ class MemoryImpl : public Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WriteByte(uint32_t address, uint8_t value) override {
|
void WriteByte(uint32_t address, uint8_t value) override {
|
||||||
// uint32_t mapped_address = GetMappedAddress(address);
|
uint32_t mapped_address = GetMappedAddress(address);
|
||||||
memory_[address] = value;
|
memory_[mapped_address] = value;
|
||||||
}
|
}
|
||||||
void WriteWord(uint32_t address, uint16_t value) override {
|
void WriteWord(uint32_t address, uint16_t value) override {
|
||||||
uint32_t mapped_address = GetMappedAddress(address);
|
uint32_t mapped_address = GetMappedAddress(address);
|
||||||
|
|||||||
128
test/cpu_test.cc
128
test/cpu_test.cc
@@ -41,6 +41,14 @@ class MockMemory : public Memory {
|
|||||||
std::copy(data.begin(), data.end(), memory_.begin());
|
std::copy(data.begin(), data.end(), memory_.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InsertMemory(const uint64_t address, const std::vector<uint8_t>& data) {
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& each : data) {
|
||||||
|
memory_[address + i] = each;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Init() {
|
void Init() {
|
||||||
ON_CALL(*this, ReadByte(::testing::_))
|
ON_CALL(*this, ReadByte(::testing::_))
|
||||||
.WillByDefault(
|
.WillByDefault(
|
||||||
@@ -111,6 +119,7 @@ class CPUTest : public ::testing::Test {
|
|||||||
public:
|
public:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
mock_memory.Init();
|
mock_memory.Init();
|
||||||
|
EXPECT_CALL(mock_memory, ClearMemory()).Times(::testing::AtLeast(1));
|
||||||
mock_memory.ClearMemory();
|
mock_memory.ClearMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +198,15 @@ TEST_F(CPUTest, ADC_AbsoluteLong) {
|
|||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 1; // PC register
|
cpu.PC = 1; // PC register
|
||||||
cpu.status = 0x00; // 16-bit mode
|
cpu.status = 0x00; // 16-bit mode
|
||||||
std::vector<uint8_t> data = {0x2F, 0x03, 0x00, 0x00, 0x05, 0x00};
|
std::vector<uint8_t> data = {0x6F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadWord(0x0004)).WillOnce(Return(0x0005));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x6F); // ADC Absolute Long
|
||||||
|
EXPECT_EQ(cpu.A, 0x06);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -295,6 +312,21 @@ TEST_F(CPUTest, AND_Absolute_16BitMode) {
|
|||||||
EXPECT_EQ(cpu.A, 0b10101010); // A register should now be 0b10101010
|
EXPECT_EQ(cpu.A, 0b10101010); // A register should now be 0b10101010
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, AND_AbsoluteLong) {
|
||||||
|
cpu.A = 0x01;
|
||||||
|
cpu.PC = 1; // PC register
|
||||||
|
cpu.status = 0x00; // 16-bit mode
|
||||||
|
std::vector<uint8_t> data = {0x2F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
||||||
|
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0004)).WillOnce(Return(0x0005));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x2F); // ADC Absolute Long
|
||||||
|
EXPECT_EQ(cpu.A, 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, AND_IndexedIndirect) {
|
TEST_F(CPUTest, AND_IndexedIndirect) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
@@ -305,6 +337,78 @@ TEST_F(CPUTest, AND_IndexedIndirect) {
|
|||||||
EXPECT_EQ(cpu.A, 0b00000000); // A register should now be 0b00000000
|
EXPECT_EQ(cpu.A, 0b00000000); // A register should now be 0b00000000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, AND_AbsoluteIndexedX) {
|
||||||
|
cpu.A = 0b11110000; // A register
|
||||||
|
cpu.X = 0x02; // X register
|
||||||
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
|
cpu.PC = 1; // PC register
|
||||||
|
std::vector<uint8_t> data = {0x3D, 0x03, 0x00,
|
||||||
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
// Get the absolute address
|
||||||
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003));
|
||||||
|
|
||||||
|
// Add the offset from the X register to the absolute address
|
||||||
|
uint16_t address = 0x0003 + static_cast<uint16_t>(cpu.X & 0xFF);
|
||||||
|
|
||||||
|
// Get the value at the absolute address + X
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x3D); // AND Absolute, X
|
||||||
|
|
||||||
|
EXPECT_THAT(cpu.PC, testing::Eq(0x03));
|
||||||
|
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, AND_AbsoluteIndexedY) {
|
||||||
|
cpu.A = 0b11110000; // A register
|
||||||
|
cpu.Y = 0x02; // Y register
|
||||||
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
|
cpu.PC = 1; // PC register
|
||||||
|
std::vector<uint8_t> data = {0x39, 0x03, 0x00,
|
||||||
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
// Get the absolute address
|
||||||
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003));
|
||||||
|
|
||||||
|
// Add the offset from the Y register to the absolute address
|
||||||
|
uint16_t address = 0x0003 + cpu.Y;
|
||||||
|
|
||||||
|
// Get the value at the absolute address + Y
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x39); // AND Absolute, Y
|
||||||
|
|
||||||
|
EXPECT_THAT(cpu.PC, testing::Eq(0x03));
|
||||||
|
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, AND_AbsoluteLongIndexedX) {
|
||||||
|
cpu.A = 0b11110000; // A register
|
||||||
|
cpu.X = 0x02; // X register
|
||||||
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
|
cpu.PC = 1; // PC register
|
||||||
|
std::vector<uint8_t> data = {0x3F, 0x03, 0x00, 0x00,
|
||||||
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
// Get the absolute address
|
||||||
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0003));
|
||||||
|
|
||||||
|
// Add the offset from the X register to the absolute address
|
||||||
|
uint16_t address = 0x0003 + static_cast<uint16_t>(cpu.X & 0xFF);
|
||||||
|
|
||||||
|
// Get the value at the absolute address + X
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x3F); // AND Absolute Long, X
|
||||||
|
|
||||||
|
EXPECT_THAT(cpu.PC, testing::Eq(0x04));
|
||||||
|
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BCC - Branch if Carry Clear
|
// BCC - Branch if Carry Clear
|
||||||
|
|
||||||
@@ -510,6 +614,28 @@ TEST_F(CPUTest, JMP_Indirect) {
|
|||||||
EXPECT_EQ(cpu.PC, 0x3005);
|
EXPECT_EQ(cpu.PC, 0x3005);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// JML - Jump Long
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
TEST_F(CPUTest, JML_AbsoluteLong) {
|
||||||
|
cpu.E = 0;
|
||||||
|
cpu.PC = 0x1001;
|
||||||
|
cpu.PB = 0x02; // Set the program bank register to 0x02
|
||||||
|
std::vector<uint8_t> data = {0x5C, 0x05, 0x00, 0x03}; // JML $030005
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x030005, {0x00, 0x20, 0x00});
|
||||||
|
|
||||||
|
// NOP to set PB to 0x02
|
||||||
|
cpu.ExecuteInstruction(0xEA);
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadWordLong(0x1001)).WillOnce(Return(0x030005));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x5C); // JML Absolute Long
|
||||||
|
EXPECT_EQ(cpu.PC, 0x0005);
|
||||||
|
EXPECT_EQ(cpu.PB, 0x03); // The PBR should be updated to 0x03
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// JSR - Jump to Subroutine
|
// JSR - Jump to Subroutine
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user