Very messy but we're running on the web now! (not in this crate, but will add web-sys to georgeemu bin in a bit)
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
use std::{
|
||||
env,
|
||||
default, env,
|
||||
fs::File,
|
||||
io::{ErrorKind, Read},
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use georgeemu::{Config, ScreenType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::video::ScreenType;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct ConfigBuilder {
|
||||
rom: Option<String>,
|
||||
@@ -27,7 +26,7 @@ impl ConfigBuilder {
|
||||
|
||||
fn build(self) -> Config {
|
||||
let rom = match self.rom {
|
||||
Some(rom) => rom,
|
||||
Some(rom) => Some(rom),
|
||||
None => {
|
||||
println!("no rom was provided :(");
|
||||
exit(1);
|
||||
@@ -44,12 +43,6 @@ impl ConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub rom: String,
|
||||
pub screen: ScreenType,
|
||||
pub char_rom: Option<String>,
|
||||
}
|
||||
|
||||
fn help(command: Option<String>) {
|
||||
let executable: String = env::args().next().unwrap();
|
||||
if let Some(command) = command {
|
||||
@@ -101,6 +94,7 @@ pub fn get_input() -> Config {
|
||||
}
|
||||
Err(_) => {
|
||||
println!("couldn't find a `george.toml` in the current directory!");
|
||||
println!("\nuse `{executable} --help` to see all config options");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
mod cli;
|
||||
|
||||
use cli::get_input;
|
||||
use georgeemu::GeorgeEmu;
|
||||
use minifb::{Scale, ScaleMode, Window, WindowOptions};
|
||||
|
||||
fn main() {
|
||||
let mut window = Window::new(
|
||||
"ʕ·ᴥ·ʔ-☆",
|
||||
512,
|
||||
380,
|
||||
WindowOptions {
|
||||
resize: true,
|
||||
borderless: true,
|
||||
title: true,
|
||||
transparency: false,
|
||||
scale: Scale::FitScreen,
|
||||
scale_mode: ScaleMode::AspectRatioStretch,
|
||||
topmost: false,
|
||||
none: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let config = get_input();
|
||||
#[cfg(not(feature = "term"))]
|
||||
let mut emu = GeorgeEmu::builder()
|
||||
.window(&mut window)
|
||||
.rom("/Users/august/projects/george-emu/src/roms/demo.rom")
|
||||
.build();
|
||||
#[cfg(feature = "term")]
|
||||
let mut emu = GeorgeEmu::builder().build();
|
||||
emu.run();
|
||||
}
|
||||
+35
-69
@@ -5,8 +5,6 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use termion::cursor::Goto;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum StatusFlag {
|
||||
Negative = 0b1000_0000,
|
||||
@@ -19,7 +17,7 @@ pub enum StatusFlag {
|
||||
Carry = 0b0000_0001,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CpuController(Sender<CpuControl>);
|
||||
|
||||
pub enum CpuControl {
|
||||
@@ -51,6 +49,7 @@ impl CpuController {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CpuReceiver(Receiver<CpuControl>);
|
||||
impl CpuReceiver {
|
||||
pub fn new(receiver: Receiver<CpuControl>) -> Self {
|
||||
@@ -58,6 +57,7 @@ impl CpuReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cpu {
|
||||
pub a: u8, // Accumulator Register
|
||||
pub x: u8, // X Register
|
||||
@@ -71,8 +71,7 @@ pub struct Cpu {
|
||||
pub pending_cycles: usize,
|
||||
receiver: Option<CpuReceiver>,
|
||||
stopped: bool,
|
||||
cycle: bool, // cycle_count: usize,
|
||||
// state_tx: Sender<CpuState>,
|
||||
cycle: bool,
|
||||
}
|
||||
|
||||
impl Display for Cpu {
|
||||
@@ -94,7 +93,6 @@ impl MemoryWriter for Cpu {
|
||||
|
||||
impl Cpu {
|
||||
pub fn new(memory: MemHandle) -> Self {
|
||||
// pub fn new(memory: MemHandle) -> Self {
|
||||
Cpu {
|
||||
a: 0x00,
|
||||
x: 0x00,
|
||||
@@ -179,13 +177,6 @@ impl Cpu {
|
||||
value & 0b1000_0000 == 0b1000_0000
|
||||
}
|
||||
|
||||
// pub fn execute(&mut self) {
|
||||
// self.cycle();
|
||||
// while self.pending_cycles != 0 {
|
||||
// self.cycle();
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn wait_for_interrupt(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
@@ -198,6 +189,10 @@ impl Cpu {
|
||||
self.pc = self.read_word(0xFFFE);
|
||||
}
|
||||
|
||||
pub fn toggle_stopped(&mut self) {
|
||||
self.stopped = !self.stopped;
|
||||
}
|
||||
|
||||
fn receive_control(&mut self) {
|
||||
match &self.receiver {
|
||||
Some(receiver) => {
|
||||
@@ -213,19 +208,21 @@ impl Cpu {
|
||||
self.stopped = !self.stopped;
|
||||
}
|
||||
CpuControl::Cycle => self.cycle = true,
|
||||
CpuControl::Data => {} // self
|
||||
// .state_tx
|
||||
// .send(CpuState {
|
||||
// a: self.a.clone(), // Accumulator Register
|
||||
// x: self.x.clone(), // X Register
|
||||
// y: self.y.clone(), // Y Register
|
||||
// pc: self.pc.clone(), // Program Counter
|
||||
// s: self.s.clone(), // Stack Pointer
|
||||
// p: self.p.clone(), // Status Register
|
||||
// irq: self.irq.clone(),
|
||||
// nmi: self.nmi.clone(),
|
||||
// })
|
||||
// .unwrap(),
|
||||
CpuControl::Data => {
|
||||
// self
|
||||
// .state_tx
|
||||
// .send(CpuState {
|
||||
// a: self.a.clone(), // Accumulator Register
|
||||
// x: self.x.clone(), // X Register
|
||||
// y: self.y.clone(), // Y Register
|
||||
// pc: self.pc.clone(), // Program Counter
|
||||
// s: self.s.clone(), // Stack Pointer
|
||||
// p: self.p.clone(), // Status Register
|
||||
// irq: self.irq.clone(),
|
||||
// nmi: self.nmi.clone(),
|
||||
// })
|
||||
// .unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
@@ -233,64 +230,33 @@ impl Cpu {
|
||||
}
|
||||
|
||||
pub fn cycle(&mut self) {
|
||||
self.receive_control();
|
||||
// self.receive_control();
|
||||
|
||||
if self.stopped & !self.cycle {
|
||||
self.set_flag(StatusFlag::IrqDisable, true);
|
||||
return;
|
||||
}
|
||||
self.cycle = false;
|
||||
// if self.stopped & !self.cycle {
|
||||
// self.set_flag(StatusFlag::IrqDisable, true);
|
||||
// return;
|
||||
// }
|
||||
// self.cycle = false;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
while self.pending_cycles != 0 {
|
||||
// roughly cycle-accurate timing
|
||||
sleep(Duration::from_nanos(500));
|
||||
self.pending_cycles -= 1;
|
||||
}
|
||||
if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
|
||||
self.interrupt();
|
||||
}
|
||||
// if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
|
||||
// self.interrupt();
|
||||
// }
|
||||
let opcode = self.read(self.pc);
|
||||
let instruction = get_instruction(opcode);
|
||||
instruction.call(self);
|
||||
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
|
||||
// println!(
|
||||
// "Instruction: {:?}, {:#04x}",
|
||||
// valid_instruction.opcode, opcode
|
||||
// );
|
||||
// println!("");
|
||||
}
|
||||
pub fn stop(&mut self) {
|
||||
self.stopped = true;
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
pub fn breakpoint(&mut self) {
|
||||
// println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
|
||||
// println!(
|
||||
// "Instruction: {:?}, {:#04x}",
|
||||
// valid_instruction.opcode, opcode
|
||||
// );
|
||||
// println!("");
|
||||
// println!(
|
||||
// "{}{:#04x}: {:#02x}, {:#04x}: {:#02x}",
|
||||
// Goto(1, 35),
|
||||
// 0x20,
|
||||
// self.read(0x20),
|
||||
// 0x21,
|
||||
// self.read(0x21)
|
||||
// );
|
||||
// println!(
|
||||
// "{}str_ptr {:#04x}: {:#02x}, {:#04x}: {:#02x}",
|
||||
// Goto(1, 36),
|
||||
// 0x30,
|
||||
// self.read(0x30),
|
||||
// 0x31,
|
||||
// self.read(0x31)
|
||||
// );
|
||||
// println!(
|
||||
// "{}cursor - x: {:#02x}, y: {:#02x}",
|
||||
// Goto(1, 37),
|
||||
// self.read(0x300),
|
||||
// self.read(0x301)
|
||||
// );
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
+161
-135
@@ -1,10 +1,7 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use core::panic;
|
||||
use std::process::exit;
|
||||
|
||||
use termion::cursor::Goto;
|
||||
use termion::{clear, color};
|
||||
#[cfg(feature = "debug")]
|
||||
use termion::{clear, color, cursor::Goto};
|
||||
|
||||
use crate::cpu::{Cpu, StatusFlag};
|
||||
use crate::memory::{MemoryReader, MemoryWriter};
|
||||
@@ -23,47 +20,12 @@ pub struct Instruction<'a> {
|
||||
|
||||
impl Instruction<'_> {
|
||||
pub fn call(&self, cpu: &mut Cpu) {
|
||||
// TODO: add flag to print these
|
||||
// print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi);
|
||||
// print!(
|
||||
// "{}instruction: {:?}, pc: {:#04x}",
|
||||
// Goto(0, 31),
|
||||
// self.name,
|
||||
// cpu.pc
|
||||
// );
|
||||
// print!(
|
||||
// "{}{}instruction: {:?}, mode: {:?}",
|
||||
// Goto(0, 32),
|
||||
// clear::UntilNewline,
|
||||
// self.name,
|
||||
// self.addr_mode
|
||||
// );
|
||||
// print!(
|
||||
// "{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
|
||||
// Goto(0, 33),
|
||||
// cpu.read(cpu.pc.wrapping_sub(8)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(7)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(6)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(5)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(4)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(3)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(2)),
|
||||
// cpu.read(cpu.pc.wrapping_sub(1)),
|
||||
// color::Bg(color::Rgb(0xFF, 0xCC, 0x00)),
|
||||
// color::Fg(color::Rgb(0x11, 0x05, 0x00)),
|
||||
// cpu.read(cpu.pc),
|
||||
// color::Fg(color::Rgb(0xFF, 0xCC, 0x00)),
|
||||
// color::Bg(color::Rgb(0x11, 0x05, 0x00)),
|
||||
// cpu.read(cpu.pc.wrapping_add(1)),
|
||||
// cpu.read(cpu.pc.wrapping_add(2)),
|
||||
// cpu.read(cpu.pc.wrapping_add(3)),
|
||||
// cpu.read(cpu.pc.wrapping_add(4)),
|
||||
// cpu.read(cpu.pc.wrapping_add(5)),
|
||||
// cpu.read(cpu.pc.wrapping_add(6)),
|
||||
// cpu.read(cpu.pc.wrapping_add(7)),
|
||||
// cpu.read(cpu.pc.wrapping_add(8)),
|
||||
// );
|
||||
#[cfg(feature = "debug")]
|
||||
self.debug(cpu);
|
||||
|
||||
cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match self.instr_fn {
|
||||
// existence of instr_fn means this is a valid instruction
|
||||
Some(instr_fn) => {
|
||||
@@ -82,17 +44,75 @@ impl Instruction<'_> {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!(
|
||||
"An invalid instruction was called at {:04x}, with opcode {:02x}",
|
||||
cpu.pc,
|
||||
cpu.read(cpu.pc)
|
||||
)
|
||||
} // idk if we should panic here, if we don't it's undefined behavior, which
|
||||
// might actually be what we want in the cases where it's an undocumented
|
||||
// 6502 instruction, but otherwise we should panic prolly, for now i'm just
|
||||
// gonna panic
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
println!(
|
||||
"{}An invalid instruction was called at {:04x}, with opcode {:02x}",
|
||||
Goto(0, 35),
|
||||
cpu.pc,
|
||||
cpu.read(cpu.pc)
|
||||
);
|
||||
cpu.breakpoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn debug(&self, cpu: &Cpu) {
|
||||
// cpu state
|
||||
print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi);
|
||||
print!(
|
||||
"{}instruction: {:?}, pc: {:#04x}",
|
||||
Goto(0, 31),
|
||||
self.name,
|
||||
cpu.pc
|
||||
);
|
||||
|
||||
// current instruction and addressing mode
|
||||
print!(
|
||||
"{}{}instruction: {:?}, mode: {:?}",
|
||||
Goto(0, 32),
|
||||
clear::UntilNewline,
|
||||
self.name,
|
||||
self.addr_mode
|
||||
);
|
||||
|
||||
// local memory
|
||||
print!(
|
||||
"{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
|
||||
Goto(0, 33),
|
||||
cpu.read(cpu.pc.wrapping_sub(8)),
|
||||
cpu.read(cpu.pc.wrapping_sub(7)),
|
||||
cpu.read(cpu.pc.wrapping_sub(6)),
|
||||
cpu.read(cpu.pc.wrapping_sub(5)),
|
||||
cpu.read(cpu.pc.wrapping_sub(4)),
|
||||
cpu.read(cpu.pc.wrapping_sub(3)),
|
||||
cpu.read(cpu.pc.wrapping_sub(2)),
|
||||
cpu.read(cpu.pc.wrapping_sub(1)),
|
||||
color::Bg(color::Rgb(0xFF, 0xCC, 0x00)),
|
||||
color::Fg(color::Rgb(0x11, 0x05, 0x00)),
|
||||
cpu.read(cpu.pc),
|
||||
color::Fg(color::Rgb(0xFF, 0xCC, 0x00)),
|
||||
color::Bg(color::Rgb(0x11, 0x05, 0x00)),
|
||||
cpu.read(cpu.pc.wrapping_add(1)),
|
||||
cpu.read(cpu.pc.wrapping_add(2)),
|
||||
cpu.read(cpu.pc.wrapping_add(3)),
|
||||
cpu.read(cpu.pc.wrapping_add(4)),
|
||||
cpu.read(cpu.pc.wrapping_add(5)),
|
||||
cpu.read(cpu.pc.wrapping_add(6)),
|
||||
cpu.read(cpu.pc.wrapping_add(7)),
|
||||
cpu.read(cpu.pc.wrapping_add(8)),
|
||||
);
|
||||
// scratchpad
|
||||
print!(
|
||||
"{} {:02x} {:02x} {:02x}",
|
||||
Goto(0, 34),
|
||||
cpu.read(0x200),
|
||||
cpu.read(0x201),
|
||||
cpu.read(0x202)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulator(cpu: &mut Cpu) -> u16 {
|
||||
@@ -209,7 +229,6 @@ fn zero_page_indirect_indexed_with_y(
|
||||
let address = cpu.read_word(zp as u16);
|
||||
let address: u16 = address.wrapping_add(cpu.y as u16);
|
||||
cpu.pc = cpu.pc.wrapping_add(1);
|
||||
println!("{}indirect addr: {:#04x}", Goto(1, 39), address);
|
||||
address
|
||||
}
|
||||
|
||||
@@ -255,7 +274,8 @@ fn branch(cpu: &mut Cpu, condition: bool, address: u16) {
|
||||
cpu.pc = cpu.pc.wrapping_add(1);
|
||||
}
|
||||
|
||||
fn brkpt(cpu: &mut Cpu, address: Option<u16>) {
|
||||
#[cfg(feature = "debug")]
|
||||
fn breakpoint(cpu: &mut Cpu, _address: Option<u16>) {
|
||||
cpu.breakpoint()
|
||||
}
|
||||
|
||||
@@ -264,7 +284,7 @@ fn adc(cpu: &mut Cpu, address: Option<u16>) {
|
||||
let byte = cpu.read(address);
|
||||
let carry = cpu.get_flag(StatusFlag::Carry);
|
||||
let result = cpu.a as u16 + byte as u16 + carry as u16;
|
||||
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value()));
|
||||
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX));
|
||||
cpu.set_flag(StatusFlag::Zero, result as u8 == 0);
|
||||
cpu.set_flag(
|
||||
StatusFlag::Overflow,
|
||||
@@ -786,7 +806,7 @@ fn sbc(cpu: &mut Cpu, address: Option<u16>) {
|
||||
let byte = cpu.read(address);
|
||||
let carry = cpu.get_flag(StatusFlag::Carry);
|
||||
let result = cpu.a as u16 + byte as u16 + !carry as u16;
|
||||
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value()));
|
||||
cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX));
|
||||
cpu.set_flag(StatusFlag::Zero, result as u8 == 0);
|
||||
cpu.set_flag(
|
||||
StatusFlag::Overflow,
|
||||
@@ -948,7 +968,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 7,
|
||||
name: "brk",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -958,18 +978,24 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
addr_mode: "zero_page_indexed_indirect",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(brkpt),
|
||||
#[cfg(feature = "debug")]
|
||||
instr_fn: Some(breakpoint),
|
||||
#[cfg(not(feature = "debug"))]
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "brkpt",
|
||||
addr_mode: "none",
|
||||
#[cfg(not(feature = "debug"))]
|
||||
name: "none",
|
||||
#[cfg(feature = "debug")]
|
||||
name: "breakpoint",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(tsb),
|
||||
@@ -1004,7 +1030,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "php",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -1025,7 +1051,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(tsb),
|
||||
@@ -1081,7 +1107,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(trb),
|
||||
@@ -1116,7 +1142,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "clc",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ora),
|
||||
@@ -1137,7 +1163,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(trb),
|
||||
@@ -1186,14 +1212,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1228,7 +1254,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "plp",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(and),
|
||||
@@ -1249,7 +1275,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1305,7 +1331,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1340,7 +1366,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sec",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(and),
|
||||
@@ -1361,7 +1387,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1396,7 +1422,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 6,
|
||||
name: "rti",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1410,21 +1436,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1452,7 +1478,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "pha",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1473,7 +1499,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1529,14 +1555,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1564,7 +1590,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "cli",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1578,21 +1604,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "phy",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(eor),
|
||||
@@ -1620,7 +1646,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 6,
|
||||
name: "rts",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1634,14 +1660,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -1676,7 +1702,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "pla",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1697,7 +1723,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1753,7 +1779,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -1788,7 +1814,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sei",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(adc),
|
||||
@@ -1802,14 +1828,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "ply",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(jmp),
|
||||
@@ -1858,14 +1884,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -1900,7 +1926,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "dey",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(bit),
|
||||
@@ -1914,14 +1940,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "txa",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -1977,7 +2003,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sty),
|
||||
@@ -2012,7 +2038,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tya",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sta),
|
||||
@@ -2026,14 +2052,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "txs",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stz),
|
||||
@@ -2089,7 +2115,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2124,7 +2150,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tay",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(lda),
|
||||
@@ -2138,14 +2164,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tax",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2201,7 +2227,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2236,7 +2262,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "clv",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(lda),
|
||||
@@ -2250,14 +2276,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "tsx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(ldy),
|
||||
@@ -2306,14 +2332,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpy),
|
||||
@@ -2348,7 +2374,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "iny",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2362,14 +2388,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "dex",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(wai),
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "wai",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpy),
|
||||
@@ -2425,14 +2451,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2460,7 +2486,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "cld",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2474,21 +2500,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "phx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(stp),
|
||||
address_fn: None,
|
||||
cycles: 3,
|
||||
name: "stp",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cmp),
|
||||
@@ -2530,14 +2556,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpx),
|
||||
@@ -2572,7 +2598,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "inx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2586,14 +2612,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "nop",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(cpx),
|
||||
@@ -2649,14 +2675,14 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2684,7 +2710,7 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 2,
|
||||
name: "sed",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
@@ -2698,21 +2724,21 @@ pub const OPCODES: [Instruction; 256] = [
|
||||
address_fn: None,
|
||||
cycles: 4,
|
||||
name: "plx",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: None,
|
||||
address_fn: None,
|
||||
cycles: 0,
|
||||
name: "none",
|
||||
addr_mode: "none",
|
||||
addr_mode: "implied",
|
||||
},
|
||||
Instruction {
|
||||
instr_fn: Some(sbc),
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use minifb::InputCallback;
|
||||
use minifb::Key as MKey;
|
||||
|
||||
#[cfg(feature = "term")]
|
||||
use termion::event::Key;
|
||||
|
||||
use crate::memory::{MemHandle, MemoryWriter};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Keyboard {
|
||||
memory: MemHandle,
|
||||
}
|
||||
@@ -22,6 +25,7 @@ impl Keyboard {
|
||||
self.memory.write(0x4405, 0x00);
|
||||
}
|
||||
|
||||
#[cfg(feature = "term")]
|
||||
pub fn read_keys(&self, key: Key) {
|
||||
let mut row0 = 0;
|
||||
let mut row1 = 0;
|
||||
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
pub mod cpu;
|
||||
pub mod error;
|
||||
pub mod instructions;
|
||||
pub mod keyboard;
|
||||
pub mod memory;
|
||||
pub mod video;
|
||||
|
||||
use crate::cpu::Cpu;
|
||||
use crate::keyboard::Keyboard;
|
||||
use crate::memory::Mem;
|
||||
use crate::video::Renderer;
|
||||
|
||||
use core::panic;
|
||||
use cpu::CpuController;
|
||||
use memory::MemHandle;
|
||||
use minifb::Window;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::thread::sleep;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
use std::{io::stdout, process::exit};
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
use termion::{
|
||||
async_stdin, clear,
|
||||
cursor::{self, Goto},
|
||||
event::Key,
|
||||
input::TermRead,
|
||||
raw::IntoRawMode,
|
||||
AsyncReader,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug)]
|
||||
pub enum ScreenType {
|
||||
Terminal,
|
||||
#[default]
|
||||
Window,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub rom: Option<String>,
|
||||
pub screen: ScreenType,
|
||||
pub char_rom: Option<String>,
|
||||
}
|
||||
|
||||
pub struct GeorgeEmuBuilder<'a> {
|
||||
cpu: Cpu,
|
||||
renderer: Option<Renderer>,
|
||||
input: Option<Keyboard>,
|
||||
config: Option<Config>,
|
||||
cpu_controller: CpuController,
|
||||
memory_handle: MemHandle,
|
||||
memory: Mem,
|
||||
window: Option<&'a mut Window>,
|
||||
}
|
||||
|
||||
impl<'a> GeorgeEmuBuilder<'a> {
|
||||
pub fn new() -> Self {
|
||||
let mut memory = Mem::new();
|
||||
// TODO: once the memory goes to the handle you can't load a rom,
|
||||
// so the method that loads a rom needs to have the handle uninitialized first,
|
||||
// so make everything here be None and set everything at the end
|
||||
memory.load_bytes(include_bytes!(
|
||||
"/Users/august/projects/george-emu/src/roms/demo.rom"
|
||||
));
|
||||
let memory_handle = MemHandle::new(memory);
|
||||
let cpu_memory = memory_handle.clone();
|
||||
let (cpu, cpu_controller) = Cpu::new_with_control(cpu_memory);
|
||||
let input = None;
|
||||
let config = None;
|
||||
let renderer = None;
|
||||
let window = None;
|
||||
GeorgeEmuBuilder {
|
||||
cpu,
|
||||
cpu_controller,
|
||||
memory,
|
||||
memory_handle,
|
||||
config,
|
||||
renderer,
|
||||
window,
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(mut self, config: Config) -> Self {
|
||||
self.config = Some(config);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a keyboard input to the emulator
|
||||
pub fn keyboard(mut self) -> Self {
|
||||
let input_memory = self.memory_handle.clone();
|
||||
let keyboard = Keyboard::new(input_memory);
|
||||
self.input = Some(keyboard);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a minifb window to the emulator
|
||||
#[cfg(not(feature = "term"))]
|
||||
pub fn window(mut self, window: &'a mut Window) -> Self {
|
||||
let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone());
|
||||
self.renderer = Some(renderer);
|
||||
self.window = Some(window);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rom<P>(mut self, path: P) -> Self
|
||||
where
|
||||
P: AsRef<Path> + Debug + Copy,
|
||||
{
|
||||
let rom = match File::open(path) {
|
||||
Ok(file) => {
|
||||
file
|
||||
// let mut bin = vec![0; 0x8000];
|
||||
// file.read_exact(&mut bin).unwrap();
|
||||
// // println!("reading char rom");
|
||||
// &bin
|
||||
}
|
||||
Err(error) => panic!("Couldn't read file at {path:?}: {error:?}"),
|
||||
};
|
||||
self.memory.read_from_bin(rom).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> GeorgeEmu<'a> {
|
||||
#[cfg(not(any(feature = "term", feature = "web")))]
|
||||
{
|
||||
match self.window {
|
||||
Some(ref mut window) => {
|
||||
if let Some(ref input) = self.input {
|
||||
window.set_input_callback(Box::new(input.clone()))
|
||||
}
|
||||
}
|
||||
None => panic!(
|
||||
"Tried to call `GeorgeEmuBuilder::build()` before `GeorgeEmuBuilder::window()`!"
|
||||
),
|
||||
}
|
||||
GeorgeEmu::new(self.cpu, self.renderer.unwrap(), self.input, self.window)
|
||||
}
|
||||
#[cfg(feature = "term")]
|
||||
{
|
||||
let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone());
|
||||
GeorgeEmu::new(self.cpu, renderer, self.input, self.window)
|
||||
}
|
||||
#[cfg(feature = "web")]
|
||||
{
|
||||
let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone());
|
||||
GeorgeEmu::new(self.cpu, renderer, self.input, self.window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GeorgeEmu<'a> {
|
||||
pub cpu: Cpu,
|
||||
pub renderer: Renderer,
|
||||
// in the future these might be set with feature flags,
|
||||
// but this is simpler
|
||||
input: Option<Keyboard>,
|
||||
pub window: Option<&'a mut Window>,
|
||||
}
|
||||
|
||||
impl<'a> GeorgeEmu<'a> {
|
||||
pub fn builder() -> GeorgeEmuBuilder<'a> {
|
||||
GeorgeEmuBuilder::new()
|
||||
}
|
||||
|
||||
fn new(
|
||||
mut cpu: Cpu,
|
||||
renderer: Renderer,
|
||||
input: Option<Keyboard>,
|
||||
window: Option<&'a mut Window>,
|
||||
) -> Self {
|
||||
cpu.reset();
|
||||
Self {
|
||||
cpu,
|
||||
input,
|
||||
renderer,
|
||||
window,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
{
|
||||
let _ = stdout().into_raw_mode();
|
||||
print!("{}{}", cursor::Hide, clear::All,);
|
||||
}
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
let mut stdin = async_stdin();
|
||||
let clock_interval = Duration::from_nanos(500);
|
||||
let frame_interval = Duration::from_millis(16);
|
||||
let mut next_clock = Instant::now() + clock_interval;
|
||||
let mut next_frame = Instant::now() + frame_interval;
|
||||
|
||||
loop {
|
||||
let now = Instant::now();
|
||||
|
||||
if now >= next_clock {
|
||||
self.cpu.cycle();
|
||||
next_clock += clock_interval;
|
||||
}
|
||||
|
||||
if now >= next_frame {
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
self.handle_input(&mut stdin);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.draw();
|
||||
next_frame += frame_interval;
|
||||
}
|
||||
|
||||
let next_run = std::cmp::min(next_clock, next_frame);
|
||||
let sleep_dur = next_run - now;
|
||||
sleep(sleep_dur)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn draw(&mut self) {
|
||||
#[cfg(feature = "term")]
|
||||
self.renderer.render();
|
||||
#[cfg(not(feature = "term"))]
|
||||
match self.window {
|
||||
Some(ref mut window) => self.renderer.render(window),
|
||||
None => todo!(),
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn draw(&self, window: &mut Window) {
|
||||
#[cfg(feature = "term")]
|
||||
self.renderer.render();
|
||||
#[cfg(not(feature = "term"))]
|
||||
self.renderer.render(window);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "term", feature = "debug"))]
|
||||
fn handle_input(&mut self, stdin: &mut AsyncReader) {
|
||||
match &self.input {
|
||||
Some(input) => {
|
||||
let mut stdin = stdin.keys();
|
||||
if let Some(Ok(key)) = stdin.next() {
|
||||
match key {
|
||||
Key::Char('q') => {
|
||||
print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1));
|
||||
exit(0);
|
||||
}
|
||||
Key::Char('`') => self.cpu.toggle_stopped(),
|
||||
Key::Char('\n') => self.cpu.cycle(),
|
||||
Key::Char('i') => self.cpu.interrupt(),
|
||||
_ => {
|
||||
#[cfg(feature = "term")]
|
||||
input.read_keys(key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#[cfg(feature = "term")]
|
||||
input.clear_keys();
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle(&mut self) {
|
||||
self.cpu.cycle()
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
mod cli;
|
||||
mod cpu;
|
||||
mod error;
|
||||
mod instructions;
|
||||
mod keyboard;
|
||||
mod memory;
|
||||
mod types;
|
||||
mod video;
|
||||
|
||||
use crate::cpu::Cpu;
|
||||
use crate::keyboard::Keyboard;
|
||||
use crate::memory::Mem;
|
||||
use crate::video::Screen;
|
||||
|
||||
use cli::get_input;
|
||||
use crossterm::cursor;
|
||||
use memory::MemHandle;
|
||||
use std::io::stdout;
|
||||
use std::thread::{self, sleep};
|
||||
use std::time::Duration;
|
||||
use termion::cursor::Goto;
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::{async_stdin, clear};
|
||||
|
||||
fn main() {
|
||||
let _stdout = stdout().into_raw_mode();
|
||||
let config = get_input();
|
||||
|
||||
let mut memory = Mem::new();
|
||||
let _ = memory.load_rom(&config.rom);
|
||||
|
||||
let shared_memory = MemHandle::new(memory);
|
||||
let screen_memory = shared_memory.clone();
|
||||
let cpu_memory = shared_memory.clone();
|
||||
let keyboard_memory = shared_memory.clone();
|
||||
|
||||
let keyboard = Keyboard::new(keyboard_memory);
|
||||
|
||||
let (mut cpu, cpu_controller) = Cpu::new_with_control(cpu_memory);
|
||||
cpu.reset();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
cpu.cycle();
|
||||
});
|
||||
|
||||
let screen_remote = cpu_controller.clone();
|
||||
|
||||
let mut screen = Screen::new(&config, screen_remote, screen_memory);
|
||||
|
||||
let mut stdin = async_stdin().keys();
|
||||
print!("{}{}", cursor::Hide, clear::All,);
|
||||
|
||||
loop {
|
||||
screen.draw();
|
||||
if let Some(Ok(key)) = stdin.next() {
|
||||
keyboard.read_keys(key);
|
||||
match key {
|
||||
Key::Char('q') => {
|
||||
break;
|
||||
}
|
||||
Key::Char('`') => cpu_controller.toggle(),
|
||||
Key::Char('\n') => cpu_controller.cycle(),
|
||||
Key::Char('i') => cpu_controller.irq(),
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
keyboard.clear_keys();
|
||||
}
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1));
|
||||
}
|
||||
+37
-24
@@ -1,10 +1,7 @@
|
||||
use anyhow::{bail, Result};
|
||||
use core::panic;
|
||||
use std::cell::RefCell;
|
||||
use std::io::{self, Write};
|
||||
use std::panic;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fs::File, io::Read};
|
||||
@@ -16,11 +13,11 @@ impl MemHandle {
|
||||
Self(Arc::new(Mutex::new(memory)))
|
||||
}
|
||||
pub fn read(&self, address: u16) -> u8 {
|
||||
let memory = self.0.lock().unwrap();
|
||||
let memory = self.0.try_lock().unwrap();
|
||||
memory.read(address)
|
||||
}
|
||||
pub fn write(&self, address: u16, data: u8) {
|
||||
let mut memory = self.0.lock().unwrap();
|
||||
let mut memory = self.0.try_lock().unwrap();
|
||||
memory.write(address, data);
|
||||
}
|
||||
|
||||
@@ -49,15 +46,25 @@ pub trait MemoryWriter {
|
||||
// 0x10000 elements
|
||||
pub struct Mem([u8; 0x10000]);
|
||||
|
||||
impl Default for Mem {
|
||||
fn default() -> Self {
|
||||
Self([0; 0x10000])
|
||||
}
|
||||
}
|
||||
// impl Default for Mem {
|
||||
// fn default() -> Self {
|
||||
// let bytes = include_bytes!("/Users/august/projects/george-emu/src/roms/george.rom");
|
||||
// let padding = [0; 0x8000];
|
||||
// let rom: [u8; 0x10000] = {
|
||||
// let mut rom: [u8; 0x10000] = [0; 0x10000];
|
||||
// let (one, two) = rom.split_at_mut(padding.len());
|
||||
// one.copy_from_slice(&padding);
|
||||
// two.copy_from_slice(bytes);
|
||||
// rom
|
||||
// };
|
||||
|
||||
// Self(rom)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Mem {
|
||||
pub fn new() -> Self {
|
||||
Mem::default()
|
||||
Self([0; 0x10000])
|
||||
}
|
||||
pub fn dump(&self, path: PathBuf) -> io::Result<()> {
|
||||
let mut outfile = File::create(path)?;
|
||||
@@ -97,16 +104,22 @@ impl Mem {
|
||||
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 u16, value),
|
||||
// Err(_) => {
|
||||
// bail!("couldn't write byte {:#04x}", address);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
//}
|
||||
pub fn load_bytes(&mut self, bytes: &[u8]) {
|
||||
for (address, byte) in bytes.into_iter().enumerate() {
|
||||
self.write(address as u16 + 0x8000, *byte)
|
||||
}
|
||||
}
|
||||
|
||||
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 u16 + 0x8000, value),
|
||||
Err(_) => {
|
||||
bail!("couldn't write byte {:#04x}", address);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
; .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
|
||||
kb_row_cache = $203 ; cache
|
||||
|
||||
.org $8000
|
||||
|
||||
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:
|
||||
jsr draw
|
||||
jmp main
|
||||
|
||||
draw:
|
||||
char = $200
|
||||
x = $201
|
||||
y = $202
|
||||
.y_loop:
|
||||
.x_loop:
|
||||
inc char
|
||||
lda x
|
||||
push
|
||||
sta 0, x
|
||||
stz 1, x
|
||||
lda y
|
||||
push
|
||||
sta 0, x
|
||||
sta 1, x
|
||||
lda char
|
||||
push
|
||||
sta 0, x
|
||||
sta 1, x
|
||||
jsr draw_char
|
||||
inc x
|
||||
lda #64
|
||||
cmp x
|
||||
bne .x_loop
|
||||
inc y
|
||||
lda #30
|
||||
cmp y
|
||||
bne .y_loop
|
||||
.end:
|
||||
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 keyboard
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
|
||||
.include "math.inc"
|
||||
|
||||
.org $fffc
|
||||
.word reset
|
||||
.word isr
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
use std::{u16, u8};
|
||||
|
||||
pub type Byte = u8;
|
||||
pub type Word = u16;
|
||||
+49
-116
@@ -1,81 +1,68 @@
|
||||
use minifb::{Scale, ScaleMode, Window, WindowOptions};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "term")]
|
||||
use termion::{
|
||||
color::{self, Bg, Color, Fg},
|
||||
color::{self, Bg, Fg},
|
||||
cursor::Goto,
|
||||
screen::IntoAlternateScreen,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cli::Config,
|
||||
cpu::CpuController,
|
||||
keyboard::Keyboard,
|
||||
memory::{MemHandle, MemoryReader},
|
||||
types::{Byte, Word},
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Read, Write},
|
||||
path::Path,
|
||||
process::exit,
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
const FG_COLOR: u32 = 0xFFCC00;
|
||||
const BG_COLOR: u32 = 0x110500;
|
||||
// const FG_COLOR: u32 = 0xFFCC00;
|
||||
// const BG_COLOR: u32 = 0x110500;
|
||||
const FG_COLOR: u32 = 0xFF00CCFF;
|
||||
const BG_COLOR: u32 = 0xFF000511;
|
||||
const WIDTH: usize = 512;
|
||||
const HEIGHT: usize = 380;
|
||||
|
||||
pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
match char_rom {
|
||||
Some(path) => {
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut bin = vec![0; 0x8000];
|
||||
file.read_exact(&mut bin).unwrap();
|
||||
// println!("reading char rom");
|
||||
bin.try_into().unwrap()
|
||||
}
|
||||
None => *include_bytes!("./roms/cozette.rom"),
|
||||
}
|
||||
}
|
||||
// pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
|
||||
// where
|
||||
// P: AsRef<Path>,
|
||||
// {
|
||||
// match char_rom {
|
||||
// Some(path) => {
|
||||
// let mut file = File::open(path).unwrap();
|
||||
// let mut bin = vec![0; 0x8000];
|
||||
// file.read_exact(&mut bin).unwrap();
|
||||
// // println!("reading char rom");
|
||||
// bin.try_into().unwrap()
|
||||
// }
|
||||
// None => *include_bytes!("./roms/cozette.rom"),
|
||||
// }
|
||||
// }
|
||||
|
||||
trait Renderer {
|
||||
fn render(&mut self) {}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub enum ScreenType {
|
||||
Window,
|
||||
#[default]
|
||||
Terminal,
|
||||
}
|
||||
|
||||
pub struct WindowRenderer {
|
||||
#[cfg(not(feature = "term"))]
|
||||
#[derive(Debug)]
|
||||
pub struct Renderer {
|
||||
char_rom: [u8; 0x8000],
|
||||
window: Window,
|
||||
memory: MemHandle,
|
||||
controller: CpuController,
|
||||
}
|
||||
|
||||
impl WindowRenderer {
|
||||
pub fn new<P>(memory: MemHandle, char_rom: Option<P>, window: Window) -> Self
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let char_rom = get_char_bin(char_rom);
|
||||
#[cfg(not(feature = "term"))]
|
||||
impl Renderer {
|
||||
// pub fn new<P>(controller: CpuController, memory: MemHandle, char_rom: Option<P>) -> Self
|
||||
// where
|
||||
// P: AsRef<Path>,
|
||||
pub fn new(controller: CpuController, memory: MemHandle) -> Self {
|
||||
// let char_rom = get_char_bin(char_rom);
|
||||
let char_rom = *include_bytes!("./roms/cozette.rom");
|
||||
Self {
|
||||
memory,
|
||||
window,
|
||||
char_rom,
|
||||
controller,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for WindowRenderer {
|
||||
fn render(&mut self) {
|
||||
pub fn render(&self, window: &mut Window) {
|
||||
// the rest of this function is arcane wizardry
|
||||
// based on the specifics of george's weird
|
||||
// display and characters... don't fuck around w it
|
||||
@@ -99,34 +86,24 @@ impl Renderer for WindowRenderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.window
|
||||
.update_with_buffer(&buffer, WIDTH, HEIGHT)
|
||||
.unwrap();
|
||||
self.controller.irq();
|
||||
window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryReader for WindowRenderer {
|
||||
fn read(&self, address: Word) -> Byte {
|
||||
impl MemoryReader for Renderer {
|
||||
fn read(&self, address: u16) -> u8 {
|
||||
self.memory.read(address)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TerminalRenderer {
|
||||
#[cfg(feature = "term")]
|
||||
pub struct Renderer {
|
||||
memory: MemHandle,
|
||||
controller: CpuController,
|
||||
}
|
||||
|
||||
impl TerminalRenderer {
|
||||
pub fn new(memory: MemHandle) -> Self {
|
||||
Self { memory }
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryReader for TerminalRenderer {
|
||||
fn read(&self, address: Word) -> Byte {
|
||||
self.memory.read(address)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "term")]
|
||||
const ASCII_LOOPUP: [&str; 256] = [
|
||||
" ", "░", "▒", "▓", "♡", "♥", "⭐", "✭", "", "✦", "✨", "♀", "♂", "⚢", "⚣", "⚥", "♩", "♪",
|
||||
"♫", "♬", "ﱝ", "", "", "", "奄", "奔", "婢", "ﱜ", "ﱛ", "", "", "", " ", "!", "\"", "#",
|
||||
@@ -144,8 +121,12 @@ const ASCII_LOOPUP: [&str; 256] = [
|
||||
"", "", "", "", "🎁", "", "", "", "", "⚐", "⚑", "", "", "",
|
||||
];
|
||||
|
||||
impl Renderer for TerminalRenderer {
|
||||
fn render(&mut self) {
|
||||
#[cfg(feature = "term")]
|
||||
impl Renderer {
|
||||
pub fn new(controller: CpuController, memory: MemHandle) -> Self {
|
||||
Self { controller, memory }
|
||||
}
|
||||
pub fn render(&self) {
|
||||
let mut stdout = io::stdout();
|
||||
let mut i = 0;
|
||||
for char_row in 0..29 {
|
||||
@@ -164,54 +145,6 @@ impl Renderer for TerminalRenderer {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Screen {
|
||||
renderer: Box<dyn Renderer>,
|
||||
controller: CpuController,
|
||||
}
|
||||
|
||||
impl Screen {
|
||||
pub fn new(config: &Config, controller: CpuController, memory: MemHandle) -> Self {
|
||||
let renderer: Box<dyn Renderer> = match config.screen {
|
||||
ScreenType::Window => {
|
||||
let mut window = Window::new(
|
||||
"ʕ·ᴥ·ʔ-☆",
|
||||
512,
|
||||
380,
|
||||
WindowOptions {
|
||||
resize: true,
|
||||
borderless: true,
|
||||
title: true,
|
||||
transparency: false,
|
||||
scale: Scale::FitScreen,
|
||||
scale_mode: ScaleMode::AspectRatioStretch,
|
||||
topmost: false,
|
||||
none: true,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
window.set_input_callback(Box::new(Keyboard::new(memory.clone())));
|
||||
Box::new(WindowRenderer::new(memory, config.char_rom.clone(), window))
|
||||
}
|
||||
ScreenType::Terminal => Box::new(TerminalRenderer::new(memory)),
|
||||
};
|
||||
|
||||
Self {
|
||||
renderer,
|
||||
controller,
|
||||
}
|
||||
}
|
||||
pub fn draw(&mut self) {
|
||||
self.controller.irq();
|
||||
self.renderer.render();
|
||||
}
|
||||
|
||||
// pub fn run(&mut self) {
|
||||
// loop {
|
||||
// // sleep(Duration::from_millis(16));
|
||||
// self.draw();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user