terminal mode! i like this better, also functional cli now
This commit is contained in:
		
							parent
							
								
									229b8b450d
								
							
						
					
					
						commit
						001d3e434c
					
				
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								Cargo.toml
								
								
								
								
							
							
						
						
									
										14
									
								
								Cargo.toml
								
								
								
								
							| 
						 | 
				
			
			@ -8,18 +8,8 @@ edition = "2021"
 | 
			
		|||
[dependencies]
 | 
			
		||||
anyhow = "1.0.81"
 | 
			
		||||
bdf = "0.6.0"
 | 
			
		||||
clap = { version = "4.5.4", features = ["derive"] }
 | 
			
		||||
criterion = "0.4"
 | 
			
		||||
crossterm = "0.27.0"
 | 
			
		||||
minifb = "0.25.0"
 | 
			
		||||
ratatui = "0.26.3"
 | 
			
		||||
# minifb = "0.25.0"
 | 
			
		||||
# ratatui = "0.26.3"
 | 
			
		||||
serde = { version = "1.0.197", features = ["serde_derive", "derive"] }
 | 
			
		||||
toml = "0.8.12"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
criterion = {version = "0.4", features = ["html_reports"]}
 | 
			
		||||
 | 
			
		||||
[[bench]]
 | 
			
		||||
name = "benchmark"
 | 
			
		||||
path = "src/benches/benchmark.rs"
 | 
			
		||||
harness = false
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								run.sh
								
								
								
								
							
							
						
						
									
										11
									
								
								run.sh
								
								
								
								
							| 
						 | 
				
			
			@ -1,5 +1,12 @@
 | 
			
		|||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
vasm6502_oldstyle ./src/roms/george.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/george.rom;
 | 
			
		||||
cargo run;
 | 
			
		||||
