From 7b031e9f0270ac431cd72011c4db19dbba3ac581 Mon Sep 17 00:00:00 2001 From: Bruno Banaszczyk Date: Sat, 11 Jan 2025 15:23:46 +0100 Subject: [PATCH 1/2] added 27 new instructions (transfer + xor) --- emulator/src/cpu/cpu.rs | 1158 ++++++++++++++++++++++-- emulator/src/cpu/micro_instructions.rs | 13 +- emulator/src/cpu/operations.rs | 247 +++++ emulator/src/cpu/registers.rs | 89 +- 4 files changed, 1402 insertions(+), 105 deletions(-) diff --git a/emulator/src/cpu/cpu.rs b/emulator/src/cpu/cpu.rs index aeb9b85..fffcb47 100644 --- a/emulator/src/cpu/cpu.rs +++ b/emulator/src/cpu/cpu.rs @@ -131,10 +131,13 @@ impl CPU { self.registers.read_bah_indirect_ial(&mut self.bus) } MicroInstruction::WriteZeroPage => self.registers.write_zero_page(&mut self.bus), - MicroInstruction::WriteAbsolute => self.registers.write_absolute(&mut self.bus), MicroInstruction::WriteZeroPageBalX => { self.registers.write_zero_page_bal_x(&mut self.bus) } + MicroInstruction::WriteZeroPageBalY => { + self.registers.write_zero_page_bal_y(&mut self.bus); + } + MicroInstruction::WriteAbsolute => self.registers.write_absolute(&mut self.bus), MicroInstruction::ShiftLeftAccumulator => self.registers.shift_left_accumulator(), MicroInstruction::ShiftLeftMemoryBuffer => self.registers.shift_left_memory_buffer(), MicroInstruction::IncrementMemoryBuffer => self.registers.increment_memory_buffer(), @@ -146,7 +149,17 @@ impl CPU { MicroInstruction::LoadAccumulator => self.registers.load_accumulator(), MicroInstruction::LoadX => self.registers.load_x(), MicroInstruction::LoadY => self.registers.load_y(), + MicroInstruction::StoreAccumulator => self.registers.store_accumulator(), + MicroInstruction::StoreX => self.registers.store_x(), + MicroInstruction::StoreY => self.registers.store_y(), + MicroInstruction::TransferAccumulatorToX => self.registers.transfer_acc_to_x(), + MicroInstruction::TransferAccumulatorToY => self.registers.transfer_acc_to_y(), + MicroInstruction::TransferStackptrToX => self.registers.transer_stackptr_to_x(), + MicroInstruction::TransferXToAccumulator => self.registers.transfer_x_to_acc(), + MicroInstruction::TransferXToStackptr => self.registers.transfer_x_to_stackptr(), + MicroInstruction::TransferYToAccumulator => self.registers.transfer_y_to_acc(), MicroInstruction::And => self.registers.and(), + MicroInstruction::Xor => self.registers.xor(), } } } @@ -169,7 +182,6 @@ impl CPUFlag { #[cfg(test)] mod tests { use crate::cpu::operations::Operation; - use std::collections::btree_map::Values; use crate::bus; // Note this useful idiom: importing names from outer (for mod tests) scope. @@ -1489,46 +1501,18 @@ mod tests { } #[test] - fn test_cpu_and_imm() { - let opcode = Operation::AndImm.get_opcode(); - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let expected_value: u8 = 0b0000_0010; - - let mut bus = TestBus::new(); - bus.write(0x0000, opcode); - bus.write(0x0001, value); - - let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; - - _test_read_and_decode_operation(&mut cpu); - - _test_immediate_read(&mut cpu); - - cpu.step(); - - assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); - - assert_eq!(cpu.registers.a, expected_value); - } - - #[test] - fn test_cpu_and_zero_page() { - let opcode = Operation::AndZeroPage.get_opcode(); + fn test_cpu_store_acc_zero_page() { + let opcode = Operation::StoreAccZeroPage.get_opcode(); let adl: u8 = 0xAA; - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let expected_value: u8 = 0b0000_0010; + let acc_value: u8 = 3; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); - bus.write(adl as u16, value); + bus.write(adl as u16, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; _test_read_and_decode_operation(&mut cpu); @@ -1536,29 +1520,39 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPage) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(adl as u16); + assert_eq!(value, cpu.registers.a); } #[test] - fn test_cpu_and_zero_page_x() { - let opcode = Operation::AndZeroPageX.get_opcode(); - let adl: u8 = 0xAA; - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let x_value: u8 = 3; - let expected_value: u8 = 0b0000_0010; - let expected_address: u8 = adl + x_value; + fn test_cpu_store_acc_zero_page_x() { + let opcode = Operation::StoreAccZeroPageX.get_opcode(); + let adl: u8 = 0x0B; + let x_value: u8 = 30; + let acc_value: u8 = 100; + let expected_address: u16 = (adl + x_value) as u16; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); - bus.write(expected_address as u16, value); + bus.write(expected_address, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; cpu.registers.x = x_value; _test_read_and_decode_operation(&mut cpu); @@ -1567,30 +1561,40 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPageBalX) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.a) } #[test] - fn test_cpu_and_absolute() { - let opcode = Operation::AndAbsolute.get_opcode(); + fn test_cpu_store_acc_absolute() { + let opcode = Operation::StoreAccAbsolute.get_opcode(); let adl: u8 = 0xAA; let adh: u8 = 0x11; let address: u16 = 0x11AA; - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let expected_value: u8 = 0b0000_0010; + let acc_value: u8 = 10; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); bus.write(0x0002, adh); - bus.write(address, value); + bus.write(address, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; _test_read_and_decode_operation(&mut cpu); @@ -1598,32 +1602,42 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(address); + assert_eq!(value, cpu.registers.a); } #[test] - fn test_cpu_and_absolute_x() { - let opcode = Operation::AndAbsoluteX.get_opcode(); + fn test_cpu_store_acc_absolute_x() { + let opcode = Operation::StoreAccAbsoluteX.get_opcode(); let adl: u8 = 0xAA; let adh: u8 = 0x11; let address: u16 = 0x11AA; - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let x_value: u8 = 2; - let expected_value: u8 = 0b0000_0010; + let acc_value: u8 = 10; + let x_value: u8 = 20; let expected_address: u16 = address + x_value as u16; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); bus.write(0x0002, adh); - bus.write(expected_address, value); + bus.write(expected_address, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; cpu.registers.x = x_value; _test_read_and_decode_operation(&mut cpu); @@ -1632,32 +1646,42 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.a); } #[test] - fn test_cpu_and_absolute_y() { - let opcode = Operation::AndAbsoluteY.get_opcode(); + fn test_cpu_store_acc_absolute_y() { + let opcode = Operation::StoreAccAbsoluteY.get_opcode(); let adl: u8 = 0xAA; let adh: u8 = 0x11; let address: u16 = 0x11AA; - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let y_value: u8 = 200; - let expected_value: u8 = 0b0000_0010; + let acc_value: u8 = 10; + let y_value: u8 = 20; let expected_address: u16 = address + y_value as u16; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); bus.write(0x0002, adh); - bus.write(expected_address, value); + bus.write(expected_address, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; cpu.registers.y = y_value; _test_read_and_decode_operation(&mut cpu); @@ -1666,31 +1690,41 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.a); } #[test] - fn test_cpu_and_indirect_x() { - let opcode = Operation::AndIndirectX.get_opcode(); - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let expected_value: u8 = 0b0000_0010; + fn test_cpu_store_acc_indirect_x() { + let opcode = Operation::StoreAccIndirectX.get_opcode(); + let a_value: u8 = 5; let x_value: u8 = 10; - let adl: u8 = 0x22; + let adl: u8 = 0xA0; let expected_address: u16 = (adl + x_value) as u16; - let indirect_adl: u8 = 0xBB; + let indirect_adl: u8 = 0x88; let indirect_adh: u8 = 0xAA; - let indirect_address: u16 = 0xAABB; + let indirect_address: u16 = 0xAA88; let mut bus = TestBus::new(); bus.write(0x0000, opcode); bus.write(0x0001, adl); bus.write(expected_address, indirect_adl); bus.write(expected_address + 1, indirect_adh); - bus.write(indirect_address, value); + bus.write(indirect_address, 0); let mut cpu = CPU::new(bus); cpu.registers.a = a_value; @@ -1702,18 +1736,28 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); - assert_eq!(cpu.registers.a, expected_value); + let value = cpu.bus.read(indirect_address); + assert_eq!(value, cpu.registers.a); } #[test] - fn test_cpu_and_indirect_y() { - let opcode = Operation::AndIndirectY.get_opcode(); - let value: u8 = 0b0000_1010; - let a_value: u8 = 0b1111_0011; - let expected_value: u8 = 0b0000_0010; + fn test_cpu_store_acc_indirect_y() { + let opcode = Operation::StoreAccIndirectY.get_opcode(); + let acc_value: u8 = 13; let y_value: u8 = 20; let adl: u8 = 0x22; let indirect_adl: u8 = 0xBB; @@ -1726,10 +1770,10 @@ mod tests { bus.write(0x0001, adl); bus.write(adl as u16, indirect_adl); bus.write((adl + 1) as u16, indirect_adh); - bus.write(expected_address, value); + bus.write(expected_address, 0); let mut cpu = CPU::new(bus); - cpu.registers.a = a_value; + cpu.registers.a = acc_value; cpu.registers.y = y_value; _test_read_and_decode_operation(&mut cpu); @@ -1738,8 +1782,928 @@ mod tests { cpu.step(); + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreAccumulator) + ); + + cpu.step(); + assert_eq!(cpu.state, CPUState::Fetching); - assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); + + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.a); + } + + #[test] + fn test_cpu_store_x_zero_page() { + let opcode = Operation::StoreXZeroPage.get_opcode(); + let adl: u8 = 0xAA; + let x_value: u8 = 5; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreX) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPage) + ); + + let value = cpu.bus.read(adl as u16); + assert_eq!(value, cpu.registers.x); + } + + #[test] + fn test_cpu_store_x_zero_page_y() { + let opcode = Operation::StoreXZeroPageY.get_opcode(); + let adl: u8 = 0x0B; + let x_value: u8 = 30; + let y_value: u8 = 10; + let expected_address: u16 = (adl + y_value) as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.x = x_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreX) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPageBalY) + ); + + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.x) + } + + #[test] + fn test_cpu_store_x_absolute() { + let opcode = Operation::StoreXAbsolute.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let x_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(address, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreX) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); + + let value = cpu.bus.read(address); + assert_eq!(value, cpu.registers.x); + } + + #[test] + fn test_cpu_store_y_zero_page() { + let opcode = Operation::StoreYZeroPage.get_opcode(); + let adl: u8 = 0xAA; + let y_value: u8 = 5; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreY) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPage) + ); + + let value = cpu.bus.read(adl as u16); + assert_eq!(value, cpu.registers.y); + } + + #[test] + fn test_cpu_store_y_zero_page_x() { + let opcode = Operation::StoreYZeroPageX.get_opcode(); + let adl: u8 = 0x0B; + let x_value: u8 = 30; + let y_value: u8 = 10; + let expected_address: u16 = (adl + x_value) as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.x = x_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreY) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteZeroPageBalX) + ); + + let value = cpu.bus.read(expected_address); + assert_eq!(value, cpu.registers.y) + } + + #[test] + fn test_cpu_store_y_absolute() { + let opcode = Operation::StoreYAbsolute.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let y_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(address, 0); + + let mut cpu = CPU::new(bus); + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Execution); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::StoreY) + ); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::WriteAbsolute) + ); + + let value = cpu.bus.read(address); + assert_eq!(value, cpu.registers.y); + } + + #[test] + fn test_cpu_transfer_acc_to_x() { + let opcode = Operation::TransferAccToX.get_opcode(); + let acc_value: u8 = 10; + let x_value: u8 = 5; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferAccumulatorToX) + ); + + assert_eq!(cpu.registers.x, cpu.registers.a); + } + + #[test] + fn test_cpu_transfer_acc_to_y() { + let opcode = Operation::TransferAccToY.get_opcode(); + let acc_value: u8 = 10; + let y_value: u8 = 5; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferAccumulatorToY) + ); + + assert_eq!(cpu.registers.y, cpu.registers.a); + } + + #[test] + fn test_cpu_transfer_stackptr_to_x() { + let opcode = Operation::TransferStackptrToX.get_opcode(); + let stack_ptr_value: u8 = 0xAB; + let x_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.stack_ptr = stack_ptr_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferStackptrToX) + ); + + assert_eq!(cpu.registers.x, cpu.registers.stack_ptr); + } + + #[test] + fn test_cpu_transfer_x_to_acc() { + let opcode = Operation::TransferXToAcc.get_opcode(); + let acc_value: u8 = 21; + let x_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferXToAccumulator) + ); + + assert_eq!(cpu.registers.a, cpu.registers.x); + } + + #[test] + fn test_cpu_transfer_x_to_stackptr() { + let opcode = Operation::TransferXToStackptr.get_opcode(); + let stack_ptr_value: u8 = 0x12; + let x_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.stack_ptr = stack_ptr_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferXToStackptr) + ); + + assert_eq!(cpu.registers.stack_ptr, cpu.registers.x); + } + + #[test] + fn test_cpu_transfer_y_to_acc() { + let opcode = Operation::TransferYToAcc.get_opcode(); + let acc_value: u8 = 133; + let y_value: u8 = 10; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::TransferYToAccumulator) + ); + + assert_eq!(cpu.registers.a, cpu.registers.y); + } + + #[test] + fn test_cpu_and_imm() { + let opcode = Operation::AndImm.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b0000_0010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_immediate_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_zero_page() { + let opcode = Operation::AndZeroPage.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b0000_0010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_zero_page_x() { + let opcode = Operation::AndZeroPageX.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let x_value: u8 = 3; + let expected_value: u8 = 0b0000_0010; + let expected_address: u8 = adl + x_value; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_absolute() { + let opcode = Operation::AndAbsolute.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b0000_0010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_absolute_x() { + let opcode = Operation::AndAbsoluteX.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let x_value: u8 = 2; + let expected_value: u8 = 0b0000_0010; + let expected_address: u16 = address + x_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_absolute_y() { + let opcode = Operation::AndAbsoluteY.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let y_value: u8 = 200; + let expected_value: u8 = 0b0000_0010; + let expected_address: u16 = address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_indirect_x() { + let opcode = Operation::AndIndirectX.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b0000_0010; + let x_value: u8 = 10; + let adl: u8 = 0x22; + let expected_address: u16 = (adl + x_value) as u16; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address, indirect_adl); + bus.write(expected_address + 1, indirect_adh); + bus.write(indirect_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_and_indirect_y() { + let opcode = Operation::AndIndirectY.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b0000_0010; + let y_value: u8 = 20; + let adl: u8 = 0x22; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + let expected_address: u16 = indirect_address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, indirect_adl); + bus.write((adl + 1) as u16, indirect_adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::And)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_imm() { + let opcode = Operation::XorImm.get_opcode(); + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let expected_value: u8 = 0b1111_0000; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_immediate_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_zero_page() { + let opcode = Operation::XorZeroPage.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let expected_value: u8 = 0b1111_0000; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_zero_page_x() { + let opcode = Operation::XorZeroPageX.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let x_value: u8 = 40; + let expected_address: u8 = adl + x_value; + let expected_value: u8 = 0b1111_0000; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_absolute() { + let opcode = Operation::XorAbsolute.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1001; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_absolute_x() { + let opcode = Operation::XorAbsoluteX.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let x_value: u8 = 2; + let expected_value: u8 = 0b1111_1001; + let expected_address: u16 = address + x_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_absolute_y() { + let opcode = Operation::XorAbsoluteY.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let y_value: u8 = 200; + let expected_value: u8 = 0b1111_1001; + let expected_address: u16 = address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_indirect_x() { + let opcode = Operation::XorIndirectX.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1001; + let x_value: u8 = 10; + let adl: u8 = 0x22; + let expected_address: u16 = (adl + x_value) as u16; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address, indirect_adl); + bus.write(expected_address + 1, indirect_adh); + bus.write(indirect_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_xor_indirect_y() { + let opcode = Operation::XorIndirectY.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1001; + let y_value: u8 = 20; + let adl: u8 = 0x22; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + let expected_address: u16 = indirect_address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, indirect_adl); + bus.write((adl + 1) as u16, indirect_adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Xor)); assert_eq!(cpu.registers.a, expected_value); } diff --git a/emulator/src/cpu/micro_instructions.rs b/emulator/src/cpu/micro_instructions.rs index 2400ef4..f8ce88f 100644 --- a/emulator/src/cpu/micro_instructions.rs +++ b/emulator/src/cpu/micro_instructions.rs @@ -22,8 +22,9 @@ pub enum MicroInstruction { ReadBahIndirectIal, WriteZeroPage, - WriteAbsolute, WriteZeroPageBalX, + WriteZeroPageBalY, + WriteAbsolute, ShiftLeftAccumulator, ShiftLeftMemoryBuffer, @@ -38,8 +39,18 @@ pub enum MicroInstruction { LoadAccumulator, LoadX, LoadY, + StoreAccumulator, + StoreX, + StoreY, + TransferAccumulatorToX, + TransferAccumulatorToY, + TransferStackptrToX, + TransferXToAccumulator, + TransferXToStackptr, + TransferYToAccumulator, And, + Xor, } pub struct MicroInstructionSequence { diff --git a/emulator/src/cpu/operations.rs b/emulator/src/cpu/operations.rs index a9be5bc..484dc59 100644 --- a/emulator/src/cpu/operations.rs +++ b/emulator/src/cpu/operations.rs @@ -6,18 +6,21 @@ pub enum Operation { AslZeroPage, AslZeroPageX, AslAbsolute, + IncMemZeroPage, IncMemZeroPageX, IncMemAbsolute, IncMemAbsoluteX, IncX, IncY, + DecMemZeroPage, DecMemZeroPageX, DecMemAbsolute, DecMemAbsoluteX, DecX, DecY, + LoadAccImm, LoadAccZeroPage, LoadAccZeroPageX, @@ -36,6 +39,28 @@ pub enum Operation { LoadYZeroPageX, LoadYAbsolute, LoadYAbsoluteX, + + StoreAccZeroPage, + StoreAccZeroPageX, + StoreAccAbsolute, + StoreAccAbsoluteX, + StoreAccAbsoluteY, + StoreAccIndirectX, + StoreAccIndirectY, + StoreXZeroPage, + StoreXZeroPageY, + StoreXAbsolute, + StoreYZeroPage, + StoreYZeroPageX, + StoreYAbsolute, + + TransferAccToX, + TransferAccToY, + TransferStackptrToX, + TransferXToAcc, + TransferXToStackptr, + TransferYToAcc, + AndImm, AndZeroPage, AndZeroPageX, @@ -44,6 +69,15 @@ pub enum Operation { AndAbsoluteY, AndIndirectX, AndIndirectY, + + XorImm, + XorZeroPage, + XorZeroPageX, + XorAbsolute, + XorAbsoluteX, + XorAbsoluteY, + XorIndirectX, + XorIndirectY, } pub struct OperationMicroInstructions { @@ -296,6 +330,133 @@ impl Operation { addressing_sequence: Some(absolute_x_addressing), operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::LoadY]), }, + Self::StoreAccZeroPage => OperationMicroInstructions { + addressing_sequence: Some(zero_page_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteZeroPage, + ]), + }, + Self::StoreAccZeroPageX => OperationMicroInstructions { + addressing_sequence: Some(zero_page_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteZeroPageBalX, + ]), + }, + Self::StoreAccAbsolute => OperationMicroInstructions { + addressing_sequence: Some(absolute_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreAccAbsoluteX => OperationMicroInstructions { + addressing_sequence: Some(absolute_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreAccAbsoluteY => OperationMicroInstructions { + addressing_sequence: Some(absolute_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreAccIndirectX => OperationMicroInstructions { + addressing_sequence: Some(indirect_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreAccIndirectY => OperationMicroInstructions { + addressing_sequence: Some(indirect_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreAccumulator, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreXZeroPage => OperationMicroInstructions { + addressing_sequence: Some(zero_page_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreX, + MicroInstruction::WriteZeroPage, + ]), + }, + Self::StoreXZeroPageY => OperationMicroInstructions { + addressing_sequence: Some(zero_page_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreX, + MicroInstruction::WriteZeroPageBalY, + ]), + }, + Self::StoreXAbsolute => OperationMicroInstructions { + addressing_sequence: Some(absolute_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreX, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::StoreYZeroPage => OperationMicroInstructions { + addressing_sequence: Some(zero_page_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreY, + MicroInstruction::WriteZeroPage, + ]), + }, + Self::StoreYZeroPageX => OperationMicroInstructions { + addressing_sequence: Some(zero_page_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreY, + MicroInstruction::WriteZeroPageBalX, + ]), + }, + Self::StoreYAbsolute => OperationMicroInstructions { + addressing_sequence: Some(absolute_addressing), + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::StoreY, + MicroInstruction::WriteAbsolute, + ]), + }, + Self::TransferAccToX => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferAccumulatorToX, + ]), + }, + Self::TransferAccToY => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferAccumulatorToY, + ]), + }, + Self::TransferStackptrToX => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferStackptrToX, + ]), + }, + Self::TransferXToAcc => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferXToAccumulator, + ]), + }, + Self::TransferXToStackptr => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferXToStackptr, + ]), + }, + Self::TransferYToAcc => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::TransferYToAccumulator, + ]), + }, Self::AndImm => OperationMicroInstructions { addressing_sequence: Some(immediate_addressing), operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::And]), @@ -328,6 +489,38 @@ impl Operation { addressing_sequence: Some(indirect_y_addressing), operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::And]), }, + Self::XorImm => OperationMicroInstructions { + addressing_sequence: Some(immediate_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorZeroPage => OperationMicroInstructions { + addressing_sequence: Some(zero_page_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorZeroPageX => OperationMicroInstructions { + addressing_sequence: Some(zero_page_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorAbsolute => OperationMicroInstructions { + addressing_sequence: Some(absolute_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorAbsoluteX => OperationMicroInstructions { + addressing_sequence: Some(absolute_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorAbsoluteY => OperationMicroInstructions { + addressing_sequence: Some(absolute_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorIndirectX => OperationMicroInstructions { + addressing_sequence: Some(indirect_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, + Self::XorIndirectY => OperationMicroInstructions { + addressing_sequence: Some(indirect_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), + }, } } @@ -367,6 +560,33 @@ impl Operation { Self::LoadYZeroPageX => 0xB4, Self::LoadYAbsolute => 0xAC, Self::LoadYAbsoluteX => 0xBC, + Self::StoreAccZeroPage => 0x85, + Self::StoreAccZeroPageX => 0x95, + Self::StoreAccAbsolute => 0x8D, + Self::StoreAccAbsoluteX => 0x9D, + Self::StoreAccAbsoluteY => 0x99, + Self::StoreAccIndirectX => 0x81, + Self::StoreAccIndirectY => 0x91, + Self::StoreXZeroPage => 0x86, + Self::StoreXZeroPageY => 0x96, + Self::StoreXAbsolute => 0x8E, + Self::StoreYZeroPage => 0x84, + Self::StoreYZeroPageX => 0x94, + Self::StoreYAbsolute => 0x8C, + Self::TransferAccToX => 0xAA, + Self::TransferAccToY => 0xA8, + Self::TransferStackptrToX => 0xBA, + Self::TransferXToAcc => 0x8A, + Self::TransferXToStackptr => 0x9A, + Self::TransferYToAcc => 0x98, + Self::XorImm => 0x49, + Self::XorZeroPage => 0x45, + Self::XorZeroPageX => 0x55, + Self::XorAbsolute => 0x4D, + Self::XorAbsoluteX => 0x5D, + Self::XorAbsoluteY => 0x59, + Self::XorIndirectX => 0x41, + Self::XorIndirectY => 0x51, Self::AndImm => 0x29, Self::AndZeroPage => 0x25, Self::AndZeroPageX => 0x35, @@ -414,6 +634,33 @@ impl Operation { 0xB4 => Some(Self::LoadYZeroPageX), 0xAC => Some(Self::LoadYAbsolute), 0xBC => Some(Self::LoadYAbsoluteX), + 0x85 => Some(Self::StoreAccZeroPage), + 0x95 => Some(Self::StoreAccZeroPageX), + 0x8D => Some(Self::StoreAccAbsolute), + 0x9D => Some(Self::StoreAccAbsoluteX), + 0x99 => Some(Self::StoreAccAbsoluteY), + 0x81 => Some(Self::StoreAccIndirectX), + 0x91 => Some(Self::StoreAccIndirectY), + 0x86 => Some(Self::StoreXZeroPage), + 0x96 => Some(Self::StoreXZeroPageY), + 0x8E => Some(Self::StoreXAbsolute), + 0x84 => Some(Self::StoreYZeroPage), + 0x94 => Some(Self::StoreYZeroPageX), + 0x8C => Some(Self::StoreYAbsolute), + 0xAA => Some(Self::TransferAccToX), + 0xA8 => Some(Self::TransferAccToY), + 0xBA => Some(Self::TransferStackptrToX), + 0x8A => Some(Self::TransferXToAcc), + 0x9A => Some(Self::TransferXToStackptr), + 0x98 => Some(Self::TransferYToAcc), + 0x49 => Some(Self::XorImm), + 0x45 => Some(Self::XorZeroPage), + 0x55 => Some(Self::XorZeroPageX), + 0x4D => Some(Self::XorAbsolute), + 0x5D => Some(Self::XorAbsoluteX), + 0x59 => Some(Self::XorAbsoluteY), + 0x41 => Some(Self::XorIndirectX), + 0x51 => Some(Self::XorIndirectY), 0x29 => Some(Self::AndImm), 0x25 => Some(Self::AndZeroPage), 0x35 => Some(Self::AndZeroPageX), diff --git a/emulator/src/cpu/registers.rs b/emulator/src/cpu/registers.rs index 058a753..a5c80b8 100644 --- a/emulator/src/cpu/registers.rs +++ b/emulator/src/cpu/registers.rs @@ -9,7 +9,7 @@ pub struct Registers { pub y: u8, pub a: u8, program_counter: u16, - stack_ptr: u8, + pub stack_ptr: u8, status: u8, operation: u8, adl: u8, @@ -159,9 +159,19 @@ impl Registers { bus.write(self.adl as u16, self.memory_buffer); } + pub fn write_zero_page_bal_x(&mut self, bus: &mut T) { + let address = (self.bal + self.x) as usize; + bus.write(address as u16, self.memory_buffer); + } + + pub fn write_zero_page_bal_y(&mut self, bus: &mut T) { + let address = (self.bal + self.y) as usize; + bus.write(address as u16, self.memory_buffer); + } + pub fn write_absolute(&mut self, bus: &mut T) { let address = (self.adh as u16) << 8 | self.adl as u16; - bus.write(address as u16, self.memory_buffer); + bus.write(address, self.memory_buffer); } pub fn read_zero_page_bal_x(&mut self, bus: &mut T) { @@ -176,11 +186,6 @@ impl Registers { self.memory_buffer = bus.read(address as u16); } - pub fn write_zero_page_bal_x(&mut self, bus: &mut T) { - let address = (self.bal + self.x) as usize; - bus.write(address as u16, self.memory_buffer); - } - pub fn read_adl_adh_absolute_index_register( &mut self, bus: &mut T, @@ -317,6 +322,67 @@ impl Registers { self.set_flag_value(CPUFlag::Negative, is_negative); } + pub fn store_accumulator(&mut self) { + self.memory_buffer = self.a; + } + + pub fn store_x(&mut self) { + self.memory_buffer = self.x; + } + + pub fn store_y(&mut self) { + self.memory_buffer = self.y; + } + + pub fn transfer_acc_to_x(&mut self) { + self.x = self.a; + let is_zero = self.x == 0; + let is_negative = self.x & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } + + pub fn transfer_acc_to_y(&mut self) { + self.y = self.a; + let is_zero = self.y == 0; + let is_negative = self.y & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } + + pub fn transer_stackptr_to_x(&mut self) { + self.x = self.stack_ptr; + let is_zero = self.x == 0; + let is_negative = self.x & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } + + pub fn transfer_x_to_acc(&mut self) { + self.a = self.x; + let is_zero = self.a == 0; + let is_negative = self.a & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } + + pub fn transfer_x_to_stackptr(&mut self) { + self.stack_ptr = self.x; + } + + pub fn transfer_y_to_acc(&mut self) { + self.a = self.y; + let is_zero = self.a == 0; + let is_negative = self.a & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } + pub fn and(&mut self) { self.a = self.a & self.memory_buffer; let is_zero = self.a == 0; @@ -325,4 +391,13 @@ impl Registers { self.set_flag_value(CPUFlag::Zero, is_zero); self.set_flag_value(CPUFlag::Negative, is_negative); } + + pub fn xor(&mut self) { + self.a = self.a ^ self.memory_buffer; + let is_zero = self.a == 0; + let is_negative = self.a & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } } From 0aa54dfc1701afa4261e07c50cca51cdbb8bbc28 Mon Sep 17 00:00:00 2001 From: Bruno Banaszczyk Date: Thu, 16 Jan 2025 13:52:17 +0100 Subject: [PATCH 2/2] added 19 new instructions (flags, push, or) --- emulator/src/cpu/cpu.rs | 551 +++++++++++++++++++++++++ emulator/src/cpu/micro_instructions.rs | 14 + emulator/src/cpu/operations.rs | 190 ++++++++- emulator/src/cpu/registers.rs | 39 +- 4 files changed, 776 insertions(+), 18 deletions(-) diff --git a/emulator/src/cpu/cpu.rs b/emulator/src/cpu/cpu.rs index fffcb47..3379d17 100644 --- a/emulator/src/cpu/cpu.rs +++ b/emulator/src/cpu/cpu.rs @@ -138,8 +138,27 @@ impl CPU { self.registers.write_zero_page_bal_y(&mut self.bus); } MicroInstruction::WriteAbsolute => self.registers.write_absolute(&mut self.bus), + MicroInstruction::ClearCarryFlag => self.registers.clear_flag(CPUFlag::CarryBit), + MicroInstruction::ClearDecimalFlag => self.registers.clear_flag(CPUFlag::DecimalMode), + MicroInstruction::ClearInterruptDisableFlag => { + self.registers.clear_flag(CPUFlag::InterruptDisable) + } + MicroInstruction::ClearOverflowFlag => self.registers.clear_flag(CPUFlag::Overflow), + MicroInstruction::SetCarryFlag => self.registers.set_flag(CPUFlag::CarryBit), + MicroInstruction::SetDecimalFlag => self.registers.set_flag(CPUFlag::DecimalMode), + MicroInstruction::SetInterruptDisableFlag => { + self.registers.set_flag(CPUFlag::InterruptDisable) + } MicroInstruction::ShiftLeftAccumulator => self.registers.shift_left_accumulator(), MicroInstruction::ShiftLeftMemoryBuffer => self.registers.shift_left_memory_buffer(), + MicroInstruction::PushAccumulator => self.registers.push_accumulator(&mut self.bus), + MicroInstruction::PushStatusRegister => { + self.registers.push_status_register(&mut self.bus) + } + MicroInstruction::PullAccumulator => self.registers.pull_accumulator(&mut self.bus), + MicroInstruction::PullStatusRegister => { + self.registers.pull_status_register(&mut self.bus) + } MicroInstruction::IncrementMemoryBuffer => self.registers.increment_memory_buffer(), MicroInstruction::IncrementX => self.registers.increment_x(), MicroInstruction::IncrementY => self.registers.increment_y(), @@ -160,6 +179,7 @@ impl CPU { MicroInstruction::TransferYToAccumulator => self.registers.transfer_y_to_acc(), MicroInstruction::And => self.registers.and(), MicroInstruction::Xor => self.registers.xor(), + MicroInstruction::Or => self.registers.or(), } } } @@ -476,6 +496,167 @@ mod tests { ); } + #[test] + fn test_cpu_clear_carry_flag() { + let opcode = Operation::ClearCarryFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.set_flag(CPUFlag::CarryBit); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::ClearCarryFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::CarryBit), false); + } + + #[test] + fn test_cpu_clear_decimal_flag() { + let opcode = Operation::ClearDecimalFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.set_flag(CPUFlag::DecimalMode); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::ClearDecimalFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::DecimalMode), false); + } + + #[test] + fn test_cpu_clear_interrupt_disable_flag() { + let opcode = Operation::ClearInterruptDisableFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.set_flag(CPUFlag::InterruptDisable); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::ClearInterruptDisableFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::InterruptDisable), false); + } + + #[test] + fn test_cpu_clear_overflow_flag() { + let opcode = Operation::ClearOverflowFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.set_flag(CPUFlag::Overflow); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::ClearOverflowFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::Overflow), false); + } + + #[test] + fn test_cpu_set_carry_flag() { + let opcode = Operation::SetCarryFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.clear_flag(CPUFlag::CarryBit); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::SetCarryFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::CarryBit), true); + } + + #[test] + fn test_cpu_set_decimal_flag() { + let opcode = Operation::SetDecimalFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.clear_flag(CPUFlag::DecimalMode); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::SetDecimalFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::DecimalMode), true); + } + + #[test] + fn test_cpu_set_interrupt_disable_flag() { + let opcode = Operation::SetInterruptDisableFlag.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.clear_flag(CPUFlag::InterruptDisable); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::SetInterruptDisableFlag) + ); + + assert_eq!(cpu.registers.is_flag_set(CPUFlag::InterruptDisable), true); + } + #[test] fn test_cpu_asl_a() { const OPCODE: u8 = 0x0A; @@ -556,6 +737,120 @@ mod tests { assert_eq!(read_value, EXPECTED_VALUE); } + #[test] + fn test_cpu_push_acc() { + let opcode = Operation::PushAcc.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + cpu.registers.a = 20; + + _test_read_and_decode_operation(&mut cpu); + + assert_eq!(cpu.registers.stack_ptr, 0xFF); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::PushAccumulator) + ); + + let stack_value: u8 = cpu.bus.read(0x01FF); + assert_eq!(stack_value, cpu.registers.a); + assert_eq!(cpu.registers.stack_ptr, 0xFF - 1); + } + + #[test] + fn test_cpu_push_status() { + let opcode = Operation::PushStatus.get_opcode(); + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + + let mut cpu = CPU::new(bus); + assert_eq!(cpu.registers.status, 0x00); + cpu.registers.set_flag(CPUFlag::Negative); + cpu.registers.set_flag(CPUFlag::CarryBit); + cpu.registers.set_flag(CPUFlag::Overflow); + + _test_read_and_decode_operation(&mut cpu); + + assert_eq!(cpu.registers.stack_ptr, 0xFF); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::PushStatusRegister) + ); + + let stack_value: u8 = cpu.bus.read(0x01FF); + assert_eq!( + stack_value, + cpu.registers.status | CPUFlag::Break.value() | CPUFlag::Unused.value() + ); + assert_eq!(cpu.registers.stack_ptr, 0xFF - 1); + } + + #[test] + fn test_cpu_pull_acc() { + let opcode = Operation::PullAcc.get_opcode(); + + let mut bus = TestBus::new(); + let stack_value: u8 = 100; + bus.write(0x0000, opcode); + bus.write(0x01FF, stack_value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = 20; + cpu.registers.stack_ptr = 0xFF - 1; + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::PullAccumulator) + ); + + assert_eq!(stack_value, cpu.registers.a); + assert_eq!(cpu.registers.stack_ptr, 0xFF); + } + + #[test] + fn test_cpu_pull_status() { + let opcode = Operation::PullStatus.get_opcode(); + + let mut bus = TestBus::new(); + let stack_status: u8 = 0b1011_1100; + bus.write(0x0000, opcode); + bus.write(0x01FF, stack_status); + + let mut cpu = CPU::new(bus); + cpu.registers.stack_ptr = 0xFF - 1; + assert_eq!(cpu.registers.status, 0x00); + + _test_read_and_decode_operation(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!( + cpu.current_micro_instruction, + Some(MicroInstruction::PullStatusRegister) + ); + + assert_eq!(stack_status & 0b1100_1111, cpu.registers.status); + assert_eq!(cpu.registers.stack_ptr, 0xFF); + } + #[test] fn test_cpu_inc_mem_zero_page() { let opcode: u8 = Operation::IncMemZeroPage.get_opcode(); @@ -2707,4 +3002,260 @@ mod tests { assert_eq!(cpu.registers.a, expected_value); } + + #[test] + fn test_cpu_or_imm() { + let opcode = Operation::OrImm.get_opcode(); + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let expected_value: u8 = 0b1111_1010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_immediate_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_zero_page() { + let opcode = Operation::OrZeroPage.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let expected_value: u8 = 0b1111_1010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_zero_page_x() { + let opcode = Operation::OrZeroPageX.get_opcode(); + let adl: u8 = 0xAA; + let value: u8 = 0b1010_1010; + let acc_value: u8 = 0b0101_1010; + let x_value: u8 = 40; + let expected_address: u8 = adl + x_value; + let expected_value: u8 = 0b1111_1010; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address as u16, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = acc_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_zero_page_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_absolute() { + let opcode = Operation::OrAbsolute.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1011; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_absolute_x() { + let opcode = Operation::OrAbsoluteX.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let x_value: u8 = 2; + let expected_value: u8 = 0b1111_1011; + let expected_address: u16 = address + x_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_absolute_y() { + let opcode = Operation::OrAbsoluteY.get_opcode(); + let adl: u8 = 0xAA; + let adh: u8 = 0x11; + let address: u16 = 0x11AA; + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let y_value: u8 = 200; + let expected_value: u8 = 0b1111_1011; + let expected_address: u16 = address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(0x0002, adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_absolute_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_indirect_x() { + let opcode = Operation::OrIndirectX.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1011; + let x_value: u8 = 10; + let adl: u8 = 0x22; + let expected_address: u16 = (adl + x_value) as u16; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(expected_address, indirect_adl); + bus.write(expected_address + 1, indirect_adh); + bus.write(indirect_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.x = x_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_x_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } + + #[test] + fn test_cpu_or_indirect_y() { + let opcode = Operation::OrIndirectY.get_opcode(); + let value: u8 = 0b0000_1010; + let a_value: u8 = 0b1111_0011; + let expected_value: u8 = 0b1111_1011; + let y_value: u8 = 20; + let adl: u8 = 0x22; + let indirect_adl: u8 = 0xBB; + let indirect_adh: u8 = 0xAA; + let indirect_address: u16 = 0xAABB; + let expected_address: u16 = indirect_address + y_value as u16; + + let mut bus = TestBus::new(); + bus.write(0x0000, opcode); + bus.write(0x0001, adl); + bus.write(adl as u16, indirect_adl); + bus.write((adl + 1) as u16, indirect_adh); + bus.write(expected_address, value); + + let mut cpu = CPU::new(bus); + cpu.registers.a = a_value; + cpu.registers.y = y_value; + + _test_read_and_decode_operation(&mut cpu); + + _test_indirect_y_read(&mut cpu); + + cpu.step(); + + assert_eq!(cpu.state, CPUState::Fetching); + assert_eq!(cpu.current_micro_instruction, Some(MicroInstruction::Or)); + + assert_eq!(cpu.registers.a, expected_value); + } } diff --git a/emulator/src/cpu/micro_instructions.rs b/emulator/src/cpu/micro_instructions.rs index f8ce88f..df8c153 100644 --- a/emulator/src/cpu/micro_instructions.rs +++ b/emulator/src/cpu/micro_instructions.rs @@ -26,9 +26,22 @@ pub enum MicroInstruction { WriteZeroPageBalY, WriteAbsolute, + ClearCarryFlag, + ClearDecimalFlag, + ClearInterruptDisableFlag, + ClearOverflowFlag, + SetCarryFlag, + SetDecimalFlag, + SetInterruptDisableFlag, + ShiftLeftAccumulator, ShiftLeftMemoryBuffer, + PushAccumulator, + PushStatusRegister, + PullAccumulator, + PullStatusRegister, + IncrementMemoryBuffer, IncrementX, IncrementY, @@ -51,6 +64,7 @@ pub enum MicroInstruction { And, Xor, + Or, } pub struct MicroInstructionSequence { diff --git a/emulator/src/cpu/operations.rs b/emulator/src/cpu/operations.rs index 484dc59..e500c5b 100644 --- a/emulator/src/cpu/operations.rs +++ b/emulator/src/cpu/operations.rs @@ -2,11 +2,24 @@ use crate::cpu::micro_instructions::{MicroInstruction, MicroInstructionSequence} #[derive(PartialEq, Debug)] pub enum Operation { + ClearCarryFlag, + ClearDecimalFlag, + ClearInterruptDisableFlag, + ClearOverflowFlag, + SetCarryFlag, + SetDecimalFlag, + SetInterruptDisableFlag, + AslA, AslZeroPage, AslZeroPageX, AslAbsolute, + PushAcc, + PushStatus, + PullAcc, + PullStatus, + IncMemZeroPage, IncMemZeroPageX, IncMemAbsolute, @@ -78,6 +91,15 @@ pub enum Operation { XorAbsoluteY, XorIndirectX, XorIndirectY, + + OrImm, + OrZeroPage, + OrZeroPageX, + OrAbsolute, + OrAbsoluteX, + OrAbsoluteY, + OrIndirectX, + OrIndirectY, } pub struct OperationMicroInstructions { @@ -135,6 +157,48 @@ impl Operation { MicroInstructionSequence::new(vec![MicroInstruction::ImmediateRead]); match self { + Self::ClearCarryFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::ClearCarryFlag, + ]), + }, + Self::ClearDecimalFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::ClearDecimalFlag, + ]), + }, + Self::ClearInterruptDisableFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::ClearInterruptDisableFlag, + ]), + }, + Self::ClearOverflowFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::ClearOverflowFlag, + ]), + }, + Self::SetCarryFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::SetCarryFlag, + ]), + }, + Self::SetDecimalFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::SetDecimalFlag, + ]), + }, + Self::SetInterruptDisableFlag => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::SetInterruptDisableFlag, + ]), + }, Self::AslA => OperationMicroInstructions { addressing_sequence: None, operation_sequence: MicroInstructionSequence::new(vec![ @@ -162,6 +226,30 @@ impl Operation { MicroInstruction::WriteAbsolute, ]), }, + Self::PushAcc => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::PushAccumulator, + ]), + }, + Self::PushStatus => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::PushStatusRegister, + ]), + }, + Self::PullAcc => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::PullAccumulator, + ]), + }, + Self::PullStatus => OperationMicroInstructions { + addressing_sequence: None, + operation_sequence: MicroInstructionSequence::new(vec![ + MicroInstruction::PullStatusRegister, + ]), + }, Self::IncMemZeroPage => OperationMicroInstructions { addressing_sequence: Some(zero_page_addressing), operation_sequence: MicroInstructionSequence::new(vec![ @@ -521,15 +609,58 @@ impl Operation { addressing_sequence: Some(indirect_y_addressing), operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Xor]), }, + Self::OrImm => OperationMicroInstructions { + addressing_sequence: Some(immediate_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrZeroPage => OperationMicroInstructions { + addressing_sequence: Some(zero_page_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrZeroPageX => OperationMicroInstructions { + addressing_sequence: Some(zero_page_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrAbsolute => OperationMicroInstructions { + addressing_sequence: Some(absolute_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrAbsoluteX => OperationMicroInstructions { + addressing_sequence: Some(absolute_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrAbsoluteY => OperationMicroInstructions { + addressing_sequence: Some(absolute_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrIndirectX => OperationMicroInstructions { + addressing_sequence: Some(indirect_x_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, + Self::OrIndirectY => OperationMicroInstructions { + addressing_sequence: Some(indirect_y_addressing), + operation_sequence: MicroInstructionSequence::new(vec![MicroInstruction::Or]), + }, } } pub fn get_opcode(&self) -> u8 { match self { + Self::ClearCarryFlag => 0x18, + Self::ClearDecimalFlag => 0xD8, + Self::ClearInterruptDisableFlag => 0x58, + Self::ClearOverflowFlag => 0xB8, + Self::SetCarryFlag => 0x38, + Self::SetDecimalFlag => 0xF8, + Self::SetInterruptDisableFlag => 0x78, Self::AslA => 0x0A, Self::AslZeroPage => 0x06, Self::AslZeroPageX => 0x16, Self::AslAbsolute => 0x0E, + Self::PushAcc => 0x48, + Self::PushStatus => 0x08, + Self::PullAcc => 0x68, + Self::PullStatus => 0x28, Self::IncMemZeroPage => 0xE6, Self::IncMemZeroPageX => 0xF6, Self::IncMemAbsolute => 0xEE, @@ -579,14 +710,6 @@ impl Operation { Self::TransferXToAcc => 0x8A, Self::TransferXToStackptr => 0x9A, Self::TransferYToAcc => 0x98, - Self::XorImm => 0x49, - Self::XorZeroPage => 0x45, - Self::XorZeroPageX => 0x55, - Self::XorAbsolute => 0x4D, - Self::XorAbsoluteX => 0x5D, - Self::XorAbsoluteY => 0x59, - Self::XorIndirectX => 0x41, - Self::XorIndirectY => 0x51, Self::AndImm => 0x29, Self::AndZeroPage => 0x25, Self::AndZeroPageX => 0x35, @@ -595,15 +718,42 @@ impl Operation { Self::AndAbsoluteY => 0x39, Self::AndIndirectX => 0x21, Self::AndIndirectY => 0x31, + Self::XorImm => 0x49, + Self::XorZeroPage => 0x45, + Self::XorZeroPageX => 0x55, + Self::XorAbsolute => 0x4D, + Self::XorAbsoluteX => 0x5D, + Self::XorAbsoluteY => 0x59, + Self::XorIndirectX => 0x41, + Self::XorIndirectY => 0x51, + Self::OrImm => 0x09, + Self::OrZeroPage => 0x05, + Self::OrZeroPageX => 0x15, + Self::OrAbsolute => 0x0D, + Self::OrAbsoluteX => 0x1D, + Self::OrAbsoluteY => 0x19, + Self::OrIndirectX => 0x01, + Self::OrIndirectY => 0x11, } } pub fn get_operation(opcode: u8) -> Option { match opcode { + 0x18 => Some(Self::ClearCarryFlag), + 0xD8 => Some(Self::ClearDecimalFlag), + 0x58 => Some(Self::ClearInterruptDisableFlag), + 0xB8 => Some(Self::ClearOverflowFlag), + 0x38 => Some(Self::SetCarryFlag), + 0xF8 => Some(Self::SetDecimalFlag), + 0x78 => Some(Self::SetInterruptDisableFlag), 0x0A => Some(Self::AslA), 0x06 => Some(Self::AslZeroPage), 0x16 => Some(Self::AslZeroPageX), 0x0E => Some(Self::AslAbsolute), + 0x48 => Some(Self::PushAcc), + 0x08 => Some(Self::PushStatus), + 0x68 => Some(Self::PullAcc), + 0x28 => Some(Self::PullStatus), 0xE6 => Some(Self::IncMemZeroPage), 0xF6 => Some(Self::IncMemZeroPageX), 0xEE => Some(Self::IncMemAbsolute), @@ -653,14 +803,6 @@ impl Operation { 0x8A => Some(Self::TransferXToAcc), 0x9A => Some(Self::TransferXToStackptr), 0x98 => Some(Self::TransferYToAcc), - 0x49 => Some(Self::XorImm), - 0x45 => Some(Self::XorZeroPage), - 0x55 => Some(Self::XorZeroPageX), - 0x4D => Some(Self::XorAbsolute), - 0x5D => Some(Self::XorAbsoluteX), - 0x59 => Some(Self::XorAbsoluteY), - 0x41 => Some(Self::XorIndirectX), - 0x51 => Some(Self::XorIndirectY), 0x29 => Some(Self::AndImm), 0x25 => Some(Self::AndZeroPage), 0x35 => Some(Self::AndZeroPageX), @@ -669,6 +811,22 @@ impl Operation { 0x39 => Some(Self::AndAbsoluteY), 0x21 => Some(Self::AndIndirectX), 0x31 => Some(Self::AndIndirectY), + 0x49 => Some(Self::XorImm), + 0x45 => Some(Self::XorZeroPage), + 0x55 => Some(Self::XorZeroPageX), + 0x4D => Some(Self::XorAbsolute), + 0x5D => Some(Self::XorAbsoluteX), + 0x59 => Some(Self::XorAbsoluteY), + 0x41 => Some(Self::XorIndirectX), + 0x51 => Some(Self::XorIndirectY), + 0x09 => Some(Self::OrImm), + 0x05 => Some(Self::OrZeroPage), + 0x15 => Some(Self::OrZeroPageX), + 0x0D => Some(Self::OrAbsolute), + 0x1D => Some(Self::OrAbsoluteX), + 0x19 => Some(Self::OrAbsoluteY), + 0x01 => Some(Self::OrIndirectX), + 0x11 => Some(Self::OrIndirectY), _ => None, } } diff --git a/emulator/src/cpu/registers.rs b/emulator/src/cpu/registers.rs index a5c80b8..9ae8ed5 100644 --- a/emulator/src/cpu/registers.rs +++ b/emulator/src/cpu/registers.rs @@ -10,7 +10,7 @@ pub struct Registers { pub a: u8, program_counter: u16, pub stack_ptr: u8, - status: u8, + pub status: u8, operation: u8, adl: u8, adh: u8, @@ -29,7 +29,7 @@ impl Registers { y: 0x00, a: 0x00, program_counter: 0x0000, - stack_ptr: 0x00, + stack_ptr: 0xFF, status: 0x00, operation: 0x00, adl: 0x00, @@ -241,6 +241,32 @@ impl Registers { self.set_flag_value(CPUFlag::Negative, is_negative); } + pub fn push_accumulator(&mut self, bus: &mut T) { + let address: u16 = self.stack_ptr as u16 | 0x0100; + bus.write(address, self.a); + self.stack_ptr -= 1; + } + + pub fn push_status_register(&mut self, bus: &mut T) { + let address: u16 = self.stack_ptr as u16 | 0x0100; + let status: u8 = self.status | 0b0011_0000; // push with B and U flag set to 1 + bus.write(address, status); + self.stack_ptr -= 1; + } + + pub fn pull_accumulator(&mut self, bus: &mut T) { + self.stack_ptr += 1; + let address: u16 = self.stack_ptr as u16 | 0x0100; + self.a = bus.read(address); + } + + pub fn pull_status_register(&mut self, bus: &mut T) { + self.stack_ptr += 1; + let address: u16 = self.stack_ptr as u16 | 0x0100; + let status: u8 = bus.read(address); + self.status = status & 0b1100_1111; // ignoring B and U flag + } + pub fn increment_memory_buffer(&mut self) { self.memory_buffer = self.memory_buffer.wrapping_add(1u8); let is_zero = self.memory_buffer == 0; @@ -400,4 +426,13 @@ impl Registers { self.set_flag_value(CPUFlag::Zero, is_zero); self.set_flag_value(CPUFlag::Negative, is_negative); } + + pub fn or(&mut self) { + self.a = self.a | self.memory_buffer; + let is_zero = self.a == 0; + let is_negative = self.a & 0x80 != 0; + + self.set_flag_value(CPUFlag::Zero, is_zero); + self.set_flag_value(CPUFlag::Negative, is_negative); + } }