Adds the simple test virtual machine and a few hard-coded test programs
parent
48bfaf500a
commit
6efdfecf54
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.showUnlinkedFileNotification": false
|
||||||
|
}
|
||||||
@ -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;
|
||||||
Loading…
Reference in New Issue