we can type all the letters!
This commit is contained in:
		
							parent
							
								
									40ede17ae1
								
							
						
					
					
						commit
						705dcd3185
					
				
							
								
								
									
										25546
									
								
								src/Cozette.sfd
								
								
								
								
							
							
						
						
									
										25546
									
								
								src/Cozette.sfd
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										19
									
								
								src/cpu.rs
								
								
								
								
							
							
						
						
									
										19
									
								
								src/cpu.rs
								
								
								
								
							| 
						 | 
				
			
			@ -2,14 +2,11 @@ use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
 | 
			
		|||
use crate::instructions::{get_instruction, Instruction};
 | 
			
		||||
use crate::memory::Mem;
 | 
			
		||||
use crate::types::{Byte, Word};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
use std::sync::Mutex;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::{
 | 
			
		||||
    str::FromStr,
 | 
			
		||||
    sync::{Arc, RwLock},
 | 
			
		||||
    thread::sleep,
 | 
			
		||||
};
 | 
			
		||||
use std::{str::FromStr, sync::Arc, thread::sleep};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum StatusFlag {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +64,7 @@ impl Cpu {
 | 
			
		|||
                return Err(MemoryError::NoDataAtAddress);
 | 
			
		||||
            } // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
 | 
			
		||||
        };
 | 
			
		||||
        memory.read(address)
 | 
			
		||||
        Ok(memory.read(address))
 | 
			
		||||
    }
 | 
			
		||||
    pub fn read_word(&self, address: Word) -> Result<Word, MemoryError> {
 | 
			
		||||
        let low_byte = self.read(address)?;
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +93,7 @@ impl Cpu {
 | 
			
		|||
 | 
			
		||||
    pub fn pop_stack(&mut self) -> Result<Byte, ExecutionError> {
 | 
			
		||||
        let byte = self.read(self.stack_addr())?;
 | 
			
		||||
        self.s += 0x1;
 | 
			
		||||
        self.s = self.s.wrapping_add(0x1);
 | 
			
		||||
        Ok(byte)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +126,7 @@ impl Cpu {
 | 
			
		|||
                return Err(MemoryError::NoDataAtAddress);
 | 
			
		||||
            } // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
 | 
			
		||||
        };
 | 
			
		||||
        memory.write(address, data)?;
 | 
			
		||||
        memory.write(address, data);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +249,11 @@ impl Cpu {
 | 
			
		|||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        memory.dump().unwrap();
 | 
			
		||||
 | 
			
		||||
                        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);
 | 
			
		||||
                        memory
 | 
			
		||||
                            .dump(PathBuf::from_str("./cpu_dump.bin").unwrap())
 | 
			
		||||
                            .unwrap();
 | 
			
		||||
                        exit(1);
 | 
			
		||||
                    }
 | 
			
		||||
                    _ => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								src/george
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								src/george
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										174
									
								
								src/george.asm
								
								
								
								
							
							
						
						
									
										174
									
								
								src/george.asm
								
								
								
								
							| 
						 | 
				
			
			@ -1,84 +1,27 @@
 | 
			
		|||
.setcpu "65C02"
 | 
			
		||||
.segment "CODE"
 | 
			
		||||
.include "macro.inc"
 | 
			
		||||
 | 
			
		||||
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
 | 
			
		||||
    n = $01 ; temporary storage for data stack operations
 | 
			
		||||
n = $01 ; temporary storage for data stack operations
 | 
			
		||||
 | 
			
		||||
.macro breakpoint ; $02 isn't a valid instruction, the emulator will see this and halt, dump memory contents
 | 
			
		||||
    .byte $02
 | 
			
		||||
.endmacro 
 | 
			
		||||
key_row = $200 ; used for character lookup when key pressed
 | 
			
		||||
key_col = $201
 | 
			
		||||
cursor = $202
 | 
			
		||||
 | 
			
		||||
.macro pop ; drops a data stack cell
 | 
			
		||||
    inx
 | 
			
		||||
    inx
 | 
			
		||||
.endmacro
 | 
			
		||||
kb_row0 = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
 | 
			
		||||
 | 
			
		||||
.macro pop2 ; drops 2 data stack cells
 | 
			
		||||
    inx
 | 
			
		||||
    inx
 | 
			
		||||
    inx
 | 
			
		||||
    inx
 | 
			
		||||
.endmacro
 | 
			
		||||
.segment "ROM"
 | 
			
		||||
 | 
			
		||||
.macro push ; push a data stack cell
 | 
			
		||||
    dex
 | 
			
		||||
    dex
 | 
			
		||||
.endmacro
 | 
			
		||||
 | 
			
		||||
.macro push2 ; push 2 data stack cells
 | 
			
		||||
    dex
 | 
			
		||||
    dex
 | 
			
		||||
    dex
 | 
			
		||||
    dex
 | 
			
		||||
.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 -- )
 | 
			
		||||
    lda 1, x
 | 
			
		||||
    pha
 | 
			
		||||
    lda 0, x
 | 
			
		||||
    pha
 | 
			
		||||
    pop
 | 
			
		||||
