diff --git a/.vscode/launch.json b/.vscode/launch.json index b9713b2..e4a9bad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "type": "lldb", "request": "launch", @@ -40,6 +41,24 @@ }, "args": [], "cwd": "${workspaceFolder}" - } + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug 'simple_test_machine'", + "cargo": { + "args": [ + "build", + "--bin=simple_test_machine", + "--package=simple_test_machine" + ], + "filter": { + "name": "simple_test_machine", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4d9636b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.showUnlinkedFileNotification": false +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ce04797..c176735 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,10 @@ version = 3 [[package]] name = "re6502" version = "0.1.0" + +[[package]] +name = "simple_test_machine" +version = "0.1.0" +dependencies = [ + "re6502", +] diff --git a/Cargo.toml b/Cargo.toml index 819ddca..9cbfe6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,6 @@ name = "re6502" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = [ "simple_test_machine"] -[dependencies] diff --git a/simple_test_machine/Cargo.toml b/simple_test_machine/Cargo.toml new file mode 100644 index 0000000..0ecad9c --- /dev/null +++ b/simple_test_machine/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "simple_test_machine" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +re6502 = { path = "../" } \ No newline at end of file diff --git a/simple_test_machine/src/machine.rs b/simple_test_machine/src/machine.rs new file mode 100644 index 0000000..9cc8cc1 --- /dev/null +++ b/simple_test_machine/src/machine.rs @@ -0,0 +1,156 @@ + + +use std::str; + +///////////////////////////////////////////////////////////////////// +// BUS +///////////////////////////////////////////////////////////////////// +use re6502::r6502::{R6502, Bus, Flags}; +struct TBus +{ + memory: [u8; 64 * 1024] +} + +impl TBus +{ + pub fn new() -> TBus + { + TBus { memory: [0; 64 * 1024] } + } + + pub fn clear_memory(&mut self) + { + for i in 0..self.memory.len() + { + self.memory[i] = 0; + } + } + + pub fn load_into_memory(&mut self, data: &[u8], at: u16) + { + for i in 0..data.len() + { + self.write(at + i as u16, data[i]); + } + } +} + +impl Bus for TBus +{ + fn read(&self, addr: u16) -> u8 + { + self.memory[addr as usize] + } + + fn write(&mut self, addr: u16, value: u8) + { + self.memory[addr as usize] = value; + } +} + +//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +// CONSOLE +//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +pub const OUTPUT_ADDR: u16 = 0x00A0; +pub const PRINT_BYTE_FLAG: u16 = 0x009E; +pub const PRINT_STR_FLAG: u16 = 0x009F; + +// TODO: Handle input +struct OutputConsole +{ + +} + +impl OutputConsole +{ + // fn new() -> Console + // { + // Console { } + // } + + fn clock(_cpu: &mut R6502, bus: &mut TBus ) + { + // Check for a string to print + let mut value = bus.read(PRINT_STR_FLAG); + if value != 0 + { + let mut msg: Vec = Vec::new(); + + let mut idx = 0; + while value != 0 + { + value = bus.read(OUTPUT_ADDR + idx); + msg.push(value); + idx += 1; + } + + // Mark the string as empty again + bus.write(PRINT_STR_FLAG, 0); + + println!("{}", str::from_utf8(&msg).unwrap()); + } + + // Check for byte to print + let flag = bus.read(PRINT_BYTE_FLAG); + if flag != 0 + { + let byte = bus.read(OUTPUT_ADDR); + bus.write(PRINT_BYTE_FLAG, 0); + println!("{}", byte as u8); + } + + } +} + +///////////////////////////////////////////////////////////////////// +// MACHINE +///////////////////////////////////////////////////////////////////// + +const PROGRAM_START_ADDR: u16 = 0x0200; // Program code starts on page 2 +const CPU_RESET_START_ADDR: u16 = 0xFFFC; // This is where the cpu looks for the address to start executing code at + +pub struct TestMachine +{ + bus: TBus, + cpu: R6502, + +} + +impl TestMachine +{ + pub fn new() -> TestMachine + { + TestMachine { bus: TBus::new(), cpu: R6502::new() } + } + + pub fn reset(&mut self) + { + self.cpu.reset(&mut self.bus); + } + + pub fn load_program(&mut self, program: &[u8]) + { + self.bus.clear_memory(); + + // Load program starting at page 2 + self.bus.load_into_memory(program, PROGRAM_START_ADDR); + + // Set the cpu program start address + self.bus.write(CPU_RESET_START_ADDR, (PROGRAM_START_ADDR & 0x00FF) as u8); + self.bus.write(CPU_RESET_START_ADDR + 1, ((PROGRAM_START_ADDR & 0xFF00) >> 8) as u8); + + self.reset(); + } + + pub fn run_program(&mut self) + { + // Program should run until the cpu detects that it has stopped + // or when the program hits a BRK instruction + while !self.cpu.is_program_stopped() && self.cpu.check_flag(Flags::B) == 0 + { + self.cpu.clock(&mut self.bus); + OutputConsole::clock(&mut self.cpu, &mut self.bus); + } + } +} \ No newline at end of file diff --git a/simple_test_machine/src/main.rs b/simple_test_machine/src/main.rs new file mode 100644 index 0000000..b1ed7ee --- /dev/null +++ b/simple_test_machine/src/main.rs @@ -0,0 +1,123 @@ +use machine::{OUTPUT_ADDR, PRINT_STR_FLAG, PRINT_BYTE_FLAG, TestMachine}; + + +mod machine; + +fn main() +{ + hello_world_test(); + println!(); + fast_mult_by_10(); +} + +fn hello_world_test() +{ + let print_flag_addr = (PRINT_STR_FLAG & 0x00FF) as u8; + let output_addr = (OUTPUT_ADDR & 0x00FF) as u8; + let program = + [ + // Load string into memory at the output address + 0xA2, b'H', // LDX H + 0x86, output_addr, // STX + 0xA2, b'e', // LDX e + 0x86, output_addr + 1, // STX + 0xA2, b'l', // LDX l + 0x86, output_addr + 2, // STX + 0xA2, b'l', // LDX l + 0x86, output_addr + 3, // STX + 0xA2, b'o', // LDX o + 0x86, output_addr + 4, // STX + + 0xA2, b' ', // LDX ' ' + 0x86, output_addr + 5, // STX + + 0xA2, b'w', // LDX w + 0x86, output_addr + 6, // STX + 0xA2, b'o', // LDX o + 0x86, output_addr + 7, // STX + 0xA2, b'r', // LDX r + 0x86, output_addr + 8, // STX + 0xA2, b'l', // LDX l + 0x86, output_addr + 9, // STX + 0xA2, b'd', // LDX d + 0x86, output_addr + 10, // STX + 0xA2, b'!', // LDX ! + 0x86, output_addr + 11, // STX + + 0xA2, 0x00, // LDX 0 + 0x86, output_addr + 12, // STX + + // Set flag to do the print + 0xA2, 0x01, // LDX 1 + 0x86, print_flag_addr, // STX + + // End the program + 0x60 // RTS + ]; + + let mut vm = TestMachine::new(); + + vm.load_program(&program); + vm.reset(); + vm.run_program(); + + println!("Program stopped"); + + +} + +fn fast_mult_by_10() +{ + // Program from: + // http://6502.org/source/integers/fastx10.htm + + // MULT10 + // ASL ;multiply by 2 + // STA TEMP ;temp store in TEMP + // ASL ;again multiply by 2 (*4) + // ASL ;again multiply by 2 (*8) + // CLC + // ADC TEMP ;as result, A = x*8 + x*2 + // RTS + // + // TEMP .byte 0 + + let print_flag_addr = (PRINT_BYTE_FLAG & 0x00FF) as u8; + let output_addr = (OUTPUT_ADDR & 0x00FF) as u8; + let temp_addr: u8 = 0xB0; + + let program = + [ + 0xA9, 0x07, // LDA 7 - The value we want to multiply + + // START OF MULT10 FUNCTION + 0x0A, // ASL ;multiply by 2 + 0x85, temp_addr, // STA TEMP ;temp store in TEMP + 0x0A, // ASL ;again multiply by 2 (*4) + 0x0A, // ASL ;again multiply by 2 (*8) + 0x18, // CLC + 0x65, temp_addr, // ADC TEMP ;as result, A = x*8 + x*2 + + // PRINT RESULT + 0x85, output_addr, // STA output addr + 0xA2, 0x00, // LDX 0 - null terminator + 0x86, output_addr + 1, // STX + + // Set flag to do the print + 0xA2, 0x01, // LDX 1 + 0x86, print_flag_addr, // STX + + // End the program + 0x60 // RTS + + ]; + + let mut vm = TestMachine::new(); + + vm.load_program(&program); + vm.reset(); + vm.run_program(); + + println!("Program stopped"); + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..86b9c12 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ + + +pub mod r6502; \ No newline at end of file diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index 56db8ae..cc5b7e6 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -59,6 +59,8 @@ pub struct R6502 addr_mode: ModeID, working_data: u16, // value fetched for the ALU working_addr: u16, + + program_stopped: bool, } impl R6502 @@ -66,7 +68,8 @@ impl R6502 // constructor pub fn new() -> R6502 { - R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, addr_mode: ModeID::IMP, working_data: 0, working_addr: 0 } + R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, addr_mode: ModeID::IMP, + working_data: 0, working_addr: 0, program_stopped: true } } // Debug Access @@ -100,6 +103,11 @@ impl R6502 } } + pub fn is_program_stopped(&self) -> bool + { + self.program_stopped + } + // signals pub fn clock(&mut self, bus: &mut dyn Bus) { @@ -115,7 +123,7 @@ impl R6502 //self.pc += 1; //} - self.cycles -= 1; + // self.cycles -= 1; } pub fn reset(&mut self, bus: &mut dyn Bus) @@ -135,6 +143,7 @@ impl R6502 // internal helper variables self.working_data = 0; // self.working_addr = 0; + self.program_stopped = false; self.cycles = 8; } @@ -254,7 +263,20 @@ fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) BRK => {Instructions::BRK(cpu, bus); return; } JSR => {Instructions::JSR(cpu, bus); return; } RTI => {Instructions::RTI(cpu, bus); return; } - RTS => {Instructions::RTS(cpu, bus); return; } + RTS => + { + // Use the stack pointer to detect if this is the end of the program + if cpu.sp == 0x01FF + { + cpu.program_stopped = true; + } + else + { + Instructions::RTS(cpu, bus); + } + + return; + } _ => () } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 7e6c2f4..2059af3 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -7,4 +7,3 @@ mod test_bus; #[cfg(test)] mod instructions; - diff --git a/todo/todo.todo b/todo/todo.todo index ecd4b0f..4434ccd 100644 --- a/todo/todo.todo +++ b/todo/todo.todo @@ -5,12 +5,15 @@ General: ☐ Add a disassembler for debugging ☐ Debug data lookup for instructions +Test Machine: + ✔ Hello world program @done(24-01-19 17:09) + CPU: Signals: ✔ Clock @done(23-11-07 20:45) ✔ Reset @done(23-11-07 20:46) - ☐ irq function - ☐ nmi function + ✔ irq function @done(24-01-19 13:17) + ✔ nmi function @done(24-01-19 13:17) Addressing modes: ✔ IMP - Implied @done(23-11-07 14:01) @@ -18,9 +21,9 @@ Addressing modes: ✔ ZP0 - Zero Page @done(23-11-07 14:40) ✔ ZPX - Zero Page, X @done(23-11-07 14:40) ✔ ZPY - Zero Page, Y @done(23-11-07 14:40) - ☐ REL - Relative + ✔ REL - Relative @done(24-01-19 13:17) ✔ Code @done(23-11-07 20:44) - ☐ Test + ✔ Test @done(24-01-19 13:17) ✔ ABS - Absolute @done(23-11-07 20:25) ✔ ABX - Absolute, X @done(23-11-07 20:25) ✔ ABY - Absolute, Y @done(23-11-07 20:25) @@ -69,36 +72,36 @@ Instructions: ✔ F0 BEQ @done(24-01-12 16:49) INTERRUPT/SUBROUTINE: - ☐ 00 BRK - ☐ 20 JSR abs - ☐ 40 RTI - ☐ 60 RTS + ✔ 00 BRK @done(24-01-19 13:16) + ✔ 20 JSR abs @done(24-01-19 13:16) + ✔ 40 RTI @done(24-01-19 13:16) + ✔ 60 RTS @done(24-01-19 13:16) SINGLE-BYTE: - ☐ 08 PHP - ☐ 28 PLP - ☐ 48 PHA - ☐ 68 PLA - ☐ 88 DEY - ☐ A8 TAY - ☐ C8 INY - ☐ E8 INX + ✔ 08 PHP @done(24-01-19 13:16) + ✔ 28 PLP @done(24-01-19 13:16) + ✔ 48 PHA @done(24-01-19 13:16) + ✔ 68 PLA @done(24-01-19 13:16) + ✔ 88 DEY @done(24-01-19 13:16) + ✔ A8 TAY @done(24-01-19 13:16) + ✔ C8 INY @done(24-01-19 13:16) + ✔ E8 INX @done(24-01-19 13:16) - ☐ 18 CLC - ☐ 38 SEC - ☐ 58 CLI - ☐ 78 SEI - ☐ 98 TYA - ☐ B8 CLV - ☐ D8 CLD - ☐ F8 SED + ✔ 18 CLC @done(24-01-19 13:16) + ✔ 38 SEC @done(24-01-19 13:16) + ✔ 58 CLI @done(24-01-19 13:16) + ✔ 78 SEI @done(24-01-19 13:16) + ✔ 98 TYA @done(24-01-19 13:16) + ✔ B8 CLV @done(24-01-19 13:16) + ✔ D8 CLD @done(24-01-19 13:16) + ✔ F8 SED @done(24-01-19 13:16) - ☐ 8A TXA - ☐ 9A TXS - ☐ AA TAX - ☐ BA TSX - ☐ CA DEX - ☐ EA NOP + ✔ 8A TXA @done(24-01-19 13:16) + ✔ 9A TXS @done(24-01-19 13:16) + ✔ AA TAX @done(24-01-19 13:16) + ✔ BA TSX @done(24-01-19 13:16) + ✔ CA DEX @done(24-01-19 13:16) + ✔ EA NOP @done(24-01-19 13:16)