we can type the letter w!
This commit is contained in:
parent
9808616203
commit
40ede17ae1
File diff suppressed because it is too large
Load Diff
|
@ -8,5 +8,4 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdf = "0.6.0"
|
bdf = "0.6.0"
|
||||||
bitvec = "1.0.1"
|
bitvec = "1.0.1"
|
||||||
iced = { version = "0.12.0", features = ["canvas", "smol"] }
|
|
||||||
minifb = "0.25.0"
|
minifb = "0.25.0"
|
||||||
|
|
70
src/cpu.rs
70
src/cpu.rs
|
@ -1,8 +1,9 @@
|
||||||
use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
|
use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
|
||||||
use crate::instructions::{get_instruction, Instruction};
|
use crate::instructions::{get_instruction, Instruction};
|
||||||
use crate::memory::{self, Mem};
|
use crate::memory::Mem;
|
||||||
use crate::types::{Byte, Word};
|
use crate::types::{Byte, Word};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
@ -31,13 +32,13 @@ pub struct Cpu {
|
||||||
pub p: Byte, // Status Register
|
pub p: Byte, // Status Register
|
||||||
pub irq: bool,
|
pub irq: bool,
|
||||||
pub nmi: bool,
|
pub nmi: bool,
|
||||||
pub memory: Arc<RwLock<Mem>>,
|
pub memory: Arc<Mutex<Mem>>,
|
||||||
pub pending_cycles: usize,
|
pub pending_cycles: usize,
|
||||||
cycle_count: usize,
|
cycle_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
pub fn new(memory: Arc<RwLock<Mem>>) -> Self {
|
pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
|
||||||
Cpu {
|
Cpu {
|
||||||
a: 0x00,
|
a: 0x00,
|
||||||
x: 0x00,
|
x: 0x00,
|
||||||
|
@ -59,7 +60,7 @@ impl Cpu {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
|
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
|
||||||
let memory = match self.memory.try_read() {
|
let memory = match self.memory.lock() {
|
||||||
Ok(read) => read,
|
Ok(read) => read,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't acquire read lock on memory in cpu thread");
|
println!("Couldn't acquire read lock on memory in cpu thread");
|
||||||
|
@ -121,7 +122,7 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
|
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
|
||||||
let mut memory = match self.memory.write() {
|
let mut memory = match self.memory.lock() {
|
||||||
Ok(write) => write,
|
Ok(write) => write,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't acquire write lock on memory in cpu thread");
|
println!("Couldn't acquire write lock on memory in cpu thread");
|
||||||
|
@ -133,9 +134,9 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&mut self) {
|
pub fn execute(&mut self) {
|
||||||
self.cycle().unwrap();
|
self.cycle();
|
||||||
while self.pending_cycles != 0 {
|
while self.pending_cycles != 0 {
|
||||||
self.cycle().unwrap();
|
self.cycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +167,7 @@ impl Cpu {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
pub fn cycle(&mut self) -> Result<(), GeorgeError> {
|
pub fn cycle(&mut self) {
|
||||||
sleep(Duration::from_nanos(100));
|
sleep(Duration::from_nanos(100));
|
||||||
if self.pending_cycles == 0 {
|
if self.pending_cycles == 0 {
|
||||||
if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) {
|
if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) {
|
||||||
|
@ -178,10 +179,11 @@ impl Cpu {
|
||||||
match self.read_word(0xFFFA) {
|
match self.read_word(0xFFFA) {
|
||||||
Ok(word) => self.pc = word,
|
Ok(word) => self.pc = word,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(GeorgeError {
|
let george_error = GeorgeError {
|
||||||
kind: GeorgeErrorKind::Memory(error),
|
kind: GeorgeErrorKind::Memory(error),
|
||||||
desc: String::from_str("Couldn't read NMI vector").unwrap(),
|
desc: String::from_str("Couldn't read NMI vector").unwrap(),
|
||||||
})
|
};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.pending_cycles = 8;
|
self.pending_cycles = 8;
|
||||||
|
@ -189,10 +191,11 @@ impl Cpu {
|
||||||
match self.read_word(0xFFFE) {
|
match self.read_word(0xFFFE) {
|
||||||
Ok(word) => self.pc = word,
|
Ok(word) => self.pc = word,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(GeorgeError {
|
let george_error = GeorgeError {
|
||||||
kind: GeorgeErrorKind::Memory(error),
|
kind: GeorgeErrorKind::Memory(error),
|
||||||
desc: String::from_str("Couldn't read IRQ vector").unwrap(),
|
desc: String::from_str("Couldn't read IRQ vector").unwrap(),
|
||||||
})
|
};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.pending_cycles = 7
|
self.pending_cycles = 7
|
||||||
|
@ -204,62 +207,69 @@ impl Cpu {
|
||||||
let opcode = match self.read(self.pc) {
|
let opcode = match self.read(self.pc) {
|
||||||
Ok(byte) => byte,
|
Ok(byte) => byte,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(GeorgeError {
|
let george_error = GeorgeError {
|
||||||
desc: String::from_str("Failed to read the program counter!").unwrap(),
|
desc: format!("Failed to read from memory at address {:#06x}!", self.pc),
|
||||||
kind: GeorgeErrorKind::Memory(error),
|
kind: GeorgeErrorKind::Memory(error),
|
||||||
})
|
};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let instruction = get_instruction(opcode);
|
let instruction = get_instruction(opcode);
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Valid(valid_instruction) => {
|
Instruction::Valid(valid_instruction) => {
|
||||||
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!("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!(
|
//println!(
|
||||||
"Instruction: {:?}, {:#04x}",
|
// "Instruction: {:?}, {:#04x}",
|
||||||
valid_instruction.opcode, opcode
|
// valid_instruction.opcode, opcode
|
||||||
);
|
//);
|
||||||
println!("");
|
//println!("");
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
match valid_instruction.opcode.call(self) {
|
match valid_instruction.opcode.call(self) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.pending_cycles += valid_instruction.cycles as usize;
|
self.pending_cycles += valid_instruction.cycles as usize;
|
||||||
},
|
}
|
||||||
Err(error) => return Err(GeorgeError{
|
Err(error) => {
|
||||||
|
let george_error = GeorgeError{
|
||||||
desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}",
|
desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}",
|
||||||
self.pc, valid_instruction).to_string(), kind: error})
|
self.pc, valid_instruction).to_string(), kind: error};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
|
Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
|
||||||
0x02 => {
|
0x02 => {
|
||||||
let memory = match self.memory.try_read() {
|
let memory = match self.memory.lock() {
|
||||||
Ok(read) => read,
|
Ok(read) => read,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't acquire read lock on memory in cpu thread");
|
println!("Couldn't acquire read lock on memory in cpu thread");
|
||||||
return Err(GeorgeError {
|
let george_error = GeorgeError {
|
||||||
kind: GeorgeErrorKind::Memory(MemoryError::Unwritable),
|
kind: GeorgeErrorKind::Memory(MemoryError::Unwritable),
|
||||||
desc: "Couldn't acquire read lock on memory in cpu thread"
|
desc: "Couldn't acquire read lock on memory in cpu thread"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
});
|
};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
memory.dump().unwrap();
|
memory.dump().unwrap();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(GeorgeError {
|
let george_error = GeorgeError {
|
||||||
kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction),
|
kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction),
|
||||||
desc: format!(
|
desc: format!(
|
||||||
"An invalid instruction with opcode {:#04x} was called at address {:#06x}",
|
"An invalid instruction with opcode {:#04x} was called at address {:#06x}",
|
||||||
invalid_instruction.opcode, self.pc
|
invalid_instruction.opcode, self.pc
|
||||||
),
|
),
|
||||||
});
|
};
|
||||||
|
println!("{:?}", george_error.desc);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.pending_cycles -= 1;
|
self.pending_cycles -= 1;
|
||||||
self.cycle_count += 1;
|
self.cycle_count += 1;
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
|
|
BIN
src/george
BIN
src/george
Binary file not shown.
|
@ -8,7 +8,7 @@
|
||||||
.byte $02
|
.byte $02
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
.macro pop ; drops a stack cell
|
.macro pop ; drops a data stack cell
|
||||||
inx
|
inx
|
||||||
inx
|
inx
|
||||||
.endmacro
|
.endmacro
|
||||||
|
@ -20,18 +20,36 @@
|
||||||
inx
|
inx
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
.macro push ;
|
.macro push ; push a data stack cell
|
||||||
dex
|
dex
|
||||||
dex
|
dex
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
.macro push2 ;
|
.macro push2 ; push 2 data stack cells
|
||||||
dex
|
dex
|
||||||
dex
|
dex
|
||||||
dex
|
dex
|
||||||
dex
|
dex
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
|
.macro push_char char ; pushes an ascii character code onto the stack
|
||||||
|
lda char
|
||||||
|
push
|
||||||
|
sta 0, x ; char low byte
|
||||||
|
stz 1, x ; char high byte
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro push_coords coord_x, coord_y ; push a set of (x,y) coordinates onto the data stack
|
||||||
|
lda coord_x
|
||||||
|
push
|
||||||
|
sta 0, x ; low byte
|
||||||
|
stz 1,x ; high byte is zero
|
||||||
|
lda coord_y
|
||||||
|
push
|
||||||
|
sta 0,x ; same here
|
||||||
|
stz 1,x
|
||||||
|
.endmacro
|
||||||
|
|
||||||
.macro to_r ; pop the top of the stack off and save it in the return (hardware) stack: (n -- )
|
.macro to_r ; pop the top of the stack off and save it in the return (hardware) stack: (n -- )
|
||||||
lda 1, x
|
lda 1, x
|
||||||
pha
|
pha
|
||||||
|
@ -54,6 +72,7 @@ init:
|
||||||
|
|
||||||
main:
|
main:
|
||||||
jsr draw
|
jsr draw
|
||||||
|
jmp main
|
||||||
|
|
||||||
initdisplay:
|
initdisplay:
|
||||||
lda #$20
|
lda #$20
|
||||||
|
@ -74,25 +93,29 @@ cleardisplay:
|
||||||
bne cleardisplay
|
bne cleardisplay
|
||||||
rts
|
rts
|
||||||
|
|
||||||
draw: ; draw a character at (20, 30)
|
draw:
|
||||||
lda #63
|
lda #%01000000
|
||||||
push
|
bit $4400
|
||||||
sta 0, x ; low byte
|
beq @1
|
||||||
stz 1,x ; high byte is zero
|
push_coords #0, #0
|
||||||
lda #28
|
push_char #$77
|
||||||
push
|
jsr draw_char
|
||||||
sta 0,x ; same here
|
@1:
|
||||||
stz 1,x
|
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
|
jsr get_char_address ; calculate where to put the character in memory
|
||||||
lda #4
|
|
||||||
sta (0, x) ; store a at the address pointed to on the stack
|
sta (0, x) ; store a at the address pointed to on the stack
|
||||||
brk
|
rts
|
||||||
|
|
||||||
get_char_address: ; gets vram address for a character at (x, y),
|
get_char_address: ; gets vram address for a character at (x, y),
|
||||||
; (n1: x n2: y -- n: $6000 + x + (64 * y))
|
; (n1: x n2: y -- n: $6000 + x + (64 * y))
|
||||||
;jsr push_lit ; push 64 onto stack, low byte first
|
;jsr push_lit ; push 64 onto stack, low byte first
|
||||||
;.byte 64
|
;.byte 64
|
||||||
;.byte 0
|
;.byte 0
|
||||||
|
pha
|
||||||
lda #64
|
lda #64
|
||||||
push ; doing this instead until `push_lit` is fixed
|
push ; doing this instead until `push_lit` is fixed
|
||||||
sta 0, x
|
sta 0, x
|
||||||
|
@ -109,18 +132,14 @@ get_char_address: ; gets vram address for a character at (x, y),
|
||||||
stz 0, x
|
stz 0, x
|
||||||
jsr plus ; add vram start address to result
|
jsr plus ; add vram start address to result
|
||||||
|
|
||||||
|
pla
|
||||||
rts
|
rts
|
||||||
|
|
||||||
|
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
|
||||||
|
|
||||||
; inc16
|
jsr get_char_address
|
||||||
; add 1 to a 16-bit pointer in zero page
|
|
||||||
|
|
||||||
;inc16:
|
|
||||||
; inc ptr
|
|
||||||
; bne :+
|
|
||||||
; inc ptr+1
|
|
||||||
;: rts
|
|
||||||
;
|
|
||||||
|
|
||||||
|
; --- Data Stack --- ;
|
||||||
; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM
|
; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM
|
||||||
; data stack is built up of 2-byte cells
|
; data stack is built up of 2-byte cells
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod error;
|
mod error;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
|
mod keyboard;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod types;
|
mod types;
|
||||||
mod video;
|
mod video;
|
||||||
|
@ -11,6 +12,7 @@ use crate::cpu::Cpu;
|
||||||
use crate::memory::{Mem, MemMappedDevice};
|
use crate::memory::{Mem, MemMappedDevice};
|
||||||
use crate::video::Crtc;
|
use crate::video::Crtc;
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
|
@ -44,17 +46,16 @@ fn main() {
|
||||||
memory.write(0xFFFC, 0x00).unwrap();
|
memory.write(0xFFFC, 0x00).unwrap();
|
||||||
memory.write(0xFFFD, 0x02).unwrap();
|
memory.write(0xFFFD, 0x02).unwrap();
|
||||||
|
|
||||||
let shared_memory = Arc::new(RwLock::new(memory));
|
let shared_memory = Arc::new(Mutex::new(memory));
|
||||||
let cpu_memory = shared_memory.clone();
|
let cpu_memory = shared_memory.clone();
|
||||||
let display_memory = shared_memory.clone();
|
let display_memory = shared_memory.clone();
|
||||||
|
|
||||||
let mut screen = Crtc::new(display_memory);
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut cpu = Cpu::new(cpu_memory);
|
let mut cpu = Cpu::new(cpu_memory);
|
||||||
cpu.reset().unwrap();
|
cpu.reset().unwrap();
|
||||||
cpu.execute();
|
cpu.execute();
|
||||||
});
|
});
|
||||||
|
let mut screen = Crtc::new(display_memory);
|
||||||
screen.run();
|
screen.run();
|
||||||
shared_memory.write().unwrap().dump().unwrap();
|
shared_memory.lock().unwrap().dump().unwrap();
|
||||||
}
|
}
|
||||||
|
|
173
src/video.rs
173
src/video.rs
|
@ -3,7 +3,7 @@ use minifb::{Key, Scale, ScaleMode, Window, WindowOptions};
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Read,
|
io::Read,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, Mutex, RwLock},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ const FG_COLOR: u32 = 0xFFCC00;
|
||||||
const BG_COLOR: u32 = 0x110500;
|
const BG_COLOR: u32 = 0x110500;
|
||||||
|
|
||||||
pub struct Crtc {
|
pub struct Crtc {
|
||||||
memory: Arc<RwLock<Mem>>,
|
memory: Arc<Mutex<Mem>>,
|
||||||
buffer: Vec<u32>,
|
buffer: Vec<u32>,
|
||||||
window: minifb::Window,
|
window: minifb::Window,
|
||||||
char_rom: Vec<u8>,
|
char_rom: Vec<u8>,
|
||||||
|
@ -26,7 +26,7 @@ pub fn get_char_bin(path: &str) -> Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Crtc {
|
impl Crtc {
|
||||||
pub fn new(memory: Arc<RwLock<Mem>>) -> Self {
|
pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
|
||||||
let window = Window::new(
|
let window = Window::new(
|
||||||
"ʕ·ᴥ·ʔ-☆",
|
"ʕ·ᴥ·ʔ-☆",
|
||||||
512,
|
512,
|
||||||
|
@ -52,82 +52,127 @@ impl Crtc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn draw(&mut self) {
|
fn draw(&mut self) {
|
||||||
match &mut self.memory.clone().try_read() {
|
let memory = match self.memory.lock() {
|
||||||
Ok(memory) => {
|
Ok(memory) => memory,
|
||||||
let hw_ctrl = memory.read(0x4000).unwrap();
|
|
||||||
match hw_ctrl & 0b0000_1000 == 0b0000_1000 {
|
|
||||||
false => self.draw_chars(),
|
|
||||||
true => self.draw_hires(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't acquire read on shared memory in main thread");
|
println!("Couldn't acquire read on shared memory in video thread");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let hw_ctrl = memory.read(0x4000).unwrap();
|
||||||
|
match hw_ctrl & 0b0000_1000 == 0b0000_1000 {
|
||||||
|
false => {
|
||||||
|
// 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
|
||||||
|
let mut i = 0;
|
||||||
|
for char_row in 0..29 {
|
||||||
|
for char_col in 0..64 {
|
||||||
|
let ascii = memory.read(0x6000 + i).unwrap();
|
||||||
|
i += 1;
|
||||||
|
for row in 0..13 {
|
||||||
|
let byte = self.char_rom[ascii as usize + (row * 0x101)];
|
||||||
|
for i in (0..8).rev() {
|
||||||
|
let buffer_index =
|
||||||
|
((char_row) * 13 + (row)) * 512 + (char_col * 8 + i);
|
||||||
|
if (byte << i) & 0x80 == 0x80 {
|
||||||
|
self.buffer[buffer_index] = FG_COLOR;
|
||||||
|
} else {
|
||||||
|
self.buffer[buffer_index] = BG_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true => {
|
||||||
|
for addr in 0x6000..0xBF00 {
|
||||||
|
// TODO: eventually this will access memory in the weird interleaved way the hardware is
|
||||||
|
// designed, but this is easiest to implement for now
|
||||||
|
let byte = memory.read(addr).unwrap();
|
||||||
|
for i in 0..8 {
|
||||||
|
match byte & 0x80 >> i == 0 {
|
||||||
|
true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR,
|
||||||
|
false => self.buffer[(addr - 0x6000) as usize * 8 + i] = FG_COLOR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.window
|
self.window
|
||||||
.update_with_buffer(&self.buffer, 512, 380)
|
.update_with_buffer(&self.buffer, 512, 380)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_chars(&mut self) {
|
|
||||||
let memory = match self.memory.try_read() {
|
|
||||||
Ok(read) => read,
|
|
||||||
Err(_) => {
|
|
||||||
println!("Couldn't acquire read on shared memory in main thread");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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
|
|
||||||
let mut i = 0;
|
|
||||||
for char_row in 0..29 {
|
|
||||||
for char_col in 0..64 {
|
|
||||||
let ascii = memory.read(0x6000 + i).unwrap();
|
|
||||||
i += 1;
|
|
||||||
for row in 0..13 {
|
|
||||||
let byte = self.char_rom[ascii as usize + (row * 0x101)];
|
|
||||||
for i in (0..8).rev() {
|
|
||||||
let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i);
|
|
||||||
if (byte << i) & 0x80 == 0x80 {
|
|
||||||
self.buffer[buffer_index] = FG_COLOR;
|
|
||||||
} else {
|
|
||||||
self.buffer[buffer_index] = BG_COLOR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn draw_hires(&mut self) {
|
|
||||||
let memory = match self.memory.try_read() {
|
|
||||||
Ok(read) => read,
|
|
||||||
Err(_) => {
|
|
||||||
println!("Couldn't acquire read on shared memory in main thread");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for addr in 0x6000..0xBF00 {
|
|
||||||
// TODO: eventually this will access memory in the weird interleaved way the hardware is
|
|
||||||
// designed, but this is easiest to implement for now
|
|
||||||
let byte = memory.read(addr).unwrap();
|
|
||||||
for i in 0..8 {
|
|
||||||
match byte & 0x80 >> i == 0 {
|
|
||||||
true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR,
|
|
||||||
false => self.buffer[(addr - 0x6000) as usize * 8 + i] = FG_COLOR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let frame_duration = Duration::from_millis(16);
|
let frame_duration = Duration::from_millis(16);
|
||||||
let mut previous_draw = Instant::now();
|
let mut previous_draw = Instant::now();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if self.window.is_key_down(Key::Q) {
|
let mut row0 = 0;
|
||||||
return;
|
let mut row1 = 0;
|
||||||
|
let mut row2 = 0;
|
||||||
|
let mut row3 = 0;
|
||||||
|
let mut row4 = 0;
|
||||||
|
let mut row5 = 0;
|
||||||
|
|
||||||
|
self.window.get_keys().iter().for_each(|key| 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 &mut self.memory.lock() {
|
||||||
|
Ok(memory) => {
|
||||||
|
memory.write(0x4400, row0).unwrap();
|
||||||
|
memory.write(0x4401, row1).unwrap();
|
||||||
|
memory.write(0x4402, row2).unwrap();
|
||||||
|
memory.write(0x4403, row3).unwrap();
|
||||||
|
memory.write(0x4404, row4).unwrap();
|
||||||
|
memory.write(0x4405, row5).unwrap();
|
||||||
|
}
|
||||||
|
Err(error) => println!("{error}"),
|
||||||
}
|
}
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now - previous_draw > frame_duration {
|
if now - previous_draw > frame_duration {
|
||||||
|
|
Loading…
Reference in New Issue