.endmacro
 | 
			
		||||
 | 
			
		||||
.macro from_r ; pop the top of the return stack off and put it on the data stack: ( -- n)
 | 
			
		||||
    push
 | 
			
		||||
    pla
 | 
			
		||||
    sta 0, x
 | 
			
		||||
    pla
 | 
			
		||||
    sta 1, x
 | 
			
		||||
.endmacro
 | 
			
		||||
 | 
			
		||||
init:
 | 
			
		||||
reset:
 | 
			
		||||
    ldx #0; initialize data stack pointer
 | 
			
		||||
    jsr initdisplay
 | 
			
		||||
 | 
			
		||||
main:
 | 
			
		||||
    jsr draw
 | 
			
		||||
    jmp main
 | 
			
		||||
;lda #$80
 | 
			
		||||
;sta $200
 | 
			
		||||
;adc $200
 | 
			
		||||
;breakpoint
 | 
			
		||||
 | 
			
		||||
initdisplay:
 | 
			
		||||
    lda #$20
 | 
			
		||||
    ldy #0
 | 
			
		||||
    jsr cleardisplay
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
cleardisplay:
 | 
			
		||||
    sta $6000,y
 | 
			
		||||
| 
						 | 
				
			
			@ -91,16 +34,89 @@ cleardisplay:
 | 
			
		|||
    sta $6700,y ; this goes slightly over but it's fine
 | 
			
		||||
    iny
 | 
			
		||||
    bne cleardisplay
 | 
			
		||||
 | 
			
		||||
main:
 | 
			
		||||
    ;jsr draw
 | 
			
		||||
    jsr keyboard
 | 
			
		||||
    jmp main
 | 
			
		||||
 | 
			
		||||
keyboard:
 | 
			
		||||
    ldy #0 ; loop through each row
 | 
			
		||||
    @loop:
 | 
			
		||||
        lda kb_row0, y
 | 
			
		||||
        beq @skip ; if row has no key pressed, skip checking which key
 | 
			
		||||
        jsr key_pressed
 | 
			
		||||
        @skip:
 | 
			
		||||
            iny
 | 
			
		||||
            cpy #5
 | 
			
		||||
            bne @loop
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
key_pressed: ; a is loaded with the row byte
 | 
			
		||||
    sty key_row ; store character row
 | 
			
		||||
    inc cursor
 | 
			
		||||
    pha
 | 
			
		||||
    phy
 | 
			
		||||
    ldy #0
 | 
			
		||||
    @find_col:            ; test each row bit, store column if key pressed
 | 
			
		||||
        lsr                ; test bit 7
 | 
			
		||||
        bcs store_col       ; if set, go store character column
 | 
			
		||||
        iny
 | 
			
		||||
        cpy #8
 | 
			
		||||
        bne @find_col     ; loop until we've checked each bit
 | 
			
		||||
 | 
			
		||||
store_col:
 | 
			
		||||
    sty key_col
 | 
			
		||||
 | 
			
		||||
