Split code into modules, add font stuff
This commit is contained in:
parent
ef438f48d2
commit
78dad90fc9
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
log
|
log
|
||||||
|
george.o
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
BIN
src/george
BIN
src/george
Binary file not shown.
|
@ -2,7 +2,7 @@
|
||||||
.segment "CODE"
|
.segment "CODE"
|
||||||
main:
|
main:
|
||||||
LDA #$25
|
LDA #$25
|
||||||
LDY #$FF
|
LDY #$2F
|
||||||
STY $7000
|
STY $7000
|
||||||
STY $7001
|
STY $7001
|
||||||
STY $7002
|
STY $7002
|
||||||
|
|
|
@ -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 #
|
File diff suppressed because it is too large
Load Diff
3987
src/main.rs
3987
src/main.rs
File diff suppressed because it is too large
Load Diff
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
use std::{u16, u8};
|
||||||
|
|
||||||
|
pub type Byte = u8;
|
||||||
|
pub type Word = u16;
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue