Split code into modules, add font stuff

This commit is contained in:
august kline 2024-02-10 00:01:32 -05:00
parent ef438f48d2
commit 78dad90fc9
13 changed files with 32737 additions and 3963 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target
log
george.o

25585
src/Cozette.sfd Normal file

File diff suppressed because it is too large Load Diff

2943
src/cozette.bdf Normal file

File diff suppressed because it is too large Load Diff

247
src/cpu.rs Normal file
View File

@ -0,0 +1,247 @@
use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
use crate::instructions::{get_instruction, Instruction};
use crate::memory::Mem;
use crate::types::{Byte, Word};
use std::time::Duration;
use std::{
str::FromStr,
sync::{Arc, RwLock},
thread::sleep,
};
#[derive(Clone, Copy)]
pub enum StatusFlag {
Negative = 0b1000_0000,
Overflow = 0b0100_0000,
Brk = 0b0011_0000,
BrkIrq = 0b0010_0000,
Decimal = 0b0000_1000,
IrqDisable = 0b0000_0100,
Zero = 0b000_0010,
Carry = 0b0000_0001,
}
pub struct Cpu {
pub a: Byte, // Accumulator Register
pub x: Byte, // X Register
pub y: Byte, // Y Register
pub pc: Word, // Program Counter
pub s: Byte, // Stack Pointer
pub p: Byte, // Status Register
pub irq: bool,
pub nmi: bool,
pub memory: Arc<RwLock<Mem>>,
pub pending_cycles: usize,
cycle_count: usize,
}
impl Cpu {
pub fn new(memory: Arc<RwLock<Mem>>) -> Self {
Cpu {
a: 0x00,
x: 0x00,
y: 0x00,
pc: 0x0000,
s: 0xFF,
p: 0b0010_0100,
irq: false,
nmi: false,
memory,
pending_cycles: 0,
cycle_count: 0,
}
}
pub fn reset(&mut self) -> Result<(), ExecutionError> {
let reset_vector_pointer = self.read_word(0xFFFC)?;
self.pending_cycles = 8;
self.pc = reset_vector_pointer;
Ok(())
}
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
let memory = match self.memory.try_read() {
Ok(read) => read,
Err(_) => {
println!("Couldn't acquire read lock on memory in cpu thread");
return Err(MemoryError::NoDataAtAddress);
} // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
};
memory.read(address)
}
pub fn read_word(&self, address: Word) -> Result<Word, MemoryError> {
let low_byte = self.read(address)?;
let high_byte = self.read(address + 0x1)?;
Ok((high_byte as u16) << 8 | (low_byte as u16))
}
fn stack_addr(&self) -> Word {
// Dunno if this is necessary, i just don't like adding the 0x0100 every time
0x0100 + self.s as u16
}
pub fn push_stack(&mut self, data: Byte) -> Result<(), ExecutionError> {
self.s -= 0x1;
self.write(self.stack_addr(), data)?;
Ok(())
}
pub fn push_stack_word(&mut self, address: Word) -> Result<(), ExecutionError> {
self.s -= 0x1;
self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first
self.s -= 0x1;
self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second
Ok(())
}
pub fn pop_stack(&mut self) -> Result<Byte, ExecutionError> {
let byte = self.read(self.stack_addr())?;
self.s += 0x1;
Ok(byte)
}
pub fn pop_stack_word(&mut self) -> Result<Word, ExecutionError> {
let low_byte = self.pop_stack()?;
let high_byte = self.pop_stack()?;
let word = ((high_byte as Word) << 8) + low_byte as u16;
Ok(word)
}
pub fn set_flag(&mut self, flag: StatusFlag, value: bool) {
self.p &= !(flag as Byte);
if value {
self.p |= flag as Byte
}
}
pub fn get_flag(&self, flag: StatusFlag) -> bool {
(self.p & flag as Byte) > 0
}
pub fn is_negative(&self, value: Byte) -> bool {
value & 0b1000_0000 == 0b1000_0000
}
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
let mut memory = match self.memory.write() {
Ok(write) => write,
Err(_) => {
println!("Couldn't acquire write lock on memory in cpu thread");
return Err(MemoryError::NoDataAtAddress);
} // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
};
memory.write(address, data)?;
Ok(())
}
pub fn execute(&mut self) {
self.cycle().unwrap();
while self.pending_cycles != 0 {
self.cycle().unwrap();
}
}
pub fn wait_for_interrupt(&self) {
unimplemented!()
}
// fn handle_interrupt(&mut self) -> Result<(), ExecutionError> {
// match self.get_flag(StatusFlag::IrqDisable) {
// // Check that interrupts aren't disabled
// true => Ok(self.execute()),
// false => {
// if self.irq {
// let irq_vector = 0xFFFE;
// match self.read_word(irq_vector) {
// Err(error) => Err(ExecutionError::MemoryError(error)),
// Ok(address) => {
// let isr_address = address;
// match self.read(isr_address) {
// Ok(_opcode) => Ok(self.execute()),
// Err(error) => Err(ExecutionError::MemoryError(error)),
// }
// }
// }
// } else {
// Ok(())
// }
// }
// }
// }
pub fn cycle(&mut self) -> Result<(), GeorgeError> {
sleep(Duration::from_nanos(500));
if self.pending_cycles == 0 {
if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) {
let _ = self.push_stack_word(self.pc);
self.set_flag(StatusFlag::BrkIrq, true);
let _ = self.push_stack(self.p);
self.set_flag(StatusFlag::IrqDisable, true);
if self.nmi {
match self.read_word(0xFFFA) {
Ok(word) => self.pc = word,
Err(error) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Memory(error),
desc: String::from_str("Couldn't read NMI vector").unwrap(),
})
}
};
self.pending_cycles = 8;
} else {
match self.read_word(0xFFFE) {
Ok(word) => self.pc = word,
Err(error) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Memory(error),
desc: String::from_str("Couldn't read IRQ vector").unwrap(),
})
}
};
self.pending_cycles = 7
}
self.nmi = false;
self.irq = false;
}
} else {
let opcode = match self.read(self.pc) {
Ok(byte) => byte,
Err(error) => {
return Err(GeorgeError {
desc: String::from_str("Failed to read the program counter!").unwrap(),
kind: GeorgeErrorKind::Memory(error),
})
}
};
let instruction = get_instruction(opcode);
match instruction {
Instruction::Valid(valid_instruction) => {
println!("Instruction: {:?}, {:?}", valid_instruction.opcode, opcode);
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!("");
self.pc += 1;
match valid_instruction.opcode.call(self) {
Ok(_) => {
self.pending_cycles += valid_instruction.cycles as usize;
},
Err(error) => return Err(GeorgeError{
desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}",
self.pc, valid_instruction).to_string(), kind: error})
};
}
Instruction::Invalid(invalid_instruction) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction),
desc: format!(
"An invalid instruction with opcode {:#04x} was called at address {:#06x}",
invalid_instruction.opcode, self.pc
),
})
}
}
}
self.pending_cycles -= 1;
self.cycle_count += 1;
Ok(())
}
pub fn stop(&mut self) {
unimplemented!()
}
}

