Added interrupts, some restructuring & refactoring
This commit is contained in:
		
							parent
							
								
									705dcd3185
								
							
						
					
					
						commit
						c154bdc89a
					
				| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
/target
 | 
					/target
 | 
				
			||||||
log
 | 
					log
 | 
				
			||||||
george.o
 | 
					george.o
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,60 @@
 | 
				
			||||||
# It is not intended for manual editing.
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
version = 3
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anstream"
 | 
				
			||||||
 | 
					version = "0.6.13"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anstyle",
 | 
				
			||||||
 | 
					 "anstyle-parse",
 | 
				
			||||||
 | 
					 "anstyle-query",
 | 
				
			||||||
 | 
					 "anstyle-wincon",
 | 
				
			||||||
 | 
					 "colorchoice",
 | 
				
			||||||
 | 
					 "utf8parse",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anstyle"
 | 
				
			||||||
 | 
					version = "1.0.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anstyle-parse"
 | 
				
			||||||
 | 
					version = "0.2.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "utf8parse",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anstyle-query"
 | 
				
			||||||
 | 
					version = "1.0.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anstyle-wincon"
 | 
				
			||||||
 | 
					version = "3.0.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anstyle",
 | 
				
			||||||
 | 
					 "windows-sys 0.52.0",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "anyhow"
 | 
				
			||||||
 | 
					version = "1.0.81"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "autocfg"
 | 
					name = "autocfg"
 | 
				
			||||||
version = "1.1.0"
 | 
					version = "1.1.0"
 | 
				
			||||||
| 
						 | 
					@ -78,6 +132,52 @@ version = "1.0.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
					checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "clap"
 | 
				
			||||||
 | 
					version = "4.5.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "clap_builder",
 | 
				
			||||||
 | 
					 "clap_derive",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "clap_builder"
 | 
				
			||||||
 | 
					version = "4.5.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anstream",
 | 
				
			||||||
 | 
					 "anstyle",
 | 
				
			||||||
 | 
					 "clap_lex",
 | 
				
			||||||
 | 
					 "strsim",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "clap_derive"
 | 
				
			||||||
 | 
					version = "4.5.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "heck",
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "clap_lex"
 | 
				
			||||||
 | 
					version = "0.7.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "colorchoice"
 | 
				
			||||||
 | 
					version = "1.0.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "cty"
 | 
					name = "cty"
 | 
				
			||||||
version = "0.2.2"
 | 
					version = "0.2.2"
 | 
				
			||||||
| 
						 | 
					@ -99,6 +199,12 @@ version = "1.2.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 | 
					checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "equivalent"
 | 
				
			||||||
 | 
					version = "1.0.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "errno"
 | 
					name = "errno"
 | 
				
			||||||
version = "0.3.8"
 | 
					version = "0.3.8"
 | 
				
			||||||
