diff --git a/core/src/cpu/mod.rs b/core/src/cpu/mod.rs index 13375b9..ea40b2c 100644 --- a/core/src/cpu/mod.rs +++ b/core/src/cpu/mod.rs @@ -51,7 +51,7 @@ bitflags! { const Zero = 0b00000010; const InterruptDisable = 0b00000100; const Decimal = 0b00001000; - const B = 0b00010000; + const Break = 0b00010000; const Overflow = 0b01000000; const Negative = 0b10000000; } @@ -100,12 +100,23 @@ pub trait CpuInternals { /// * `value` - The value to push fn stack_push(&mut self, value: u8); - /// Pops the value on the top of the stack + /// Pushes a word value to the top of the stack + /// + /// # Arguments + /// * `value` - The word to push + fn stack_push_word(&mut self, value: u16); + + /// Pops the byte from the top of the stack /// /// # Returns /// The byte on the top of the stack fn stack_pop(&mut self) -> u8; + /// Pops a word from the top of the stack + /// + /// # Returns + /// The word on the top of the stack + fn stack_pop_word(&mut self) -> u16; /// Pushes the context to the top of the stack /// The context consists of the program counter and the status register. fn stack_push_context(&mut self); @@ -182,6 +193,11 @@ impl CpuInternals for Cpu { self.registers.sp -= 1; } + fn stack_push_word(&mut self, value: u16) { + self.stack_push((value >> 8) as u8); + self.stack_push((value & 0xFF) as u8); + } + fn stack_pop(&mut self) -> u8 { assert!(self.registers.sp < 0xff); @@ -191,6 +207,13 @@ impl CpuInternals for Cpu { self.memory_bus.get_byte(addr) } + fn stack_pop_word(&mut self) -> u16 { + let low = self.stack_pop() as u16; + let high = self.stack_pop() as u16; + + low | (high << 8) + } + fn stack_push_context(&mut self) { self.stack_push((self.registers.pc >> 8) as u8); self.stack_push((self.registers.pc & 0xff) as u8); diff --git a/core/src/cpu/op.rs b/core/src/cpu/op.rs index 41eee8c..0721bff 100644 --- a/core/src/cpu/op.rs +++ b/core/src/cpu/op.rs @@ -264,7 +264,13 @@ impl Cpu { let base_addr = self.registers.pc; let offset = self.program_get_next_byte() as u16; - base_addr + offset + // The offset is signed + let positive_offset = offset & 0x7F; + if offset & 0x80 { + base_addr.wrapping_sub(positive_offset) + } else { + base_addr.wrapping_add(positive_offset) + } } } diff --git a/core/src/cpu/operations.rs b/core/src/cpu/operations.rs index b248c97..02580c6 100644 --- a/core/src/cpu/operations.rs +++ b/core/src/cpu/operations.rs @@ -1,4 +1,4 @@ -use crate::cpu::op::Operand; +use crate::cpu::op::{AddressingMode, Operand}; use crate::cpu::{Cpu, CpuInternals, CpuStatus}; // Unofficial ops documentation: https://www.masswerk.at/6502/6502_instruction_set.html#SAX @@ -25,6 +25,25 @@ impl Cpu { self.set_status_flag(CpuStatus::Overflow, is_sign_overflow(a, value, result)); } + fn branch(&mut self, operand: Operand, condition: bool) { + let mut cycle_count = 2; + + if condition { + cycle_count += 1; + + let source_addr = self.registers.pc; + let target_addr = operand.value; + self.registers.pc = target_addr; + + // More cycles are used if the target address is in a new page + if target_addr & 0xFF00 != source_addr & 0xFF00 { + cycle_count += 2; + } + } + + self.busy_cycle_count = cycle_count; + } + fn set_common_flags(&mut self, result: u8) { self.set_status_flag(CpuStatus::Zero, result == 0); self.set_status_flag(CpuStatus::Negative, result & 0x80 != 0); @@ -116,7 +135,7 @@ impl Cpu { } /// Arithmetic shift left - fn op_asl(&mut self, operand: Operand) { + fn op_asl(&mut self, operand: Operand) { let value = operand.read(self); let result = value << 1; @@ -126,6 +145,91 @@ impl Cpu { self.set_status_flag_u8(CpuStatus::Carry, value & 0x80); } + /// Branch if carry clear + fn op_bcc(&mut self, operand: Operand) { + let condition = !self.get_status_flag(CpuStatus::Carry); + self.branch(operand, condition); + } + + /// Branch if carry set + fn op_bcs(&mut self, operand: Operand) { + let condition = self.get_status_flag(CpuStatus::Carry); + self.branch(operand, condition); + } + + /// Branch if equal (zero is set) + fn op_beq(&mut self, operand: Operand) { + let condition = self.get_status_flag(CpuStatus::Zero); + self.branch(operand, condition); + } + + /// Bit test, only changes flags from bitwise AND with accumulator + fn op_bit(&mut self, operand: Operand) { + let value = operand.read(self); + let a = self.registers.a; + + let result = a & value; + + self.set_status_flag(CpuStatus::Zero, result == 0); + self.set_status_flag_u8(CpuStatus::Overflow, value & 0b01000000); + self.set_status_flag_u8(CpuStatus::Negative, value & 0b10000000); + } + + /// Branch if minus (negative is set) + fn op_bmi(&mut self, operand: Operand) { + let condition = self.get_status_flag(CpuStatus::Negative); + self.branch(operand, condition); + } + + /// Branch if not equal (zero is not set) + fn op_bne(&mut self, operand: Operand) { + let condition = !self.get_status_flag(CpuStatus::Zero); + self.branch(operand, condition); + } + + /// Branch if plus (negative not set) + fn op_bpl(&mut self, operand: Operand) { + let condition = !self.get_status_flag(CpuStatus::Negative); + self.branch(operand, condition); + } + + /// Break, software interrupt + fn op_brk(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::Break, true); + self.stack_push_context(); + + // TODO: If an NMI is triggered at the same time, this interrupt is skipped but the break flag is still set + // Load IRQ interrupt vector in PC at $FFFE/F + // Cycle count: 7 + } + + /// Branch if overflow clear + fn op_bvc(&mut self, operand: Operand) { + let condition = !self.get_status_flag(CpuStatus::Overflow); + self.branch(operand, condition); + } + + /// Branch if overflow set + fn op_bvs(&mut self, operand: Operand) { + let condition = self.get_status_flag(CpuStatus::Overflow); + self.branch(operand, condition); + } + + /// Clear carry flag + fn op_clc(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::Carry, false); + } + + /// Clear interrupt disable flag + fn op_cli(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::InterruptDisable, false); + } + + /// Clear overflow flag + fn op_clv(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::Overflow, false); + } + /// Compare to accumulator fn op_cmp(&mut self, operand: Operand) { let value = operand.read(self); @@ -137,6 +241,58 @@ impl Cpu { self.set_common_flags(result); } + /// Compare to X + fn op_cpx(&mut self, operand: Operand) { + let value = operand.read(self); + let x = self.registers.x; + + let result = x.wrapping_sub(value); + + self.set_status_flag(CpuStatus::Carry, x >= value); + self.set_common_flags(result); + } + + /// Compare to Y + fn op_cpy(&mut self, operand: Operand) { + let value = operand.read(self); + let y = self.registers.y; + + let result = y.wrapping_sub(value); + + self.set_status_flag(CpuStatus::Carry, y >= value); + self.set_common_flags(result); + } + + /// Decrement memory + fn op_dec(&mut self, operand: Operand) { + let value = operand.read(self); + + let result = value.wrapping_sub(1); + operand.write(result, self); + + self.set_common_flags(result); + } + + /// Decrement X + fn op_dex(&mut self, operand: Operand) { + let x = self.registers.x; + + let result = x.wrapping_sub(1); + self.registers.x = result; + + self.set_common_flags(result) + } + + /// Decrement Y + fn op_dey(&mut self, operand: Operand) { + let y = self.registers.y; + + let result = y.wrapping_sub(1); + self.registers.y = result; + + self.set_common_flags(result) + } + /// Bitwise exclusive OR with accumulator fn op_eor(&mut self, operand: Operand) { let value = operand.read(self); @@ -148,6 +304,67 @@ impl Cpu { self.set_common_flags(result); } + /// Increment memory + fn op_inc(&mut self, operand: Operand) { + let value = operand.read(self); + + let result = value.wrapping_add(1); + operand.write(result, self); + + self.set_common_flags(result); + } + + /// Increment X + fn op_inx(&mut self, operand: Operand) { + let x = self.registers.x; + + let result = x.wrapping_add(1); + self.registers.x = result; + + self.set_common_flags(result); + } + + /// Increment Y + fn op_iny(&mut self, operand: Operand) { + let y = self.registers.y; + + let result = y.wrapping_add(1); + self.registers.y = result; + + self.set_common_flags(result); + } + + /// Jump + fn op_jmp(&mut self, operand: Operand, addr_mode: AddressingMode) { + let value = operand.value; + + let target_addr = if addr_mode == AddressingMode::ABS { + value + } else { + assert_eq!(addr_mode, AddressingMode::IND); + self.memory_bus.get_word(value) + }; + + self.registers.pc = target_addr; + + // TODO + // int cycle_count = 3; + // if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { + // cycle_count = 5; + // } + } + + /// Jump to subroutine + fn op_jsr(&mut self, operand: Operand) { + let target_addr = operand.value; + let pc = self.registers.pc; + + self.stack_push_word(pc); + self.registers.pc = target_addr; + + // TODO: Cycle count + } + /// Load to accumulator fn op_lda(&mut self, operand: Operand) { let value = operand.read(self); @@ -157,6 +374,40 @@ impl Cpu { self.set_common_flags(value); } + /// Load to X + fn op_ldx(&mut self, operand: Operand) { + let value = operand.read(self); + + self.registers.x = value; + + self.set_common_flags(value); + } + + /// Load to Y + fn op_ldy(&mut self, operand: Operand) { + let value = operand.read(self); + + self.registers.y = value; + + self.set_common_flags(value); + } + + /// Logical shift right + fn op_lsr(&mut self, operand: Operand) { + let value = operand.read(self); + + let result = value >> 1; + operand.write(result, self); + + self.set_common_flags(result); + self.set_status_flag_u8(CpuStatus::Carry, value & 0x1); + } + + /// No operation + fn op_nop(&mut self, operand: Operand) { + // TODO: Cycle count (2) + } + /// Bitwise OR with accumulator fn op_ora(&mut self, operand: Operand) { let value = operand.read(self); @@ -166,12 +417,121 @@ impl Cpu { self.registers.a = result; } + /// Push A to stack + fn op_pha(&mut self, operand: Operand) { + let a = self.registers.a; + + self.stack_push(a); + + // TODO: Cycle count (3) + } + + /// Push status to stack + fn op_php(&mut self, operand: Operand) { + let mut status = self.registers.status; + status |= 0b00110000; + + self.stack_push(status); + + // TODO: Cycle count (3) + } + + /// Pull A from stack + fn op_pla(&mut self, operand: Operand) { + let a = self.stack_pop(); + + self.registers.a = a; + + self.set_common_flags(a); + // TODO: Cycle count (4) + } + + /// Pull status from stack + fn op_plp(&mut self, operand: Operand) { + let current_status = self.registers.status & 0b00110000; + let status = self.stack_pop() & 0b11001111; // bits 4 and 5 are ignored + + let result = status | current_status; + + self.registers.status = result; + // TODO: Cycle count (4) + } + + /// Rotate left + fn op_rol(&mut self, operand: Operand) { + let value = operand.read(self); + let carry = self.get_status_flag_u8(CpuStatus::Carry); + + let result = (value << 1) | carry; + operand.write(result, self); + + self.set_common_flags(result); + self.set_status_flag_u8(CpuStatus::Carry, value & 0x80); + } + + /// Rotate right + fn op_ror(&mut self, operand: Operand) { + let value = operand.read(self); + let carry = self.get_status_flag_u8(CpuStatus::Carry); + + let result = (value >> 1) | (carry << 7); + operand.write(result, self); + + self.set_common_flags(result); + self.set_status_flag_u8(CpuStatus::Carry, value & 0x01); + } + + /// Return from interrupt + fn op_rti(&mut self, operand: Operand) { + let current_status = self.registers.status & 0b00110000; + let status = self.stack_pop() & 0b11001111; // bits 4 and 5 are ignored + + let result_status = status | current_status; + let pc = self.stack_pop_word(); + + self.registers.status = result_status; + self.registers.pc = pc; + + // TODO: Cycle count (6) + } + + /// Return from subroutine + fn op_rts(&mut self, operand: Operand) { + let pc = self.stack_pop_word(); + + let target_pc = pc.wrapping_add(1); + self.registers.pc = target_pc; + + // TODO: Cycle count (6) + } + /// Subtract with carry from accumulator fn op_sbc(&mut self, operand: Operand) { let value = operand.read(self); self.add_with_carry(!value); } + /// Set carry flag + fn op_sec(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::Carry, true); + + // TODO: Cycle count (2) + } + + /// Set decimal + fn op_sed(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::Decimal, true); + + // TODO: Cycle count (2) + } + + /// Set interrupt disable + fn op_sei(&mut self, operand: Operand) { + self.set_status_flag(CpuStatus::InterruptDisable, true); + + // TODO: Cycle count (2) + } + /// Store accumulator fn op_sta(&mut self, operand: Operand) { assert!(operand.is_address()); @@ -180,4 +540,65 @@ impl Cpu { operand.write(a, self); // TODO: C code enabled page crossing in the operand, but I didn't find it in the docs } + + /// Store X + fn op_stx(&mut self, operand: Operand) { + assert!(operand.is_address()); + + let x = self.registers.x; + operand.write(x, self); + } + + /// Store Y + fn op_sty(&mut self, operand: Operand) { + assert!(operand.is_address()); + + let y = self.registers.y; + operand.write(y, self); + } + + /// Transfer A to X + fn op_tax(&mut self, operand: Operand) { + let a = self.registers.a; + + self.registers.x = a; + + self.set_common_flags(a); + } + + /// Transfer A to Y + fn op_tay(&mut self, operand: Operand) { + let a = self.registers.a; + + self.registers.y = a; + + self.set_common_flags(a); + } + + /// Transfer stack pointer to X + fn op_tsx(&mut self, operand: Operand) { + let sp = self.registers.sp; + + self.registers.x = sp; + + self.set_common_flags(sp); + } + + /// Transfer X to A + fn op_txa(&mut self, operand: Operand) { + let x = self.registers.x; + + self.registers.a = x; + + self.set_common_flags(x); + } + + /// Transfer Y to A + fn op_tya(&mut self, operand: Operand) { + let y = self.registers.y; + + self.registers.a = y; + + self.set_common_flags(y); + } }