66
src/error.rs Normal file
View File

@ -0,0 +1,66 @@
#[derive(Debug)]
pub enum GeorgeErrorKind {
Memory(MemoryError),
Execution(ExecutionError),
AddrMode(AddressingModeError),
}
impl From<MemoryError> for GeorgeErrorKind {
fn from(value: MemoryError) -> Self {
Self::Memory(value)
}
}
impl From<AddressingModeError> for GeorgeErrorKind {
fn from(value: AddressingModeError) -> Self {
Self::AddrMode(value)
}
}
impl From<ExecutionError> for GeorgeErrorKind {
fn from(value: ExecutionError) -> Self {
Self::Execution(value)
}
}
#[derive(Debug)]
pub enum ExecutionError {
InvalidInstruction,
InterruptsDisabled,
StackOverflow,
MemoryError(MemoryError),
}
#[derive(Debug)]
pub enum AddressingModeError {
IncompatibleAddrMode,
}
impl From<ExecutionError> for AddressingModeError {
fn from(_value: ExecutionError) -> Self {
Self::IncompatibleAddrMode
}
}
impl From<MemoryError> for ExecutionError {
fn from(error: MemoryError) -> Self {
ExecutionError::MemoryError(error)
}
}
#[derive(Debug)]
pub struct GeorgeError {
pub desc: String,
pub kind: GeorgeErrorKind,
}
#[derive(Debug)]
pub enum MemoryError {
Unmapped,
NoDataAtAddress,
Unwritable,
}
#[derive(Debug)]
pub enum MappingError {
RegionOccupied,
InvalidPage,
}