keymap_index:
 | 
			
		||||
    push
 | 
			
		||||
    lda key_col
 | 
			
		||||
    stz 1, x
 | 
			
		||||
    sta 0, x
 | 
			
		||||
    push
 | 
			
		||||
    lda #8
 | 
			
		||||
    stz 1, x
 | 
			
		||||
    sta 0, x
 | 
			
		||||
    push
 | 
			
		||||
    lda key_row
 | 
			
		||||
    stz 1, x
 | 
			
		||||
    sta 0, x
 | 
			
		||||
    jsr mult
 | 
			
		||||
    jsr plus
 | 
			
		||||
    lda 0, x
 | 
			
		||||
    tay
 | 
			
		||||
 | 
			
		||||
do_something_w_key: ; we've stored the character position, now let's 
 | 
			
		||||
    lda keymap, y
 | 
			
		||||
    ldy cursor
 | 
			
		||||
    sta $6000, y
 | 
			
		||||
    pla
 | 
			
		||||
    ply
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
keymap:
 | 
			
		||||
    .byte "?outrew?"
 | 
			
		||||
    .byte "?piygsq?"
 | 
			
		||||
    .byte "a??khvd?"
 | 
			
		||||
    .byte "42ljbfz?"
 | 
			
		||||
    .byte "31?mncx?"
 | 
			
		||||
    .byte "?????ssm"
 | 
			
		||||
 | 
			
		||||
draw:
 | 
			
		||||
    lda #%01000000
 | 
			
		||||
    bit $4400
 | 
			
		||||
    beq @1
 | 
			
		||||
    push_coords #0, #0
 | 
			
		||||
    push_char #$77
 | 
			
		||||
    jsr draw_char
 | 
			
		||||
    @1: 
 | 
			
		||||
;    lda #%01000000
 | 
			
		||||
;    bit $4400
 | 
			
		||||
;    beq @1
 | 
			
		||||
;    push_coords #0, #0
 | 
			
		||||
;    push_char #$77
 | 
			
		||||
;    jsr draw_char
 | 
			
		||||
;    @1: 
 | 
			
		||||
;    lda #%00100000
 | 
			
		||||
;    bit $4400
 | 
			
		||||
;    beq @2
 | 
			
		||||
;    push_coords #0, #0
 | 
			
		||||
;    push_char #$65
 | 
			
		||||
;    jsr draw_char
 | 
			
		||||
;    @2: 
 | 
			
		||||
    rts
 | 
			
		||||
 | 
			
		||||
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
 | 
			
		||||
| 
						 | 
				
			
			@ -217,6 +233,8 @@ mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TO
 | 
			
		|||
    pop
 | 
			
		||||
    ply
 | 
			
		||||
    rts
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
.segment "VRAM"
 | 
			
		||||
 | 
			
		||||
.segment "VECTOR"
 | 
			
		||||
.word reset
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,10 @@
 | 
			
		|||