| 
						 | 
					@ -214,9 +320,35 @@ dependencies = [
 | 
				
			||||||
name = "georgeemu"
 | 
					name = "georgeemu"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "anyhow",
 | 
				
			||||||
 "bdf",
 | 
					 "bdf",
 | 
				
			||||||
 "bitvec",
 | 
					 "bitvec",
 | 
				
			||||||
 | 
					 "clap",
 | 
				
			||||||
 "minifb",
 | 
					 "minifb",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "toml",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "hashbrown"
 | 
				
			||||||
 | 
					version = "0.14.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "heck"
 | 
				
			||||||
 | 
					version = "0.5.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "indexmap"
 | 
				
			||||||
 | 
					version = "2.2.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "equivalent",
 | 
				
			||||||
 | 
					 "hashbrown",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -460,24 +592,33 @@ dependencies = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde"
 | 
					name = "serde"
 | 
				
			||||||
version = "1.0.196"
 | 
					version = "1.0.197"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
 | 
					checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "serde_derive",
 | 
					 "serde_derive",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_derive"
 | 
					name = "serde_derive"
 | 
				
			||||||
version = "1.0.196"
 | 
					version = "1.0.197"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
 | 
					checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "serde_spanned"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "slab"
 | 
					name = "slab"
 | 
				
			||||||
version = "0.4.9"
 | 
					version = "0.4.9"
 | 
				
			||||||
| 
						 | 
					@ -493,6 +634,12 @@ version = "1.13.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
 | 
					checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "strsim"
 | 
				
			||||||
 | 
					version = "0.11.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "syn"
 | 
					name = "syn"
 | 
				
			||||||
version = "2.0.48"
 | 
					version = "2.0.48"
 | 
				
			||||||
| 
						 | 
					@ -543,12 +690,52 @@ dependencies = [
 | 
				
			||||||
 "syn",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml"
 | 
				
			||||||
 | 
					version = "0.8.12"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_spanned",
 | 
				
			||||||
 | 
					 "toml_datetime",
 | 
				
			||||||
 | 
					 "toml_edit",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml_datetime"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "toml_edit"
 | 
				
			||||||
 | 
					version = "0.22.9"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					 "serde",
 | 
				
			||||||
 | 
					 "serde_spanned",
 | 
				
			||||||
 | 
					 "toml_datetime",
 | 
				
			||||||
 | 
					 "winnow",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "unicode-ident"
 | 
					name = "unicode-ident"
 | 
				
			||||||
version = "1.0.12"
 | 
					version = "1.0.12"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 | 
					checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "utf8parse"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "version-compare"
 | 
					name = "version-compare"
 | 
				
			||||||
version = "0.1.1"
 | 
					version = "0.1.1"
 | 
				
			||||||
| 
						 | 
					@ -858,6 +1045,15 @@ version = "0.52.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
 | 
					checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winnow"
 | 
				
			||||||
 | 
					version = "0.6.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wyz"
 | 
					name = "wyz"
 | 
				
			||||||
version = "0.5.1"
 | 
					version = "0.5.1"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,10 @@ edition = "2021"
 | 
				
			||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
 | 
					anyhow = "1.0.81"
 | 
				
			||||||
bdf = "0.6.0"
 | 
					bdf = "0.6.0"
 | 
				
			||||||
bitvec = "1.0.1"
 | 
					bitvec = "1.0.1"
 | 
				
			||||||
 | 
					clap = { version = "4.5.4", features = ["derive"] }
 | 
				
			||||||
minifb = "0.25.0"
 | 
					minifb = "0.25.0"
 | 
				
			||||||
 | 
					serde = { version = "1.0.197", features = ["serde_derive", "derive"] }
 | 
				
			||||||
 | 
					toml = "0.8.12"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								README.md
								
								
								
								
							
							
						
						
									
										12
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -9,3 +9,15 @@ plenty of inspiration & guidance taken from [emulator_6502](https://docs.rs/emul
 | 
				
			||||||
see [the george wiki](https://git.augustkline.com/august/george/wiki) for how george works, why she exists, who she is, etc.
 | 
					see [the george wiki](https://git.augustkline.com/august/george/wiki) for how george works, why she exists, who she is, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
the george emulator contains 3 main structs: `Cpu`, `Mem`, and `MemMappedDevice`. `Cpu` represents a 65c02 processor, with functions for interacting with memory and executing instructions. `Mem` is a collection of `MemMappedDevice`'s, which each hold the data for some address space. `Mem` can add `MemMappedDevice`'s, and read from and write to them. A `MemMappedDevice` can have multiple banks at the same address space, and can translate 'global' addresses to 'local' ones (e.g. 0xFFFF in the system's [memory map](https://git.augustkline.com/august/george-hardware/src/branch/main/memory.md) corresponds to 0x1FFF in the ROM).
 | 
					the george emulator contains 3 main structs: `Cpu`, `Mem`, and `MemMappedDevice`. `Cpu` represents a 65c02 processor, with functions for interacting with memory and executing instructions. `Mem` is a collection of `MemMappedDevice`'s, which each hold the data for some address space. `Mem` can add `MemMappedDevice`'s, and read from and write to them. A `MemMappedDevice` can have multiple banks at the same address space, and can translate 'global' addresses to 'local' ones (e.g. 0xFFFF in the system's [memory map](https://git.augustkline.com/august/george-hardware/src/branch/main/memory.md) corresponds to 0x1FFF in the ROM).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## font generation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					george uses a modified version of [cozette](https://github.com/slavfox/Cozette) for her main font. the modified version has 8 pixel wide characters for use with the character generator rom, and has a limit of 256 (0xFF) characters. the toolchain to generate a rom binary is still pretty clunky, and someday i might get around to streamlining it, but for now to make any changes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- open `./src/Cozette.sfd` in fontforge
 | 
				
			||||||
 | 
					- make your edits
 | 
				
			||||||
 | 
					  - to reorder glyphs, edit `./src/georgeencoding.txt` and load it with "Encoding -> Load Encoding -> (select georgeencoding.txt, name it whatever) -> Reencode -> (name of encoding)"
 | 
				
			||||||
 | 
					  - ensure that there are exactly 256 characters in the font
 | 
				
			||||||
 | 
					- generate a bdf font with "File -> Generate Fonts... -> (name of the font, select bdf in the options panel)"
 | 
				
			||||||
 | 
					- open the generated bdf file in a text editor and change the line `FONTBOUNDINGBOX 11 13 0 -3` to `FONTBOUNDINGBOX 8 13 0 -3`
 | 
				
			||||||
 | 
					- now open the font in [bdf view](https://emurenmrz.github.io/bdf_view/), and export a single-row png
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					char_rom = "./src/roms/cozette.rom"
 | 
				
			||||||
 | 
					rom = "./src/roms/george.rom"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vasm6502_oldstyle ./src/george.asm -dotdir -wdc02 -ldots -Fbin -o ./src/george.rom;
 | 
				
			||||||
 | 
					cargo run;
 | 
				
			||||||
 | 
					# hexdump -C ./cpu_dump.bin;
 | 
				
			||||||
							
								
								
									
										2943
									
								
								src/cozette.bdf
								
								
								
								
							
							
						
						
									
										2943
									
								
								src/cozette.bdf
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/cozette.bin
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								src/cozette.bin
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										237
									
								
								src/cpu.rs
								
								
								
								
							
							
						
						
									
										237
									
								
								src/cpu.rs
								
								
								
								
							| 
						 | 
					@ -1,19 +1,23 @@
 | 
				
			||||||
use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
 | 
					// use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError};
 | 
				
			||||||
use crate::instructions::{get_instruction, Instruction};
 | 
					use crate::instructions::{get_instruction, Instruction};
 | 
				
			||||||
use crate::memory::Mem;
 | 
					use crate::memory::Mem;
 | 
				
			||||||
use crate::types::{Byte, Word};
 | 
					use crate::types::{Byte, Word};
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
use std::process::exit;
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					use std::sync::mpsc::Receiver;
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
use std::sync::Mutex;
 | 
					use std::sync::Mutex;
 | 
				
			||||||
 | 
					use std::thread::sleep;
 | 
				
			||||||
use std::time::Duration;
 | 
					use std::time::Duration;
 | 
				
			||||||
use std::{str::FromStr, sync::Arc, thread::sleep};
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{bail, Result};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
pub enum StatusFlag {
 | 
					pub enum StatusFlag {
 | 
				
			||||||
    Negative = 0b1000_0000,
 | 
					    Negative = 0b1000_0000,
 | 
				
			||||||
    Overflow = 0b0100_0000,
 | 
					    Overflow = 0b0100_0000,
 | 
				
			||||||
    Brk = 0b0011_0000,
 | 
					    Brk = 0b0011_0000,
 | 
				
			||||||
    BrkIrq = 0b0010_0000,
 | 
					    //BrkIrq = 0b0010_0000,
 | 
				
			||||||
    Decimal = 0b0000_1000,
 | 
					    Decimal = 0b0000_1000,
 | 
				
			||||||
    IrqDisable = 0b0000_0100,
 | 
					    IrqDisable = 0b0000_0100,
 | 
				
			||||||
    Zero = 0b000_0010,
 | 
					    Zero = 0b000_0010,
 | 
				
			||||||
| 
						 | 
					@ -27,7 +31,7 @@ pub struct Cpu {
 | 
				
			||||||
    pub pc: Word, // Program Counter
 | 
					    pub pc: Word, // Program Counter
 | 
				
			||||||
    pub s: Byte,  // Stack Pointer
 | 
					    pub s: Byte,  // Stack Pointer
 | 
				
			||||||
    pub p: Byte,  // Status Register
 | 
					    pub p: Byte,  // Status Register
 | 
				
			||||||
    pub irq: bool,
 | 
					    pub irq: Receiver<bool>,
 | 
				
			||||||
    pub nmi: bool,
 | 
					    pub nmi: bool,
 | 
				
			||||||
    pub memory: Arc<Mutex<Mem>>,
 | 
					    pub memory: Arc<Mutex<Mem>>,
 | 
				
			||||||
    pub pending_cycles: usize,
 | 
					    pub pending_cycles: usize,
 | 
				
			||||||
| 
						 | 
					@ -35,7 +39,7 @@ pub struct Cpu {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
    pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
 | 
					    pub fn new(memory: Arc<Mutex<Mem>>, irq: Receiver<bool>) -> Self {
 | 
				
			||||||
        Cpu {
 | 
					        Cpu {
 | 
				
			||||||
            a: 0x00,
 | 
					            a: 0x00,
 | 
				
			||||||
            x: 0x00,
 | 
					            x: 0x00,
 | 
				
			||||||
| 
						 | 
					@ -43,30 +47,29 @@ impl Cpu {
 | 
				
			||||||
            pc: 0x0000,
 | 
					            pc: 0x0000,
 | 
				
			||||||
            s: 0xFF,
 | 
					            s: 0xFF,
 | 
				
			||||||
            p: 0b0010_0100,
 | 
					            p: 0b0010_0100,
 | 
				
			||||||
            irq: false,
 | 
					            irq,
 | 
				
			||||||
            nmi: false,
 | 
					            nmi: false,
 | 
				
			||||||
            memory,
 | 
					            memory,
 | 
				
			||||||
            pending_cycles: 0,
 | 
					            pending_cycles: 0,
 | 
				
			||||||
            cycle_count: 0,
 | 
					            cycle_count: 0,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn reset(&mut self) -> Result<(), ExecutionError> {
 | 
					    pub fn reset(&mut self) -> Result<()> {
 | 
				
			||||||
        let reset_vector_pointer = self.read_word(0xFFFC)?;
 | 
					        let reset_vector_pointer = self.read_word(0xFFFC)?;
 | 
				
			||||||
        self.pc = reset_vector_pointer;
 | 
					        self.pc = reset_vector_pointer;
 | 
				
			||||||
        self.pending_cycles = 8;
 | 
					        self.pending_cycles = 0;
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
 | 
					    pub fn read(&self, address: Word) -> Result<Byte> {
 | 
				
			||||||
        let memory = match self.memory.lock() {
 | 
					        let memory = match self.memory.lock() {
 | 
				
			||||||
            Ok(read) => read,
 | 
					            Ok(read) => read,
 | 
				
			||||||
            Err(_) => {
 | 
					            Err(_) => {
 | 
				
			||||||
                println!("Couldn't acquire read lock on memory in cpu thread");
 | 
					                bail!("Couldn't acquire 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
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        Ok(memory.read(address))
 | 
					        Ok(memory.read(address))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pub fn read_word(&self, address: Word) -> Result<Word, MemoryError> {
 | 
					    pub fn read_word(&self, address: Word) -> Result<Word> {
 | 
				
			||||||
        let low_byte = self.read(address)?;
 | 
					        let low_byte = self.read(address)?;
 | 
				
			||||||
        let high_byte = self.read(address + 0x1)?;
 | 
					        let high_byte = self.read(address + 0x1)?;
 | 
				
			||||||
        Ok((high_byte as u16) << 8 | (low_byte as u16))
 | 
					        Ok((high_byte as u16) << 8 | (low_byte as u16))
 | 
				
			||||||
| 
						 | 
					@ -77,27 +80,27 @@ impl Cpu {
 | 
				
			||||||
        0x0100 + self.s as u16
 | 
					        0x0100 + self.s as u16
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn push_stack(&mut self, data: Byte) -> Result<(), ExecutionError> {
 | 
					    pub fn push_stack(&mut self, data: Byte) -> Result<()> {
 | 
				
			||||||
        self.s -= 0x1;
 | 
					        self.s = self.s.wrapping_sub(0x1);
 | 
				
			||||||
        self.write(self.stack_addr(), data)?;
 | 
					        self.write(self.stack_addr(), data)?;
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn push_stack_word(&mut self, address: Word) -> Result<(), ExecutionError> {
 | 
					    pub fn push_stack_word(&mut self, address: Word) -> Result<()> {
 | 
				
			||||||
        self.s -= 0x1;
 | 
					        self.s = self.s.wrapping_sub(0x1);
 | 
				
			||||||
        self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first
 | 
					        self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first
 | 
				
			||||||
        self.s -= 0x1;
 | 
					        self.s = self.s.wrapping_sub(0x1);
 | 
				
			||||||
        self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second
 | 
					        self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn pop_stack(&mut self) -> Result<Byte, ExecutionError> {
 | 
					    pub fn pop_stack(&mut self) -> Result<Byte> {
 | 
				
			||||||
        let byte = self.read(self.stack_addr())?;
 | 
					        let byte = self.read(self.stack_addr())?;
 | 
				
			||||||
        self.s = self.s.wrapping_add(0x1);
 | 
					        self.s = self.s.wrapping_add(0x1);
 | 
				
			||||||
        Ok(byte)
 | 
					        Ok(byte)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn pop_stack_word(&mut self) -> Result<Word, ExecutionError> {
 | 
					    pub fn pop_stack_word(&mut self) -> Result<Word> {
 | 
				
			||||||
        let low_byte = self.pop_stack()?;
 | 
					        let low_byte = self.pop_stack()?;
 | 
				
			||||||
        let high_byte = self.pop_stack()?;
 | 
					        let high_byte = self.pop_stack()?;
 | 
				
			||||||
        let word = ((high_byte as Word) << 8) + low_byte as u16;
 | 
					        let word = ((high_byte as Word) << 8) + low_byte as u16;
 | 
				
			||||||
| 
						 | 
					@ -114,17 +117,17 @@ impl Cpu {
 | 
				
			||||||
    pub fn get_flag(&self, flag: StatusFlag) -> bool {
 | 
					    pub fn get_flag(&self, flag: StatusFlag) -> bool {
 | 
				
			||||||
        (self.p & flag as Byte) > 0
 | 
					        (self.p & flag as Byte) > 0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn is_negative(&self, value: Byte) -> bool {
 | 
					    pub fn is_negative(&self, value: Byte) -> bool {
 | 
				
			||||||
        value & 0b1000_0000 == 0b1000_0000
 | 
					        value & 0b1000_0000 == 0b1000_0000
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
 | 
					    pub fn write(&mut self, address: Word, data: Byte) -> Result<()> {
 | 
				
			||||||
        let mut memory = match self.memory.lock() {
 | 
					        let mut memory = match self.memory.lock() {
 | 
				
			||||||
            Ok(write) => write,
 | 
					            Ok(write) => write,
 | 
				
			||||||
            Err(_) => {
 | 
					            Err(_) => {
 | 
				
			||||||
                println!("Couldn't acquire write lock on memory in cpu thread");
 | 
					                bail!("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);
 | 
					        memory.write(address, data);
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
| 
						 | 
					@ -141,135 +144,71 @@ impl Cpu {
 | 
				
			||||||
        unimplemented!()
 | 
					        unimplemented!()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // fn handle_interrupt(&mut self) -> Result<(), ExecutionError> {
 | 
					    pub fn interrupt(&mut self) {
 | 
				
			||||||
    //     match self.get_flag(StatusFlag::IrqDisable) {
 | 
					        self.push_stack_word(self.pc).unwrap();
 | 
				
			||||||
    //         // Check that interrupts aren't disabled
 | 
					        self.push_stack(self.p).unwrap();
 | 
				
			||||||
    //         true => Ok(self.execute()),
 | 
					        self.set_flag(StatusFlag::IrqDisable, true);
 | 
				
			||||||
    //         false => {
 | 
					        self.pc = self.read_word(0xFFFE).unwrap();
 | 
				
			||||||
    //             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) {
 | 
					 | 
				
			||||||
        sleep(Duration::from_nanos(100));
 | 
					 | 
				
			||||||
        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) => {
 | 
					 | 
				
			||||||
                            let george_error = GeorgeError {
 | 
					 | 
				
			||||||
                                kind: GeorgeErrorKind::Memory(error),
 | 
					 | 
				
			||||||
                                desc: String::from_str("Couldn't read NMI vector").unwrap(),
 | 
					 | 
				
			||||||
                            };
 | 
					 | 
				
			||||||
                            println!("{:?}", george_error.desc);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    self.pending_cycles = 8;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    match self.read_word(0xFFFE) {
 | 
					 | 
				
			||||||
                        Ok(word) => self.pc = word,
 | 
					 | 
				
			||||||
                        Err(error) => {
 | 
					 | 
				
			||||||
                            let george_error = GeorgeError {
 | 
					 | 
				
			||||||
                                kind: GeorgeErrorKind::Memory(error),
 | 
					 | 
				
			||||||
                                desc: String::from_str("Couldn't read IRQ vector").unwrap(),
 | 
					 | 
				
			||||||
                            };
 | 
					 | 
				
			||||||
                            println!("{:?}", george_error.desc);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    self.pending_cycles = 7
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                self.nmi = false;
 | 
					 | 
				
			||||||
                self.irq = false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            let opcode = match self.read(self.pc) {
 | 
					 | 
				
			||||||
                Ok(byte) => byte,
 | 
					 | 
				
			||||||
                Err(error) => {
 | 
					 | 
				
			||||||
                    let george_error = GeorgeError {
 | 
					 | 
				
			||||||
                        desc: format!("Failed to read from memory at address {:#06x}!", self.pc),
 | 
					 | 
				
			||||||
                        kind: GeorgeErrorKind::Memory(error),
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                    println!("{:?}", george_error.desc);
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            let instruction = get_instruction(opcode);
 | 
					 | 
				
			||||||
            match instruction {
 | 
					 | 
				
			||||||
                Instruction::Valid(valid_instruction) => {
 | 
					 | 
				
			||||||
                    //println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
 | 
					 | 
				
			||||||
                    //println!(
 | 
					 | 
				
			||||||
                    //    "Instruction: {:?}, {:#04x}",
 | 
					 | 
				
			||||||
                    //    valid_instruction.opcode, opcode
 | 
					 | 
				
			||||||
                    //);
 | 
					 | 
				
			||||||
                    //println!("");
 | 
					 | 
				
			||||||
                    self.pc += 1;
 | 
					 | 
				
			||||||
                    match valid_instruction.opcode.call(self) {
 | 
					 | 
				
			||||||
                        Ok(_) => {
 | 
					 | 
				
			||||||
                            self.pending_cycles += valid_instruction.cycles as usize;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        Err(error) => {
 | 
					 | 
				
			||||||
                            let george_error = GeorgeError{
 | 
					 | 
				
			||||||
                            desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", 
 | 
					 | 
				
			||||||
                                          self.pc, valid_instruction).to_string(), kind: error};
 | 
					 | 
				
			||||||
                            println!("{:?}", george_error.desc);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
 | 
					 | 
				
			||||||
                    0x02 => {
 | 
					 | 
				
			||||||
                        let memory = match self.memory.lock() {
 | 
					 | 
				
			||||||
                            Ok(read) => read,
 | 
					 | 
				
			||||||
                            Err(_) => {
 | 
					 | 
				
			||||||
                                println!("Couldn't acquire read lock on memory in cpu thread");
 | 
					 | 
				
			||||||
                                let george_error = GeorgeError {
 | 
					 | 
				
			||||||
                                    kind: GeorgeErrorKind::Memory(MemoryError::Unwritable),
 | 
					 | 
				
			||||||
                                    desc: "Couldn't acquire read lock on memory in cpu thread"
 | 
					 | 
				
			||||||
                                        .to_string(),
 | 
					 | 
				
			||||||
                                };
 | 
					 | 
				
			||||||
                                println!("{:?}", george_error.desc);
 | 
					 | 
				
			||||||
                                return;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        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);
 | 
					    pub fn cycle(&mut self) {
 | 
				
			||||||
                        memory
 | 
					        while self.pending_cycles != 0 {
 | 
				
			||||||
                            .dump(PathBuf::from_str("./cpu_dump.bin").unwrap())
 | 
					            // roughly cycle-accurate timing
 | 
				
			||||||
                            .unwrap();
 | 
					            sleep(Duration::from_nanos(100));
 | 
				
			||||||
                        exit(1);
 | 
					            self.pending_cycles -= 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if !self.get_flag(StatusFlag::IrqDisable) && self.irq.recv().unwrap() {
 | 
				
			||||||
 | 
					            self.interrupt();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let opcode = match self.read(self.pc) {
 | 
				
			||||||
 | 
					            Ok(byte) => byte,
 | 
				
			||||||
 | 
					            Err(_) => {
 | 
				
			||||||
 | 
					                println!("Failed to read from memory at address {:#06x}!", self.pc);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let instruction = get_instruction(opcode);
 | 
				
			||||||
 | 
					        match instruction {
 | 
				
			||||||
 | 
					            Instruction::Valid(valid_instruction) => {
 | 
				
			||||||
 | 
					                //println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
 | 
				
			||||||
 | 
					                //println!(
 | 
				
			||||||
 | 
					                //    "Instruction: {:?}, {:#04x}",
 | 
				
			||||||
 | 
					                //    valid_instruction.opcode, opcode
 | 
				
			||||||
 | 
					                //);
 | 
				
			||||||
 | 
					                //println!("");
 | 
				
			||||||
 | 
					                self.pc += 1;
 | 
				
			||||||
 | 
					                match valid_instruction.opcode.call(self) {
 | 
				
			||||||
 | 
					                    Ok(_) => {
 | 
				
			||||||
 | 
					                        self.pending_cycles += valid_instruction.cycles as usize;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    _ => {
 | 
					                    Err(_) => {
 | 
				
			||||||
                        let george_error = GeorgeError {
 | 
					                        println!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}", self.pc, valid_instruction);
 | 
				
			||||||
                            kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction),
 | 
					                    }
 | 
				
			||||||
                            desc: format!(
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Instruction::Invalid(invalid_instruction) => match invalid_instruction.opcode {
 | 
				
			||||||
 | 
					                0x02 => {
 | 
				
			||||||
 | 
					                    let memory = match self.memory.lock() {
 | 
				
			||||||
 | 
					                        Ok(read) => read,
 | 
				
			||||||
 | 
					                        Err(_) => {
 | 
				
			||||||
 | 
					                            println!("Couldn't acquire read lock on memory in cpu thread");
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    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.try_recv().unwrap_or_default(), nmi = self.nmi);
 | 
				
			||||||
 | 
					                    memory
 | 
				
			||||||
 | 
					                        .dump(PathBuf::from_str("./cpu_dump.bin").unwrap())
 | 
				
			||||||
 | 
					                        .unwrap();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => {
 | 
				
			||||||
 | 
					                    println!(
 | 
				
			||||||
                        "An invalid instruction with opcode {:#04x} was called at address {:#06x}",
 | 
					                        "An invalid instruction with opcode {:#04x} was called at address {:#06x}",
 | 
				
			||||||
                        invalid_instruction.opcode, self.pc
 | 
					                        invalid_instruction.opcode, self.pc
 | 
				
			||||||
                    ),
 | 
					                    );
 | 
				
			||||||
                        };
 | 
					                }
 | 
				
			||||||
                        println!("{:?}", george_error.desc);
 | 
					            },
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.pending_cycles -= 1;
 | 
					 | 
				
			||||||
        self.cycle_count += 1;
 | 
					        self.cycle_count += 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/george
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								src/george
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										240
									
								
								src/george.asm
								
								
								
								
							
							
						
						
									
										240
									
								
								src/george.asm
								
								
								
								
							| 
						 | 
					@ -1,240 +0,0 @@
 | 
				
			||||||
.setcpu "65C02"
 | 
					 | 
				
			||||||
.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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
key_row = $200 ; used for character lookup when key pressed
 | 
					 | 
				
			||||||
key_col = $201
 | 
					 | 
				
			||||||
cursor = $202
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
kb_row0 = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.segment "ROM"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
reset:
 | 
					 | 
				
			||||||
    ldx #0; initialize data stack pointer
 | 
					 | 
				
			||||||
;lda #$80
 | 
					 | 
				
			||||||
;sta $200
 | 
					 | 
				
			||||||
;adc $200
 | 
					 | 
				
			||||||
;breakpoint
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
initdisplay:
 | 
					 | 
				
			||||||
    lda #$20
 | 
					 | 
				
			||||||
    ldy #0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cleardisplay:
 | 
					 | 
				
			||||||
    sta $6000,y
 | 
					 | 
				
			||||||
    sta $6100,y
 | 
					 | 
				
			||||||
    sta $6200,y
 | 
					 | 
				
			||||||
    sta $6300,y
 | 
					 | 
				
			||||||
    sta $6400,y
 | 
					 | 
				
			||||||
    sta $6500,y
 | 
					 | 
				
			||||||
    sta $6600,y
 | 
					 | 
				
			||||||
    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 #%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 -- )
 | 
					 | 
				
			||||||
    lda 0, x ; load a with character to draw
 | 
					 | 
				
			||||||
    pop ; and pop it off the stack
 | 
					 | 
				
			||||||
    jsr get_char_address ; calculate where to put the character in memory
 | 
					 | 
				
			||||||
    sta (0, x) ; store a at the address pointed to on the stack
 | 
					 | 
				
			||||||
    rts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
get_char_address:   ; gets vram address for a character at (x, y),
 | 
					 | 
				
			||||||
                    ; (n1: x n2: y -- n: $6000 + x + (64 * y))
 | 
					 | 
				
			||||||
    ;jsr push_lit ; push 64 onto stack, low byte first
 | 
					 | 
				
			||||||
    ;.byte 64
 | 
					 | 
				
			||||||
    ;.byte 0
 | 
					 | 
				
			||||||
    pha
 | 
					 | 
				
			||||||
    lda #64
 | 
					 | 
				
			||||||
    push ; doing this instead until `push_lit` is fixed
 | 
					 | 
				
			||||||
    sta 0, x
 | 
					 | 
				
			||||||
    stz 1, x
 | 
					 | 
				
			||||||
    jsr mult ; multiply 64 with y (n2)
 | 
					 | 
				
			||||||
    jsr plus ; add result with x (n1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ;jsr push_lit ; push vram address onto the stack
 | 
					 | 
				
			||||||
    ;.byte $00
 | 
					 | 
				
			||||||
    ;.byte $60
 | 
					 | 
				
			||||||
    lda #$60
 | 
					 | 
				
			||||||
    push
 | 
					 | 
				
			||||||
    sta 1, x
 | 
					 | 
				
			||||||
    stz 0, x
 | 
					 | 
				
			||||||
    jsr plus ; add vram start address to result
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pla
 | 
					 | 
				
			||||||
    rts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    jsr get_char_address
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; --- Data Stack --- ;
 | 
					 | 
				
			||||||
; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM
 | 
					 | 
				
			||||||
; data stack is built up of 2-byte cells
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; TODO: this is broken, jumping here does nothing to the stack and skips several instructions, could be an emulator problem tho
 | 
					 | 
				
			||||||
push_lit: ; this bad boy lets you inline a literal (low byte first) right after `jsr push_lit` and put it on the stack, once again, on this channel we love garth wilson
 | 
					 | 
				
			||||||
   push2
 | 
					 | 
				
			||||||
   phx
 | 
					 | 
				
			||||||
       tsx
 | 
					 | 
				
			||||||
       txa
 | 
					 | 
				
			||||||
       tay
 | 
					 | 
				
			||||||
   plx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   lda $102, y
 | 
					 | 
				
			||||||
   sta 0, x
 | 
					 | 
				
			||||||
   clc
 | 
					 | 
				
			||||||
   adc #2
 | 
					 | 
				
			||||||
   sta $102, y
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   lda $103, y
 | 
					 | 
				
			||||||
   sta 1, x
 | 
					 | 
				
			||||||
   adc #0
 | 
					 | 
				
			||||||
   sta $103, y
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fetch: 
 | 
					 | 
				
			||||||
    lda (0, x)
 | 
					 | 
				
			||||||
    pha
 | 
					 | 
				
			||||||
    inc 0, x
 | 
					 | 
				
			||||||
    bne @1
 | 
					 | 
				
			||||||
    inc 1, x
 | 
					 | 
				
			||||||
@1:     lda (0, x)
 | 
					 | 
				
			||||||
        bra put
 | 
					 | 
				
			||||||
    push
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
put:
 | 
					 | 
				
			||||||
    sta 1, x
 | 
					 | 
				
			||||||
    pla
 | 
					 | 
				
			||||||
    sta 0, x
 | 
					 | 
				
			||||||
    rts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
plus: ; add: (n1 n2 -- n1+n2)
 | 
					 | 
				
			||||||
    clc
 | 
					 | 
				
			||||||
    lda 0, x
 | 
					 | 
				
			||||||
    adc 2, x
 | 
					 | 
				
			||||||
    sta 2, x
 | 
					 | 
				
			||||||
    lda 1, x
 | 
					 | 
				
			||||||
    adc 3, x
 | 
					 | 
				
			||||||
    sta 3, x
 | 
					 | 
				
			||||||
    pop
 | 
					 | 
				
			||||||
    rts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TODO: will try to figure it out later
 | 
					 | 
				
			||||||
    phy
 | 
					 | 
				
			||||||
    stz n
 | 
					 | 
				
			||||||
    ldy #0
 | 
					 | 
				
			||||||
@1:     lsr 3, x
 | 
					 | 
				
			||||||
        ror 2, x
 | 
					 | 
				
			||||||
        bcc @2
 | 
					 | 
				
			||||||
            clc
 | 
					 | 
				
			||||||
            lda n
 | 
					 | 
				
			||||||
            adc 0, x
 | 
					 | 
				
			||||||
            sta n
 | 
					 | 
				
			||||||
            tya
 | 
					 | 
				
			||||||
            adc 1, x
 | 
					 | 
				
			||||||
            tay
 | 
					 | 
				
			||||||
@2:     asl 0, x
 | 
					 | 
				
			||||||
        rol 1, x
 | 
					 | 
				
			||||||
        lda 2, x
 | 
					 | 
				
			||||||
        ora 3, x
 | 
					 | 
				
			||||||
    bne @1
 | 
					 | 
				
			||||||
    lda n
 | 
					 | 
				
			||||||
    sta 2, x
 | 
					 | 
				
			||||||
    sty 3, x
 | 
					 | 
				
			||||||
    pop
 | 
					 | 
				
			||||||
    ply
 | 
					 | 
				
			||||||
    rts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.segment "VECTOR"
 | 
					 | 
				
			||||||
.word reset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,10 +0,0 @@
 | 
				
			||||||
MEMORY {
 | 
					 | 
				
			||||||
        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 {
 | 
					 | 
				
			||||||
        ROM: load = "ROM", type = ro;
 | 
					 | 
				
			||||||
        VECTOR: load = "VECTOR", type = ro;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -0,0 +1,92 @@
 | 
				
			||||||
 | 
					use minifb::{InputCallback, Key};
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    path::PathBuf,
 | 
				
			||||||
 | 
					    process::exit,
 | 
				
			||||||
 | 
					    str::FromStr,
 | 
				
			||||||
 | 
					    sync::{Arc, Mutex},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::memory::Mem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Keyboard {
 | 
				
			||||||
 | 
					    memory: Arc<Mutex<Mem>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Keyboard {
 | 
				
			||||||
 | 
					    pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
 | 
				
			||||||
 | 
					        Self { memory }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl InputCallback for Keyboard {
 | 
				
			||||||
 | 
					    fn add_char(&mut self, _uni_char: u32) {}
 | 
				
			||||||
 | 
					    fn set_key_state(&mut self, key: Key, _state: bool) {
 | 
				
			||||||
 | 
					        let mut row0 = 0;
 | 
				
			||||||
 | 
					        let mut row1 = 0;
 | 
				
			||||||
 | 
					        let mut row2 = 0;
 | 
				
			||||||
 | 
					        let mut row3 = 0;
 | 
				
			||||||
 | 
					        let mut row4 = 0;
 | 
				
			||||||
 | 
					        let mut row5 = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match key {
 | 
				
			||||||
 | 
					            Key::Escape => row0 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::W => row0 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::E => row0 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            Key::R => row0 ^= 0b0001_0000,
 | 
				
			||||||
 | 
					            Key::T => row0 ^= 0b0000_1000,
 | 
				
			||||||
 | 
					            Key::U => row0 ^= 0b0000_0100,
 | 
				
			||||||
 | 
					            Key::O => row0 ^= 0b0000_0010,
 | 
				
			||||||
 | 
					            Key::Backspace => row0 ^= 0b0000_0001,
 | 
				
			||||||
 | 
					            Key::Tab => row1 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::Q => row1 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::S => row1 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            Key::G => row1 ^= 0b0001_0000,
 | 
				
			||||||
 | 
					            Key::Y => row1 ^= 0b0000_1000,
 | 
				
			||||||
 | 
					            Key::I => row1 ^= 0b0000_0100,
 | 
				
			||||||
 | 
					            Key::P => row1 ^= 0b0000_0010,
 | 
				
			||||||
 | 
					            Key::Enter => row1 ^= 0b0000_0001,
 | 
				
			||||||
 | 
					            Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::D => row2 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::V => row2 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            Key::H => row2 ^= 0b0001_0000,
 | 
				
			||||||
 | 
					            Key::K => row2 ^= 0b0000_1000,
 | 
				
			||||||
 | 
					            Key::Apostrophe => row2 ^= 0b0000_0100,
 | 
				
			||||||
 | 
					            Key::Slash => row2 ^= 0b0000_0010,
 | 
				
			||||||
 | 
					            Key::A => row2 ^= 0b0000_0001,
 | 
				
			||||||
 | 
					            Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::Z => row3 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::F => row3 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            Key::B => row3 ^= 0b0001_0000,
 | 
				
			||||||
 | 
					            Key::J => row3 ^= 0b0000_1000,
 | 
				
			||||||
 | 
					            Key::L => row3 ^= 0b0000_0100,
 | 
				
			||||||
 | 
					            Key::Key2 => row3 ^= 0b0000_0010,
 | 
				
			||||||
 | 
					            Key::Key4 => row3 ^= 0b0000_0001,
 | 
				
			||||||
 | 
					            Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::X => row4 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::C => row4 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            Key::N => row4 ^= 0b0001_0000,
 | 
				
			||||||
 | 
					            Key::M => row4 ^= 0b0000_1000,
 | 
				
			||||||
 | 
					            Key::Comma => row4 ^= 0b0000_0100,
 | 
				
			||||||
 | 
					            Key::Key1 => row4 ^= 0b0000_0010,
 | 
				
			||||||
 | 
					            Key::Key3 => row4 ^= 0b0000_0001,
 | 
				
			||||||
 | 
					            Key::LeftSuper => row5 ^= 0b1000_0000,
 | 
				
			||||||
 | 
					            Key::Space => row5 ^= 0b0100_0000,
 | 
				
			||||||
 | 
					            Key::RightSuper => row5 ^= 0b0010_0000,
 | 
				
			||||||
 | 
					            _ => {}
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match &mut self.memory.lock() {
 | 
				
			||||||
 | 
					            Ok(memory) => {
 | 
				
			||||||
 | 
					                memory.write(0x4400, row0);
 | 
				
			||||||
 | 
					                memory.write(0x4401, row1);
 | 
				
			||||||
 | 
					                memory.write(0x4402, row2);
 | 
				
			||||||
 | 
					                memory.write(0x4403, row3);
 | 
				
			||||||
 | 
					                memory.write(0x4404, row4);
 | 
				
			||||||
 | 
					                memory.write(0x4405, row5);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Err(_error) => {
 | 
				
			||||||
 | 
					                println!("couldnt get a lock on memory while saving keyboard registers");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										90
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
					@ -9,27 +9,58 @@ mod types;
 | 
				
			||||||
mod video;
 | 
					mod video;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::cpu::Cpu;
 | 
					use crate::cpu::Cpu;
 | 
				
			||||||
 | 
					use crate::keyboard::Keyboard;
 | 
				
			||||||
use crate::memory::Mem;
 | 
					use crate::memory::Mem;
 | 
				
			||||||
use crate::video::Crtc;
 | 
					use crate::video::Crtc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use clap::Parser;
 | 
				
			||||||
 | 
					use minifb::{Scale, ScaleMode, Window, WindowOptions};
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::fs::File;
 | 
				
			||||||
 | 
					use std::io::Read;
 | 
				
			||||||
 | 
					use std::process::exit;
 | 
				
			||||||
use std::str::FromStr;
 | 
					use std::str::FromStr;
 | 
				
			||||||
use std::sync::Mutex;
 | 
					use std::sync::{mpsc, Mutex};
 | 
				
			||||||
use std::thread::sleep;
 | 
					 | 
				
			||||||
use std::time::Duration;
 | 
					 | 
				
			||||||
use std::{path::PathBuf, sync::Arc, thread};
 | 
					use std::{path::PathBuf, sync::Arc, thread};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use toml::Table;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#[derive(Parser)]
 | 
				
			||||||
 | 
					//struct Cli {
 | 
				
			||||||
 | 
					//    // Load a rom onto the system rom
 | 
				
			||||||
 | 
					//    #[arg(short, required = true)]
 | 
				
			||||||
 | 
					//    rom: Option<std::path::PathBuf>,
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Serialize, Deserialize, Debug)]
 | 
				
			||||||
 | 
					struct Config {
 | 
				
			||||||
 | 
					    char_rom: Option<String>,
 | 
				
			||||||
 | 
					    rom: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    let mut memory = Mem::new();
 | 
					    let config: Config = match File::open("./config.toml") {
 | 
				
			||||||
    let binary = match std::fs::File::open(PathBuf::from(
 | 
					        Ok(mut file) => {
 | 
				
			||||||
        "/Users/kline/projects/winter/george-emu/src/george",
 | 
					            let mut string = String::new();
 | 
				
			||||||
    )) {
 | 
					            file.read_to_string(&mut string).unwrap();
 | 
				
			||||||
        Ok(file) => file,
 | 
					            toml::from_str(string.as_str()).unwrap()
 | 
				
			||||||
        Err(error) => panic!("Couldn't open binary file! {:?}", error),
 | 
					        }
 | 
				
			||||||
 | 
					        Err(_) => return,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if let Err(error) = memory.read_from_bin(binary) {
 | 
					    println!("{config:#?}");
 | 
				
			||||||
 | 
					    let mut memory = Mem::new();
 | 
				
			||||||
 | 
					    let rom = match std::fs::File::open(config.rom) {
 | 
				
			||||||
 | 
					        Ok(file) => file,
 | 
				
			||||||
 | 
					        Err(error) => panic!("Couldn't open main rom! {:?}", error),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if let Err(error) = memory.load_rom(rom) {
 | 
				
			||||||
        println!("{:?}", error);
 | 
					        println!("{:?}", error);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (interrupt_tx, interrupt_rx) = mpsc::channel();
 | 
				
			||||||
 | 
					    let (window_tx, window_rx) = mpsc::channel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memory
 | 
					    memory
 | 
				
			||||||
        .dump(PathBuf::from_str("./coredump.bin").unwrap())
 | 
					        .dump(PathBuf::from_str("./coredump.bin").unwrap())
 | 
				
			||||||
        .unwrap();
 | 
					        .unwrap();
 | 
				
			||||||
| 
						 | 
					@ -37,12 +68,45 @@ fn main() {
 | 
				
			||||||
    let shared_memory = Arc::new(Mutex::new(memory));
 | 
					    let shared_memory = Arc::new(Mutex::new(memory));
 | 
				
			||||||
    let cpu_memory = shared_memory.clone();
 | 
					    let cpu_memory = shared_memory.clone();
 | 
				
			||||||
    let display_memory = shared_memory.clone();
 | 
					    let display_memory = shared_memory.clone();
 | 
				
			||||||
 | 
					    let keyboard_memory = shared_memory.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    thread::spawn(move || {
 | 
					    thread::spawn(move || {
 | 
				
			||||||
        let mut cpu = Cpu::new(cpu_memory);
 | 
					        let mut cpu = Cpu::new(cpu_memory, interrupt_rx);
 | 
				
			||||||
        cpu.reset().unwrap();
 | 
					        cpu.reset().unwrap();
 | 
				
			||||||
        cpu.execute();
 | 
					        cpu.execute();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    let mut screen = Crtc::new(display_memory);
 | 
					
 | 
				
			||||||
    screen.run();
 | 
					    thread::spawn(move || {
 | 
				
			||||||
 | 
					        let mut screen = Crtc::new(
 | 
				
			||||||
 | 
					            display_memory,
 | 
				
			||||||
 | 
					            config.char_rom.as_ref(),
 | 
				
			||||||
 | 
					            interrupt_tx,
 | 
				
			||||||
 | 
					            window_tx,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        screen.run();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut window = Window::new(
 | 
				
			||||||
 | 
					        "ʕ·ᴥ·ʔ-☆",
 | 
				
			||||||
 | 
					        512,
 | 
				
			||||||
 | 
					        380,
 | 
				
			||||||
 | 
					        WindowOptions {
 | 
				
			||||||
 | 
					            resize: true,
 | 
				
			||||||
 | 
					            borderless: true,
 | 
				
			||||||
 | 
					            title: true,
 | 
				
			||||||
 | 
					            transparency: false,
 | 
				
			||||||
 | 
					            scale: Scale::X2,
 | 
				
			||||||
 | 
					            scale_mode: ScaleMode::AspectRatioStretch,
 | 
				
			||||||
 | 
					            topmost: false,
 | 
				
			||||||
 | 
					            none: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.set_input_callback(Box::new(Keyboard::new(keyboard_memory)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while window.is_open() {
 | 
				
			||||||
 | 
					        let buffer = window_rx.recv().unwrap();
 | 
				
			||||||
 | 
					        window.update_with_buffer(&buffer, 512, 380).unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,17 +30,30 @@ impl Mem {
 | 
				
			||||||
        self.data[address as usize] = data;
 | 
					        self.data[address as usize] = data;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> {
 | 
					    pub fn load_rom(&mut self, rom: File) -> Result<(), MemoryError> {
 | 
				
			||||||
        let bytes = f.bytes();
 | 
					        let bytes = rom.bytes();
 | 
				
			||||||
        for (address, byte) in bytes.enumerate() {
 | 
					        for (address, byte) in bytes.enumerate() {
 | 
				
			||||||
            match byte {
 | 
					            match byte {
 | 
				
			||||||
                Ok(value) => self.write(address as Word, value),
 | 
					                Ok(value) => self.write(address as Word + 0x8000, value),
 | 
				
			||||||
                Err(_) => {
 | 
					                Err(_) => {
 | 
				
			||||||
                    println!("couldn't write byte {:#04x}", address);
 | 
					                    println!("Loading rom: couldn't write byte {:#04x}", address);
 | 
				
			||||||
                    return Err(MemoryError::Unwritable);
 | 
					                    return Err(MemoryError::Unwritable);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    //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(_) => {
 | 
				
			||||||
 | 
					    //                println!("couldn't write byte {:#04x}", address);
 | 
				
			||||||
 | 
					    //                return Err(MemoryError::Unwritable);
 | 
				
			||||||
 | 
					    //            }
 | 
				
			||||||
 | 
					    //        }
 | 
				
			||||||
 | 
					    //    }
 | 
				
			||||||
 | 
					    //    Ok(())
 | 
				
			||||||
 | 
					    //}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,164 @@
 | 
				
			||||||
 | 
					; .setcpu "65C02"
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					key_row = $200 ; used for character lookup when key pressed
 | 
				
			||||||
 | 
					key_col = $201
 | 
				
			||||||
 | 
					cursor = $202
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char_buffer = $300 ; 256 byte character buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kb_row = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
 | 
				
			||||||
 | 
					kb_row_cache = $203 ; cache 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .org $8000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reset:
 | 
				
			||||||
 | 
					    sei
 | 
				
			||||||
 | 
					    ldx #0; initialize data stack pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					initdisplay:
 | 
				
			||||||
 | 
					    lda #$20
 | 
				
			||||||
 | 
					    ldy #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cleardisplay:
 | 
				
			||||||
 | 
					    sta $6000,y
 | 
				
			||||||
 | 
					    sta $6100,y
 | 
				
			||||||
 | 
					    sta $6200,y
 | 
				
			||||||
 | 
					    sta $6300,y
 | 
				
			||||||
 | 
					    sta $6400,y
 | 
				
			||||||
 | 
					    sta $6500,y
 | 
				
			||||||
 | 
					    sta $6600,y
 | 
				
			||||||
 | 
					    sta $6700,y ; this goes slightly over but it's fine
 | 
				
			||||||
 | 
					    iny
 | 
				
			||||||
 | 
					    bne cleardisplay
 | 
				
			||||||
 | 
					    cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main:
 | 
				
			||||||
 | 
					    jsr draw
 | 
				
			||||||
 | 
					    jmp main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboard:
 | 
				
			||||||
 | 
					    ldy #0 
 | 
				
			||||||
 | 
					    .loop: ; loop through each row
 | 
				
			||||||
 | 
					        lda kb_row, y
 | 
				
			||||||
 | 
					        beq .skip ; if row has no key pressed, skip checking which key
 | 
				
			||||||
 | 
					        sta kb_row_cache, y ; if key pressed, cache it
 | 
				
			||||||
 | 
					        lda kb_row, y
 | 
				
			||||||
 | 
					        cmp kb_row_cache, y ; has key changed?
 | 
				
			||||||
 | 
					        beq key_down
 | 
				
			||||||
 | 
					        .skip:
 | 
				
			||||||
 | 
					            iny
 | 
				
			||||||
 | 
					            cpy #5
 | 
				
			||||||
 | 
					            bne .loop
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					key_down:               ; 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 unset, don't go store character columnb
 | 
				
			||||||
 | 
					        .skip:
 | 
				
			||||||
 | 
					            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
 | 
				
			||||||
 | 
					    ply
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keymap:
 | 
				
			||||||
 | 
					    .byte "?outrew?"
 | 
				
			||||||
 | 
					    .byte "?piygsq?"
 | 
				
			||||||
 | 
					    .byte "a??khvd?"
 | 
				
			||||||
 | 
					    .byte "42ljbfz?"
 | 
				
			||||||
 | 
					    .byte "31?mncx?"
 | 
				
			||||||
 | 
					    .byte "?????  m"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					draw:
 | 
				
			||||||
 | 
					    push_coords #0, #0
 | 
				
			||||||
 | 
					    push_char #$00
 | 
				
			||||||
 | 
					    jsr draw_char
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
 | 
				
			||||||
 | 
					    lda 0, x ; load a with character to draw
 | 
				
			||||||
 | 
					    pop ; and pop it off the stack
 | 
				
			||||||
 | 
					    jsr get_char_address ; calculate where to put the character in memory
 | 
				
			||||||
 | 
					    sta (0, x) ; store a at the address pointed to on the stack
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					get_char_address:   ; gets vram address for a character at (x, y),
 | 
				
			||||||
 | 
					                    ; (n1: x n2: y -- n: $6000 + x + (64 * y))
 | 
				
			||||||
 | 
					    ;jsr push_lit ; push 64 onto stack, low byte first
 | 
				
			||||||
 | 
					    ;.byte 64
 | 
				
			||||||
 | 
					    ;.byte 0
 | 
				
			||||||
 | 
					    pha
 | 
				
			||||||
 | 
					    lda #64
 | 
				
			||||||
 | 
					    push ; doing this instead until `push_lit` is fixed
 | 
				
			||||||
 | 
					    sta 0, x
 | 
				
			||||||
 | 
					    stz 1, x
 | 
				
			||||||
 | 
					    jsr mult ; multiply 64 with y (n2)
 | 
				
			||||||
 | 
					    jsr plus ; add result with x (n1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ;jsr push_lit ; push vram address onto the stack
 | 
				
			||||||
 | 
					    ;.byte $00
 | 
				
			||||||
 | 
					    ;.byte $60
 | 
				
			||||||
 | 
					    lda #$60
 | 
				
			||||||
 | 
					    push
 | 
				
			||||||
 | 
					    sta 1, x
 | 
				
			||||||
 | 
					    stz 0, x
 | 
				
			||||||
 | 
					    jsr plus ; add vram start address to result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
 | 
				
			||||||
 | 
					    jsr get_char_address
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					isr: ; interrupt service routine
 | 
				
			||||||
 | 
					    pha
 | 
				
			||||||
 | 
					    phx
 | 
				
			||||||
 | 
					    phy
 | 
				
			||||||
 | 
					    jsr keyboard
 | 
				
			||||||
 | 
					    ply
 | 
				
			||||||
 | 
					    plx
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rti
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .include "math.inc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .org $fffc
 | 
				
			||||||
 | 
					    .word reset
 | 
				
			||||||
 | 
					    .word isr
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,142 @@
 | 
				
			||||||
 | 
					; .setcpu "65C02"
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					key_row = $200 ; used for character lookup when key pressed
 | 
				
			||||||
 | 
					key_col = $201
 | 
				
			||||||
 | 
					cursor = $202
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char_buffer = $300 ; 256 byte character buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kb_row = $4400 ; keyboard hardware register, there are 5 more but i can just increment from here
 | 
				
			||||||
 | 
					kb_row_cache = $203 ; cache 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .org $8000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reset:
 | 
				
			||||||
 | 
					    sei
 | 
				
			||||||
 | 
					    ldx #0; initialize data stack pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					initdisplay:
 | 
				
			||||||
 | 
					    lda #$20
 | 
				
			||||||
 | 
					    ldy #0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cleardisplay:
 | 
				
			||||||
 | 
					    sta $6000,y
 | 
				
			||||||
 | 
					    sta $6100,y
 | 
				
			||||||
 | 
					    sta $6200,y
 | 
				
			||||||
 | 
					    sta $6300,y
 | 
				
			||||||
 | 
					    sta $6400,y
 | 
				
			||||||
 | 
					    sta $6500,y
 | 
				
			||||||
 | 
					    sta $6600,y
 | 
				
			||||||
 | 
					    sta $6700,y ; this goes slightly over but it's fine
 | 
				
			||||||
 | 
					    iny
 | 
				
			||||||
 | 
					    bne cleardisplay
 | 
				
			||||||
 | 
					    cli
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main:
 | 
				
			||||||
 | 
					    jsr draw
 | 
				
			||||||
 | 
					    jmp main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboard:
 | 
				
			||||||
 | 
					    ldy #5
 | 
				
			||||||
 | 
					    .loop: ; loop through each row
 | 
				
			||||||
 | 
					        lda kb_row, y
 | 
				
			||||||
 | 
					        bne key_down; if row has key pressed, go to key_down subroutine
 | 
				
			||||||
 | 
					        dey
 | 
				
			||||||
 | 
					        bne .loop
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					key_down:               ; 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 unset, don't go store character columnb
 | 
				
			||||||
 | 
					        .skip:
 | 
				
			||||||
 | 
					            iny
 | 
				
			||||||
 | 
					            cpy #8
 | 
				
			||||||
 | 
					            bne .find_col   ; loop until we've checked each bit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_something_w_key: ; we've stored the character position, now let's 
 | 
				
			||||||
 | 
					    lda keymap, y
 | 
				
			||||||
 | 
					    ldy cursor
 | 
				
			||||||
 | 
					    sta $6000, y
 | 
				
			||||||
 | 
					    ply
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keymap_0:
 | 
				
			||||||
 | 
					    .byte "?outrew?"
 | 
				
			||||||
 | 
					keymap_1:
 | 
				
			||||||
 | 
					    .byte "?piygsq?"
 | 
				
			||||||
 | 
					keymap_2:
 | 
				
			||||||
 | 
					    .byte "a??khvd?"
 | 
				
			||||||
 | 
					keymap_3:
 | 
				
			||||||
 | 
					    .byte "42ljbfz?"
 | 
				
			||||||
 | 
					keymap_4:
 | 
				
			||||||
 | 
					    .byte "31?mncx?"
 | 
				
			||||||
 | 
					keymap_5:
 | 
				
			||||||
 | 
					    .byte "?????  m"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					draw:
 | 
				
			||||||
 | 
					    push_coords #0, #0
 | 
				
			||||||
 | 
					    push_char #$00
 | 
				
			||||||
 | 
					    jsr draw_char
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
 | 
				
			||||||
 | 
					    lda 0, x ; load a with character to draw
 | 
				
			||||||
 | 
					    pop ; and pop it off the stack
 | 
				
			||||||
 | 
					    jsr get_char_address ; calculate where to put the character in memory
 | 
				
			||||||
 | 
					    sta (0, x) ; store a at the address pointed to on the stack
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					get_char_address:   ; gets vram address for a character at (x, y),
 | 
				
			||||||
 | 
					                    ; (n1: x n2: y -- n: $6000 + x + (64 * y))
 | 
				
			||||||
 | 
					    ;jsr push_lit ; push 64 onto stack, low byte first
 | 
				
			||||||
 | 
					    ;.byte 64
 | 
				
			||||||
 | 
					    ;.byte 0
 | 
				
			||||||
 | 
					    pha
 | 
				
			||||||
 | 
					    lda #64
 | 
				
			||||||
 | 
					    push ; doing this instead until `push_lit` is fixed
 | 
				
			||||||
 | 
					    sta 0, x
 | 
				
			||||||
 | 
					    stz 1, x
 | 
				
			||||||
 | 
					    jsr mult ; multiply 64 with y (n2)
 | 
				
			||||||
 | 
					    jsr plus ; add result with x (n1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ;jsr push_lit ; push vram address onto the stack
 | 
				
			||||||
 | 
					    ;.byte $00
 | 
				
			||||||
 | 
					    ;.byte $60
 | 
				
			||||||
 | 
					    lda #$60
 | 
				
			||||||
 | 
					    push
 | 
				
			||||||
 | 
					    sta 1, x
 | 
				
			||||||
 | 
					    stz 0, x
 | 
				
			||||||
 | 
					    jsr plus ; add vram start address to result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
 | 
				
			||||||
 | 
					    jsr get_char_address
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					isr: ; interrupt service routine
 | 
				
			||||||
 | 
					    pha
 | 
				
			||||||
 | 
					    phx
 | 
				
			||||||
 | 
					    phy
 | 
				
			||||||
 | 
					    jsr keyboard
 | 
				
			||||||
 | 
					    ply
 | 
				
			||||||
 | 
					    plx
 | 
				
			||||||
 | 
					    pla
 | 
				
			||||||
 | 
					    rti
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .include "math.inc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .org $fffc
 | 
				
			||||||
 | 
					    .word reset
 | 
				
			||||||
 | 
					    .word isr
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					    .macro breakpoint ; $02 isn't a valid instruction, the emulator will see this and halt, dump memory contents
 | 
				
			||||||
 | 
					        .byte $02
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .macro pop ; drops a data stack cell
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .macro pop2 ; drops 2 data stack cells
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					        inx
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .macro push ; push a data stack cell
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .macro push2 ; push 2 data stack cells
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					        dex
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    .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
 | 
				
			||||||
 | 
					    .endm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,81 @@
 | 
				
			||||||
 | 
					; --- Data Stack --- ;
 | 
				
			||||||
 | 
					; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM
 | 
				
			||||||
 | 
					; data stack is built up of 2-byte cells
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					; TODO: this is broken, jumping here does nothing to the stack and skips several instructions, could be an emulator problem tho
 | 
				
			||||||
 | 
					; push_lit: ; this bad boy lets you inline a literal (low byte first) right after `jsr push_lit` and put it on the stack, once again, on this channel we love garth wilson
 | 
				
			||||||
 | 
					;    push2
 | 
				
			||||||
 | 
					;    phx
 | 
				
			||||||
 | 
					;        tsx
 | 
				
			||||||
 | 
					;        txa
 | 
				
			||||||
 | 
					;        tay
 | 
				
			||||||
 | 
					;    plx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;    lda $102, y
 | 
				
			||||||
 | 
					;    sta 0, x
 | 
				
			||||||
 | 
					;    clc
 | 
				
			||||||
 | 
					;    adc #2
 | 
				
			||||||
 | 
					;    sta $102, y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;    lda $103, y
 | 
				
			||||||
 | 
					;    sta 1, x
 | 
				
			||||||
 | 
					;    adc #0
 | 
				
			||||||
 | 
					;    sta $103, y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					; fetch: 
 | 
				
			||||||
 | 
					;     lda (0, x)
 | 
				
			||||||
 | 
					;     pha
 | 
				
			||||||
 | 
					;     inc 0, x
 | 
				
			||||||
 | 
					;     bne .1
 | 
				
			||||||
 | 
					;     inc 1, x
 | 
				
			||||||
 | 
					;     .1:     
 | 
				
			||||||
 | 
					;         lda (0, x)
 | 
				
			||||||
 | 
					;         bra put
 | 
				
			||||||
 | 
					;     push
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					; put:
 | 
				
			||||||
 | 
					;     sta 1, x
 | 
				
			||||||
 | 
					;     pla
 | 
				
			||||||
 | 
					;     sta 0, x
 | 
				
			||||||
 | 
					;     rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					plus: ; add: (n1 n2 -- n1+n2)
 | 
				
			||||||
 | 
					    clc
 | 
				
			||||||
 | 
					    lda 0, x
 | 
				
			||||||
 | 
					    adc 2, x
 | 
				
			||||||
 | 
					    sta 2, x
 | 
				
			||||||
 | 
					    lda 1, x
 | 
				
			||||||
 | 
					    adc 3, x
 | 
				
			||||||
 | 
					    sta 3, x
 | 
				
			||||||
 | 
					    pop
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TODO: will try to figure it out later
 | 
				
			||||||
 | 
					    phy
 | 
				
			||||||
 | 
					    stz n
 | 
				
			||||||
 | 
					    ldy #0
 | 
				
			||||||
 | 
					    .1:     
 | 
				
			||||||
 | 
					        lsr 3, x
 | 
				
			||||||
 | 
					        ror 2, x
 | 
				
			||||||
 | 
					        bcc .2
 | 
				
			||||||
 | 
					            clc
 | 
				
			||||||
 | 
					            lda n
 | 
				
			||||||
 | 
					            adc 0, x
 | 
				
			||||||
 | 
					            sta n
 | 
				
			||||||
 | 
					            tya
 | 
				
			||||||
 | 
					            adc 1, x
 | 
				
			||||||
 | 
					            tay
 | 
				
			||||||
 | 
					    .2:     
 | 
				
			||||||
 | 
					        asl 0, x
 | 
				
			||||||
 | 
					        rol 1, x
 | 
				
			||||||
 | 
					        lda 2, x
 | 
				
			||||||
 | 
					        ora 3, x
 | 
				
			||||||
 | 
					    bne .1
 | 
				
			||||||
 | 
					    lda n
 | 
				
			||||||
 | 
					    sta 2, x
 | 
				
			||||||
 | 
					    sty 3, x
 | 
				
			||||||
 | 
					    pop
 | 
				
			||||||
 | 
					    ply
 | 
				
			||||||
 | 
					    rts
 | 
				
			||||||
							
								
								
									
										193
									
								
								src/video.rs
								
								
								
								
							
							
						
						
									
										193
									
								
								src/video.rs
								
								
								
								
							| 
						 | 
					@ -1,9 +1,12 @@
 | 
				
			||||||
use crate::Mem;
 | 
					use crate::Mem;
 | 
				
			||||||
use minifb::{Key, Scale, ScaleMode, Window, WindowOptions};
 | 
					 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    fs::File,
 | 
					    fs::File,
 | 
				
			||||||
    io::Read,
 | 
					    io::Read,
 | 
				
			||||||
    sync::{Arc, Mutex},
 | 
					    path::Path,
 | 
				
			||||||
 | 
					    sync::{
 | 
				
			||||||
 | 
					        mpsc::{Sender, SyncSender},
 | 
				
			||||||
 | 
					        Arc, Mutex,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    thread::sleep,
 | 
					    thread::sleep,
 | 
				
			||||||
    time::{Duration, Instant},
 | 
					    time::{Duration, Instant},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -14,41 +17,44 @@ const BG_COLOR: u32 = 0x110500;
 | 
				
			||||||
pub struct Crtc {
 | 
					pub struct Crtc {
 | 
				
			||||||
    memory: Arc<Mutex<Mem>>,
 | 
					    memory: Arc<Mutex<Mem>>,
 | 
				
			||||||
    buffer: Vec<u32>,
 | 
					    buffer: Vec<u32>,
 | 
				
			||||||
    window: minifb::Window,
 | 
					 | 
				
			||||||
    char_rom: Vec<u8>,
 | 
					    char_rom: Vec<u8>,
 | 
				
			||||||
 | 
					    interrupt: Sender<bool>,
 | 
				
			||||||
 | 
					    window: Sender<Vec<u32>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn get_char_bin(path: &str) -> Vec<u8> {
 | 
					pub fn get_char_bin<P>(char_rom: Option<P>) -> Vec<u8>
 | 
				
			||||||
    let mut file = File::open(path).unwrap();
 | 
					where
 | 
				
			||||||
    let mut buffer = vec![0; 0xFFFF];
 | 
					    P: AsRef<Path>,
 | 
				
			||||||
    file.read_exact(&mut buffer).unwrap();
 | 
					{
 | 
				
			||||||
    buffer
 | 
					    match char_rom {
 | 
				
			||||||
 | 
					        Some(path) => {
 | 
				
			||||||
 | 
					            let mut file = File::open(path).unwrap();
 | 
				
			||||||
 | 
					            let mut bin = vec![0; 0x8000];
 | 
				
			||||||
 | 
					            file.read_exact(&mut bin).unwrap();
 | 
				
			||||||
 | 
					            println!("reading char rom");
 | 
				
			||||||
 | 
					            bin
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        None => include_bytes!("./roms/cozette.rom").to_vec(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Crtc {
 | 
					impl Crtc {
 | 
				
			||||||
    pub fn new(memory: Arc<Mutex<Mem>>) -> Self {
 | 
					    pub fn new<P>(
 | 
				
			||||||
        let window = Window::new(
 | 
					        memory: Arc<Mutex<Mem>>,
 | 
				
			||||||
            "ʕ·ᴥ·ʔ-☆",
 | 
					        char_rom: Option<P>,
 | 
				
			||||||
            512,
 | 
					        interrupt: Sender<bool>,
 | 
				
			||||||
            380,
 | 
					        window: Sender<Vec<u32>>,
 | 
				
			||||||
            WindowOptions {
 | 
					    ) -> Self
 | 
				
			||||||
                resize: true,
 | 
					    where
 | 
				
			||||||
                borderless: true,
 | 
					        P: AsRef<Path>,
 | 
				
			||||||
                title: true,
 | 
					    {
 | 
				
			||||||
                transparency: false,
 | 
					        let char_rom = get_char_bin(char_rom);
 | 
				
			||||||
                scale: Scale::X2,
 | 
					 | 
				
			||||||
                scale_mode: ScaleMode::AspectRatioStretch,
 | 
					 | 
				
			||||||
                topmost: false,
 | 
					 | 
				
			||||||
                none: true,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
        let char_rom = get_char_bin("./src/cozette.bin");
 | 
					 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            memory,
 | 
					            memory,
 | 
				
			||||||
            buffer: vec![0; 512 * 380],
 | 
					            buffer: vec![0; 512 * 380],
 | 
				
			||||||
            window,
 | 
					            window,
 | 
				
			||||||
            char_rom,
 | 
					            char_rom,
 | 
				
			||||||
 | 
					            interrupt,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn draw(&mut self) {
 | 
					    fn draw(&mut self) {
 | 
				
			||||||
| 
						 | 
					@ -59,127 +65,38 @@ impl Crtc {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let hw_ctrl = memory.read(0x4000);
 | 
					        // the rest of this function is arcane wizardry based on the specifics of george's weird
 | 
				
			||||||
        match hw_ctrl & 0b0000_1000 == 0b0000_1000 {
 | 
					        // display and characters... don't fuck around w it
 | 
				
			||||||
            false => {
 | 
					        let mut i = 0;
 | 
				
			||||||
                // the rest of this function is arcane wizardry based on the specifics of george's weird
 | 
					        for char_row in 0..29 {
 | 
				
			||||||
                // display and characters... don't fuck around w it
 | 
					            for char_col in 0..64 {
 | 
				
			||||||
                let mut i = 0;
 | 
					                let ascii = memory.read(0x6000 + i);
 | 
				
			||||||
                for char_row in 0..29 {
 | 
					                i += 1;
 | 
				
			||||||
                    for char_col in 0..64 {
 | 
					                for row in 0..13 {
 | 
				
			||||||
                        let ascii = memory.read(0x6000 + i);
 | 
					                    let byte = self.char_rom[ascii as usize + (row * 0x100)];
 | 
				
			||||||
                        i += 1;
 | 
					                    for i in (0..8).rev() {
 | 
				
			||||||
                        for row in 0..13 {
 | 
					                        let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i);
 | 
				
			||||||
                            let byte = self.char_rom[ascii as usize + (row * 0x101)];
 | 
					                        if (byte << i) & 0x80 == 0x80 {
 | 
				
			||||||
                            for i in (0..8).rev() {
 | 
					                            self.buffer[buffer_index] = FG_COLOR;
 | 
				
			||||||
                                let buffer_index =
 | 
					                        } else {
 | 
				
			||||||
                                    ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i);
 | 
					                            self.buffer[buffer_index] = BG_COLOR;
 | 
				
			||||||
                                if (byte << i) & 0x80 == 0x80 {
 | 
					 | 
				
			||||||
                                    self.buffer[buffer_index] = FG_COLOR;
 | 
					 | 
				
			||||||
                                } else {
 | 
					 | 
				
			||||||
                                    self.buffer[buffer_index] = BG_COLOR;
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            true => {
 | 
					        }
 | 
				
			||||||
                for addr in 0x6000..0xBF00 {
 | 
					 | 
				
			||||||
                    // TODO: eventually this will access memory in the weird interleaved way the hardware is
 | 
					 | 
				
			||||||
                    // designed, but this is easiest to implement for now
 | 
					 | 
				
			||||||
                    let byte = memory.read(addr);
 | 
					 | 
				
			||||||
                    for i in 0..8 {
 | 
					 | 
				
			||||||
                        match byte & 0x80 >> i == 0 {
 | 
					 | 
				
			||||||
                            true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR,
 | 
					 | 
				
			||||||
                            false => self.buffer[(addr - 0x6000) as usize * 8 + i] = FG_COLOR,
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.window
 | 
					        let buffer = self.buffer.to_owned();
 | 
				
			||||||
            .update_with_buffer(&self.buffer, 512, 380)
 | 
					
 | 
				
			||||||
            .unwrap();
 | 
					        self.window.send(buffer).unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn run(&mut self) {
 | 
					    pub fn run(&mut self) {
 | 
				
			||||||
        let frame_duration = Duration::from_millis(16);
 | 
					 | 
				
			||||||
        let mut previous_draw = Instant::now();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let mut row0 = 0;
 | 
					            self.interrupt.send(false).unwrap();
 | 
				
			||||||
            let mut row1 = 0;
 | 
					            sleep(Duration::from_millis(16));
 | 
				
			||||||
            let mut row2 = 0;
 | 
					            self.draw();
 | 
				
			||||||
            let mut row3 = 0;
 | 
					            self.interrupt.send(true).unwrap();
 | 
				
			||||||
            let mut row4 = 0;
 | 
					 | 
				
			||||||
            let mut row5 = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.window.get_keys().iter().for_each(|key| match key {
 | 
					 | 
				
			||||||
                Key::Escape => row0 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::W => row0 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::E => row0 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                Key::R => row0 ^= 0b0001_0000,
 | 
					 | 
				
			||||||
                Key::T => row0 ^= 0b0000_1000,
 | 
					 | 
				
			||||||
                Key::U => row0 ^= 0b0000_0100,
 | 
					 | 
				
			||||||
                Key::O => row0 ^= 0b0000_0010,
 | 
					 | 
				
			||||||
                Key::Backspace => row0 ^= 0b0000_0001,
 | 
					 | 
				
			||||||
                Key::Tab => row1 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::Q => row1 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::S => row1 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                Key::G => row1 ^= 0b0001_0000,
 | 
					 | 
				
			||||||
                Key::Y => row1 ^= 0b0000_1000,
 | 
					 | 
				
			||||||
                Key::I => row1 ^= 0b0000_0100,
 | 
					 | 
				
			||||||
                Key::P => row1 ^= 0b0000_0010,
 | 
					 | 
				
			||||||
                Key::Enter => row1 ^= 0b0000_0001,
 | 
					 | 
				
			||||||
                Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::D => row2 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::V => row2 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                Key::H => row2 ^= 0b0001_0000,
 | 
					 | 
				
			||||||
                Key::K => row2 ^= 0b0000_1000,
 | 
					 | 
				
			||||||
                Key::Apostrophe => row2 ^= 0b0000_0100,
 | 
					 | 
				
			||||||
                Key::Slash => row2 ^= 0b0000_0010,
 | 
					 | 
				
			||||||
                Key::A => row2 ^= 0b0000_0001,
 | 
					 | 
				
			||||||
                Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::Z => row3 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::F => row3 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                Key::B => row3 ^= 0b0001_0000,
 | 
					 | 
				
			||||||
                Key::J => row3 ^= 0b0000_1000,
 | 
					 | 
				
			||||||
                Key::L => row3 ^= 0b0000_0100,
 | 
					 | 
				
			||||||
                Key::Key2 => row3 ^= 0b0000_0010,
 | 
					 | 
				
			||||||
                Key::Key4 => row3 ^= 0b0000_0001,
 | 
					 | 
				
			||||||
                Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::X => row4 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::C => row4 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                Key::N => row4 ^= 0b0001_0000,
 | 
					 | 
				
			||||||
                Key::M => row4 ^= 0b0000_1000,
 | 
					 | 
				
			||||||
                Key::Comma => row4 ^= 0b0000_0100,
 | 
					 | 
				
			||||||
                Key::Key1 => row4 ^= 0b0000_0010,
 | 
					 | 
				
			||||||
                Key::Key3 => row4 ^= 0b0000_0001,
 | 
					 | 
				
			||||||
                Key::LeftSuper => row5 ^= 0b1000_0000,
 | 
					 | 
				
			||||||
                Key::Space => row5 ^= 0b0100_0000,
 | 
					 | 
				
			||||||
                Key::RightSuper => row5 ^= 0b0010_0000,
 | 
					 | 
				
			||||||
                _ => {}
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            match &mut self.memory.lock() {
 | 
					 | 
				
			||||||
                Ok(memory) => {
 | 
					 | 
				
			||||||
                    memory.write(0x4400, row0);
 | 
					 | 
				
			||||||
                    memory.write(0x4401, row1);
 | 
					 | 
				
			||||||
                    memory.write(0x4402, row2);
 | 
					 | 
				
			||||||
                    memory.write(0x4403, row3);
 | 
					 | 
				
			||||||
                    memory.write(0x4404, row4);
 | 
					 | 
				
			||||||
                    memory.write(0x4405, row5);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Err(error) => println!("{error}"),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            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