Binary file not shown.

View File

@ -2,7 +2,7 @@
.segment "CODE"
main:
LDA #$25
LDY #$FF
LDY #$2F
STY $7000
STY $7001
STY $7002

265
src/georgeencoding.txt Normal file
View File

@ -0,0 +1,265 @@
# this is august's version of extended ascii for george <3
# we're limited to 255 characters
# format: ascii byte, unicode hex, # unicode name
#
# format: three tab-separated columns
# column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
# column #2 is the Unicode (in hex as 0xXXXX)
# column #3 the Unicode name (follows a comment sign, '#')
0x00 0x0295 # ʕ
0x01 0x0097 # Middle Dot
0x02 0x1D25 # ᴥ
0x03 0x0294 # ʔ
0x04 0x2661 # White heart
0x05 0x2665 # Black heart
0x06 0x2B50 # White star
0x07 0x272D # Black star
0x08 0xF005 # Alternate Black Star
0x09 0x2726 # Black four pointed star
0x0a 0x2728 # Sparkles
0x0b 0x2640 # Female Sign
0x0c 0x2642 # Male Sign
0x0d 0x26A2 # Doubled female sign
0x0E 0x26A3 # Doubled male sign
0x0F 0x26A5 # Male and Female sign0x000E
0x10 0x2669 # Quarter Note
0x11 0x266A # Eighth note
0x12 0x266B # Beamed eighth notes
0x13 0x266C # Beamed sixteenth notes
0x14 0x0020 #
0x15 0x0020 #
0x16 0x0020 #
0x17 0x0020 #
0x18 0x0020 #
0x19 0x0020 #
0x1A 0x0020 #
0x1B 0x0020 #
0x1C 0x0020 #
0x1D 0x0020 #
0x1E 0x0020 #
0x1F 0x0020 #
0x20 0x0020 # SPACE
0x21 0x0021 # EXCLAMATION MARK
0x22 0x0022 # QUOTATION MARK
0x23 0x0023 # NUMBER SIGN
0x24 0x0024 # DOLLAR SIGN
0x25 0x0025 # PERCENT SIGN
0x26 0x0026 # AMPERSAND
0x27 0x0027 # APOSTROPHE
0x28 0x0028 # LEFT PARENTHESIS
0x29 0x0029 # RIGHT PARENTHESIS
0x2A 0x002A # ASTERISK
0x2B 0x002B # PLUS SIGN
0x2C 0x002C # COMMA
0x2D 0x002D # HYPHEN-MINUS
0x2E 0x002E # FULL STOP
0x2F 0x002F # SOLIDUS
0x30 0x0030 # DIGIT ZERO
0x31 0x0031 # DIGIT ONE
0x32 0x0032 # DIGIT TWO
0x33 0x0033 # DIGIT THREE
0x34 0x0034 # DIGIT FOUR
0x35 0x0035 # DIGIT FIVE
0x36 0x0036 # DIGIT SIX
0x37 0x0037 # DIGIT SEVEN
0x38 0x0038 # DIGIT EIGHT
0x39 0x0039 # DIGIT NINE
0x3A 0x003A # COLON
0x3B 0x003B # SEMICOLON
0x3C 0x003C # LESS-THAN SIGN
0x3D 0x003D # EQUALS SIGN
0x3E 0x003E # GREATER-THAN SIGN
0x3F 0x003F # QUESTION MARK
0x40 0x0040 # COMMERCIAL AT
0x41 0x0041 # LATIN CAPITAL LETTER A
0x42 0x0042 # LATIN CAPITAL LETTER B
0x43 0x0043 # LATIN CAPITAL LETTER C
0x44 0x0044 # LATIN CAPITAL LETTER D
0x45 0x0045 # LATIN CAPITAL LETTER E
0x46 0x0046 # LATIN CAPITAL LETTER F
0x47 0x0047 # LATIN CAPITAL LETTER G
0x48 0x0048 # LATIN CAPITAL LETTER H
0x49 0x0049 # LATIN CAPITAL LETTER I
0x4A 0x004A # LATIN CAPITAL LETTER J
0x4B 0x004B # LATIN CAPITAL LETTER K
0x4C 0x004C # LATIN CAPITAL LETTER L
0x4D 0x004D # LATIN CAPITAL LETTER M
0x4E 0x004E # LATIN CAPITAL LETTER N
0x4F 0x004F # LATIN CAPITAL LETTER O
0x50 0x0050 # LATIN CAPITAL LETTER P
0x51 0x0051 # LATIN CAPITAL LETTER Q
0x52 0x0052 # LATIN CAPITAL LETTER R
0x53 0x0053 # LATIN CAPITAL LETTER S
0x54 0x0054 # LATIN CAPITAL LETTER T
0x55 0x0055 # LATIN CAPITAL LETTER U
0x56 0x0056 # LATIN CAPITAL LETTER V
0x57 0x0057 # LATIN CAPITAL LETTER W
0x58 0x0058 # LATIN CAPITAL LETTER X
0x59 0x0059 # LATIN CAPITAL LETTER Y
0x5A 0x005A # LATIN CAPITAL LETTER Z
0x5B 0x005B # LEFT SQUARE BRACKET
0x5C 0x005C # REVERSE SOLIDUS
0x5D 0x005D # RIGHT SQUARE BRACKET
0x5E 0x005E # CIRCUMFLEX ACCENT
0x5F 0x005F # LOW LINE
0x60 0x0060 # GRAVE ACCENT
0x61 0x0061 # LATIN SMALL LETTER A
0x62 0x0062 # LATIN SMALL LETTER B
0x63 0x0063 # LATIN SMALL LETTER C
0x64 0x0064 # LATIN SMALL LETTER D
0x65 0x0065 # LATIN SMALL LETTER E
0x66 0x0066 # LATIN SMALL LETTER F
0x67 0x0067 # LATIN SMALL LETTER G
0x68 0x0068 # LATIN SMALL LETTER H
0x69 0x0069 # LATIN SMALL LETTER I
0x6A 0x006A # LATIN SMALL LETTER J
0x6B 0x006B # LATIN SMALL LETTER K
0x6C 0x006C # LATIN SMALL LETTER L
0x6D 0x006D # LATIN SMALL LETTER M
0x6E 0x006E # LATIN SMALL LETTER N
0x6F 0x006F # LATIN SMALL LETTER O
0x70 0x0070 # LATIN SMALL LETTER P
0x71 0x0071 # LATIN SMALL LETTER Q
0x72 0x0072 # LATIN SMALL LETTER R
0x73 0x0073 # LATIN SMALL LETTER S
0x74 0x0074 # LATIN SMALL LETTER T
0x75 0x0075 # LATIN SMALL LETTER U
0x76 0x0076 # LATIN SMALL LETTER V
0x77 0x0077 # LATIN SMALL LETTER W
0x78 0x0078 # LATIN SMALL LETTER X
0x79 0x0079 # LATIN SMALL LETTER Y
0x7A 0x007A # LATIN SMALL LETTER Z
0x7B 0x007B # LEFT CURLY BRACKET
0x7C 0x007C # VERTICAL LINE
0x7D 0x007D # RIGHT CURLY BRACKET
0x7E 0x007E # TILDE
0x7F 0x2500 # Box Drawings light horizontal
0x80 0x2502 # Box drawings light vertical
0x81 0x250C # Box drawing light down and right
0x82 0x2514 # Box drawing light up and right
0x83 0x251C # Box drawings light vertical and right
0x84 0x2524 #
0x85 0x252C #
0x86 0x2534 #
0x87 0x253C #
0x88 0x256D #
0x89 0x256E #
0x8A 0x256F #
0x8B 0x2570 #
0x8C 0x2571 #
0x8D 0x2572 #
0x8E 0x2573 #
0x8F 0x2550 #
0x90 0x2551 #
0x91 0x2554 #
0x92 0x2557 #
0x93 0x255a #
0x94 0x255D #
0x95 0x2560 #
0x96 0x2563 #
0x97 0x2566 #
0x98 0x2569 #
0x99 0x256C #
0x9A 0x0020 #
0x9B 0x0020 #
0x9C 0x0020 #
0x9D 0x0020 #
0x9E 0x0020 #
0x9F 0xE0B0 #
0xA0 0xE0B2 #
0xA1 0xE0B4 #
0xA2 0xE0B6 #
0xA3 0xE0B8 #
0xA4 0xE0BA #
0xA5 0xE0BC #
0xA6 0xE0BE #
0xA7 0x2581 #
0xA8 0x2582 #
0xA9 0x2583 #
0xAA 0x2584 #
0xAB 0x2585 #
0xAC 0x2586 #
0xAD 0x2587 #
0xAE 0x2588 #
0xAF 0x2589 #
0xB0 0x258A #
0xB1 0x258B #
0xB2 0x258C #
0xB3 0x258D #
0xB4 0x258E #
0xB5 0x258F #
0xB6 0x2591 #
0xB7 0x2592 #
0xB8 0x2593 #
0xB9 0x2596 #
0xBA 0x2597 #
0xBB 0x2598 #
0xBC 0x2599 #
0xBD 0x259A #
0xBE 0x259B #
0xBF 0x259C #
0xC0 0x259D #
0xC1 0x259E #
0xC2 0x259F #
0xC3 0x2190 #
0xC4 0x2191 #
0xC5 0x2192 #
0xC6 0x2193 #
0xC7 0x2B60 #
0xC8 0x2B61 #
0xC9 0x2B62 #
0xCA 0x2B63 #
0xCB 0x2B80 #
0xCC 0x2B81 #
0xCD 0x2B82 #
0xCE 0x2B83 #
0xCF 0x0020 #
0xD0 0x0020 #
0xD1 0x0020 #
0xD2 0x0020 #
0xD3 0x0020 #
0xD4 0x0020 #
0xD5 0x0020 #
0xD6 0x0020 #
0xD7 0x0020 #
0xD8 0x0020 #
0xD9 0x0020 #
0xDA 0x0020 #
0xDB 0x0020 #
0xDC 0x0020 #
0xDD 0x0020 #
0xDE 0x0020 #
0xDF 0x0020 #
0xE0 0x0020 #
0xE1 0x0020 #
0xE2 0x0020 #
0xE3 0x0020 #
0xE4 0x0020 #
0xE5 0x0020 #
0xE6 0x0020 #
0xE7 0x0020 #
0xE8 0x0020 #
0xE9 0x0020 #
0xEA 0x0020 #
0xEB 0x0020 #
0xEC 0x0020 #
0xED 0x0020 #
0xEE 0x0020 #
0xEF 0x0020 #
0xF0 0x0020 #
0xF1 0x0020 #
0xF2 0x0020 #
0xF3 0x0020 #
0xF4 0x0020 #
0xF5 0x0020 #
0xF6 0x0020 #
0xF7 0x0020 #
0xF8 0x0020 #
0xF9 0x0020 #
0xFA 0x0020 #
0xFB 0x0020 #
0xFC 0x0020 #
0xFD 0x0020 #
0xFE 0x0020 #
0xFF 0x0020 #