if [ $# -eq 0 ]; then
 | 
			
		||||
 echo "Provide the name of the rom/asm file to run"
 | 
			
		||||
 exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
vasm6502_oldstyle ./src/roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/$1.rom;
 | 
			
		||||
cargo run rom "./src/roms/$1.rom";
 | 
			
		||||
# hexdump -C ./cpu_dump.bin;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/cpu.rs
								
								
								
								
							
							
						
						
									
										22
									
								
								src/cpu.rs
								
								
								
								
							| 
						 | 
				
			
			@ -2,8 +2,8 @@ use crate::instructions::{get_instruction, Instruction};
 | 
			
		|||
use crate::memory::{MemHandle, MemoryReader, MemoryWriter};
 | 
			
		||||
use crate::types::{Byte, Word};
 | 
			
		||||
use std::fmt::Display;
 | 
			
		||||
use std::sync::mpsc::{Receiver, Sender};
 | 
			
		||||
use std::thread::sleep;
 | 
			
		||||
use std::sync::mpsc::{channel, Receiver, Sender};
 | 
			
		||||
use std::thread::{sleep, LocalKey};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +124,13 @@ impl Cpu {
 | 
			
		|||
            // state_tx,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_with_control(memory: MemHandle) -> (Self, CpuController) {
 | 
			
		||||
        let (tx, rx) = channel::<CpuControl>();
 | 
			
		||||
        let controller = CpuController(tx);
 | 
			
		||||
        let receiver = CpuReceiver(rx);
 | 
			
		||||
        let cpu = Cpu::new(memory, receiver);
 | 
			
		||||
        (cpu, controller)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn reset(&mut self) -> Result<()> {
 | 
			
		||||
        let reset_vector_pointer = self.read_word(0xFFFC)?;
 | 
			
		||||
        self.pc = reset_vector_pointer;
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +210,11 @@ impl Cpu {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn receive_control(&mut self) {
 | 
			
		||||
        let control = self.receiver.0.recv().unwrap();
 | 
			
		||||
        let control = match self.receiver.0.try_recv() {
 | 
			
		||||
            Ok(control) => control,
 | 
			
		||||
            Err(_error) => return, // most of the time we won't have any impending control
 | 
			
		||||
                                   // messages, just return if that's the case
 | 
			
		||||
        };
 | 
			
		||||
        match control {
 | 
			
		||||
            CpuControl::Nmi => self.nmi = true,
 | 
			
		||||
            CpuControl::Irq => self.irq = true,
 | 
			
		||||
| 
						 | 
				
			
			@ -226,13 +237,13 @@ impl Cpu {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn cycle(&mut self) {
 | 
			
		||||
        // self.receive_control();
 | 
			
		||||
        self.receive_control();
 | 
			
		||||
        if self.stopped {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        while self.pending_cycles != 0 {
 | 
			
		||||
            // roughly cycle-accurate timing
 | 
			
		||||
            sleep(Duration::from_nanos(100));
 | 
			
		||||
            sleep(Duration::from_nanos(500));
 | 
			
		||||
            self.pending_cycles -= 1;
 | 
			
		||||
        }
 | 
			
		||||
        if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
 | 
			
		||||
| 
						 | 
				
			
			@ -272,7 +283,6 @@ impl Cpu {
 | 
			
		|||
            },
 | 
			
		||||
        }
 | 
			
		||||
        // self.cycle_count += 1;
 | 
			
		||||
        sleep(Duration::from_nanos(100));
 | 
			
		||||
    }
 | 
			
		||||
    pub fn stop(&mut self) {
 | 
			
		||||
        self.stopped = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										182
									
								
								src/keyboard.rs
								
								
								
								
							
							
						
						
									
										182
									
								
								src/keyboard.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
use minifb::{InputCallback, Key};
 | 
			
		||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
 | 
			
		||||
// use minifb::{InputCallback, Key};
 | 
			
		||||
 | 
			
		||||
use crate::memory::{MemHandle, MemoryWriter};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,69 +11,64 @@ impl Keyboard {
 | 
			
		|||
    pub fn new(memory: MemHandle) -> Self {
 | 
			
		||||
        Self { memory }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MemoryWriter for Keyboard {
 | 
			
		||||
    fn write(&self, address: u16, data: u8) {
 | 
			
		||||
        self.memory.write(address, data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InputCallback for Keyboard {
 | 
			
		||||
    fn add_char(&mut self, _uni_char: u32) {}
 | 
			
		||||
    fn set_key_state(&mut self, key: Key, _state: bool) {
 | 
			
		||||
    pub fn read_keys(&self, key: KeyEvent) {
 | 
			
		||||
        let mut row0 = 0;
 | 
			
		||||
        let mut row1 = 0;
 | 
			
		||||
        let mut row2 = 0;
 | 
			
		||||
        let mut row3 = 0;
 | 
			
		||||
        let mut row4 = 0;
 | 
			
		||||
        let mut row5 = 0;
 | 
			
		||||
        if key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat {
 | 
			
		||||
            match key.modifiers {
 | 
			
		||||
                KeyModifiers::SHIFT => row2 ^= 0b1000_0000,
 | 
			
		||||
                KeyModifiers::CONTROL => row3 ^= 0b1000_0000,
 | 
			
		||||
                KeyModifiers::ALT => row4 ^= 0b1000_0000,
 | 
			
		||||
                KeyModifiers::META => row5 ^= 0b1000_0000,
 | 
			
		||||
                KeyModifiers::SUPER => row5 ^= 0b0010_0000,
 | 
			
		||||
                _ => {}
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        match key {
 | 
			
		||||
            Key::Escape => row0 ^= 0b1000_0000,
 | 
			
		||||
            Key::W => row0 ^= 0b0100_0000,
 | 
			
		||||
            Key::E => row0 ^= 0b0010_0000,
 | 
			
		||||
            Key::R => row0 ^= 0b0001_0000,
 | 
			
		||||
            Key::T => row0 ^= 0b0000_1000,
 | 
			
		||||
            Key::U => row0 ^= 0b0000_0100,
 | 
			
		||||
            Key::O => row0 ^= 0b0000_0010,
 | 
			
		||||
            Key::Backspace => row0 ^= 0b0000_0001,
 | 
			
		||||
            Key::Tab => row1 ^= 0b1000_0000,
 | 
			
		||||
            Key::Q => row1 ^= 0b0100_0000,
 | 
			
		||||
            Key::S => row1 ^= 0b0010_0000,
 | 
			
		||||
            Key::G => row1 ^= 0b0001_0000,
 | 
			
		||||
            Key::Y => row1 ^= 0b0000_1000,
 | 
			
		||||
            Key::I => row1 ^= 0b0000_0100,
 | 
			
		||||
            Key::P => row1 ^= 0b0000_0010,
 | 
			
		||||
            Key::Enter => row1 ^= 0b0000_0001,
 | 
			
		||||
            Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000,
 | 
			
		||||
            Key::D => row2 ^= 0b0100_0000,
 | 
			
		||||
            Key::V => row2 ^= 0b0010_0000,
 | 
			
		||||
            Key::H => row2 ^= 0b0001_0000,
 | 
			
		||||
            Key::K => row2 ^= 0b0000_1000,
 | 
			
		||||
            Key::Apostrophe => row2 ^= 0b0000_0100,
 | 
			
		||||
            Key::Slash => row2 ^= 0b0000_0010,
 | 
			
		||||
            Key::A => row2 ^= 0b0000_0001,
 | 
			
		||||
            Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000,
 | 
			
		||||
            Key::Z => row3 ^= 0b0100_0000,
 | 
			
		||||
            Key::F => row3 ^= 0b0010_0000,
 | 
			
		||||
            Key::B => row3 ^= 0b0001_0000,
 | 
			
		||||
            Key::J => row3 ^= 0b0000_1000,
 | 
			
		||||
            Key::L => row3 ^= 0b0000_0100,
 | 
			
		||||
            Key::Key2 => row3 ^= 0b0000_0010,
 | 
			
		||||
            Key::Key4 => row3 ^= 0b0000_0001,
 | 
			
		||||
            Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000,
 | 
			
		||||
            Key::X => row4 ^= 0b0100_0000,
 | 
			
		||||
            Key::C => row4 ^= 0b0010_0000,
 | 
			
		||||
            Key::N => row4 ^= 0b0001_0000,
 | 
			
		||||
            Key::M => row4 ^= 0b0000_1000,
 | 
			
		||||
            Key::Comma => row4 ^= 0b0000_0100,
 | 
			
		||||
            Key::Key1 => row4 ^= 0b0000_0010,
 | 
			
		||||
            Key::Key3 => row4 ^= 0b0000_0001,
 | 
			
		||||
            Key::LeftSuper => row5 ^= 0b1000_0000,
 | 
			
		||||
            Key::Space => row5 ^= 0b0100_0000,
 | 
			
		||||
            Key::RightSuper => row5 ^= 0b0010_0000,
 | 
			
		||||
            _ => {}
 | 
			
		||||
            match key.code {
 | 
			
		||||
                KeyCode::Esc => row0 ^= 0b1000_0000,
 | 
			
		||||
                KeyCode::Char('w') => row0 ^= 0b0100_0000,
 | 
			
		||||
                KeyCode::Char('e') => row0 ^= 0b0010_0000,
 | 
			
		||||
                KeyCode::Char('r') => row0 ^= 0b0001_0000,
 | 
			
		||||
                KeyCode::Char('t') => row0 ^= 0b0000_1000,
 | 
			
		||||
                KeyCode::Char('u') => row0 ^= 0b0000_0100,
 | 
			
		||||
                KeyCode::Char('o') => row0 ^= 0b0000_0010,
 | 
			
		||||
                KeyCode::Backspace => row0 ^= 0b0000_0001,
 | 
			
		||||
                KeyCode::Tab => row1 ^= 0b1000_0000,
 | 
			
		||||
                KeyCode::Char('q') => row1 ^= 0b0100_0000,
 | 
			
		||||
                KeyCode::Char('s') => row1 ^= 0b0010_0000,
 | 
			
		||||
                KeyCode::Char('g') => row1 ^= 0b0001_0000,
 | 
			
		||||
                KeyCode::Char('y') => row1 ^= 0b0000_1000,
 | 
			
		||||
                KeyCode::Char('i') => row1 ^= 0b0000_0100,
 | 
			
		||||
                KeyCode::Char('p') => row1 ^= 0b0000_0010,
 | 
			
		||||
                KeyCode::Enter => row1 ^= 0b0000_0001,
 | 
			
		||||
                KeyCode::Char('d') => row2 ^= 0b0100_0000,
 | 
			
		||||
                KeyCode::Char('v') => row2 ^= 0b0010_0000,
 | 
			
		||||
                KeyCode::Char('h') => row2 ^= 0b0001_0000,
 | 
			
		||||
                KeyCode::Char('k') => row2 ^= 0b0000_1000,
 | 
			
		||||
                KeyCode::Char('\'') => row2 ^= 0b0000_0100,
 | 
			
		||||
                KeyCode::Char('/') => row2 ^= 0b0000_0010,
 | 
			
		||||
                KeyCode::Char('a') => row2 ^= 0b0000_0001,
 | 
			
		||||
                KeyCode::Char('z') => row3 ^= 0b0100_0000,
 | 
			
		||||
                KeyCode::Char('f') => row3 ^= 0b0010_0000,
 | 
			
		||||
                KeyCode::Char('b') => row3 ^= 0b0001_0000,
 | 
			
		||||
                KeyCode::Char('j') => row3 ^= 0b0000_1000,
 | 
			
		||||
                KeyCode::Char('l') => row3 ^= 0b0000_0100,
 | 
			
		||||
                KeyCode::Char('2') => row3 ^= 0b0000_0010,
 | 
			
		||||
                KeyCode::Char('4') => row3 ^= 0b0000_0001,
 | 
			
		||||
                KeyCode::Char('x') => row4 ^= 0b0100_0000,
 | 
			
		||||
                KeyCode::Char('c') => row4 ^= 0b0010_0000,
 | 
			
		||||
                KeyCode::Char('n') => row4 ^= 0b0001_0000,
 | 
			
		||||
                KeyCode::Char('m') => row4 ^= 0b0000_1000,
 | 
			
		||||
                KeyCode::Char(',') => row4 ^= 0b0000_0100,
 | 
			
		||||
                KeyCode::Char('1') => row4 ^= 0b0000_0010,
 | 
			
		||||
                KeyCode::Char('3') => row4 ^= 0b0000_0001,
 | 
			
		||||
                KeyCode::Char(' ') => row5 ^= 0b0100_0000,
 | 
			
		||||
                _ => {}
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.memory.write(0x4400, row0);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,3 +79,75 @@ impl InputCallback for Keyboard {
 | 
			
		|||
        self.memory.write(0x4405, row5);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MemoryWriter for Keyboard {
 | 
			
		||||
    fn write(&self, address: u16, data: u8) {
 | 
			
		||||
        self.memory.write(address, data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// impl InputCallback for Keyboard {
 | 
			
		||||
//     fn add_char(&mut self, _uni_char: u32) {}
 | 
			
		||||
//     fn set_key_state(&mut self, key: Key, _state: bool) {
 | 
			
		||||
//         let mut row0 = 0;
 | 
			
		||||
//         let mut row1 = 0;
 | 
			
		||||
//         let mut row2 = 0;
 | 
			
		||||
//         let mut row3 = 0;
 | 
			
		||||
//         let mut row4 = 0;
 | 
			
		||||
//         let mut row5 = 0;
 | 
			
		||||
 | 
			
		||||
//         match key {
 | 
			
		||||
//             Key::Escape => row0 ^= 0b1000_0000,
 | 
			
		||||
//             Key::W => row0 ^= 0b0100_0000,
 | 
			
		||||
//             Key::E => row0 ^= 0b0010_0000,
 | 
			
		||||
//             Key::R => row0 ^= 0b0001_0000,
 | 
			
		||||
//             Key::T => row0 ^= 0b0000_1000,
 | 
			
		||||
//             Key::U => row0 ^= 0b0000_0100,
 | 
			
		||||
//             Key::O => row0 ^= 0b0000_0010,
 | 
			
		||||
//             Key::Backspace => row0 ^= 0b0000_0001,
 | 
			
		||||
//             Key::Tab => row1 ^= 0b1000_0000,
 | 
			
		||||
//             Key::Q => row1 ^= 0b0100_0000,
 | 
			
		||||
//             Key::S => row1 ^= 0b0010_0000,
 | 
			
		||||
//             Key::G => row1 ^= 0b0001_0000,
 | 
			
		||||
//             Key::Y => row1 ^= 0b0000_1000,
 | 
			
		||||
//             Key::I => row1 ^= 0b0000_0100,
 | 
			
		||||
//             Key::P => row1 ^= 0b0000_0010,
 | 
			
		||||
//             Key::Enter => row1 ^= 0b0000_0001,
 | 
			
		||||
//             Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000,
 | 
			
		||||
//             Key::D => row2 ^= 0b0100_0000,
 | 
			
		||||
//             Key::V => row2 ^= 0b0010_0000,
 | 
			
		||||
//             Key::H => row2 ^= 0b0001_0000,
 | 
			
		||||
//             Key::K => row2 ^= 0b0000_1000,
 | 
			
		||||
//             Key::Apostrophe => row2 ^= 0b0000_0100,
 | 
			
		||||
//             Key::Slash => row2 ^= 0b0000_0010,
 | 
			
		||||
//             Key::A => row2 ^= 0b0000_0001,
 | 
			
		||||
//             Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000,
 | 
			
		||||
//             Key::Z => row3 ^= 0b0100_0000,
 | 
			
		||||
//             Key::F => row3 ^= 0b0010_0000,
 | 
			
		||||
//             Key::B => row3 ^= 0b0001_0000,
 | 
			
		||||
//             Key::J => row3 ^= 0b0000_1000,
 | 
			
		||||
//             Key::L => row3 ^= 0b0000_0100,
 | 
			
		||||
//             Key::Key2 => row3 ^= 0b0000_0010,
 | 
			
		||||
//             Key::Key4 => row3 ^= 0b0000_0001,
 | 
			
		||||
//             Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000,
 | 
			
		||||
//             Key::X => row4 ^= 0b0100_0000,
 | 
			
		||||
//             Key::C => row4 ^= 0b0010_0000,
 | 
			
		||||
//             Key::N => row4 ^= 0b0001_0000,
 | 
			
		||||
//             Key::M => row4 ^= 0b0000_1000,
 | 
			
		||||
//             Key::Comma => row4 ^= 0b0000_0100,
 | 
			
		||||
//             Key::Key1 => row4 ^= 0b0000_0010,
 | 
			
		||||
//             Key::Key3 => row4 ^= 0b0000_0001,
 | 
			
		||||
//             Key::LeftSuper => row5 ^= 0b1000_0000,
 | 
			
		||||
//             Key::Space => row5 ^= 0b0100_0000,
 | 
			
		||||
//             Key::RightSuper => row5 ^= 0b0010_0000,
 | 
			
		||||
//             _ => {}
 | 
			
		||||
//         };
 | 
			
		||||
 | 
			
		||||
//         self.memory.write(0x4400, row0);
 | 
			
		||||
//         self.memory.write(0x4401, row1);
 | 
			
		||||
//         self.memory.write(0x4402, row2);
 | 
			
		||||
//         self.memory.write(0x4403, row3);
 | 
			
		||||
//         self.memory.write(0x4404, row4);
 | 
			
		||||
//         self.memory.write(0x4405, row5);
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										179
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										179
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
				
			
			@ -5,7 +5,6 @@ mod error;
 | 
			
		|||
mod instructions;
 | 
			
		||||
mod keyboard;
 | 
			
		||||
mod memory;
 | 
			
		||||
mod tui;
 | 
			
		||||
mod types;
 | 
			
		||||
mod video;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,41 +13,31 @@ use crate::keyboard::Keyboard;
 | 
			
		|||
use crate::memory::Mem;
 | 
			
		||||
use crate::video::{Screen, TerminalRenderer};
 | 
			
		||||
 | 
			
		||||
use cpu::{CpuController, CpuReceiver};
 | 
			
		||||
use crossterm::cursor::Hide;
 | 
			
		||||
use crossterm::execute;
 | 
			
		||||
use crossterm::terminal::{size, Clear, ClearType, SetSize};
 | 
			
		||||
// use cpu::CpuController;
 | 
			
		||||
use memory::MemHandle;
 | 
			
		||||
// use clap::Parser;
 | 
			
		||||
// use minifb::{Scale, ScaleMode, Window, WindowOptions};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use std::env;
 | 
			
		||||
use std::io::ErrorKind;
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
use std::thread::{self, sleep};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::{
 | 
			
		||||
    fs::File,
 | 
			
		||||
    io::{stdout, Read, Result},
 | 
			
		||||
    path::PathBuf,
 | 
			
		||||
    str::FromStr,
 | 
			
		||||
    sync::mpsc,
 | 
			
		||||
    thread,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crossterm::{
 | 
			
		||||
    cursor,
 | 
			
		||||
    event::{self, KeyCode, KeyEventKind},
 | 
			
		||||
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 | 
			
		||||
    // ExecutableCommand,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// use toml::Table;
 | 
			
		||||
 | 
			
		||||
//#[derive(Parser)]
 | 
			
		||||
//struct Cli {
 | 
			
		||||
//    // Load a rom onto the system rom
 | 
			
		||||
//    #[arg(short, required = true)]
 | 
			
		||||
//    rom: Option<std::path::PathBuf>,
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
struct Config {
 | 
			
		||||
    char_rom: Option<String>,
 | 
			
		||||
| 
						 | 
				
			
			@ -56,29 +45,114 @@ struct Config {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<()> {
 | 
			
		||||
    let args: Vec<String> = env::args().collect();
 | 
			
		||||
 | 
			
		||||
    let mut memory = Mem::new();
 | 
			
		||||
    match args.len() {
 | 
			
		||||
        0 => {
 | 
			
		||||
            println!("george-emu must be run in the terminal, don't know what went wrong here!");
 | 
			
		||||
            exit(1)
 | 
			
		||||
        }
 | 
			
		||||
        1 => {
 | 
			
		||||
            let config: Config = match File::open("./george.toml") {
 | 
			
		||||
                Ok(mut file) => {
 | 
			
		||||
                    let mut string = String::new();
 | 
			
		||||
                    file.read_to_string(&mut string).unwrap();
 | 
			
		||||
                    toml::from_str(string.as_str()).unwrap()
 | 
			
		||||
                }
 | 
			
		||||
                Err(_) => {
 | 
			
		||||
                    println!("couldn't find a `george.toml` in the current directory!");
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            let rom = match std::fs::File::open(config.rom) {
 | 
			
		||||
                Ok(file) => file,
 | 
			
		||||
                Err(error) => panic!("Couldn't open main rom! {:?}", error),
 | 
			
		||||
            };
 | 
			
		||||
            if let Err(error) = memory.load_rom(rom) {
 | 
			
		||||
                println!("{:?}", error);
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        2 => match &args[1] as &str {
 | 
			
		||||
            "help" => {
 | 
			
		||||
                println!("ʕ·ᴥ·ʔ- george-emu is an emulator for george:");
 | 
			
		||||
                println!("https://git.augustkline.com/august/george\n");
 | 
			
		||||
                println!("commands:");
 | 
			
		||||
                println!("  help: print this help screen");
 | 
			
		||||
                println!("  help <command>: print help info for any command");
 | 
			
		||||
                println!("  rom <path>: load a rom/binary from path\n");
 | 
			
		||||
                println!("configuration:");
 | 
			
		||||
                println!("  george-emu searches for a `george.toml` in the current directory. in `george.toml` you can specify a path for the character rom using the key `char_rom` and the main rom/binary with the key `rom`");
 | 
			
		||||
                exit(0);
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                println!(
 | 
			
		||||
                    "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands",
 | 
			
		||||
                    &args[1], &args[0]
 | 
			
		||||
                );
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        3 => match &args[1] as &str {
 | 
			
		||||
            "help" => match &args[2] as &str {
 | 
			
		||||
                "rom" => {
 | 
			
		||||
                    println!("{:?} rom <path>\nload a rom/binary from path", &args[0]);
 | 
			
		||||
                }
 | 
			
		||||
                _ => {
 | 
			
		||||
                    println!(
 | 
			
		||||
                        "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands",
 | 
			
		||||
                        &args[2], &args[0]
 | 
			
		||||
                    );
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "rom" => {
 | 
			
		||||
                let rom = match std::fs::File::open(&args[2]) {
 | 
			
		||||
                    Ok(file) => file,
 | 
			
		||||
                    Err(error) => {
 | 
			
		||||
                        match error.kind() {
 | 
			
		||||
                            ErrorKind::NotFound => {
 | 
			
		||||
                                println!("couldn't find the rom at {:?}", &args[2]);
 | 
			
		||||
                            }
 | 
			
		||||
                            ErrorKind::PermissionDenied => {
 | 
			
		||||
                                println!(
 | 
			
		||||
                                    "didn't have sufficient permissions to open the rom at {:?}",
 | 
			
		||||
                                    &args[2]
 | 
			
		||||
                                );
 | 
			
		||||
                            }
 | 
			
		||||
                            _ => {
 | 
			
		||||
                                println!("something went wrong! try again in a moment? really not sure why you're getting this error");
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        exit(1);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                if let Err(error) = memory.load_rom(rom) {
 | 
			
		||||
                    println!("oh no! this rom couldn't be loaded: {:?}", error);
 | 
			
		||||
                    exit(1);
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                println!(
 | 
			
		||||
                    "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands",
 | 
			
		||||
                    &args[1], &args[0]
 | 
			
		||||
                );
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        _ => {
 | 
			
		||||
            println!(
 | 
			
		||||
                "too many arguments were provided!\n\nuse `{} help` to see all valid commands",
 | 
			
		||||
                &args[0]
 | 
			
		||||
            );
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let mut stdout = stdout();
 | 
			
		||||
    let (cols, rows) = size()?;
 | 
			
		||||
    execute!(stdout, SetSize(64, 29), cursor::Hide, EnterAlternateScreen)?;
 | 
			
		||||
    enable_raw_mode()?;
 | 
			
		||||
 | 
			
		||||
    let config: Config = match File::open("./config.toml") {
 | 
			
		||||
        Ok(mut file) => {
 | 
			
		||||
            let mut string = String::new();
 | 
			
		||||
            file.read_to_string(&mut string).unwrap();
 | 
			
		||||
            toml::from_str(string.as_str()).unwrap()
 | 
			
		||||
        }
 | 
			
		||||
        Err(_) => return Ok(()),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut memory = Mem::new();
 | 
			
		||||
    let rom = match std::fs::File::open(config.rom) {
 | 
			
		||||
        Ok(file) => file,
 | 
			
		||||
        Err(error) => panic!("Couldn't open main rom! {:?}", error),
 | 
			
		||||
    };
 | 
			
		||||
    if let Err(error) = memory.load_rom(rom) {
 | 
			
		||||
        println!("{:?}", error);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    memory
 | 
			
		||||
        .dump(PathBuf::from_str("./coredump.bin").unwrap())
 | 
			
		||||
        .unwrap();
 | 
			
		||||
| 
						 | 
				
			
			@ -88,11 +162,8 @@ fn main() -> Result<()> {
 | 
			
		|||
    let cpu_memory = shared_memory.clone();
 | 
			
		||||
    let keyboard_memory = shared_memory.clone();
 | 
			
		||||
 | 
			
		||||
    let (cpu_tx, cpu_rx) = mpsc::channel();
 | 
			
		||||
    // let (state_tx, state_rx) = mpsc::channel();
 | 
			
		||||
    let screen_cpu_tx = cpu_tx.clone();
 | 
			
		||||
    // let (window_tx, window_rx) = mpsc::channel();
 | 
			
		||||
 | 
			
		||||
    // For when we want to leave the terminal again :sigh:
 | 
			
		||||
    //
 | 
			
		||||
    // thread::spawn(move || {
 | 
			
		||||
    //     let mut screen = Crtc::new(
 | 
			
		||||
    //         screen_memory,
 | 
			
		||||
| 
						 | 
				
			
			@ -120,40 +191,30 @@ fn main() -> Result<()> {
 | 
			
		|||
    // )
 | 
			
		||||
    // .unwrap();
 | 
			
		||||
 | 
			
		||||
    // window.set_input_callback(Box::new(Keyboard::new(keyboard_memory)));
 | 
			
		||||
    let keyboard = Keyboard::new(keyboard_memory);
 | 
			
		||||
 | 
			
		||||
    let mut cpu = Cpu::new(cpu_memory, CpuReceiver::new(cpu_rx));
 | 
			
		||||
    // let mut cpu = Cpu::new(cpu_memory);
 | 
			
		||||
    // let mut tui = tui::App::new(
 | 
			
		||||
    //     CpuController::new(cpu_tx.clone()),
 | 
			
		||||
    //     shared_memory.clone(),
 | 
			
		||||
    //     state_rx,
 | 
			
		||||
    // );
 | 
			
		||||
    thread::spawn(move || {
 | 
			
		||||
        cpu.reset().unwrap();
 | 
			
		||||
        cpu.memory.write(0x4400, 0b0000_0100);
 | 
			
		||||
        cpu.execute()
 | 
			
		||||
    let (mut cpu, cpu_controller) = Cpu::new_with_control(cpu_memory);
 | 
			
		||||
    let _ = cpu.reset();
 | 
			
		||||
 | 
			
		||||
    thread::spawn(move || loop {
 | 
			
		||||
        cpu.cycle();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let stdout_lock = stdout.lock();
 | 
			
		||||
    let renderer = TerminalRenderer::new(screen_memory, stdout_lock);
 | 
			
		||||
    let mut screen = Screen::new(CpuController::new(cpu_tx.clone()), renderer);
 | 
			
		||||
 | 
			
		||||
    // thread::spawn(move || {
 | 
			
		||||
    //     screen.run();
 | 
			
		||||
    // });
 | 
			
		||||
    let mut screen = Screen::new(renderer, cpu_controller);
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        sleep(Duration::from_millis(16));
 | 
			
		||||
        screen.draw();
 | 
			
		||||
        if event::poll(std::time::Duration::from_millis(16))? {
 | 
			
		||||
            if let event::Event::Key(key) = event::read()? {
 | 
			
		||||
                keyboard.read_keys(key);
 | 
			
		||||
                if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // let buffer = window_rx.recv().unwrap();
 | 
			
		||||
        screen.draw()
 | 
			
		||||
        // tui.update(&mut terminal)?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    execute!(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
use crate::types::{Byte, Word};
 | 
			
		||||
use anyhow::{bail, Result};
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::io::{self, Write};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
use std::sync::{Arc, Mutex, MutexGuard};
 | 
			
		||||
use std::u16;
 | 
			
		||||
use std::sync::{Arc, Mutex};
 | 
			
		||||
use std::{fs::File, io::Read};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -13,30 +13,25 @@ impl MemHandle {
 | 
			
		|||
    pub fn new(memory: Mem) -> Self {
 | 
			
		||||
        Self(Arc::new(Mutex::new(memory)))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn read(&self, address: Word) -> Byte {
 | 
			
		||||
        let memory = self.lock().unwrap();
 | 
			
		||||
    pub fn read(&self, address: u16) -> u8 {
 | 
			
		||||
        let memory = self.0.lock().unwrap();
 | 
			
		||||
        memory.read(address)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn write(&self, address: Word, data: Byte) {
 | 
			
		||||
        let mut memory = self.lock().unwrap();
 | 
			
		||||
    pub fn write(&self, address: u16, data: u8) {
 | 
			
		||||
        let mut memory = self.0.lock().unwrap();
 | 
			
		||||
        memory.write(address, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn dump(&self) {
 | 
			
		||||
        let memory = self.lock().unwrap();
 | 
			
		||||
        memory.dump(PathBuf::from_str("./cpu_dump.bin").unwrap());
 | 
			
		||||
        let memory = self.0.lock().unwrap();
 | 
			
		||||
        let _ = memory.dump(PathBuf::from_str("./cpu_dump.bin").unwrap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn lock(&self) -> Result<MutexGuard<'_, Mem>> {
 | 
			
		||||
        match self.0.lock() {
 | 
			
		||||
            Ok(result) => Ok(result),
 | 
			
		||||
            Err(error) => bail!("{error}"),
 | 
			
		||||
        }
 | 
			
		||||
    pub fn poke(&self, address: u16) {
 | 
			
		||||
        let memory = self.0.lock().unwrap();
 | 
			
		||||
        memory.poke(address)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: impl Iterator for MemHandle so we can get references to the underlying data from other threads without cloning
 | 
			
		||||
 | 
			
		||||
pub trait MemoryReader {
 | 
			
		||||
    fn read(&self, address: u16) -> u8;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,19 +40,16 @@ pub trait MemoryWriter {
 | 
			
		|||
    fn write(&self, address: u16, data: u8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pub trait MemoryAccess: MemoryReader + MemoryWriter {}
 | 
			
		||||
// impl<T> MemoryAccess for T where T: MemoryReader + MemoryWriter {}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Mem {
 | 
			
		||||
    pub data: Vec<u8>,
 | 
			
		||||
}
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
// This always feels wrong instead of 0xFFFF,
 | 
			
		||||
// but remember it's the number of elements,
 | 
			
		||||
// not the maximum index. 0x0000 to 0xFFFF addresses
 | 
			
		||||
// 0x10000 elements
 | 
			
		||||
pub struct Mem([u8; 0x10000]);
 | 
			
		||||
 | 
			
		||||
impl Default for Mem {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            data: vec![0; u16::MAX as usize + 1],
 | 
			
		||||
        }
 | 
			
		||||
        Self([0; 0x10000])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,23 +59,24 @@ impl Mem {
 | 
			
		|||
    }
 | 
			
		||||
    pub fn dump(&self, path: PathBuf) -> io::Result<()> {
 | 
			
		||||
        let mut outfile = File::create(path)?;
 | 
			
		||||
        outfile.write_all(&self.data)?;
 | 
			
		||||
        outfile.write_all(&self.0)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn read(&self, address: Word) -> Byte {
 | 
			
		||||
        self.data[address as usize]
 | 
			
		||||
    pub fn read(&self, address: u16) -> u8 {
 | 
			
		||||
        self.0[address as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn write(&mut self, address: Word, data: Byte) {
 | 
			
		||||
        self.data[address as usize] = data;
 | 
			
		||||
    pub fn write(&mut self, address: u16, data: u8) {
 | 
			
		||||
        self.0[address as usize] = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn load_rom(&mut self, rom: File) -> Result<()> {
 | 
			
		||||
        let bytes = rom.bytes();
 | 
			
		||||
        for (address, byte) in bytes.enumerate() {
 | 
			
		||||
            // println!("{address}");
 | 
			
		||||
            match byte {
 | 
			
		||||
                Ok(value) => self.write(address as Word + 0x8000, value),
 | 
			
		||||
                Ok(value) => self.write(address as u16 + 0x8000, value),
 | 
			
		||||
                Err(_) => {
 | 
			
		||||
                    bail!("Loading rom: couldn't write byte {:#04x}", address);
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -91,11 +84,15 @@ impl Mem {
 | 
			
		|||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn poke(&self, address: u16) {
 | 
			
		||||
        println!("{:02x}", self.read(address));
 | 
			
		||||
    }
 | 
			
		||||
    //pub fn read_from_bin(&mut self, f: File) -> Result<()> {
 | 
			
		||||
    //    let bytes = f.bytes();
 | 
			
		||||
    //    for (address, byte) in bytes.enumerate() {
 | 
			
		||||
    //        match byte {
 | 
			
		||||
    //            Ok(value) => self.write(address as Word, value),
 | 
			
		||||
    //            Ok(value) => self.write(address as u16, value),
 | 
			
		||||
    //            Err(_) => {
 | 
			
		||||
    //                bail!("couldn't write byte {:#04x}", address);
 | 
			
		||||
    //            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ cleardisplay:
 | 
			
		|||
    ; cli
 | 
			
		||||
 | 
			
		||||
main:
 | 
			
		||||
    jsr keyboard
 | 
			
		||||
    jsr printtext
 | 
			
		||||
    ; key_zero:
 | 
			
		||||
    ;     stz keyboard_cache, x
 | 
			
		||||
    ;     dex
 | 
			
		||||
| 
						 | 
				
			
			@ -45,164 +45,28 @@ main:
 | 
			
		|||
    ; fim: 
 | 
			
		||||
    ;     cli
 | 
			
		||||
    ;     bra fim
 | 
			
		||||
    ; jsr kitty_keys
 | 
			
		||||
    jmp main
 | 
			
		||||
 | 
			
		||||
; ; copying @smal rn: https://github.com/smaldragon/KittyEMU/blob/main/roms/foxmon.asm
 | 
			
		||||
; char_timer = $10
 | 
			
		||||
; line_buffer = $0200
 | 
			
		||||
; char_cur = $11
 | 
			
		||||
; line_buffer_i = $12
 | 
			
		||||
; line_buffer_l = $13
 | 
			
		||||
printtext:
 | 
			
		||||
    ldx 0
 | 
			
		||||
    loop:
 | 
			
		||||
        lda text, x
 | 
			
		||||
        beq end
 | 
			
		||||
        sta $6000, x
 | 
			
		||||
        inx
 | 
			
		||||
        bra loop
 | 
			
		||||
    end:
 | 
			
		||||
        rts
 | 
			
		||||
 | 
			
		||||
; keyboard_cache = $14
 | 
			
		||||
; line_cur = $20
 | 
			
		||||
text: 
 | 
			
		||||
    .asciiz "hi <3"
 | 
			
		||||
 | 
			
		||||
; irq:
 | 
			
		||||
 | 
			
		||||
;     lda line_buffer_i
 | 
			
		||||
;     lda $e0
 | 
			
		||||
;     lda $6f
 | 
			
		||||
;     sei
 | 
			
		||||
;     stz char_cur
 | 
			
		||||
;     lda line_buffer_i
 | 
			
		||||
;     sta line_buffer_l
 | 
			
		||||
;     ldx #4
 | 
			
		||||
 | 
			
		||||
; kitty_keys: ; reads pressed key and writes keymap value to char_cur
 | 
			
		||||
;     phx 
 | 
			
		||||
;     txa
 | 
			
		||||
;     asl
 | 
			
		||||
;     asl
 | 
			
		||||
;     asl
 | 
			
		||||
;     asl
 | 
			
		||||
;     asl ; i think this is supposed to be once for every keyboard row
 | 
			
		||||
;     tax
 | 
			
		||||
;     lda kb_row, x
 | 
			
		||||
;     plx
 | 
			
		||||
 | 
			
		||||
;     pha
 | 
			
		||||
;     cmp keyboard_cache, x
 | 
			
		||||
;     bne change
 | 
			
		||||
;     jmp nochange
 | 
			
		||||
;     change:
 | 
			
		||||
;         bit7:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit6
 | 
			
		||||
;             bit #0b10000000
 | 
			
		||||
;             beq bit6
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_7, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit6:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit5
 | 
			
		||||
;             bit #0b01000000
 | 
			
		||||
;             beq bit5
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_6, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit5:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit4
 | 
			
		||||
;             bit #0b00100000
 | 
			
		||||
;             beq bit4
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_5, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit4:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit3
 | 
			
		||||
;             bit #0b00010000
 | 
			
		||||
;             beq bit3
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_4, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit3:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit2
 | 
			
		||||
;             bit #0b00001000
 | 
			
		||||
;             beq bit2
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_3, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit2:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit1
 | 
			
		||||
;             bit #0b00000100
 | 
			
		||||
;             beq bit1
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_2, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit1:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bit0
 | 
			
		||||
;             bit #0b00000010
 | 
			
		||||
;             beq bit0
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_1, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bit0:
 | 
			
		||||
;             asl keyboard_cache, x
 | 
			
		||||
;             bcs bitend
 | 
			
		||||
;             bit #0b00000001
 | 
			
		||||
;             beq bitend
 | 
			
		||||
;             pha
 | 
			
		||||
;             lda keymap_0, x
 | 
			
		||||
;             sta char_cur
 | 
			
		||||
;             pla
 | 
			
		||||
;         bitend:
 | 
			
		||||
;     nochange:
 | 
			
		||||
;         pla
 | 
			
		||||
;         sta keyboard_cache, x
 | 
			
		||||
;         dex
 | 
			
		||||
;         bmi keyend
 | 
			
		||||
;         jmp kitty_keys
 | 
			
		||||
;         keyend:
 | 
			
		||||
 | 
			
		||||
;     write:
 | 
			
		||||
;         lda char_cur
 | 
			
		||||
;         ldy cursor
 | 
			
		||||
;         sta $6000, y
 | 
			
		||||
;         inc cursor
 | 
			
		||||
;         rts
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
; ; col = keyboard row, row = keyboard bit placement inverted
 | 
			
		||||
; keymap_0:
 | 
			
		||||
;     .byte "??????"
 | 
			
		||||
; keymap_1:
 | 
			
		||||
;     .byte "wqdzx "
 | 
			
		||||
; keymap_2:
 | 
			
		||||
;     .byte "esvfc "
 | 
			
		||||
; keymap_3:
 | 
			
		||||
;     .byte "rghbn?"
 | 
			
		||||
; keymap_4:
 | 
			
		||||
;     .byte "tykjm?"
 | 
			
		||||
; keymap_5:
 | 
			
		||||
;     .byte "ui?l??"
 | 
			
		||||
; keymap_6:
 | 
			
		||||
;     .byte "op?21?"
 | 
			
		||||
; keymap_7:
 | 
			
		||||
;     .byte "??a43?"
 | 
			
		||||
;
 | 
			
		||||
;
 | 
			
		||||
keyboard:
 | 
			
		||||
    ldy #0
 | 
			
		||||
    x
 | 
			
		||||
 | 
			
		||||
not_keyboard:
 | 
			
		||||
    ldy #0 
 | 
			
		||||
    .check_row: ; loop through each row
 | 
			
		||||
        lda kb_row, y
 | 
			
		||||
        beq .skip_row ; if row has no key pressed, skip checking which key
 | 
			
		||||
        ; jmp key_down
 | 
			
		||||
        sta kb_row_cache, y ; if key pressed, cache it
 | 
			
		||||
        lda kb_row, y
 | 
			
		||||
        cmp kb_row_cache, y ; has key changed?
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1,26 +1,16 @@
 | 
			
		|||
; .setcpu "65C02"
 | 
			
		||||
    .include "./macro.inc"
 | 
			
		||||
 | 
			
		||||
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
 | 
			
		||||
n = $01 ; temporary storage for data stack operations
 | 
			
		||||
 | 
			
		||||
key_row = $200 ; used for character lookup when key pressed
 | 
			
		||||
key_col = $201
 | 
			
		||||
cursor = $202
 | 
			
		||||
 | 
			
		||||
char_buffer = $300 ; 256 byte character buffer
 | 
			
		||||
 | 
			
		||||
kb_row = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
 | 
			
		||||
kb_row_cache = $203 ; cache 
 | 
			
		||||
 | 
			
		||||
    .org $8000
 | 
			
		||||
 | 
			
		||||
n = $01 ; temporary storage for data stack operations
 | 
			
		||||
 | 
			
		||||
reset:
 | 
			
		||||
    sei
 | 
			
		||||
    ldx #0; initialize data stack pointer
 | 
			
		||||
 | 
			
		||||
initdisplay:
 | 
			
		||||
    lda #$20
 | 
			
		||||
    lda #0
 | 
			
		||||
    ldy #0
 | 
			
		||||
 | 
			
		||||
cleardisplay:
 | 
			
		||||
| 
						 | 
				
			
			@ -37,56 +27,13 @@ cleardisplay:
 | 
			
		|||
    cli
 | 
			
		||||
 | 
			
		||||
main:
 | 
			
		||||
    jsr draw
 | 
			
		||||
    ; jmp draw
 | 
			
		||||
    jmp cleardisplay
 | 
			
		||||
    jmp main
 | 
			
		||||
 | 
			
		||||
keyboard:
 | 
			
		||||
    ldy #5
 | 
			
		||||
    .loop: ; loop through each row
 | 
			
		||||
        lda kb_row, y
 | 
			
		||||
        bne key_down; if row has key pressed, go to key_down subroutine
 | 
			
		||||
        dey
 | 
			
		||||
        bne .loop
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
key_down:               ; a is loaded with the row byte
 | 
			
		||||
    sty key_row         ; store character row
 | 
			
		||||
    inc cursor
 | 
			
		||||
    pha
 | 
			
		||||
    phy
 | 
			
		||||
    ldy #0
 | 
			
		||||
    .find_col:          ; test each row bit, store column if key pressed
 | 
			
		||||
        lsr             ; test bit 7
 | 
			
		||||
        bcs store_col       ; if unset, don't go store character columnb
 | 
			
		||||
        .skip:
 | 
			
		||||
            iny
 | 
			
		||||
            cpy #8
 | 
			
		||||
            bne .find_col   ; loop until we've checked each bit
 | 
			
		||||
 | 
			
		||||
do_something_w_key: ; we've stored the character position, now let's 
 | 
			
		||||
    lda keymap, y
 | 
			
		||||
    ldy cursor
 | 
			
		||||
    sta $6000, y
 | 
			
		||||
    ply
 | 
			
		||||
    pla
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
keymap_0:
 | 
			
		||||
    .byte "?outrew?"
 | 
			
		||||
keymap_1:
 | 
			
		||||
    .byte "?piygsq?"
 | 
			
		||||
keymap_2:
 | 
			
		||||
    .byte "a??khvd?"
 | 
			
		||||
keymap_3:
 | 
			
		||||
    .byte "42ljbfz?"
 | 
			
		||||
keymap_4:
 | 
			
		||||
    .byte "31?mncx?"
 | 
			
		||||
keymap_5:
 | 
			
		||||
    .byte "?????  m"
 | 
			
		||||
 | 
			
		||||
draw:
 | 
			
		||||
    push_coords #0, #0
 | 
			
		||||
    push_char #$00
 | 
			
		||||
    push_char #$af
 | 
			
		||||
    jsr draw_char
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -125,11 +72,16 @@ get_char_address:   ; gets vram address for a character at (x, y),
 | 
			
		|||
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
 | 
			
		||||
    jsr get_char_address
 | 
			
		||||
 | 
			
		||||
irq: 
 | 
			
		||||
 | 
			
		||||
    keyboard:
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
isr: ; interrupt service routine
 | 
			
		||||
    pha
 | 
			
		||||
    phx
 | 
			
		||||
    phy
 | 
			
		||||
    jsr keyboard
 | 
			
		||||
    jsr irq
 | 
			
		||||
    ply
 | 
			
		||||
    plx
 | 
			
		||||
    pla
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
; .setcpu "65C02"
 | 
			
		||||
    .include "./macro.inc"
 | 
			
		||||
 | 
			
		||||
    .org $8000
 | 
			
		||||
 | 
			
		||||
n = $01 ; temporary storage for data stack operations
 | 
			
		||||
 | 
			
		||||
reset:
 | 
			
		||||
    sei
 | 
			
		||||
    ldx #0; initialize data stack pointer
 | 
			
		||||
 | 
			
		||||
initdisplay:
 | 
			
		||||
    lda #0
 | 
			
		||||
    ldy #0
 | 
			
		||||
 | 
			
		||||
cleardisplay:
 | 
			
		||||
    sta $6000,y
 | 
			
		||||
    sta $6100,y
 | 
			
		||||
    sta $6200,y
 | 
			
		||||
    sta $6300,y
 | 
			
		||||
    sta $6400,y
 | 
			
		||||
    sta $6500,y
 | 
			
		||||
    sta $6600,y
 | 
			
		||||
    sta $6700,y ; this goes slightly over but it's fine
 | 
			
		||||
    iny
 | 
			
		||||
    bne cleardisplay
 | 
			
		||||
    cli
 | 
			
		||||
 | 
			
		||||
main:
 | 
			
		||||
    jmp draw
 | 
			
		||||
    jmp main
 | 
			
		||||
 | 
			
		||||
draw:
 | 
			
		||||
    push_coords #0, #0
 | 
			
		||||
    push_char #$af
 | 
			
		||||
    jsr draw_char
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
 | 
			
		||||
    lda 0, x ; load a with character to draw
 | 
			
		||||
    pop ; and pop it off the stack
 | 
			
		||||
    jsr get_char_address ; calculate where to put the character in memory
 | 
			
		||||
    sta (0, x) ; store a at the address pointed to on the stack
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
get_char_address:   ; gets vram address for a character at (x, y),
 | 
			
		||||
                    ; (n1: x n2: y -- n: $6000 + x + (64 * y))
 | 
			
		||||
    ;jsr push_lit ; push 64 onto stack, low byte first
 | 
			
		||||
    ;.byte 64
 | 
			
		||||
    ;.byte 0
 | 
			
		||||
    pha
 | 
			
		||||
    lda #64
 | 
			
		||||
    push ; doing this instead until `push_lit` is fixed
 | 
			
		||||
    sta 0, x
 | 
			
		||||
    stz 1, x
 | 
			
		||||
    jsr mult ; multiply 64 with y (n2)
 | 
			
		||||
    jsr plus ; add result with x (n1)
 | 
			
		||||
 | 
			
		||||
    ;jsr push_lit ; push vram address onto the stack
 | 
			
		||||
    ;.byte $00
 | 
			
		||||
    ;.byte $60
 | 
			
		||||
    lda #$60
 | 
			
		||||
    push
 | 
			
		||||
    sta 1, x
 | 
			
		||||
    stz 0, x
 | 
			
		||||
    jsr plus ; add vram start address to result
 | 
			
		||||
 | 
			
		||||
    pla
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
 | 
			
		||||
    jsr get_char_address
 | 
			
		||||
 | 
			
		||||
isr: ; interrupt service routine
 | 
			
		||||
    pha
 | 
			
		||||
    phx
 | 
			
		||||
    phy
 | 
			
		||||
    ; jsr irq
 | 
			
		||||
    ply
 | 
			
		||||
    plx
 | 
			
		||||
    pla
 | 
			
		||||
    rti
 | 
			
		||||
 | 
			
		||||
    .include "math.inc"
 | 
			
		||||
 | 
			
		||||
    .org $fffc
 | 
			
		||||
    .word reset
 | 
			
		||||
    .word isr
 | 
			
		||||
							
								
								
									
										41
									
								
								src/video.rs
								
								
								
								
							
							
						
						
									
										41
									
								
								src/video.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,21 +1,21 @@
 | 
			
		|||
use crossterm::{
 | 
			
		||||
    cursor::{MoveTo, SavePosition},
 | 
			
		||||
    cursor::{MoveTo, RestorePosition, SavePosition},
 | 
			
		||||
    execute, queue,
 | 
			
		||||
    style::{Color, PrintStyledContent, Stylize},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    cpu::CpuController,
 | 
			
		||||
    memory::{self, MemHandle, MemoryReader},
 | 
			
		||||
    memory::{MemHandle, MemoryReader},
 | 
			
		||||
    types::{Byte, Word},
 | 
			
		||||
};
 | 
			
		||||
use std::{
 | 
			
		||||
    fs::File,
 | 
			
		||||
    io::{Read, Stdout, StdoutLock, Write},
 | 
			
		||||
    io::{Read, StdoutLock, Write},
 | 
			
		||||
    path::Path,
 | 
			
		||||
    process::exit,
 | 
			
		||||
    sync::mpsc::Sender,
 | 
			
		||||
    thread::sleep,
 | 
			
		||||
    time::Duration,
 | 
			
		||||
    time::Instant,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const FG_COLOR: u32 = 0xFFCC00;
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ impl Renderer for WindowRenderer {
 | 
			
		|||
        let mut buffer = vec![0; 512 * 380];
 | 
			
		||||
        for char_row in 0..29 {
 | 
			
		||||
            for char_col in 0..64 {
 | 
			
		||||
                let ascii = self.memory.read(0x6000 + i);
 | 
			
		||||
                let ascii = self.read(0x6000 + i);
 | 
			
		||||
                i += 1;
 | 
			
		||||
                for row in 0..13 {
 | 
			
		||||
                    let byte = self.char_rom[ascii as usize + (row * 0x100)];
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ impl MemoryReader for TerminalRenderer<'_> {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ASCII_LOOPUP: [&str; 256] = [
 | 
			
		||||
const ASCII_LOOPUP: [&str; 256] = [
 | 
			
		||||
    " ", "░", "▒", "▓", "♡", "♥", "⭐", "✭", "", "✦", "✨", "♀", "♂", "⚢", "⚣", "⚥", "♩", "♪",
 | 
			
		||||
    "♫", "♬", "ﱝ", "", "", "", "奄", "奔", "婢", "ﱜ", "ﱛ", "", "", "", " ", "!", "\"", "#",
 | 
			
		||||
    "$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6",
 | 
			
		||||
| 
						 | 
				
			
			@ -137,11 +137,12 @@ static ASCII_LOOPUP: [&str; 256] = [
 | 
			
		|||
 | 
			
		||||
impl Renderer for TerminalRenderer<'_> {
 | 
			
		||||
    fn render(&mut self) {
 | 
			
		||||
        // let now = Instant::now();
 | 
			
		||||
        let _ = execute!(self.stdout, SavePosition);
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        for char_row in 0..29 {
 | 
			
		||||
            for char_col in 0..64 {
 | 
			
		||||
                let ascii = self.memory.read(0x6000 + i);
 | 
			
		||||
                let ascii = self.read(0x6000 + i);
 | 
			
		||||
                i += 1;
 | 
			
		||||
                let char = ASCII_LOOPUP[ascii as usize];
 | 
			
		||||
                let _ = queue!(
 | 
			
		||||
| 
						 | 
				
			
			@ -165,32 +166,36 @@ impl Renderer for TerminalRenderer<'_> {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let _ = self.stdout.flush();
 | 
			
		||||
        let _ = execute!(self.stdout, RestorePosition);
 | 
			
		||||
        // let elapsed = now.elapsed();
 | 
			
		||||
        // println!("{elapsed:?}");
 | 
			
		||||
        // exit(0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Screen<'a> {
 | 
			
		||||
    cpu_controller: CpuController,
 | 
			
		||||
    // renderer: Box<dyn Renderer>,
 | 
			
		||||
    renderer: TerminalRenderer<'a>,
 | 
			
		||||
    controller: CpuController,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Screen<'a> {
 | 
			
		||||
    // pub fn new(cpu_controller: CpuController, renderer: Box<dyn Renderer>) -> Self {
 | 
			
		||||
    pub fn new(cpu_controller: CpuController, renderer: TerminalRenderer<'a>) -> Self {
 | 
			
		||||
    pub fn new(renderer: TerminalRenderer<'a>, controller: CpuController) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            cpu_controller,
 | 
			
		||||
            renderer,
 | 
			
		||||
            controller,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn draw(&mut self) {
 | 
			
		||||
        self.controller.irq();
 | 
			
		||||
        self.renderer.render();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn run(&mut self) {
 | 
			
		||||
        loop {
 | 
			
		||||
            self.cpu_controller.irq();
 | 
			
		||||
            sleep(Duration::from_millis(16));
 | 
			
		||||
            self.draw();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // pub fn run(&mut self) {
 | 
			
		||||
    //     loop {
 | 
			
		||||
    //         // sleep(Duration::from_millis(16));
 | 
			
		||||
    //         self.draw();
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue