Split code into modules, add font stuff
This commit is contained in:
		
							parent
							
								
									ef438f48d2
								
							
						
					
					
						commit
						78dad90fc9
					
				| 
						 | 
				
			
			@ -1,2 +1,3 @@
 | 
			
		|||
/target
 | 
			
		||||
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"
 | 
			
		||||
main:
 | 
			
		||||
    LDA #$25
 | 
			
		||||
    LDY #$FF
 | 
			
		||||
    LDY #$2F
 | 
			
		||||
    STY $7000
 | 
			
		||||
    STY $7001
 | 
			
		||||
    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