3430
src/instructions.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

106
src/memory.rs Normal file
View File

@ -0,0 +1,106 @@
use crate::error::{GeorgeError, GeorgeErrorKind, MappingError, MemoryError};
use crate::types::{Byte, Word};
use std::{fs::File, io::Read};
#[derive(Debug, Clone)]
pub struct MemMappedDevice {
start: Word,
end: Word,
pages: usize,
page: usize,
data: Vec<Byte>,
label: String,
}
impl MemMappedDevice {
pub fn new(start: Word, end: Word, pages: usize, label: String) -> Self {
Self {
start,
end,
pages,
page: 0,
data: vec![0x00; (end as usize + 1 - start as usize) * pages],
label,
}
}
fn contains(&self, address: Word) -> bool {
self.start <= address && self.end >= address
}
fn swap_page(&mut self, page: usize) -> Result<(), GeorgeError> {
match page > self.pages {
true => Err(GeorgeError {
kind: GeorgeErrorKind::Memory(MemoryError::Unmapped), //TODO: should be MappingError::InvalidPage,
desc: format!(
"{:?} tried to swap to a page outside of its range",
self.label
),
}),
false => {
self.page = page;
Ok(())
}
}
}
fn size(&self) -> Word {
self.end - self.start + 1
}
fn translate_address(&self, address: Word) -> Word {
(address - self.start) + self.size() * (self.page as Word)
} // This needs to translate memory address from CPU land to local land, so
// for rom an address like 0xFFFF needs to be translated to Page X, 0xFFF
}
#[derive(Debug, Clone)]
pub struct Mem {
areas: Vec<MemMappedDevice>,
}
impl Mem {
pub fn new(area: MemMappedDevice) -> Self {
Self { areas: vec![area] }
}
pub fn add_area(&mut self, area: MemMappedDevice) -> Result<(), MappingError> {
for existing_area in &self.areas {
if existing_area.contains(area.end) || existing_area.contains(area.start) {
return Err(MappingError::RegionOccupied);
}
}
self.areas.push(area);
Ok(())
}
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
for area in &self.areas {
if area.contains(address) {
let translated_address = area.translate_address(address);
match area.data.get(translated_address as usize) {
Some(data) => return Ok(*data),
None => return Err(MemoryError::NoDataAtAddress),
}
}
}
Err(MemoryError::Unmapped)
}
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
for area in self.areas.iter_mut() {
if area.contains(address) {
// println!("Writing to area {label}", label = area.label);
let translated_address = area.translate_address(address);
area.data[translated_address as usize] = data;
return Ok(());
};
}
Err(MemoryError::Unmapped)
}
pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> {
let bytes = f.bytes();
for (address, byte) in bytes.enumerate() {
match byte {
Ok(value) => self.write(address as Word, value)?,
Err(_) => return Err(MemoryError::Unwritable),
}
}
Ok(())
}
}