MEMORY {
 | 
			
		||||
        RAM: start = $0000, size = $0200, type = rw, fill = true;
 | 
			
		||||
        PROGRAM: start = $0200, size = $3E00, type = rw, fill = true;
 | 
			
		||||
        CTRL: start = $4000, size = $2000, type = rw, fill = true;
 | 
			
		||||
        VRAM: start = $6000, size = $8000, type = rw, fill = true;
 | 
			
		||||
        ROM: start = $E000, size = $2000, type = ro, fill = true;
 | 
			
		||||
        RAM: start = $0000, size = $8000, type = ro, fill = yes;
 | 
			
		||||
        ROM: start = $8000, size = $7FFC, type = ro, fill = yes;
 | 
			
		||||
        VECTOR: start = $FFFC, size = $4, type = ro, fill = yes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
SEGMENTS {
 | 
			
		||||
        CODE: load = "PROGRAM", type = rw;
 | 
			
		||||
        VRAM: load = "VRAM", type = rw;
 | 
			
		||||
        ROM: load = "ROM", type = ro;
 | 
			
		||||
        VECTOR: load = "VECTOR", type = ro;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,10 @@
 | 
			
		|||
#		 column #2 is the Unicode (in hex as 0xXXXX)
 | 
			
		||||
#		 column #3 the Unicode name (follows a comment sign, '#')
 | 
			
		||||
 | 
			
		||||
0x00	0x0295	# ʕ
 | 
			
		||||
0x01	0x00B7  # Middle Dot
 | 
			
		||||
0x02	0x1D25	# ᴥ
 | 
			
		||||
0x03	0x0294	# ʔ
 | 
			
		||||
0x00    0x00	# 
 | 
			
		||||
0x01	0x2591  # 
 | 
			
		||||
0x02	0x2592  # ᴥ
 | 
			
		||||
0x03	0x2593  # ʔ
 | 
			
		||||
0x04	0x2661	# White heart 
 | 
			
		||||
0x05    0x2665	# Black heart 
 | 
			
		||||
0x06	0x2B50	# White star
 | 
			
		||||
| 
						 | 
				
			
			@ -189,70 +189,70 @@
 | 
			
		|||
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	0xF049	#
 | 
			
		||||
0xD0	0xF04A	#
 | 
			
		||||
0xD1	0x23F3	#
 | 
			
		||||
0xD2	0xF07B	#
 | 
			
		||||
0xD3	0xF07C	#
 | 
			
		||||
0xD4	0xF114	#
 | 
			
		||||
0xD5	0xF115	#
 | 
			
		||||
0xD6	0xF250	#
 | 
			
		||||
0xD7	0xF251	#
 | 
			
		||||
0xD8	0xF253	#
 | 
			
		||||
0xD9	0xF254	#
 | 
			
		||||
0xDA	0xF461	#
 | 
			
		||||
0xDB	0xF016	#
 | 
			
		||||
0xDC	0xF401	#
 | 
			
		||||
0xDD	0x1F52E	#
 | 
			
		||||
0xDE	0xF2DB	#
 | 
			
		||||
0xDF	0xF008	#
 | 
			
		||||
0xE0	0x25C7	#
 | 
			
		||||
0xE1	0x25C8	#
 | 
			
		||||
0xE2	0x1F311	#
 | 
			
		||||
0xE3	0x1F312	#
 | 
			
		||||
0xE4	0x1F313	#
 | 
			
		||||
0xE5	0x1F314	#
 | 
			
		||||
0xE6	0x1F315	#
 | 
			
		||||
0xE7	0x1F316	#
 | 
			
		||||
0xE8	0x1F317	#
 | 
			
		||||
0xE9	0x1F318	#
 | 
			
		||||
0xEA	0xF04C	#
 | 
			
		||||
0xEB	0x2714	#
 | 
			
		||||
0xEC	0x2718	#
 | 
			
		||||
0xED	0x25C6	#
 | 
			
		||||
0xEE	0xF15D	#
 | 
			
		||||
0xEF	0xF15E	#
 | 
			
		||||
0xF0	0xF071	#
 | 
			
		||||
0xF1	0xF449	#
 | 
			
		||||
0xF2	0xF529	#
 | 
			
		||||
0xF3	0xF658	#
 | 
			
		||||
0xF4	0xF659 	#
 | 
			
		||||
0xF5	0x0020	#
 | 
			
		||||
0xB6    0x0295  #	
 | 
			
		||||
0xB7    0x00B7  #	
 | 
			
		||||
0xB8    0x1D25  #	
 | 
			
		||||
0xB9	0x0294
 | 
			
		||||
0xBA	0x2596  
 | 
			
		||||
0xBB	0x2597  
 | 
			
		||||
0xBC	0x2598  
 | 
			
		||||
0xBD	0x2599  
 | 
			
		||||
0xBE	0x259A  
 | 
			
		||||
0xBF	0x259B  
 | 
			
		||||
0xC0	0x259C  
 | 
			
		||||
0xC1	0x259D	
 | 
			
		||||
0xC2	0x259E	
 | 
			
		||||
0xC3    0x259F	
 | 
			
		||||
0xC4	0x2190	
 | 
			
		||||
0xC5	0x2191	
 | 
			
		||||
0xC6	0x2192	
 | 
			
		||||
0xC7	0x2193	
 | 
			
		||||
0xC8	0x2B60	
 | 
			
		||||
0xC9	0x2B61	
 | 
			
		||||
0xCA	0x2B62	
 | 
			
		||||
0xCB	0x2B63	
 | 
			
		||||
0xCC	0x2B80  
 | 
			
		||||
0xCD	0x2B81  
 | 
			
		||||
0xCE	0x2B82  
 | 
			
		||||
0xCF	0x2B83  
 | 
			
		||||
0xD0	0xF049	
 | 
			
		||||
0xD1	0xF04A	
 | 
			
		||||
0xD2	0x23F3	
 | 
			
		||||
0xD3	0xF07B	
 | 
			
		||||
0xD4	0xF07C	
 | 
			
		||||
0xD5	0xF114	
 | 
			
		||||
0xD6	0xF115	
 | 
			
		||||
0xD7	0xF250	
 | 
			
		||||
0xD8	0xF251	
 | 
			
		||||
0xD9	0xF253	
 | 
			
		||||
0xDA	0xF254	
 | 
			
		||||
0xDB	0xF461	
 | 
			
		||||
0xDC	0xF016	
 | 
			
		||||
0xDD	0xF401	
 | 
			
		||||
0xDE	0x1F52E	
 | 
			
		||||
0xDF	0xF2DB	
 | 
			
		||||
0xE0	0xF008	
 | 
			
		||||
0xE1	0x25C7	
 | 
			
		||||
0xE2	0x25C8	
 | 
			
		||||
0xE3	0x1F311	
 | 
			
		||||
0xE4	0x1F312	
 | 
			
		||||
0xE5	0x1F313	
 | 
			
		||||
0xE6	0x1F314	
 | 
			
		||||
0xE7	0x1F315	
 | 
			
		||||
0xE8	0x1F316	
 | 
			
		||||
0xE9	0x1F317	
 | 
			
		||||
0xEA	0x1F318	
 | 
			
		||||
0xEB	0xF04C	
 | 
			
		||||
0xEC	0x2714	
 | 
			
		||||
0xED	0x2718	
 | 
			
		||||
0xEE	0x25C6	
 | 
			
		||||
0xEF	0xF15D	
 | 
			
		||||
0xF0	0xF15E	
 | 
			
		||||
0xF1	0xF071	
 | 
			
		||||
0xF2	0xF449	
 | 
			
		||||
0xF3	0xF529	
 | 
			
		||||
0xF4	0xF658	
 | 
			
		||||
0xF5	0xF659 		#
 | 
			
		||||
0xF6	0x1f381	#	Space
 | 
			
		||||
0xF7	0xf05a	#	Space
 | 
			
		||||
0xF8	0xf06a	#	Space
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2063,6 +2063,7 @@ impl Opcode {
 | 
			
		|||
                    let byte = cpu.read(address.try_into()?)?;
 | 
			
		||||
                    let carry = cpu.get_flag(StatusFlag::Carry);
 | 
			
		||||
                    let result = cpu.a as Word + byte as Word + carry as Word;
 | 
			
		||||
                    println!("{result:#04x}");
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value()));
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, result as Byte == 0);
 | 
			
		||||
                    cpu.set_flag(
 | 
			
		||||
| 
						 | 
				
			
			@ -2073,6 +2074,7 @@ impl Opcode {
 | 
			
		|||
                    );
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as Byte));
 | 
			
		||||
                    cpu.a = result as Byte;
 | 
			
		||||
                    println!("{:#04x}", cpu.a);
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                }
 | 
			
		||||
                _ => Err(GeorgeErrorKind::AddrMode(
 | 
			
		||||
| 
						 | 
				
			
			@ -2103,10 +2105,7 @@ impl Opcode {
 | 
			
		|||
                fn asl(cpu: &mut Cpu, value: Byte) -> Byte {
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Carry, value & 0b1000_0000 == 0b1000_0000);
 | 
			
		||||
                    let shifted_value = value << 1;
 | 
			
		||||
                    cpu.set_flag(
 | 
			
		||||
                        StatusFlag::Negative,
 | 
			
		||||
                        value & StatusFlag::Negative as Byte > 0,
 | 
			
		||||
                    );
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value));
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, shifted_value == 0);
 | 
			
		||||
                    shifted_value
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -2532,7 +2531,8 @@ impl Opcode {
 | 
			
		|||
            Opcode::BRK(mode) => match mode {
 | 
			
		||||
                AddressingMode::Implied => {
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Brk, true);
 | 
			
		||||
                    panic!("Interrupts unimplemented!");
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                    //panic!("Interrupts unimplemented!");
 | 
			
		||||
                }
 | 
			
		||||
                _ => Err(GeorgeErrorKind::AddrMode(
 | 
			
		||||
                    AddressingModeError::IncompatibleAddrMode,
 | 
			
		||||
| 
						 | 
				
			
			@ -2662,6 +2662,8 @@ impl Opcode {
 | 
			
		|||
            Opcode::DEX(mode) => match mode {
 | 
			
		||||
                AddressingMode::Implied => {
 | 
			
		||||
                    cpu.x = cpu.x.wrapping_sub(1);
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, cpu.x == 0);
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x));
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                }
 | 
			
		||||
                _ => Err(GeorgeErrorKind::AddrMode(
 | 
			
		||||
| 
						 | 
				
			
			@ -2670,7 +2672,9 @@ impl Opcode {
 | 
			
		|||
            },
 | 
			
		||||
            Opcode::DEY(mode) => match mode {
 | 
			
		||||
                AddressingMode::Implied => {
 | 
			
		||||
                    cpu.y -= 1;
 | 
			
		||||
                    cpu.y = cpu.y.wrapping_sub(1);
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, cpu.y == 0);
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y));
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                }
 | 
			
		||||
                _ => Err(GeorgeErrorKind::AddrMode(
 | 
			
		||||
| 
						 | 
				
			
			@ -2824,11 +2828,8 @@ impl Opcode {
 | 
			
		|||
                fn lsr(cpu: &mut Cpu, value: Byte) -> Byte {
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Carry, value & 1 == 1);
 | 
			
		||||
                    let shifted_value = value >> 1;
 | 
			
		||||
                    cpu.set_flag(
 | 
			
		||||
                        StatusFlag::Negative,
 | 
			
		||||
                        value & StatusFlag::Negative as Byte > 0,
 | 
			
		||||
                    );
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, shifted_value == 0);
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(value));
 | 
			
		||||
                    shifted_value
 | 
			
		||||
                }
 | 
			
		||||
                match mode {
 | 
			
		||||
| 
						 | 
				
			
			@ -3048,10 +3049,7 @@ impl Opcode {
 | 
			
		|||
                    let carry = cpu.get_flag(StatusFlag::Carry) as Byte;
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Carry, value >> 7 == 1);
 | 
			
		||||
                    let shifted_value = (value << 1) + carry;
 | 
			
		||||
                    cpu.set_flag(
 | 
			
		||||
                        StatusFlag::Negative,
 | 
			
		||||
                        shifted_value & StatusFlag::Negative as Byte > 0,
 | 
			
		||||
                    );
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value));
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, shifted_value == 0);
 | 
			
		||||
                    shifted_value
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -3080,10 +3078,7 @@ impl Opcode {
 | 
			
		|||
                    let carry = cpu.get_flag(StatusFlag::Carry) as Byte;
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Carry, value & 1 == 1);
 | 
			
		||||
                    let shifted_value = (value >> 1) + (carry << 7);
 | 
			
		||||
                    cpu.set_flag(
 | 
			
		||||
                        StatusFlag::Negative,
 | 
			
		||||
                        shifted_value & StatusFlag::Negative as Byte > 0,
 | 
			
		||||
                    );
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value));
 | 
			
		||||
                    cpu.set_flag(StatusFlag::Zero, shifted_value == 0);
 | 
			
		||||
                    shifted_value
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										33
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
				
			
			@ -9,31 +9,17 @@ mod types;
 | 
			
		|||
