Adds the simple test virtual machine and a few hard-coded test programs

master
Joey Pollack 2 years ago
parent 48bfaf500a
commit 6efdfecf54

@ -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}"
},
]
}

@ -0,0 +1,3 @@
{
"rust-analyzer.showUnlinkedFileNotification": false
}

7
Cargo.lock generated

@ -5,3 +5,10 @@ version = 3
[[package]]
name = "re6502"
version = "0.1.0"
[[package]]
name = "simple_test_machine"
version = "0.1.0"
dependencies = [
"re6502",
]

@ -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]

@ -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 = "../" }

@ -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<u8> = 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);
}
}
}

@ -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");
}

@ -0,0 +1,3 @@
pub mod r6502;

@ -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;
}
_ => ()
}

@ -7,4 +7,3 @@ mod test_bus;
#[cfg(test)]
mod instructions;

@ -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)

Loading…
Cancel
Save