4
src/types.rs Normal file
View File

@ -0,0 +1,4 @@
use std::{u16, u8};
pub type Byte = u8;
pub type Word = u16;

64
src/video.rs Normal file
View File

@ -0,0 +1,64 @@
use crate::Mem;
use minifb::{Window, WindowOptions};
use std::{
sync::{Arc, RwLock},
thread::sleep,
time::{Duration, Instant},
};
pub struct Crtc {
memory: Arc<RwLock<Mem>>,
buffer: Vec<u32>,
window: minifb::Window,
}
impl Crtc {
pub fn new(memory: Arc<RwLock<Mem>>) -> Self {
let window = Window::new("screen", 512, 380, WindowOptions::default()).unwrap();
Self {
memory,
buffer: vec![0; 512 * 380],
window,
}
}
fn draw(&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;
}
};
// let bitmask = [128, 64, 32, 16, 8, 4, 2, 1];
let bitmask = [1, 2, 4, 8, 16, 32, 64, 128]; // I think this is how the bytes will be
// serialized? low bits first?
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, mask) in bitmask.iter().enumerate() {
match byte & mask == 0 {
true => self.buffer[(addr - 0x6000) as usize * 8 + i] = 0x110500,
false => self.buffer[(addr - 0x6000) as usize * 8 + i] = 0xFFCC00,
}
}
}
self.window
.update_with_buffer(&self.buffer, 512, 380)
.unwrap();
}
pub fn run(&mut self) {
let frame_duration = Duration::from_millis(16);
let mut previous_draw = Instant::now();
loop {
let now = Instant::now();
if now - previous_draw > frame_duration {
self.draw();
previous_draw = now;
}
sleep(Duration::from_millis(1));
}
}
}