mod video;
 | 
			
		||||
 | 
			
		||||
use crate::cpu::Cpu;
 | 
			
		||||
use crate::memory::{Mem, MemMappedDevice};
 | 
			
		||||
use crate::memory::Mem;
 | 
			
		||||
use crate::video::Crtc;
 | 
			
		||||
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
use std::sync::Mutex;
 | 
			
		||||
use std::{
 | 
			
		||||
    path::PathBuf,
 | 
			
		||||
    sync::{Arc, RwLock},
 | 
			
		||||
    thread,
 | 
			
		||||
};
 | 
			
		||||
use std::thread::sleep;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use std::{path::PathBuf, sync::Arc, thread};
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let ram = MemMappedDevice::new(0x0000, 0x3FFF, 2, "ram".into());
 | 
			
		||||
    let control = MemMappedDevice::new(0x4000, 0x5FFF, 4, "control".into());
 | 
			
		||||
    let vram = MemMappedDevice::new(0x6000, 0xDFFF, 1, "vram".into());
 | 
			
		||||
    let rom = MemMappedDevice::new(0xE000, 0xFFFF, 4, "rom".into());
 | 
			
		||||
    let mut memory = Mem::new(ram);
 | 
			
		||||
    if let Err(error) = memory.add_area(control) {
 | 
			
		||||
        println!("Error adding vram: {:?}", error.desc);
 | 
			
		||||
    };
 | 
			
		||||
    if let Err(error) = memory.add_area(vram) {
 | 
			
		||||
        println!("Error adding vram: {:?}", error.desc);
 | 
			
		||||
    };
 | 
			
		||||
    if let Err(error) = memory.add_area(rom) {
 | 
			
		||||
        println!("Error adding rom: {:?}", error.desc);
 | 
			
		||||
    };
 | 
			
		||||
    let mut memory = Mem::new();
 | 
			
		||||
    let binary = match std::fs::File::open(PathBuf::from(
 | 
			
		||||
        "/Users/kline/projects/winter/george-emu/src/george",
 | 
			
		||||
    )) {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +29,10 @@ fn main() {
 | 
			
		|||
    if let Err(error) = memory.read_from_bin(binary) {
 | 
			
		||||
        println!("{:?}", error);
 | 
			
		||||
    };
 | 
			
		||||
    memory.write(0xFFFC, 0x00).unwrap();
 | 
			
		||||
    memory.write(0xFFFD, 0x02).unwrap();
 | 
			
		||||
 | 
			
		||||
    memory
 | 
			
		||||
        .dump(PathBuf::from_str("./coredump.bin").unwrap())
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let shared_memory = Arc::new(Mutex::new(memory));
 | 
			
		||||
    let cpu_memory = shared_memory.clone();
 | 
			
		||||
| 
						 | 
				
			
			@ -57,5 +45,4 @@ fn main() {
 | 
			
		|||
    });
 | 
			
		||||
    let mut screen = Crtc::new(display_memory);
 | 
			
		||||
    screen.run();
 | 
			
		||||
    shared_memory.lock().unwrap().dump().unwrap();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										122
									
								
								src/memory.rs
								
								
								
								
							
							
						
						
									
										122
									
								
								src/memory.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,124 +1,44 @@
 | 
			
		|||
use crate::error::{GeorgeError, GeorgeErrorKind, MappingError, MemoryError};
 | 
			
		||||
use crate::error::MemoryError;
 | 
			
		||||
use crate::types::{Byte, Word};
 | 
			
		||||
use std::io::{self, Write};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::u16;
 | 
			
		||||
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>,
 | 
			
		||||
    pub data: Vec<Byte>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mem {
 | 
			
		||||
    pub fn new(area: MemMappedDevice) -> Self {
 | 
			
		||||
        Self { areas: vec![area] }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn dump(&self) -> io::Result<()> {
 | 
			
		||||
        let mut outfile = File::create("./coredump.bin")?;
 | 
			
		||||
        let mut data = Vec::new();
 | 
			
		||||
        for area in &self.areas {
 | 
			
		||||
            for byte in &area.data {
 | 
			
		||||
                data.push(byte.to_owned());
 | 
			
		||||
            }
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            data: vec![0; u16::MAX as usize + 1],
 | 
			
		||||
        }
 | 
			
		||||
        outfile.set_len(0xFFFF)?;
 | 
			
		||||
        outfile.write_all(&data)?;
 | 
			
		||||
    }
 | 
			
		||||
    pub fn dump(&self, path: PathBuf) -> io::Result<()> {
 | 
			
		||||
        let mut outfile = File::create(path)?;
 | 
			
		||||
        outfile.write_all(&self.data)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    pub fn add_area(&mut self, area: MemMappedDevice) -> Result<(), GeorgeError> {
 | 
			
		||||
        for existing_area in &self.areas {
 | 
			
		||||
            if existing_area.contains(area.end) || existing_area.contains(area.start) {
 | 
			
		||||
                return Err(GeorgeError {
 | 
			
		||||
                    desc: format!(
 | 
			
		||||
                        "Tried to assign an area already allocated to {:?}",
 | 
			
		||||
                        existing_area
 | 
			
		||||
                    ),
 | 
			
		||||
                    kind: GeorgeErrorKind::Mapping(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(&self, address: Word) -> Byte {
 | 
			
		||||
        self.data[address as usize]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn write(&mut self, address: Word, data: Byte) {
 | 
			
		||||
        self.data[address as usize] = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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(value) => self.write(address as Word, value),
 | 
			
		||||
                Err(_) => {
 | 
			
		||||
                    println!("couldn't write byte {:#04x}", address);
 | 
			
		||||
                    return Err(MemoryError::Unwritable);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								src/video.rs
								
								
								
								
							
							
						
						
									
										20
									
								
								src/video.rs
								
								
								
								
							| 
						 | 
				
			
			@ -3,7 +3,7 @@ use minifb::{Key, Scale, ScaleMode, Window, WindowOptions};
 | 
			
		|||
use std::{
 | 
			
		||||
    fs::File,
 | 
			
		||||
    io::Read,
 | 
			
		||||
    sync::{Arc, Mutex, RwLock},
 | 
			
		||||
    sync::{Arc, Mutex},
 | 
			
		||||
    thread::sleep,
 | 
			
		||||
    time::{Duration, Instant},
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ impl Crtc {
 | 
			
		|||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        let hw_ctrl = memory.read(0x4000).unwrap();
 | 
			
		||||
        let hw_ctrl = memory.read(0x4000);
 | 
			
		||||
        match hw_ctrl & 0b0000_1000 == 0b0000_1000 {
 | 
			
		||||
            false => {
 | 
			
		||||
                // the rest of this function is arcane wizardry based on the specifics of george's weird
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ impl Crtc {
 | 
			
		|||
                let mut i = 0;
 | 
			
		||||
                for char_row in 0..29 {
 | 
			
		||||
                    for char_col in 0..64 {
 | 
			
		||||
                        let ascii = memory.read(0x6000 + i).unwrap();
 | 
			
		||||
                        let ascii = memory.read(0x6000 + i);
 | 
			
		||||
                        i += 1;
 | 
			
		||||
                        for row in 0..13 {
 | 
			
		||||
                            let byte = self.char_rom[ascii as usize + (row * 0x101)];
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ impl Crtc {
 | 
			
		|||
                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();
 | 
			
		||||
                    let byte = memory.read(addr);
 | 
			
		||||
                    for i in 0..8 {
 | 
			
		||||
                        match byte & 0x80 >> i == 0 {
 | 
			
		||||
                            true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR,
 | 
			
		||||
| 
						 | 
				
			
			@ -165,12 +165,12 @@ impl Crtc {
 | 
			
		|||
 | 
			
		||||
            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();
 | 
			
		||||
                    memory.write(0x4400, row0);
 | 
			
		||||
                    memory.write(0x4401, row1);
 | 
			
		||||
                    memory.write(0x4402, row2);
 | 
			
		||||
                    memory.write(0x4403, row3);
 | 
			
		||||
                    memory.write(0x4404, row4);
 | 
			
		||||
                    memory.write(0x4405, row5);
 | 
			
		||||
                }
 | 
			
		||||
                Err(error) => println!("{error}"),
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue