Compare commits
	
		
			1 Commits
		
	
	
		
			2ef8fefbb7
			...
			refactor
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9baa15050e | 
							
								
								
									
										416
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										416
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -23,7 +23,7 @@ dependencies = [
 | 
				
			|||||||
 "alsa-sys",
 | 
					 "alsa-sys",
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -58,20 +58,11 @@ dependencies = [
 | 
				
			|||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "ansi_term"
 | 
					 | 
				
			||||||
version = "0.12.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "winapi",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "anyhow"
 | 
					name = "anyhow"
 | 
				
			||||||
version = "1.0.44"
 | 
					version = "1.0.43"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
 | 
					checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "arrayvec"
 | 
					name = "arrayvec"
 | 
				
			||||||
@@ -85,7 +76,7 @@ version = "0.33.3+1.2.191"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
 | 
					checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -153,9 +144,9 @@ checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "bitflags"
 | 
					name = "bitflags"
 | 
				
			||||||
version = "1.3.2"
 | 
					version = "1.2.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
					checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "block"
 | 
					name = "block"
 | 
				
			||||||
@@ -165,9 +156,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "bumpalo"
 | 
					name = "bumpalo"
 | 
				
			||||||
version = "3.7.1"
 | 
					version = "3.7.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
 | 
					checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "bytemuck"
 | 
					name = "bytemuck"
 | 
				
			||||||
@@ -205,9 +196,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "cc"
 | 
					name = "cc"
 | 
				
			||||||
version = "1.0.71"
 | 
					version = "1.0.70"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
 | 
					checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "jobserver",
 | 
					 "jobserver",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -253,7 +244,7 @@ checksum = "10612c0ec0e0a1ff0e97980647cb058a6e7aedb913d01d009c406b8b7d0b26ee"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "glob",
 | 
					 "glob",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -262,7 +253,7 @@ version = "2.33.3"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 | 
					checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "ansi_term 0.11.0",
 | 
					 "ansi_term",
 | 
				
			||||||
 "atty",
 | 
					 "atty",
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "strsim 0.8.0",
 | 
					 "strsim 0.8.0",
 | 
				
			||||||
@@ -286,7 +277,7 @@ dependencies = [
 | 
				
			|||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "block",
 | 
					 "block",
 | 
				
			||||||
 "cocoa-foundation",
 | 
					 "cocoa-foundation",
 | 
				
			||||||
 "core-foundation 0.9.2",
 | 
					 "core-foundation 0.9.1",
 | 
				
			||||||
 "core-graphics 0.22.2",
 | 
					 "core-graphics 0.22.2",
 | 
				
			||||||
 "foreign-types",
 | 
					 "foreign-types",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
@@ -301,7 +292,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "block",
 | 
					 "block",
 | 
				
			||||||
 "core-foundation 0.9.2",
 | 
					 "core-foundation 0.9.1",
 | 
				
			||||||
 "core-graphics-types",
 | 
					 "core-graphics-types",
 | 
				
			||||||
 "foreign-types",
 | 
					 "foreign-types",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
@@ -356,11 +347,11 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "core-foundation"
 | 
					name = "core-foundation"
 | 
				
			||||||
version = "0.9.2"
 | 
					version = "0.9.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
 | 
					checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "core-foundation-sys 0.8.3",
 | 
					 "core-foundation-sys 0.8.2",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -378,9 +369,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "core-foundation-sys"
 | 
					name = "core-foundation-sys"
 | 
				
			||||||
version = "0.8.3"
 | 
					version = "0.8.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 | 
					checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "core-graphics"
 | 
					name = "core-graphics"
 | 
				
			||||||
@@ -401,7 +392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86"
 | 
					checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "core-foundation 0.9.2",
 | 
					 "core-foundation 0.9.1",
 | 
				
			||||||
 "core-graphics-types",
 | 
					 "core-graphics-types",
 | 
				
			||||||
 "foreign-types",
 | 
					 "foreign-types",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
@@ -414,7 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
 | 
					checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "core-foundation 0.9.2",
 | 
					 "core-foundation 0.9.1",
 | 
				
			||||||
 "foreign-types",
 | 
					 "foreign-types",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -458,7 +449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418"
 | 
					checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "alsa",
 | 
					 "alsa",
 | 
				
			||||||
 "core-foundation-sys 0.8.3",
 | 
					 "core-foundation-sys 0.8.2",
 | 
				
			||||||
 "coreaudio-rs",
 | 
					 "coreaudio-rs",
 | 
				
			||||||
 "jni",
 | 
					 "jni",
 | 
				
			||||||
 "js-sys",
 | 
					 "js-sys",
 | 
				
			||||||
@@ -467,7 +458,7 @@ dependencies = [
 | 
				
			|||||||
 "mach 0.3.2",
 | 
					 "mach 0.3.2",
 | 
				
			||||||
 "ndk 0.3.0",
 | 
					 "ndk 0.3.0",
 | 
				
			||||||
 "ndk-glue 0.3.0",
 | 
					 "ndk-glue 0.3.0",
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
 "oboe",
 | 
					 "oboe",
 | 
				
			||||||
 "parking_lot",
 | 
					 "parking_lot",
 | 
				
			||||||
 "stdweb 0.1.3",
 | 
					 "stdweb 0.1.3",
 | 
				
			||||||
@@ -551,7 +542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c"
 | 
					checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -601,47 +592,6 @@ dependencies = [
 | 
				
			|||||||
 "syn",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "directories-next"
 | 
					 | 
				
			||||||
version = "2.0.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 | 
				
			||||||
 "dirs-sys-next",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "dirs"
 | 
					 | 
				
			||||||
version = "3.0.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "dirs-sys",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "dirs-sys"
 | 
					 | 
				
			||||||
version = "0.3.6"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "libc",
 | 
					 | 
				
			||||||
 "redox_users",
 | 
					 | 
				
			||||||
 "winapi",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "dirs-sys-next"
 | 
					 | 
				
			||||||
version = "0.1.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "libc",
 | 
					 | 
				
			||||||
 "redox_users",
 | 
					 | 
				
			||||||
 "winapi",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "discard"
 | 
					name = "discard"
 | 
				
			||||||
version = "1.0.4"
 | 
					version = "1.0.4"
 | 
				
			||||||
@@ -669,7 +619,7 @@ version = "0.5.0"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
 | 
					checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -678,6 +628,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 = "fixedbitset"
 | 
				
			||||||
 | 
					version = "0.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "fnv"
 | 
					name = "fnv"
 | 
				
			||||||
version = "1.0.7"
 | 
					version = "1.0.7"
 | 
				
			||||||
@@ -715,28 +671,14 @@ dependencies = [
 | 
				
			|||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "bitfield",
 | 
					 "bitfield",
 | 
				
			||||||
 "clap",
 | 
					 "clap",
 | 
				
			||||||
 "directories-next",
 | 
					 | 
				
			||||||
 "gilrs",
 | 
					 "gilrs",
 | 
				
			||||||
 "pixels",
 | 
					 "pixels",
 | 
				
			||||||
 "rodio",
 | 
					 "rodio",
 | 
				
			||||||
 "rtrb",
 | 
					 "rtrb",
 | 
				
			||||||
 "tracing",
 | 
					 | 
				
			||||||
 "tracing-subscriber",
 | 
					 | 
				
			||||||
 "winit",
 | 
					 "winit",
 | 
				
			||||||
 "winit_input_helper",
 | 
					 "winit_input_helper",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "getrandom"
 | 
					 | 
				
			||||||
version = "0.2.3"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 | 
				
			||||||
 "libc",
 | 
					 | 
				
			||||||
 "wasi",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "gilrs"
 | 
					name = "gilrs"
 | 
				
			||||||
version = "0.8.1"
 | 
					version = "0.8.1"
 | 
				
			||||||
@@ -761,7 +703,7 @@ dependencies = [
 | 
				
			|||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "libudev-sys",
 | 
					 "libudev-sys",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
 "rusty-xinput",
 | 
					 "rusty-xinput",
 | 
				
			||||||
 "stdweb 0.4.20",
 | 
					 "stdweb 0.4.20",
 | 
				
			||||||
 "uuid",
 | 
					 "uuid",
 | 
				
			||||||
@@ -789,9 +731,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "gpu-alloc"
 | 
					name = "gpu-alloc"
 | 
				
			||||||
version = "0.5.2"
 | 
					version = "0.5.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "0e64cbb8d36508d3e19da95e56e196a84f674fc190881f2cc010000798838aa6"
 | 
					checksum = "ab8524eac5fc9d05625c891adf78fcf64dc0ee9f8d0882874b9f220f42b442bf"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "gpu-alloc-types",
 | 
					 "gpu-alloc-types",
 | 
				
			||||||
@@ -850,12 +792,6 @@ dependencies = [
 | 
				
			|||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "hexf-parse"
 | 
					 | 
				
			||||||
version = "0.2.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "hound"
 | 
					name = "hound"
 | 
				
			||||||
version = "3.4.0"
 | 
					version = "3.4.0"
 | 
				
			||||||
@@ -886,14 +822,11 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "instant"
 | 
					name = "instant"
 | 
				
			||||||
version = "0.1.11"
 | 
					version = "0.1.10"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
 | 
					checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
 "js-sys",
 | 
					 | 
				
			||||||
 "wasm-bindgen",
 | 
					 | 
				
			||||||
 "web-sys",
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -943,9 +876,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "js-sys"
 | 
					name = "js-sys"
 | 
				
			||||||
version = "0.3.55"
 | 
					version = "0.3.54"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
 | 
					checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "wasm-bindgen",
 | 
					 "wasm-bindgen",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -957,7 +890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
 | 
					checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -985,9 +918,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "libc"
 | 
					name = "libc"
 | 
				
			||||||
version = "0.2.103"
 | 
					version = "0.2.101"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
 | 
					checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "libloading"
 | 
					name = "libloading"
 | 
				
			||||||
@@ -1001,9 +934,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "libloading"
 | 
					name = "libloading"
 | 
				
			||||||
version = "0.7.1"
 | 
					version = "0.7.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
 | 
					checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
@@ -1065,13 +998,10 @@ dependencies = [
 | 
				
			|||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "matchers"
 | 
					name = "maybe-uninit"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "2.0.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
 | 
					checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "regex-automata",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "memchr"
 | 
					name = "memchr"
 | 
				
			||||||
@@ -1113,9 +1043,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "minimal-lexical"
 | 
					name = "minimal-lexical"
 | 
				
			||||||
version = "0.1.4"
 | 
					version = "0.1.3"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677"
 | 
					checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "minimp3"
 | 
					name = "minimp3"
 | 
				
			||||||
@@ -1173,18 +1103,17 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "naga"
 | 
					name = "naga"
 | 
				
			||||||
version = "0.7.1"
 | 
					version = "0.6.3"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "eda66d09f712e1f0a6ab436137da4fac312f78301f6d4ac7cb8bfe96e988734f"
 | 
					checksum = "8c5859e55c51da10b98e7a73068e0a0c5da7bbcae4fc38f86043d0c6d1b917cf"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bit-set",
 | 
					 "bit-set",
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "codespan-reporting",
 | 
					 "codespan-reporting",
 | 
				
			||||||
 "fxhash",
 | 
					 "fxhash",
 | 
				
			||||||
 "hexf-parse",
 | 
					 | 
				
			||||||
 "indexmap",
 | 
					 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "num-traits",
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					 "petgraph",
 | 
				
			||||||
 "spirv",
 | 
					 "spirv",
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1275,14 +1204,15 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "nix"
 | 
					name = "nix"
 | 
				
			||||||
version = "0.20.0"
 | 
					version = "0.20.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
 | 
					checksum = "df8e5e343312e7fbeb2a52139114e9e702991ef9c2aea6817ff2440b35647d56"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "cc",
 | 
					 "cc",
 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "memoffset",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -1351,7 +1281,7 @@ version = "0.5.4"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
 | 
					checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro-crate 1.1.0",
 | 
					 "proc-macro-crate 1.0.0",
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn",
 | 
					 "syn",
 | 
				
			||||||
@@ -1461,16 +1391,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			|||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 | 
					checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pin-project-lite"
 | 
					name = "petgraph"
 | 
				
			||||||
version = "0.2.7"
 | 
					version = "0.6.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
 | 
					checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "fixedbitset",
 | 
				
			||||||
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pixels"
 | 
					name = "pixels"
 | 
				
			||||||
version = "0.7.0"
 | 
					version = "0.6.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "94ab1e297051c39cc7b7511e7e2b3ab151f14aff6a44e73bdde652b1e2190950"
 | 
					checksum = "6e794d4eab6519352618cca630ce4970a65576b9df54eca2068d4aad4b143903"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bytemuck",
 | 
					 "bytemuck",
 | 
				
			||||||
 "pollster",
 | 
					 "pollster",
 | 
				
			||||||
@@ -1482,9 +1416,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pkg-config"
 | 
					name = "pkg-config"
 | 
				
			||||||
version = "0.3.20"
 | 
					version = "0.3.19"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
 | 
					checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pollster"
 | 
					name = "pollster"
 | 
				
			||||||
@@ -1503,9 +1437,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "proc-macro-crate"
 | 
					name = "proc-macro-crate"
 | 
				
			||||||
version = "1.1.0"
 | 
					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 = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
 | 
					checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
 "toml",
 | 
					 "toml",
 | 
				
			||||||
@@ -1513,9 +1447,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "proc-macro2"
 | 
					name = "proc-macro2"
 | 
				
			||||||
version = "1.0.30"
 | 
					version = "1.0.29"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
 | 
					checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "unicode-xid",
 | 
					 "unicode-xid",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1528,9 +1462,9 @@ checksum = "87dfd5592a8eed7e74f56ad7b125f8234763b805c30f0c7c95c486920026a6ec"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "quote"
 | 
					name = "quote"
 | 
				
			||||||
version = "1.0.10"
 | 
					version = "1.0.9"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
 | 
					checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1559,16 +1493,6 @@ dependencies = [
 | 
				
			|||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "redox_users"
 | 
					 | 
				
			||||||
version = "0.4.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "getrandom",
 | 
					 | 
				
			||||||
 "redox_syscall",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "regex"
 | 
					name = "regex"
 | 
				
			||||||
version = "1.5.4"
 | 
					version = "1.5.4"
 | 
				
			||||||
@@ -1578,15 +1502,6 @@ dependencies = [
 | 
				
			|||||||
 "regex-syntax",
 | 
					 "regex-syntax",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "regex-automata"
 | 
					 | 
				
			||||||
version = "0.1.10"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "regex-syntax",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "regex-syntax"
 | 
					name = "regex-syntax"
 | 
				
			||||||
version = "0.6.25"
 | 
					version = "0.6.25"
 | 
				
			||||||
@@ -1614,9 +1529,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "rtrb"
 | 
					name = "rtrb"
 | 
				
			||||||
version = "0.2.0"
 | 
					version = "0.1.4"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7d911d82e9745a598dd118e6bda2165531e316cf8eae65240776637ae8168458"
 | 
					checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cache-padded",
 | 
					 "cache-padded",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1727,9 +1642,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_json"
 | 
					name = "serde_json"
 | 
				
			||||||
version = "1.0.68"
 | 
					version = "1.0.67"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
 | 
					checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "itoa",
 | 
					 "itoa",
 | 
				
			||||||
 "ryu",
 | 
					 "ryu",
 | 
				
			||||||
@@ -1742,15 +1657,6 @@ version = "0.6.0"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
 | 
					checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "sharded-slab"
 | 
					 | 
				
			||||||
version = "0.1.4"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "lazy_static",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "shlex"
 | 
					name = "shlex"
 | 
				
			||||||
version = "0.1.1"
 | 
					version = "0.1.1"
 | 
				
			||||||
@@ -1779,9 +1685,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "smallvec"
 | 
					name = "smallvec"
 | 
				
			||||||
version = "1.7.0"
 | 
					version = "1.6.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
 | 
					checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "smithay-client-toolkit"
 | 
					name = "smithay-client-toolkit"
 | 
				
			||||||
@@ -1883,9 +1789,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "syn"
 | 
					name = "syn"
 | 
				
			||||||
version = "1.0.80"
 | 
					version = "1.0.76"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
 | 
					checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
@@ -1912,38 +1818,29 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "thiserror"
 | 
					name = "thiserror"
 | 
				
			||||||
version = "1.0.30"
 | 
					version = "1.0.29"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
 | 
					checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "thiserror-impl",
 | 
					 "thiserror-impl",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "thiserror-impl"
 | 
					name = "thiserror-impl"
 | 
				
			||||||
version = "1.0.30"
 | 
					version = "1.0.29"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
 | 
					checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "syn",
 | 
					 "syn",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "thread_local"
 | 
					 | 
				
			||||||
version = "1.1.3"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "once_cell",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "tinyvec"
 | 
					name = "tinyvec"
 | 
				
			||||||
version = "1.5.0"
 | 
					version = "1.3.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
 | 
					checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "tinyvec_macros",
 | 
					 "tinyvec_macros",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -1963,67 +1860,6 @@ dependencies = [
 | 
				
			|||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tracing"
 | 
					 | 
				
			||||||
version = "0.1.29"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 | 
				
			||||||
 "pin-project-lite",
 | 
					 | 
				
			||||||
 "tracing-attributes",
 | 
					 | 
				
			||||||
 "tracing-core",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tracing-attributes"
 | 
					 | 
				
			||||||
version = "0.1.18"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "proc-macro2",
 | 
					 | 
				
			||||||
 "quote",
 | 
					 | 
				
			||||||
 "syn",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tracing-core"
 | 
					 | 
				
			||||||
version = "0.1.21"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "lazy_static",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tracing-log"
 | 
					 | 
				
			||||||
version = "0.1.2"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "lazy_static",
 | 
					 | 
				
			||||||
 "log",
 | 
					 | 
				
			||||||
 "tracing-core",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tracing-subscriber"
 | 
					 | 
				
			||||||
version = "0.3.1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "ansi_term 0.12.1",
 | 
					 | 
				
			||||||
 "lazy_static",
 | 
					 | 
				
			||||||
 "matchers",
 | 
					 | 
				
			||||||
 "regex",
 | 
					 | 
				
			||||||
 "sharded-slab",
 | 
					 | 
				
			||||||
 "smallvec",
 | 
					 | 
				
			||||||
 "thread_local",
 | 
					 | 
				
			||||||
 "tracing",
 | 
					 | 
				
			||||||
 "tracing-core",
 | 
					 | 
				
			||||||
 "tracing-log",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "ttf-parser"
 | 
					name = "ttf-parser"
 | 
				
			||||||
version = "0.6.2"
 | 
					version = "0.6.2"
 | 
				
			||||||
@@ -2041,9 +1877,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "unicode-width"
 | 
					name = "unicode-width"
 | 
				
			||||||
version = "0.1.9"
 | 
					version = "0.1.8"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
 | 
					checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "unicode-xid"
 | 
					name = "unicode-xid"
 | 
				
			||||||
@@ -2080,17 +1916,11 @@ dependencies = [
 | 
				
			|||||||
 "winapi-util",
 | 
					 "winapi-util",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "wasi"
 | 
					 | 
				
			||||||
version = "0.10.2+wasi-snapshot-preview1"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen"
 | 
					name = "wasm-bindgen"
 | 
				
			||||||
version = "0.2.78"
 | 
					version = "0.2.77"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
 | 
					checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
 "wasm-bindgen-macro",
 | 
					 "wasm-bindgen-macro",
 | 
				
			||||||
@@ -2098,9 +1928,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen-backend"
 | 
					name = "wasm-bindgen-backend"
 | 
				
			||||||
version = "0.2.78"
 | 
					version = "0.2.77"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
 | 
					checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bumpalo",
 | 
					 "bumpalo",
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
@@ -2113,9 +1943,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen-futures"
 | 
					name = "wasm-bindgen-futures"
 | 
				
			||||||
version = "0.4.28"
 | 
					version = "0.4.27"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
 | 
					checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "cfg-if 1.0.0",
 | 
					 "cfg-if 1.0.0",
 | 
				
			||||||
 "js-sys",
 | 
					 "js-sys",
 | 
				
			||||||
@@ -2125,9 +1955,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen-macro"
 | 
					name = "wasm-bindgen-macro"
 | 
				
			||||||
version = "0.2.78"
 | 
					version = "0.2.77"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
 | 
					checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
 "wasm-bindgen-macro-support",
 | 
					 "wasm-bindgen-macro-support",
 | 
				
			||||||
@@ -2135,9 +1965,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen-macro-support"
 | 
					name = "wasm-bindgen-macro-support"
 | 
				
			||||||
version = "0.2.78"
 | 
					version = "0.2.77"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
 | 
					checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "proc-macro2",
 | 
				
			||||||
 "quote",
 | 
					 "quote",
 | 
				
			||||||
@@ -2148,9 +1978,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wasm-bindgen-shared"
 | 
					name = "wasm-bindgen-shared"
 | 
				
			||||||
version = "0.2.78"
 | 
					version = "0.2.77"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
 | 
					checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wayland-client"
 | 
					name = "wayland-client"
 | 
				
			||||||
@@ -2161,7 +1991,7 @@ dependencies = [
 | 
				
			|||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "downcast-rs",
 | 
					 "downcast-rs",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
 "scoped-tls",
 | 
					 "scoped-tls",
 | 
				
			||||||
 "wayland-commons",
 | 
					 "wayland-commons",
 | 
				
			||||||
 "wayland-scanner",
 | 
					 "wayland-scanner",
 | 
				
			||||||
@@ -2174,7 +2004,7 @@ version = "0.28.6"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda"
 | 
					checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
 "once_cell",
 | 
					 "once_cell",
 | 
				
			||||||
 "smallvec",
 | 
					 "smallvec",
 | 
				
			||||||
 "wayland-sys",
 | 
					 "wayland-sys",
 | 
				
			||||||
@@ -2186,7 +2016,7 @@ version = "0.28.6"
 | 
				
			|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a"
 | 
					checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "nix 0.20.0",
 | 
					 "nix 0.20.1",
 | 
				
			||||||
 "wayland-client",
 | 
					 "wayland-client",
 | 
				
			||||||
 "xcursor",
 | 
					 "xcursor",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -2227,9 +2057,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "web-sys"
 | 
					name = "web-sys"
 | 
				
			||||||
version = "0.3.55"
 | 
					version = "0.3.51"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
 | 
					checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "js-sys",
 | 
					 "js-sys",
 | 
				
			||||||
 "wasm-bindgen",
 | 
					 "wasm-bindgen",
 | 
				
			||||||
@@ -2237,9 +2067,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wgpu"
 | 
					name = "wgpu"
 | 
				
			||||||
version = "0.11.0"
 | 
					version = "0.10.1"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d1577ecc4f6992b9e965878ac594efb24eed2bdf089c11f45b3d1c5f216e2e30"
 | 
					checksum = "3d92a4fe73b1e7d7ef99938dacd49258cbf1ad87cdb5bf6efa20c27447442b45"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "js-sys",
 | 
					 "js-sys",
 | 
				
			||||||
@@ -2257,9 +2087,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wgpu-core"
 | 
					name = "wgpu-core"
 | 
				
			||||||
version = "0.11.0"
 | 
					version = "0.10.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "45af76ba5545b61a6904f26cdcf4287329144ae9e12f0c23ec4c9be982d675a6"
 | 
					checksum = "425b975c319d311e051bf3afb54120a34b187f9d889edc68e347567e512774c8"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
@@ -2279,9 +2109,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wgpu-hal"
 | 
					name = "wgpu-hal"
 | 
				
			||||||
version = "0.11.2"
 | 
					version = "0.10.4"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "11095a81f4406b1e594dab7dc35d6508409d364e62458f2e5b07b3edc7aca517"
 | 
					checksum = "b33daf5eff68118c0aad030886655824052f82a164fd2e257211f742c6cade53"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "arrayvec",
 | 
					 "arrayvec",
 | 
				
			||||||
 "ash",
 | 
					 "ash",
 | 
				
			||||||
@@ -2296,30 +2126,26 @@ dependencies = [
 | 
				
			|||||||
 "gpu-alloc",
 | 
					 "gpu-alloc",
 | 
				
			||||||
 "gpu-descriptor",
 | 
					 "gpu-descriptor",
 | 
				
			||||||
 "inplace_it",
 | 
					 "inplace_it",
 | 
				
			||||||
 "js-sys",
 | 
					 | 
				
			||||||
 "khronos-egl",
 | 
					 "khronos-egl",
 | 
				
			||||||
 "libloading 0.7.1",
 | 
					 "libloading 0.7.0",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "metal",
 | 
					 "metal",
 | 
				
			||||||
 "naga",
 | 
					 "naga",
 | 
				
			||||||
 "objc",
 | 
					 "objc",
 | 
				
			||||||
 "parking_lot",
 | 
					 "parking_lot",
 | 
				
			||||||
 "profiling",
 | 
					 | 
				
			||||||
 "range-alloc",
 | 
					 "range-alloc",
 | 
				
			||||||
 "raw-window-handle",
 | 
					 "raw-window-handle",
 | 
				
			||||||
 "renderdoc-sys",
 | 
					 "renderdoc-sys",
 | 
				
			||||||
 "thiserror",
 | 
					 "thiserror",
 | 
				
			||||||
 "wasm-bindgen",
 | 
					 | 
				
			||||||
 "web-sys",
 | 
					 | 
				
			||||||
 "wgpu-types",
 | 
					 "wgpu-types",
 | 
				
			||||||
 "winapi",
 | 
					 "winapi",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "wgpu-types"
 | 
					name = "wgpu-types"
 | 
				
			||||||
version = "0.11.0"
 | 
					version = "0.10.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "e15e44ba88ec415466e18e91881319e7c9e96cb905dc623305168aea65b85ccc"
 | 
					checksum = "25feb2fbf24ab3219a9f10890ceb8e1ef02b13314ed89d64a9ae99dcad883e18"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -2373,7 +2199,7 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8"
 | 
				
			|||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bitflags",
 | 
					 "bitflags",
 | 
				
			||||||
 "cocoa",
 | 
					 "cocoa",
 | 
				
			||||||
 "core-foundation 0.9.2",
 | 
					 "core-foundation 0.9.1",
 | 
				
			||||||
 "core-graphics 0.22.2",
 | 
					 "core-graphics 0.22.2",
 | 
				
			||||||
 "core-video-sys",
 | 
					 "core-video-sys",
 | 
				
			||||||
 "dispatch",
 | 
					 "dispatch",
 | 
				
			||||||
@@ -2408,12 +2234,13 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "x11-dl"
 | 
					name = "x11-dl"
 | 
				
			||||||
version = "2.19.1"
 | 
					version = "2.18.5"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59"
 | 
					checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "lazy_static",
 | 
					 "lazy_static",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "maybe-uninit",
 | 
				
			||||||
 "pkg-config",
 | 
					 "pkg-config",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2428,12 +2255,9 @@ dependencies = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "xdg"
 | 
					name = "xdg"
 | 
				
			||||||
version = "2.4.0"
 | 
					version = "2.2.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803"
 | 
					checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "dirs",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "xml-rs"
 | 
					name = "xml-rs"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -2,23 +2,21 @@
 | 
				
			|||||||
name = "gb"
 | 
					name = "gb"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
authors = ["Rekai Musuka <rekai@musuka.dev>"]
 | 
					authors = ["Rekai Musuka <rekai@musuka.dev>"]
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2018"
 | 
				
			||||||
 | 
					resolver = "2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 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"
 | 
					anyhow = "^1.0"
 | 
				
			||||||
bitfield = "0.13"
 | 
					bitfield = "^0.13"
 | 
				
			||||||
clap = "2.33"
 | 
					clap = "^2.33"
 | 
				
			||||||
gilrs = "0.8"
 | 
					gilrs = "^0.8"
 | 
				
			||||||
pixels = "0.7"
 | 
					pixels = "^0.6"
 | 
				
			||||||
winit = "0.25"
 | 
					winit = "^0.25"
 | 
				
			||||||
winit_input_helper = "0.10"
 | 
					winit_input_helper = "^0.10"
 | 
				
			||||||
rodio = "0.14"
 | 
					rodio = "^0.14"
 | 
				
			||||||
rtrb = "0.2"
 | 
					rtrb = "^0.1.4"
 | 
				
			||||||
directories-next = "2.0"
 | 
					 | 
				
			||||||
tracing = "0.1"
 | 
					 | 
				
			||||||
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[profile.release]
 | 
					[profile.release]
 | 
				
			||||||
debug = true
 | 
					debug = true
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
									
									
									
									
								
							@@ -2,37 +2,13 @@
 | 
				
			|||||||
[](https://ci.paoda.moe/paoda/gb)
 | 
					[](https://ci.paoda.moe/paoda/gb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Status
 | 
					### Status
 | 
				
			||||||
* From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/)
 | 
					* Passes Blargg's cpu_instrs Test
 | 
				
			||||||
    * [x] cpu_instrs
 | 
					* Renders Background & Window Tiles 
 | 
				
			||||||
    * [x] instr_timing
 | 
					* Implements a PPU FIFO
 | 
				
			||||||
    * [x] mem_timing
 | 
					 | 
				
			||||||
    * [x] mem_timing-2
 | 
					 | 
				
			||||||
    * [ ] Partially dmg_sound 
 | 
					 | 
				
			||||||
* [x] [dmg-acid2](https://github.com/mattcurrie/dmg-acid2)
 | 
					 | 
				
			||||||
* From [mooneye-gb](https://github.com/Gekkio/mooneye-gb):
 | 
					 | 
				
			||||||
    * Cartridges:
 | 
					 | 
				
			||||||
        * [x] MBC1
 | 
					 | 
				
			||||||
        * [ ] MBC1M
 | 
					 | 
				
			||||||
        * [x] MBC2
 | 
					 | 
				
			||||||
        * [x] MBC5
 | 
					 | 
				
			||||||
* Implements a cycle-accurate PPU FIFO
 | 
					 | 
				
			||||||
    * Doesn't \*exactly\* work right just yet
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Supports: ROM-only, MBC1, MBC2, MBC3 and MBC5 games.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Notes
 | 
				
			||||||
### Controls
 | 
					* [gameboy-logs](https://github.com/wheremyfoodat/Gameboy-logs) suggests that there are still some underlying problems with the cpu implementation
 | 
				
			||||||
Controls are defined [here](https://git.musuka.dev/paoda/gb/src/branch/main/src/joypad.rs#L114)
 | 
					* The Sprite FIFO does not work as expected yet
 | 
				
			||||||
 | 
					* Sound is neither emulated nor stubbed. Upon writing / reading to a APU related register the emulator will panic. 
 | 
				
			||||||
Key | Button
 | 
					* Code cleanup is pending completion of some minimum viable product of the emulator
 | 
				
			||||||
--- | ---
 | 
					 | 
				
			||||||
<kbd>X</kbd> | B
 | 
					 | 
				
			||||||
<kbd>Z</kbd> | A
 | 
					 | 
				
			||||||
<kbd>Enter</kbd> | START
 | 
					 | 
				
			||||||
<kbd>Shift</kbd> | SELECT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Then use the Arrow keys for the D-Pad
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Credits
 | 
					 | 
				
			||||||
The Boot ROM found in the `bin/` directory was made by [Optix](https://github.com/Hacktix) over [here](https://github.com/Hacktix/Bootix)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										96
									
								
								src/apu.rs
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/apu.rs
									
									
									
									
									
								
							@@ -1,4 +1,3 @@
 | 
				
			|||||||
use crate::apu::gen::SAMPLE_RATE;
 | 
					 | 
				
			||||||
use crate::bus::BusIo;
 | 
					use crate::bus::BusIo;
 | 
				
			||||||
use crate::emu::SM83_CLOCK_SPEED;
 | 
					use crate::emu::SM83_CLOCK_SPEED;
 | 
				
			||||||
use gen::SampleProducer;
 | 
					use gen::SampleProducer;
 | 
				
			||||||
@@ -6,7 +5,7 @@ use types::ch1::{Sweep, SweepDirection};
 | 
				
			|||||||
use types::ch3::Volume as Ch3Volume;
 | 
					use types::ch3::Volume as Ch3Volume;
 | 
				
			||||||
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
 | 
					use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
 | 
				
			||||||
use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
 | 
					use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
 | 
				
			||||||
use types::fs::{FrameSequencer, State as FSState};
 | 
					use types::fs::{FrameSequencer, State as FrameSequencerState};
 | 
				
			||||||
use types::{ChannelControl, SoundOutput};
 | 
					use types::{ChannelControl, SoundOutput};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod gen;
 | 
					pub mod gen;
 | 
				
			||||||
@@ -32,8 +31,6 @@ pub struct Apu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    prod: Option<SampleProducer<f32>>,
 | 
					    prod: Option<SampleProducer<f32>>,
 | 
				
			||||||
    sample_counter: u64,
 | 
					    sample_counter: u64,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    cap: f32,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BusIo for Apu {
 | 
					impl BusIo for Apu {
 | 
				
			||||||
@@ -57,7 +54,7 @@ impl BusIo for Apu {
 | 
				
			|||||||
            0x26 => self.ctrl.status(self),
 | 
					            0x26 => self.ctrl.status(self),
 | 
				
			||||||
            0x30..=0x3F => self.ch3.read_byte(addr),
 | 
					            0x30..=0x3F => self.ch3.read_byte(addr),
 | 
				
			||||||
            _ => {
 | 
					            _ => {
 | 
				
			||||||
                tracing::warn!("Attempted read from {:#06X}", addr);
 | 
					                eprintln!("Read 0xFF from unused IO register {:#06X} [APU]", addr);
 | 
				
			||||||
                0xFF
 | 
					                0xFF
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -69,7 +66,7 @@ impl BusIo for Apu {
 | 
				
			|||||||
            0x11 if self.ctrl.enabled => self.ch1.set_duty(byte),
 | 
					            0x11 if self.ctrl.enabled => self.ch1.set_duty(byte),
 | 
				
			||||||
            0x12 if self.ctrl.enabled => self.ch1.set_envelope(byte),
 | 
					            0x12 if self.ctrl.enabled => self.ch1.set_envelope(byte),
 | 
				
			||||||
            0x13 if self.ctrl.enabled => self.ch1.set_freq_lo(byte),
 | 
					            0x13 if self.ctrl.enabled => self.ch1.set_freq_lo(byte),
 | 
				
			||||||
            0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(&self.sequencer, byte),
 | 
					            0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(byte),
 | 
				
			||||||
            0x16 if self.ctrl.enabled => self.ch2.set_duty(byte),
 | 
					            0x16 if self.ctrl.enabled => self.ch2.set_duty(byte),
 | 
				
			||||||
            0x17 if self.ctrl.enabled => self.ch2.set_envelope(byte),
 | 
					            0x17 if self.ctrl.enabled => self.ch2.set_envelope(byte),
 | 
				
			||||||
            0x18 if self.ctrl.enabled => self.ch2.set_freq_lo(byte),
 | 
					            0x18 if self.ctrl.enabled => self.ch2.set_freq_lo(byte),
 | 
				
			||||||
@@ -88,7 +85,10 @@ impl BusIo for Apu {
 | 
				
			|||||||
            0x26 => self.set_status(byte),
 | 
					            0x26 => self.set_status(byte),
 | 
				
			||||||
            0x30..=0x3F => self.ch3.write_byte(addr, byte),
 | 
					            0x30..=0x3F => self.ch3.write_byte(addr, byte),
 | 
				
			||||||
            _ if !self.ctrl.enabled => {}
 | 
					            _ if !self.ctrl.enabled => {}
 | 
				
			||||||
            _ => tracing::warn!("Attempted write of {:#04X} to {:#06X}", byte, addr),
 | 
					            _ => eprintln!(
 | 
				
			||||||
 | 
					                "Wrote {:#04X} to unused IO register {:#06X} [APU]",
 | 
				
			||||||
 | 
					                byte, addr
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -99,7 +99,7 @@ impl Apu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Frame Sequencer (512Hz)
 | 
					        // Frame Sequencer (512Hz)
 | 
				
			||||||
        if self.is_falling_edge(12, div) {
 | 
					        if self.is_falling_edge(12, div) {
 | 
				
			||||||
            use FSState::*;
 | 
					            use FrameSequencerState::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self.sequencer.state() {
 | 
					            match self.sequencer.state() {
 | 
				
			||||||
                Length => self.clock_length(),
 | 
					                Length => self.clock_length(),
 | 
				
			||||||
@@ -124,33 +124,25 @@ impl Apu {
 | 
				
			|||||||
        if self.sample_counter >= SM83_CLOCK_SPEED {
 | 
					        if self.sample_counter >= SM83_CLOCK_SPEED {
 | 
				
			||||||
            self.sample_counter %= SM83_CLOCK_SPEED;
 | 
					            self.sample_counter %= SM83_CLOCK_SPEED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(prod) = self.prod.as_mut() {
 | 
					            if let Some(ref mut prod) = self.prod {
 | 
				
			||||||
                if prod.available_blocking() {
 | 
					                if prod.available_blocking() {
 | 
				
			||||||
                    // Sample the APU
 | 
					                    // Sample the APU
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let ch1_amplitude =
 | 
					 | 
				
			||||||
                        Self::high_pass(&mut self.cap, self.ch1.amplitude(), self.ch1.enabled);
 | 
					 | 
				
			||||||
                    let (left, right) = self.ctrl.out.ch1();
 | 
					                    let (left, right) = self.ctrl.out.ch1();
 | 
				
			||||||
                    let ch1_left = if left { ch1_amplitude } else { 0.0 };
 | 
					                    let ch1_left = if left { self.ch1.amplitude() } else { 0.0 };
 | 
				
			||||||
                    let ch1_right = if right { ch1_amplitude } else { 0.0 };
 | 
					                    let ch1_right = if right { self.ch1.amplitude() } else { 0.0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let ch2_amplitude =
 | 
					 | 
				
			||||||
                        Self::high_pass(&mut self.cap, self.ch2.amplitude(), self.ch2.enabled);
 | 
					 | 
				
			||||||
                    let (left, right) = self.ctrl.out.ch2();
 | 
					                    let (left, right) = self.ctrl.out.ch2();
 | 
				
			||||||
                    let ch2_left = if left { ch2_amplitude } else { 0.0 };
 | 
					                    let ch2_left = if left { self.ch2.amplitude() } else { 0.0 };
 | 
				
			||||||
                    let ch2_right = if right { ch2_amplitude } else { 0.0 };
 | 
					                    let ch2_right = if right { self.ch2.amplitude() } else { 0.0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let ch3_amplitude =
 | 
					 | 
				
			||||||
                        Self::high_pass(&mut self.cap, self.ch3.amplitude(), self.ch3.enabled);
 | 
					 | 
				
			||||||
                    let (left, right) = self.ctrl.out.ch3();
 | 
					                    let (left, right) = self.ctrl.out.ch3();
 | 
				
			||||||
                    let ch3_left = if left { ch3_amplitude } else { 0.0 };
 | 
					                    let ch3_left = if left { self.ch3.amplitude() } else { 0.0 };
 | 
				
			||||||
                    let ch3_right = if right { ch3_amplitude } else { 0.0 };
 | 
					                    let ch3_right = if right { self.ch3.amplitude() } else { 0.0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let ch4_amplitude =
 | 
					 | 
				
			||||||
                        Self::high_pass(&mut self.cap, self.ch4.amplitude(), self.ch4.enabled);
 | 
					 | 
				
			||||||
                    let (left, right) = self.ctrl.out.ch4();
 | 
					                    let (left, right) = self.ctrl.out.ch4();
 | 
				
			||||||
                    let ch4_left = if left { ch4_amplitude } else { 0.0 };
 | 
					                    let ch4_left = if left { self.ch4.amplitude() } else { 0.0 };
 | 
				
			||||||
                    let ch4_right = if right { ch4_amplitude } else { 0.0 };
 | 
					                    let ch4_right = if right { self.ch4.amplitude() } else { 0.0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let left_mixed = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
 | 
					                    let left_mixed = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
 | 
				
			||||||
                    let right_mixed = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
 | 
					                    let right_mixed = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
 | 
				
			||||||
@@ -225,20 +217,20 @@ impl Apu {
 | 
				
			|||||||
        self.ch4.enabled = Default::default();
 | 
					        self.ch4.enabled = Default::default();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn process_length(freq_hi: &FrequencyHigh, counter: &mut u16, enabled: &mut bool) {
 | 
					    fn process_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
 | 
				
			||||||
        if freq_hi.length_enable() && *counter > 0 {
 | 
					        if freq_hi.length_disable() && *length_timer > 0 {
 | 
				
			||||||
            *counter -= 1;
 | 
					            *length_timer -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Check in this scope ensures (only) the above subtraction
 | 
					            // Check in this scope ensures (only) the above subtraction
 | 
				
			||||||
            // made length_timer 0
 | 
					            // made length_timer 0
 | 
				
			||||||
            if *counter == 0 {
 | 
					            if *length_timer == 0 {
 | 
				
			||||||
                *enabled = false;
 | 
					                *enabled = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn ch4_process_length(freq: &Ch4Frequency, length_timer: &mut u16, enabled: &mut bool) {
 | 
					    fn ch4_process_length(freq: &Ch4Frequency, length_timer: &mut u16, enabled: &mut bool) {
 | 
				
			||||||
        if freq.length_enable() && *length_timer > 0 {
 | 
					        if freq.length_disable() && *length_timer > 0 {
 | 
				
			||||||
            *length_timer -= 1;
 | 
					            *length_timer -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Check in this scope ensures (only) the above subtraction
 | 
					            // Check in this scope ensures (only) the above subtraction
 | 
				
			||||||
@@ -252,7 +244,7 @@ impl Apu {
 | 
				
			|||||||
    fn clock_length(&mut self) {
 | 
					    fn clock_length(&mut self) {
 | 
				
			||||||
        Self::process_length(
 | 
					        Self::process_length(
 | 
				
			||||||
            &self.ch1.freq_hi,
 | 
					            &self.ch1.freq_hi,
 | 
				
			||||||
            &mut self.ch1.length_counter,
 | 
					            &mut self.ch1.length_timer,
 | 
				
			||||||
            &mut self.ch1.enabled,
 | 
					            &mut self.ch1.enabled,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -345,19 +337,6 @@ impl Apu {
 | 
				
			|||||||
            None => false,
 | 
					            None => false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn high_pass(capacitor: &mut f32, input: f32, enabled: bool) -> f32 {
 | 
					 | 
				
			||||||
        const CHARGE_FACTOR: f32 = 0.999958;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut output = 0.0;
 | 
					 | 
				
			||||||
        if enabled {
 | 
					 | 
				
			||||||
            output = input - *capacitor;
 | 
					 | 
				
			||||||
            *capacitor =
 | 
					 | 
				
			||||||
                input - output * CHARGE_FACTOR.powi(SM83_CLOCK_SPEED as i32 / SAMPLE_RATE as i32);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        output
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
@@ -429,7 +408,7 @@ pub(crate) struct Channel1 {
 | 
				
			|||||||
    sweep_enabled: bool,
 | 
					    sweep_enabled: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Length Functionality
 | 
					    // Length Functionality
 | 
				
			||||||
    length_counter: u16,
 | 
					    length_timer: u16,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    freq_timer: u16,
 | 
					    freq_timer: u16,
 | 
				
			||||||
    duty_pos: u8,
 | 
					    duty_pos: u8,
 | 
				
			||||||
@@ -456,11 +435,11 @@ impl Channel1 {
 | 
				
			|||||||
    /// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
 | 
					    /// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
 | 
				
			||||||
    pub(crate) fn set_duty(&mut self, byte: u8) {
 | 
					    pub(crate) fn set_duty(&mut self, byte: u8) {
 | 
				
			||||||
        self.duty = byte.into();
 | 
					        self.duty = byte.into();
 | 
				
			||||||
        self.length_counter = 64 - self.duty.sound_length() as u16;
 | 
					        self.length_timer = 64 - self.duty.sound_length() as u16;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// 0xFF12 | NR12 - Channel 1 Volume Envelope
 | 
					    /// 0xFF12 | NR12 - Channel 1 Volume Envelope
 | 
				
			||||||
    pub(crate) fn envelope(&self) -> u8 {
 | 
					    pub fn envelope(&self) -> u8 {
 | 
				
			||||||
        u8::from(self.envelope)
 | 
					        u8::from(self.envelope)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -484,20 +463,9 @@ impl Channel1 {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// 0xFF14 | NR14 - Channel 1 Frequency high
 | 
					    /// 0xFF14 | NR14 - Channel 1 Frequency high
 | 
				
			||||||
    pub(crate) fn set_freq_hi(&mut self, fs: &FrameSequencer, byte: u8) {
 | 
					    pub(crate) fn set_freq_hi(&mut self, byte: u8) {
 | 
				
			||||||
        let prev_le = self.freq_hi.length_enable();
 | 
					 | 
				
			||||||
        self.freq_hi = byte.into();
 | 
					        self.freq_hi = byte.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !fs.next_clocks_length() {
 | 
					 | 
				
			||||||
            if !prev_le && self.freq_hi.length_enable() && self.length_counter != 0 {
 | 
					 | 
				
			||||||
                self.length_counter -= 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if self.length_counter == 0 && !self.freq_hi.initial() {
 | 
					 | 
				
			||||||
                    self.enabled = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // If this bit is set, a trigger event occurs
 | 
					        // If this bit is set, a trigger event occurs
 | 
				
			||||||
        if self.freq_hi.initial() {
 | 
					        if self.freq_hi.initial() {
 | 
				
			||||||
            if self.is_dac_enabled() {
 | 
					            if self.is_dac_enabled() {
 | 
				
			||||||
@@ -505,13 +473,9 @@ impl Channel1 {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Length behaviour during trigger event
 | 
					            // Length behaviour during trigger event
 | 
				
			||||||
            if self.length_counter == 0 {
 | 
					            if self.length_timer == 0 {
 | 
				
			||||||
                self.length_counter = if self.freq_hi.length_enable() && !fs.next_clocks_length() {
 | 
					                self.length_timer = 64;
 | 
				
			||||||
                    63
 | 
					            }
 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    64
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Envelope Behaviour during trigger event
 | 
					            // Envelope Behaviour during trigger event
 | 
				
			||||||
            self.period_timer = self.envelope.period();
 | 
					            self.period_timer = self.envelope.period();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,33 @@ pub(crate) const SAMPLE_RATE: u32 = 48000; // Hz
 | 
				
			|||||||
const CHANNEL_COUNT: usize = 2;
 | 
					const CHANNEL_COUNT: usize = 2;
 | 
				
			||||||
const BUFFER_CAPACITY: usize = 2048 * CHANNEL_COUNT; // # of samples * the # of channels
 | 
					const BUFFER_CAPACITY: usize = 2048 * CHANNEL_COUNT; // # of samples * the # of channels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn init<T>() -> (SampleProducer<T>, SampleConsumer<T>) {
 | 
					pub struct AudioSPSC<T> {
 | 
				
			||||||
    let (prod, cons) = RingBuffer::new(BUFFER_CAPACITY);
 | 
					    inner: RingBuffer<T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Default for AudioSPSC<T> {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            inner: RingBuffer::new(BUFFER_CAPACITY),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> AudioSPSC<T> {
 | 
				
			||||||
 | 
					    pub fn new(capacity: usize) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            inner: RingBuffer::new(capacity),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn init(self) -> (SampleProducer<T>, SampleConsumer<T>) {
 | 
				
			||||||
 | 
					        let (prod, cons) = self.inner.split();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            SampleProducer { inner: prod },
 | 
					            SampleProducer { inner: prod },
 | 
				
			||||||
            SampleConsumer { inner: cons },
 | 
					            SampleConsumer { inner: cons },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct SampleProducer<T> {
 | 
					pub struct SampleProducer<T> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,12 +178,12 @@ pub(super) mod ch4 {
 | 
				
			|||||||
        pub struct Frequency(u8);
 | 
					        pub struct Frequency(u8);
 | 
				
			||||||
        impl Debug;
 | 
					        impl Debug;
 | 
				
			||||||
        _initial, _: 7;
 | 
					        _initial, _: 7;
 | 
				
			||||||
        _length_enable, _: 6;
 | 
					        _length_disable, _: 6;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Frequency {
 | 
					    impl Frequency {
 | 
				
			||||||
        pub(crate) fn length_enable(&self) -> bool {
 | 
					        pub(crate) fn length_disable(&self) -> bool {
 | 
				
			||||||
            self._length_enable()
 | 
					            self._length_disable()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pub(crate) fn initial(&self) -> bool {
 | 
					        pub(crate) fn initial(&self) -> bool {
 | 
				
			||||||
@@ -224,7 +224,7 @@ pub(super) mod common {
 | 
				
			|||||||
        pub struct FrequencyHigh(u8);
 | 
					        pub struct FrequencyHigh(u8);
 | 
				
			||||||
        impl Debug;
 | 
					        impl Debug;
 | 
				
			||||||
        _initial, _: 7;
 | 
					        _initial, _: 7;
 | 
				
			||||||
        _length_enable, _: 6;
 | 
					        _length_disable, _: 6;
 | 
				
			||||||
        pub freq_bits, set_freq_bits: 2, 0;
 | 
					        pub freq_bits, set_freq_bits: 2, 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -233,8 +233,8 @@ pub(super) mod common {
 | 
				
			|||||||
            self._initial()
 | 
					            self._initial()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pub(crate) fn length_enable(&self) -> bool {
 | 
					        pub(crate) fn length_disable(&self) -> bool {
 | 
				
			||||||
            self._length_enable()
 | 
					            self._length_disable()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -557,27 +557,6 @@ pub(super) mod fs {
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn peek(&self) -> State {
 | 
					 | 
				
			||||||
            use State::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            match (self.step + 1) % 8 {
 | 
					 | 
				
			||||||
                1 | 3 | 5 => Nothing,
 | 
					 | 
				
			||||||
                0 | 4 => Length,
 | 
					 | 
				
			||||||
                2 | 6 => LengthAndSweep,
 | 
					 | 
				
			||||||
                7 => Envelope,
 | 
					 | 
				
			||||||
                num => unreachable!("Step {} is invalid for the Frame Sequencer", num),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub(crate) fn next_clocks_length(&self) -> bool {
 | 
					 | 
				
			||||||
            use State::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            match self.peek() {
 | 
					 | 
				
			||||||
                Length | LengthAndSweep => true,
 | 
					 | 
				
			||||||
                Nothing | Envelope => false,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pub(crate) fn state(&self) -> State {
 | 
					        pub(crate) fn state(&self) -> State {
 | 
				
			||||||
            self.state
 | 
					            self.state
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -588,7 +567,7 @@ pub(super) mod fs {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
					    #[derive(Debug, Clone, Copy)]
 | 
				
			||||||
    pub(crate) enum State {
 | 
					    pub(crate) enum State {
 | 
				
			||||||
        Length,
 | 
					        Length,
 | 
				
			||||||
        Nothing,
 | 
					        Nothing,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										64
									
								
								src/bus.rs
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								src/bus.rs
									
									
									
									
									
								
							@@ -14,15 +14,15 @@ pub(crate) const BOOT_SIZE: usize = 0x100;
 | 
				
			|||||||
pub struct Bus {
 | 
					pub struct Bus {
 | 
				
			||||||
    boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
 | 
					    boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
 | 
				
			||||||
    cart: Option<Cartridge>,
 | 
					    cart: Option<Cartridge>,
 | 
				
			||||||
    ppu: Ppu,
 | 
					    pub(crate) ppu: Ppu,
 | 
				
			||||||
    work_ram: WorkRam,
 | 
					    work_ram: WorkRam,
 | 
				
			||||||
    var_ram: VariableWorkRam,
 | 
					    var_ram: VariableWorkRam,
 | 
				
			||||||
    timer: Timer,
 | 
					    pub(crate) timer: Timer,
 | 
				
			||||||
    int: Interrupt,
 | 
					    int: Interrupt,
 | 
				
			||||||
    apu: Apu,
 | 
					    pub(crate) apu: Apu,
 | 
				
			||||||
    high_ram: HighRam,
 | 
					    high_ram: HighRam,
 | 
				
			||||||
    serial: Serial,
 | 
					    serial: Serial,
 | 
				
			||||||
    joypad: Joypad,
 | 
					    pub(crate) joypad: Joypad,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for Bus {
 | 
					impl Default for Bus {
 | 
				
			||||||
@@ -64,12 +64,10 @@ impl Bus {
 | 
				
			|||||||
        self.boot.is_some()
 | 
					        self.boot.is_some()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn clock(&mut self) {
 | 
					    pub(crate) fn clock(&mut self) {
 | 
				
			||||||
        self.tick(4);
 | 
					        self.tick(4);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    fn tick(&mut self, limit: u8) {
 | 
					    fn tick(&mut self, limit: u8) {
 | 
				
			||||||
        for _ in 0..limit {
 | 
					        for _ in 0..limit {
 | 
				
			||||||
            self.timer.tick();
 | 
					            self.timer.tick();
 | 
				
			||||||
@@ -85,35 +83,10 @@ impl Bus {
 | 
				
			|||||||
            self.oam_write_byte(dest_addr, byte);
 | 
					            self.oam_write_byte(dest_addr, byte);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn joyp_mut(&mut self) -> &mut Joypad {
 | 
					 | 
				
			||||||
        &mut self.joypad
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn cart(&self) -> Option<&Cartridge> {
 | 
					 | 
				
			||||||
        self.cart.as_ref()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn cart_mut(&mut self) -> Option<&mut Cartridge> {
 | 
					 | 
				
			||||||
        self.cart.as_mut()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn apu_mut(&mut self) -> &mut Apu {
 | 
					 | 
				
			||||||
        &mut self.apu
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub fn ppu(&self) -> &Ppu {
 | 
					 | 
				
			||||||
        &self.ppu
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Bus {
 | 
					impl Bus {
 | 
				
			||||||
    pub(crate) fn oam_read_byte(&self, addr: u16) -> u8 {
 | 
					    pub fn oam_read_byte(&self, addr: u16) -> u8 {
 | 
				
			||||||
        match addr {
 | 
					        match addr {
 | 
				
			||||||
            0x0000..=0x7FFF => {
 | 
					            0x0000..=0x7FFF => {
 | 
				
			||||||
                // 16KB ROM bank 00 (ends at 0x3FFF)
 | 
					                // 16KB ROM bank 00 (ends at 0x3FFF)
 | 
				
			||||||
@@ -126,14 +99,14 @@ impl Bus {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                match self.cart.as_ref() {
 | 
					                match self.cart.as_ref() {
 | 
				
			||||||
                    Some(cart) => cart.read_byte(addr),
 | 
					                    Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                    None => 0xFF,
 | 
					                    None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
 | 
					            0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
 | 
				
			||||||
            0xA000..=0xBFFF => match self.cart.as_ref() {
 | 
					            0xA000..=0xBFFF => match self.cart.as_ref() {
 | 
				
			||||||
                // 8KB External RAM
 | 
					                // 8KB External RAM
 | 
				
			||||||
                Some(cart) => cart.read_byte(addr),
 | 
					                Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                None => 0xFF,
 | 
					                None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
 | 
					            0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
 | 
				
			||||||
            0xD000..=0xDFFF => self.var_ram.read_byte(addr),  // 4KB Work RAM Bank 1 -> N
 | 
					            0xD000..=0xDFFF => self.var_ram.read_byte(addr),  // 4KB Work RAM Bank 1 -> N
 | 
				
			||||||
@@ -160,7 +133,7 @@ impl Bus {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn oam_write_byte(&mut self, addr: u16, byte: u8) {
 | 
					    pub fn oam_write_byte(&mut self, addr: u16, byte: u8) {
 | 
				
			||||||
        self.ppu.oam.write_byte(addr, byte);
 | 
					        self.ppu.oam.write_byte(addr, byte);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -179,7 +152,7 @@ impl BusIo for Bus {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                match self.cart.as_ref() {
 | 
					                match self.cart.as_ref() {
 | 
				
			||||||
                    Some(cart) => cart.read_byte(addr),
 | 
					                    Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                    None => 0xFF,
 | 
					                    None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0x8000..=0x9FFF => {
 | 
					            0x8000..=0x9FFF => {
 | 
				
			||||||
@@ -192,7 +165,7 @@ impl BusIo for Bus {
 | 
				
			|||||||
            0xA000..=0xBFFF => match self.cart.as_ref() {
 | 
					            0xA000..=0xBFFF => match self.cart.as_ref() {
 | 
				
			||||||
                // 8KB External RAM
 | 
					                // 8KB External RAM
 | 
				
			||||||
                Some(cart) => cart.read_byte(addr),
 | 
					                Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                None => 0xFF,
 | 
					                None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
 | 
					            0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
 | 
				
			||||||
            0xD000..=0xDFFF => self.var_ram.read_byte(addr),  // 4KB Work RAM Bank 1 -> N
 | 
					            0xD000..=0xDFFF => self.var_ram.read_byte(addr),  // 4KB Work RAM Bank 1 -> N
 | 
				
			||||||
@@ -260,9 +233,8 @@ impl BusIo for Bus {
 | 
				
			|||||||
                    0x49 => self.ppu.monochrome.obj_palette_1.into(),
 | 
					                    0x49 => self.ppu.monochrome.obj_palette_1.into(),
 | 
				
			||||||
                    0x4A => self.ppu.pos.window_y,
 | 
					                    0x4A => self.ppu.pos.window_y,
 | 
				
			||||||
                    0x4B => self.ppu.pos.window_x,
 | 
					                    0x4B => self.ppu.pos.window_x,
 | 
				
			||||||
                    0x4F => 0xFF, // CGB VRAM Bank Select
 | 
					 | 
				
			||||||
                    _ => {
 | 
					                    _ => {
 | 
				
			||||||
                        tracing::warn!("Attempted read from {:#06X} on IO", addr);
 | 
					                        eprintln!("Read 0xFF from unused IO register {:#06X}.", addr);
 | 
				
			||||||
                        0xFF
 | 
					                        0xFF
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -283,8 +255,9 @@ impl BusIo for Bus {
 | 
				
			|||||||
            0x0000..=0x7FFF => {
 | 
					            0x0000..=0x7FFF => {
 | 
				
			||||||
                // 16KB ROM bank 00 (ends at 0x3FFF)
 | 
					                // 16KB ROM bank 00 (ends at 0x3FFF)
 | 
				
			||||||
                // and 16KB ROM Bank 01 -> NN (switchable via MB)
 | 
					                // and 16KB ROM Bank 01 -> NN (switchable via MB)
 | 
				
			||||||
                if let Some(cart) = self.cart.as_mut() {
 | 
					                match self.cart.as_mut() {
 | 
				
			||||||
                    cart.write_byte(addr, byte);
 | 
					                    Some(cart) => cart.write_byte(addr, byte),
 | 
				
			||||||
 | 
					                    None => panic!("Tried to write into non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0x8000..=0x9FFF => {
 | 
					            0x8000..=0x9FFF => {
 | 
				
			||||||
@@ -296,8 +269,9 @@ impl BusIo for Bus {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            0xA000..=0xBFFF => {
 | 
					            0xA000..=0xBFFF => {
 | 
				
			||||||
                // 8KB External RAM
 | 
					                // 8KB External RAM
 | 
				
			||||||
                if let Some(cart) = self.cart.as_mut() {
 | 
					                match self.cart.as_mut() {
 | 
				
			||||||
                    cart.write_byte(addr, byte);
 | 
					                    Some(cart) => cart.write_byte(addr, byte),
 | 
				
			||||||
 | 
					                    None => panic!("Tried to write into non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
 | 
					            0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
 | 
				
			||||||
@@ -371,15 +345,13 @@ impl BusIo for Bus {
 | 
				
			|||||||
                    0x4A => self.ppu.pos.window_y = byte,
 | 
					                    0x4A => self.ppu.pos.window_y = byte,
 | 
				
			||||||
                    0x4B => self.ppu.pos.window_x = byte,
 | 
					                    0x4B => self.ppu.pos.window_x = byte,
 | 
				
			||||||
                    0x4D => {} // CGB Specific Register
 | 
					                    0x4D => {} // CGB Specific Register
 | 
				
			||||||
                    0x4F => {} // CGB VRAM Bank Select
 | 
					 | 
				
			||||||
                    0x50 => {
 | 
					                    0x50 => {
 | 
				
			||||||
                        // Disable Boot ROM
 | 
					                        // Disable Boot ROM
 | 
				
			||||||
                        if byte != 0 {
 | 
					                        if byte != 0 {
 | 
				
			||||||
                            self.boot = None;
 | 
					                            self.boot = None;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    0x70 => {} // CGB WRAM Bank Select
 | 
					                    _ => eprintln!("Wrote {:#04X} to unused IO register {:#06X}.", byte, addr),
 | 
				
			||||||
                    _ => tracing::warn!("Attempted write of {:#04X} to {:#06X} on IO", byte, addr),
 | 
					 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0xFF80..=0xFFFE => {
 | 
					            0xFF80..=0xFFFE => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										180
									
								
								src/cartridge.rs
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								src/cartridge.rs
									
									
									
									
									
								
							@@ -3,8 +3,7 @@ use crate::bus::BusIo;
 | 
				
			|||||||
const RAM_SIZE_ADDRESS: usize = 0x0149;
 | 
					const RAM_SIZE_ADDRESS: usize = 0x0149;
 | 
				
			||||||
const ROM_SIZE_ADDRESS: usize = 0x0148;
 | 
					const ROM_SIZE_ADDRESS: usize = 0x0148;
 | 
				
			||||||
const MBC_TYPE_ADDRESS: usize = 0x0147;
 | 
					const MBC_TYPE_ADDRESS: usize = 0x0147;
 | 
				
			||||||
const ROM_TITLE_START: usize = 0x134;
 | 
					const ROM_TITLE_RANGE: std::ops::RangeInclusive<usize> = 0x0134..=0x0143;
 | 
				
			||||||
const ROM_TITLE_MAX_SIZE: usize = 16;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
pub(crate) struct Cartridge {
 | 
					pub(crate) struct Cartridge {
 | 
				
			||||||
@@ -16,7 +15,7 @@ pub(crate) struct Cartridge {
 | 
				
			|||||||
impl Cartridge {
 | 
					impl Cartridge {
 | 
				
			||||||
    pub(crate) fn new(memory: Vec<u8>) -> Self {
 | 
					    pub(crate) fn new(memory: Vec<u8>) -> Self {
 | 
				
			||||||
        let title = Self::find_title(&memory);
 | 
					        let title = Self::find_title(&memory);
 | 
				
			||||||
        tracing::info!("Title: {:?}", title);
 | 
					        eprintln!("Cartridge Title: {:?}", title);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            mbc: Self::detect_mbc(&memory),
 | 
					            mbc: Self::detect_mbc(&memory),
 | 
				
			||||||
@@ -25,14 +24,6 @@ impl Cartridge {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        self.mbc.ext_ram()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(crate) fn write_ext_ram(&mut self, memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        self.mbc.write_ext_ram(memory)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
 | 
					    fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
 | 
				
			||||||
        let ram_size = Self::detect_ram_info(memory);
 | 
					        let ram_size = Self::detect_ram_info(memory);
 | 
				
			||||||
        let rom_size = Self::detect_rom_info(memory);
 | 
					        let rom_size = Self::detect_rom_info(memory);
 | 
				
			||||||
@@ -40,35 +31,31 @@ impl Cartridge {
 | 
				
			|||||||
        let ram_cap = ram_size.capacity();
 | 
					        let ram_cap = ram_size.capacity();
 | 
				
			||||||
        let rom_cap = rom_size.capacity();
 | 
					        let rom_cap = rom_size.capacity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tracing::info!("RAM size: {} bytes", ram_cap);
 | 
					        eprintln!("Cartridge Ram Size: {} bytes", ram_cap);
 | 
				
			||||||
        tracing::info!("ROM size: {} bytes", rom_size.capacity());
 | 
					        eprintln!("Cartridge ROM Size: {} bytes", rom_size.capacity());
 | 
				
			||||||
        tracing::info!("MBC kind: {:?}", mbc_kind);
 | 
					        eprintln!("MBC Type: {:?}", mbc_kind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match mbc_kind {
 | 
					        match mbc_kind {
 | 
				
			||||||
            MBCKind::None => Box::new(NoMBC),
 | 
					            MBCKind::None => Box::new(NoMBC),
 | 
				
			||||||
            MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)),
 | 
					            MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)),
 | 
				
			||||||
            MBCKind::MBC1WithBattery => Box::new(MBC1::with_battery(ram_size, rom_size)),
 | 
					            MBCKind::MBC1WithBattery => Box::new(MBC1::new(ram_size, rom_size)), // TODO: Implement Saving
 | 
				
			||||||
            MBCKind::MBC2 => Box::new(MBC2::new(rom_cap)),
 | 
					            MBCKind::MBC2 => Box::new(MBC2::new(rom_cap)),
 | 
				
			||||||
            MBCKind::MBC2WithBattery => Box::new(MBC2::with_battery(rom_cap)),
 | 
					            MBCKind::MBC2WithBattery => Box::new(MBC2::new(rom_cap)), // TODO: Implement Saving
 | 
				
			||||||
            MBCKind::MBC3 => Box::new(MBC3::new(ram_cap)),
 | 
					            MBCKind::MBC3 => Box::new(MBC3::new(ram_cap)),
 | 
				
			||||||
            MBCKind::MBC3WithBattery => Box::new(MBC3::with_battery(ram_cap)),
 | 
					            MBCKind::MBC3WithBattery => Box::new(MBC3::new(ram_cap)), // TODO: Implement Saving
 | 
				
			||||||
            MBCKind::MBC5 => Box::new(MBC5::new(ram_cap, rom_cap)),
 | 
					            MBCKind::MBC5 => Box::new(MBC5::new(ram_cap, rom_cap)),
 | 
				
			||||||
            MBCKind::MBC5WithBattery => Box::new(MBC5::with_battery(ram_cap, rom_cap)),
 | 
					            MBCKind::MBC5WithBattery => Box::new(MBC5::new(ram_cap, rom_cap)), // TDO: Implement Saving
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn find_title(memory: &[u8]) -> Option<String> {
 | 
					    fn find_title(memory: &[u8]) -> Option<String> {
 | 
				
			||||||
        let title_bytes = &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE)];
 | 
					        let slice = &memory[ROM_TITLE_RANGE];
 | 
				
			||||||
 | 
					        let with_nulls = std::str::from_utf8(slice).ok();
 | 
				
			||||||
 | 
					        let trimmed = with_nulls.map(|s| s.trim_matches('\0').trim());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ASCII Byte array purposely does not have null terminator
 | 
					        match trimmed {
 | 
				
			||||||
        let ascii = match title_bytes.iter().position(|byte| *byte == 0x00) {
 | 
					 | 
				
			||||||
            Some(end) => &memory[ROM_TITLE_START..(ROM_TITLE_START + end)],
 | 
					 | 
				
			||||||
            None => &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE - 1)],
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match std::str::from_utf8(ascii).ok() {
 | 
					 | 
				
			||||||
            Some("") | None => None,
 | 
					            Some("") | None => None,
 | 
				
			||||||
            Some(title) => Some(String::from(title)),
 | 
					            Some(_) => trimmed.map(String::from),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -130,8 +117,6 @@ struct MBC1 {
 | 
				
			|||||||
    memory: Vec<u8>,
 | 
					    memory: Vec<u8>,
 | 
				
			||||||
    rom_size: RomSize,
 | 
					    rom_size: RomSize,
 | 
				
			||||||
    mem_enabled: bool,
 | 
					    mem_enabled: bool,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    has_battery: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MBC1 {
 | 
					impl MBC1 {
 | 
				
			||||||
@@ -144,20 +129,6 @@ impl MBC1 {
 | 
				
			|||||||
            ram_bank: Default::default(),
 | 
					            ram_bank: Default::default(),
 | 
				
			||||||
            mode: Default::default(),
 | 
					            mode: Default::default(),
 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					            mem_enabled: Default::default(),
 | 
				
			||||||
            has_battery: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn with_battery(ram_size: RamSize, rom_size: RomSize) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            rom_bank: 0x01,
 | 
					 | 
				
			||||||
            memory: vec![0; ram_size.capacity() as usize],
 | 
					 | 
				
			||||||
            ram_size,
 | 
					 | 
				
			||||||
            rom_size,
 | 
					 | 
				
			||||||
            ram_bank: Default::default(),
 | 
					 | 
				
			||||||
            mode: Default::default(),
 | 
					 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					 | 
				
			||||||
            has_battery: true,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -226,21 +197,6 @@ impl MBC1 {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Savable for MBC1 {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        match self.has_battery {
 | 
					 | 
				
			||||||
            true => Some(&self.memory),
 | 
					 | 
				
			||||||
            false => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        if self.has_battery {
 | 
					 | 
				
			||||||
            self.memory.copy_from_slice(&memory);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MBCIo for MBC1 {
 | 
					impl MBCIo for MBC1 {
 | 
				
			||||||
    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
					    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
				
			||||||
        use MBCResult::*;
 | 
					        use MBCResult::*;
 | 
				
			||||||
@@ -300,8 +256,6 @@ struct MBC3 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // RTC Data Latch Previous Write
 | 
					    // RTC Data Latch Previous Write
 | 
				
			||||||
    prev_latch_write: Option<u8>,
 | 
					    prev_latch_write: Option<u8>,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    has_battery: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MBC3 {
 | 
					impl MBC3 {
 | 
				
			||||||
@@ -313,34 +267,6 @@ impl MBC3 {
 | 
				
			|||||||
            devs_enabled: Default::default(),
 | 
					            devs_enabled: Default::default(),
 | 
				
			||||||
            mapped: Default::default(),
 | 
					            mapped: Default::default(),
 | 
				
			||||||
            prev_latch_write: Default::default(),
 | 
					            prev_latch_write: Default::default(),
 | 
				
			||||||
            has_battery: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn with_battery(ram_cap: usize) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            memory: vec![0; ram_cap],
 | 
					 | 
				
			||||||
            rom_bank: Default::default(),
 | 
					 | 
				
			||||||
            ram_bank: Default::default(),
 | 
					 | 
				
			||||||
            devs_enabled: Default::default(),
 | 
					 | 
				
			||||||
            mapped: Default::default(),
 | 
					 | 
				
			||||||
            prev_latch_write: Default::default(),
 | 
					 | 
				
			||||||
            has_battery: true,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Savable for MBC3 {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        match self.has_battery {
 | 
					 | 
				
			||||||
            true => Some(&self.memory),
 | 
					 | 
				
			||||||
            false => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        if self.has_battery {
 | 
					 | 
				
			||||||
            self.memory.copy_from_slice(&memory);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -416,10 +342,9 @@ struct MBC5 {
 | 
				
			|||||||
    ram_bank: u8,
 | 
					    ram_bank: u8,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rom_cap: usize,
 | 
					    rom_cap: usize,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    memory: Vec<u8>,
 | 
					    memory: Vec<u8>,
 | 
				
			||||||
    mem_enabled: bool,
 | 
					    mem_enabled: bool,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    has_battery: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MBC5 {
 | 
					impl MBC5 {
 | 
				
			||||||
@@ -430,18 +355,6 @@ impl MBC5 {
 | 
				
			|||||||
            rom_cap,
 | 
					            rom_cap,
 | 
				
			||||||
            ram_bank: Default::default(),
 | 
					            ram_bank: Default::default(),
 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					            mem_enabled: Default::default(),
 | 
				
			||||||
            has_battery: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn with_battery(ram_cap: usize, rom_cap: usize) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            rom_bank: 0x01,
 | 
					 | 
				
			||||||
            memory: vec![0; ram_cap],
 | 
					 | 
				
			||||||
            rom_cap,
 | 
					 | 
				
			||||||
            ram_bank: Default::default(),
 | 
					 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					 | 
				
			||||||
            has_battery: true,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -450,21 +363,6 @@ impl MBC5 {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Savable for MBC5 {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        match self.has_battery {
 | 
					 | 
				
			||||||
            true => Some(&self.memory),
 | 
					 | 
				
			||||||
            false => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        if self.has_battery {
 | 
					 | 
				
			||||||
            self.memory.copy_from_slice(&memory);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MBCIo for MBC5 {
 | 
					impl MBCIo for MBC5 {
 | 
				
			||||||
    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
					    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
				
			||||||
        use MBCResult::*;
 | 
					        use MBCResult::*;
 | 
				
			||||||
@@ -503,7 +401,6 @@ struct MBC2 {
 | 
				
			|||||||
    mem_enabled: bool,
 | 
					    mem_enabled: bool,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rom_cap: usize,
 | 
					    rom_cap: usize,
 | 
				
			||||||
    has_battery: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MBC2 {
 | 
					impl MBC2 {
 | 
				
			||||||
@@ -513,19 +410,8 @@ impl MBC2 {
 | 
				
			|||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            rom_bank: 0x01,
 | 
					            rom_bank: 0x01,
 | 
				
			||||||
            memory: Box::new([0; Self::RAM_SIZE]),
 | 
					            memory: Box::new([0; Self::RAM_SIZE]),
 | 
				
			||||||
            rom_cap,
 | 
					 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					            mem_enabled: Default::default(),
 | 
				
			||||||
            has_battery: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn with_battery(rom_cap: usize) -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            rom_bank: 0x01,
 | 
					 | 
				
			||||||
            memory: Box::new([0; Self::RAM_SIZE]),
 | 
					 | 
				
			||||||
            rom_cap,
 | 
					            rom_cap,
 | 
				
			||||||
            mem_enabled: Default::default(),
 | 
					 | 
				
			||||||
            has_battery: true,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -534,21 +420,6 @@ impl MBC2 {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Savable for MBC2 {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        match self.has_battery {
 | 
					 | 
				
			||||||
            true => Some(self.memory.as_ref()),
 | 
					 | 
				
			||||||
            false => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        if self.has_battery {
 | 
					 | 
				
			||||||
            self.memory.copy_from_slice(&memory);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MBCIo for MBC2 {
 | 
					impl MBCIo for MBC2 {
 | 
				
			||||||
    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
					    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
				
			||||||
        use MBCResult::*;
 | 
					        use MBCResult::*;
 | 
				
			||||||
@@ -586,27 +457,17 @@ impl MBCIo for MBC2 {
 | 
				
			|||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
struct NoMBC;
 | 
					struct NoMBC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Savable for NoMBC {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]> {
 | 
					 | 
				
			||||||
        None
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, _memory: Vec<u8>) {
 | 
					 | 
				
			||||||
        // Nothing Happens Here
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MBCIo for NoMBC {
 | 
					impl MBCIo for NoMBC {
 | 
				
			||||||
    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
					    fn handle_read(&self, addr: u16) -> MBCResult {
 | 
				
			||||||
        MBCResult::Address(addr as usize)
 | 
					        MBCResult::Address(addr as usize)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handle_write(&mut self, _: u16, byte: u8) {
 | 
					    fn handle_write(&mut self, _addr: u16, _byte: u8) {
 | 
				
			||||||
        tracing::warn!("Attempted write of {:#04X} to cartridge w/out MBC", byte);
 | 
					        // eprintln!("Tried to write {:#04X} to a read-only cartridge", byte);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trait MBCIo: Savable {
 | 
					trait MBCIo {
 | 
				
			||||||
    fn handle_read(&self, addr: u16) -> MBCResult;
 | 
					    fn handle_read(&self, addr: u16) -> MBCResult;
 | 
				
			||||||
    fn handle_write(&mut self, addr: u16, byte: u8);
 | 
					    fn handle_write(&mut self, addr: u16, byte: u8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -746,8 +607,3 @@ impl Default for Box<dyn MBCIo> {
 | 
				
			|||||||
        Box::new(NoMBC)
 | 
					        Box::new(NoMBC)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
trait Savable {
 | 
					 | 
				
			||||||
    fn ext_ram(&self) -> Option<&[u8]>;
 | 
					 | 
				
			||||||
    fn write_ext_ram(&mut self, memory: Vec<u8>);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/cpu.rs
									
									
									
									
									
								
							@@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
pub struct Cpu {
 | 
					pub struct Cpu {
 | 
				
			||||||
    pub(crate) bus: Bus,
 | 
					    pub bus: Bus,
 | 
				
			||||||
    reg: Registers,
 | 
					    reg: Registers,
 | 
				
			||||||
    flags: Flags,
 | 
					    flags: Flags,
 | 
				
			||||||
    ime: ImeState,
 | 
					    ime: ImeState,
 | 
				
			||||||
@@ -15,7 +15,6 @@ pub struct Cpu {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
    #[allow(dead_code)]
 | 
					 | 
				
			||||||
    pub(crate) fn without_boot() -> Self {
 | 
					    pub(crate) fn without_boot() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            reg: Registers {
 | 
					            reg: Registers {
 | 
				
			||||||
@@ -58,7 +57,10 @@ impl Cpu {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn is_halted(&self) -> bool {
 | 
					    pub(crate) fn is_halted(&self) -> bool {
 | 
				
			||||||
        matches!(self.state, State::Halt(_))
 | 
					        match self.state {
 | 
				
			||||||
 | 
					            State::Halt(_) => true,
 | 
				
			||||||
 | 
					            _ => false,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn halt_kind(&self) -> Option<HaltKind> {
 | 
					    pub(crate) fn halt_kind(&self) -> Option<HaltKind> {
 | 
				
			||||||
@@ -103,7 +105,7 @@ impl Cpu {
 | 
				
			|||||||
    /// routine.
 | 
					    /// routine.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Handle HALT and interrupts.
 | 
					    /// Handle HALT and interrupts.
 | 
				
			||||||
    pub(crate) fn step(&mut self) -> Cycle {
 | 
					    pub fn step(&mut self) -> Cycle {
 | 
				
			||||||
        // Log instructions
 | 
					        // Log instructions
 | 
				
			||||||
        // if self.reg.pc > 0xFF {
 | 
					        // if self.reg.pc > 0xFF {
 | 
				
			||||||
        //     let out = std::io::stdout();
 | 
					        //     let out = std::io::stdout();
 | 
				
			||||||
@@ -118,10 +120,13 @@ impl Cpu {
 | 
				
			|||||||
            use HaltKind::*;
 | 
					            use HaltKind::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.bus.clock();
 | 
					            self.bus.clock();
 | 
				
			||||||
            return match kind {
 | 
					
 | 
				
			||||||
 | 
					            let elapsed = match kind {
 | 
				
			||||||
                ImeEnabled | NonePending => 4,
 | 
					                ImeEnabled | NonePending => 4,
 | 
				
			||||||
                SomePending => todo!("Implement HALT bug"),
 | 
					                SomePending => todo!("Implement HALT bug"),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return elapsed;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let opcode = self.fetch();
 | 
					        let opcode = self.fetch();
 | 
				
			||||||
@@ -130,11 +135,11 @@ impl Cpu {
 | 
				
			|||||||
        self.handle_ei();
 | 
					        self.handle_ei();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // For use in Blargg's Test ROMs
 | 
					        // For use in Blargg's Test ROMs
 | 
				
			||||||
        // if self.read_byte(0xFF02) == 0x81 {
 | 
					        if self.read_byte(0xFF02) == 0x81 {
 | 
				
			||||||
        //     let c = self.read_byte(0xFF01) as char;
 | 
					            let c = self.read_byte(0xFF01) as char;
 | 
				
			||||||
        //     self.write_byte(0xFF02, 0x00);
 | 
					            self.write_byte(0xFF02, 0x00);
 | 
				
			||||||
        //     eprint!("{}", c);
 | 
					            eprint!("{}", c);
 | 
				
			||||||
        // }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elapsed
 | 
					        elapsed
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -151,12 +156,10 @@ impl BusIo for Cpu {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn bus(&self) -> &Bus {
 | 
					    pub(crate) fn bus(&self) -> &Bus {
 | 
				
			||||||
        &self.bus
 | 
					        &self.bus
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn bus_mut(&mut self) -> &mut Bus {
 | 
					    pub(crate) fn bus_mut(&mut self) -> &mut Bus {
 | 
				
			||||||
        &mut self.bus
 | 
					        &mut self.bus
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										124
									
								
								src/emu.rs
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								src/emu.rs
									
									
									
									
									
								
							@@ -1,13 +1,8 @@
 | 
				
			|||||||
use crate::apu::gen::SampleProducer;
 | 
					use crate::apu::gen::SampleProducer;
 | 
				
			||||||
use crate::bus::BOOT_SIZE;
 | 
					 | 
				
			||||||
use crate::cpu::Cpu;
 | 
					use crate::cpu::Cpu;
 | 
				
			||||||
use crate::joypad::{self, Joypad};
 | 
					use crate::joypad::{self, Joypad};
 | 
				
			||||||
use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
 | 
					use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
 | 
				
			||||||
use clap::crate_name;
 | 
					 | 
				
			||||||
use gilrs::Gilrs;
 | 
					use gilrs::Gilrs;
 | 
				
			||||||
use std::fs::File;
 | 
					 | 
				
			||||||
use std::io::{Read, Write};
 | 
					 | 
				
			||||||
use std::path::{Path, PathBuf};
 | 
					 | 
				
			||||||
use std::time::Duration;
 | 
					use std::time::Duration;
 | 
				
			||||||
use winit_input_helper::WinitInputHelper;
 | 
					use winit_input_helper::WinitInputHelper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,8 +17,8 @@ pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper
 | 
				
			|||||||
    if let Some(event) = gamepad.next_event() {
 | 
					    if let Some(event) = gamepad.next_event() {
 | 
				
			||||||
        joypad::handle_gamepad_input(emu.joyp_mut(), event);
 | 
					        joypad::handle_gamepad_input(emu.joyp_mut(), event);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    joypad::handle_keyboard_input(emu.joyp_mut(), key);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    joypad::handle_keyboard_input(emu.joyp_mut(), key);
 | 
				
			||||||
    while elapsed < CYCLES_IN_FRAME {
 | 
					    while elapsed < CYCLES_IN_FRAME {
 | 
				
			||||||
        elapsed += emu.step();
 | 
					        elapsed += emu.step();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -32,7 +27,7 @@ pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
 | 
					pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
 | 
				
			||||||
    buf.copy_from_slice(emu.cpu.bus().ppu().frame_buf());
 | 
					    buf.copy_from_slice(emu.cpu.bus().ppu.frame_buf());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Emulator {
 | 
					pub struct Emulator {
 | 
				
			||||||
@@ -40,106 +35,87 @@ pub struct Emulator {
 | 
				
			|||||||
    timestamp: Cycle,
 | 
					    timestamp: Cycle,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for Emulator {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self::new()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Emulator {
 | 
					impl Emulator {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    fn new(cpu: Cpu) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            cpu: Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")),
 | 
					            cpu,
 | 
				
			||||||
            timestamp: Default::default(),
 | 
					            timestamp: Default::default(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            cpu: Cpu::with_boot(Self::read_boot(path)?),
 | 
					 | 
				
			||||||
            timestamp: Default::default(),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn read_boot<P: AsRef<Path>>(path: P) -> std::io::Result<[u8; BOOT_SIZE]> {
 | 
					 | 
				
			||||||
        let mut buf = [0; BOOT_SIZE];
 | 
					 | 
				
			||||||
        let mut file = File::open(path.as_ref())?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        file.read_exact(&mut buf)?;
 | 
					 | 
				
			||||||
        Ok(buf)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn step(&mut self) -> Cycle {
 | 
					    fn step(&mut self) -> Cycle {
 | 
				
			||||||
        let cycles = self.cpu.step();
 | 
					        self.cpu.step()
 | 
				
			||||||
        self.timestamp += cycles;
 | 
					 | 
				
			||||||
        cycles
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn read_game_rom<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
 | 
					    fn load_cart(&mut self, rom: Vec<u8>) {
 | 
				
			||||||
        self.load_rom(std::fs::read(path.as_ref())?);
 | 
					        self.cpu.bus_mut().load_cart(rom)
 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn load_rom(&mut self, rom: Vec<u8>) {
 | 
					 | 
				
			||||||
        self.cpu.bus_mut().load_cart(rom);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    fn joyp_mut(&mut self) -> &mut Joypad {
 | 
					    fn joyp_mut(&mut self) -> &mut Joypad {
 | 
				
			||||||
        self.cpu.bus_mut().joyp_mut()
 | 
					        &mut self.cpu.bus_mut().joypad
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
 | 
					    pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
 | 
				
			||||||
        self.cpu.bus_mut().apu_mut().attach_producer(prod)
 | 
					        self.cpu.bus_mut().apu.attach_producer(prod)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn title(&self) -> &str {
 | 
					    pub fn title(&self) -> &str {
 | 
				
			||||||
        self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE)
 | 
					        self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn try_write_sav(&self) -> std::io::Result<()> {
 | 
					pub mod build {
 | 
				
			||||||
        if let Some(ext_ram) = self.cpu.bus().cart().map(|c| c.ext_ram()).flatten() {
 | 
					    use std::fs::File;
 | 
				
			||||||
            if let Some(title) = self.cpu.bus().cart_title() {
 | 
					    use std::io::{Read, Result};
 | 
				
			||||||
                let mut save_path = Self::data_path().unwrap_or_else(|| PathBuf::from("."));
 | 
					    use std::path::Path;
 | 
				
			||||||
                save_path.push(title);
 | 
					 | 
				
			||||||
                save_path.set_extension("sav");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let mut file = File::create(save_path)?;
 | 
					    use crate::bus::BOOT_SIZE;
 | 
				
			||||||
                file.write_all(ext_ram)?;
 | 
					    use crate::cpu::Cpu;
 | 
				
			||||||
            }
 | 
					
 | 
				
			||||||
 | 
					    use super::Emulator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[derive(Debug, Default)]
 | 
				
			||||||
 | 
					    pub struct EmulatorBuilder {
 | 
				
			||||||
 | 
					        boot: Option<[u8; BOOT_SIZE]>,
 | 
				
			||||||
 | 
					        cart: Option<Vec<u8>>,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					    impl EmulatorBuilder {
 | 
				
			||||||
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
 | 
					            Default::default()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn try_load_sav(&mut self) -> std::io::Result<()> {
 | 
					        pub fn with_boot<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
 | 
				
			||||||
        if let Some(cart) = self.cpu.bus_mut().cart_mut() {
 | 
					            let mut file = File::open(path.as_ref())?;
 | 
				
			||||||
            if let Some(title) = cart.title() {
 | 
					 | 
				
			||||||
                let mut save_path = Self::data_path().unwrap_or_else(|| PathBuf::from("."));
 | 
					 | 
				
			||||||
                save_path.push(title);
 | 
					 | 
				
			||||||
                save_path.set_extension("sav");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Ok(mut file) = File::open(&save_path) {
 | 
					            let mut buf = [0x00; BOOT_SIZE];
 | 
				
			||||||
                    tracing::info!("Load {:?}", save_path);
 | 
					            file.read_exact(&mut buf)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let mut memory = Vec::new();
 | 
					            self.boot = Some(buf);
 | 
				
			||||||
                    file.read_to_end(&mut memory)?;
 | 
					            Ok(self)
 | 
				
			||||||
                    cart.write_ext_ram(memory);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(())
 | 
					        pub fn with_cart<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
 | 
				
			||||||
 | 
					            let mut file = File::open(path.as_ref())?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut buf = Vec::new();
 | 
				
			||||||
 | 
					            file.read_to_end(&mut buf)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.cart = Some(buf);
 | 
				
			||||||
 | 
					            Ok(self)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn data_path() -> Option<PathBuf> {
 | 
					        pub fn finish(mut self) -> Emulator {
 | 
				
			||||||
        match directories_next::ProjectDirs::from("dev", "musuka", crate_name!()) {
 | 
					            let mut emu = Emulator::new(match self.boot {
 | 
				
			||||||
            Some(dirs) => {
 | 
					                Some(rom) => Cpu::with_boot(rom),
 | 
				
			||||||
                let data_local = dirs.data_local_dir();
 | 
					                None => Cpu::without_boot(),
 | 
				
			||||||
                std::fs::create_dir_all(data_local).ok()?;
 | 
					            });
 | 
				
			||||||
                Some(data_local.to_path_buf())
 | 
					
 | 
				
			||||||
 | 
					            if let Some(rom) = self.cart.take() {
 | 
				
			||||||
 | 
					                emu.load_cart(rom)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            None => None,
 | 
					
 | 
				
			||||||
 | 
					            emu
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,7 +116,7 @@ impl std::fmt::Debug for Instruction {
 | 
				
			|||||||
impl Instruction {
 | 
					impl Instruction {
 | 
				
			||||||
    pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
 | 
					    pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
 | 
				
			||||||
        match instruction {
 | 
					        match instruction {
 | 
				
			||||||
            Instruction::NOP => 4,
 | 
					            Instruction::NOP => (4),
 | 
				
			||||||
            Instruction::LD(target, src) => match (target, src) {
 | 
					            Instruction::LD(target, src) => match (target, src) {
 | 
				
			||||||
                (LDTarget::IndirectImmediateWord, LDSource::SP) => {
 | 
					                (LDTarget::IndirectImmediateWord, LDSource::SP) => {
 | 
				
			||||||
                    // LD (u16), SP | Store stack pointer in byte at 16-bit register
 | 
					                    // LD (u16), SP | Store stack pointer in byte at 16-bit register
 | 
				
			||||||
@@ -334,6 +334,7 @@ impl Instruction {
 | 
				
			|||||||
            Instruction::ADD(target, src) => match (target, src) {
 | 
					            Instruction::ADD(target, src) => match (target, src) {
 | 
				
			||||||
                (AddTarget::HL, AddSource::Group1(pair)) => {
 | 
					                (AddTarget::HL, AddSource::Group1(pair)) => {
 | 
				
			||||||
                    // ADD HL, r16 | Add 16-bit register to HL
 | 
					                    // ADD HL, r16 | Add 16-bit register to HL
 | 
				
			||||||
 | 
					                    // FIXME: Memory Timings are not properly emulated for this instruction
 | 
				
			||||||
                    use Group1RegisterPair::*;
 | 
					                    use Group1RegisterPair::*;
 | 
				
			||||||
                    let mut flags: Flags = *cpu.flags();
 | 
					                    let mut flags: Flags = *cpu.flags();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -341,11 +342,10 @@ impl Instruction {
 | 
				
			|||||||
                        BC | DE | HL | SP => {
 | 
					                        BC | DE | HL | SP => {
 | 
				
			||||||
                            let left = cpu.register_pair(RegisterPair::HL);
 | 
					                            let left = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = cpu.register_pair(pair.as_register_pair());
 | 
					                            let right = cpu.register_pair(pair.as_register_pair());
 | 
				
			||||||
                            let result = Self::add_u16(left, right, &mut flags);
 | 
					                            cpu.set_register_pair(
 | 
				
			||||||
 | 
					                                RegisterPair::HL,
 | 
				
			||||||
                            cpu.set_register(CpuRegister::L, result as u8);
 | 
					                                Self::add_u16(left, right, &mut flags),
 | 
				
			||||||
                            cpu.bus.clock();
 | 
					                            );
 | 
				
			||||||
                            cpu.set_register(CpuRegister::H, (result >> 8) as u8);
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    cpu.set_flags(flags);
 | 
					                    cpu.set_flags(flags);
 | 
				
			||||||
@@ -361,12 +361,12 @@ impl Instruction {
 | 
				
			|||||||
                    let (cycles, sum) = match reg {
 | 
					                    let (cycles, sum) = match reg {
 | 
				
			||||||
                        B | C | D | E | H | L | A => {
 | 
					                        B | C | D | E | H | L | A => {
 | 
				
			||||||
                            let right = cpu.register(reg.cpu_register());
 | 
					                            let right = cpu.register(reg.cpu_register());
 | 
				
			||||||
                            (4, Self::add(left, right, &mut flags))
 | 
					                            ((4), Self::add(left, right, &mut flags))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            (8, Self::add(left, right, &mut flags))
 | 
					                            ((8), Self::add(left, right, &mut flags))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -376,15 +376,12 @@ impl Instruction {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                (AddTarget::SP, AddSource::ImmediateSignedByte) => {
 | 
					                (AddTarget::SP, AddSource::ImmediateSignedByte) => {
 | 
				
			||||||
                    // ADD SP, i8 | Add i8 to stack pointer
 | 
					                    // ADD SP, i8 | Add i8 to stack pointer
 | 
				
			||||||
 | 
					                    // FIXME: Memory Timings are not properly emulated for this instruction
 | 
				
			||||||
                    let mut flags: Flags = *cpu.flags();
 | 
					                    let mut flags: Flags = *cpu.flags();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let left = cpu.register_pair(RegisterPair::SP);
 | 
					                    let left = cpu.register_pair(RegisterPair::SP);
 | 
				
			||||||
                    let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags);
 | 
					                    let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags);
 | 
				
			||||||
                    cpu.bus.clock(); // internal
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    cpu.set_register_pair(RegisterPair::SP, sum);
 | 
					                    cpu.set_register_pair(RegisterPair::SP, sum);
 | 
				
			||||||
                    cpu.bus.clock();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    cpu.set_flags(flags);
 | 
					                    cpu.set_flags(flags);
 | 
				
			||||||
                    16
 | 
					                    16
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -426,6 +423,7 @@ impl Instruction {
 | 
				
			|||||||
                    AllRegisters::Group1(pair) => {
 | 
					                    AllRegisters::Group1(pair) => {
 | 
				
			||||||
                        // INC r16 | Increment 16-bit register
 | 
					                        // INC r16 | Increment 16-bit register
 | 
				
			||||||
                        // Note: No flags are set with this version of the INC instruction
 | 
					                        // Note: No flags are set with this version of the INC instruction
 | 
				
			||||||
 | 
					                        // FIXME: Memory Timings are not properly emulated for this instruction
 | 
				
			||||||
                        use Group1RegisterPair::*;
 | 
					                        use Group1RegisterPair::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        match pair {
 | 
					                        match pair {
 | 
				
			||||||
@@ -433,7 +431,6 @@ impl Instruction {
 | 
				
			|||||||
                                let pair = pair.as_register_pair();
 | 
					                                let pair = pair.as_register_pair();
 | 
				
			||||||
                                let left = cpu.register_pair(pair);
 | 
					                                let left = cpu.register_pair(pair);
 | 
				
			||||||
                                cpu.set_register_pair(pair, left.wrapping_add(1));
 | 
					                                cpu.set_register_pair(pair, left.wrapping_add(1));
 | 
				
			||||||
                                cpu.bus.clock(); // internal
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        8
 | 
					                        8
 | 
				
			||||||
@@ -465,6 +462,7 @@ impl Instruction {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    AllRegisters::Group1(pair) => {
 | 
					                    AllRegisters::Group1(pair) => {
 | 
				
			||||||
                        // DEC r16 | Decrement Register Pair
 | 
					                        // DEC r16 | Decrement Register Pair
 | 
				
			||||||
 | 
					                        // FIXME: Memory Timings are not properly emulated for this instruction
 | 
				
			||||||
                        use Group1RegisterPair::*;
 | 
					                        use Group1RegisterPair::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        match pair {
 | 
					                        match pair {
 | 
				
			||||||
@@ -472,7 +470,6 @@ impl Instruction {
 | 
				
			|||||||
                                let pair = pair.as_register_pair();
 | 
					                                let pair = pair.as_register_pair();
 | 
				
			||||||
                                let left = cpu.register_pair(pair);
 | 
					                                let left = cpu.register_pair(pair);
 | 
				
			||||||
                                cpu.set_register_pair(pair, left.wrapping_sub(1));
 | 
					                                cpu.set_register_pair(pair, left.wrapping_sub(1));
 | 
				
			||||||
                                cpu.bus.clock(); // internal
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        8
 | 
					                        8
 | 
				
			||||||
@@ -613,13 +610,13 @@ impl Instruction {
 | 
				
			|||||||
                        B | C | D | E | H | L | A => {
 | 
					                        B | C | D | E | H | L | A => {
 | 
				
			||||||
                            let right = cpu.register(reg.cpu_register());
 | 
					                            let right = cpu.register(reg.cpu_register());
 | 
				
			||||||
                            let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
 | 
					                            let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
 | 
				
			||||||
                            (4, sum)
 | 
					                            ((4), sum)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
 | 
					                            let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
 | 
				
			||||||
                            (8, sum)
 | 
					                            ((8), sum)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, sum);
 | 
					                    cpu.set_register(CpuRegister::A, sum);
 | 
				
			||||||
@@ -649,12 +646,12 @@ impl Instruction {
 | 
				
			|||||||
                    let (cycles, diff) = match reg {
 | 
					                    let (cycles, diff) = match reg {
 | 
				
			||||||
                        B | C | D | E | H | L | A => {
 | 
					                        B | C | D | E | H | L | A => {
 | 
				
			||||||
                            let right = cpu.register(reg.cpu_register());
 | 
					                            let right = cpu.register(reg.cpu_register());
 | 
				
			||||||
                            (4, Self::sub(left, right, &mut flags))
 | 
					                            ((4), Self::sub(left, right, &mut flags))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            (8, Self::sub(left, right, &mut flags))
 | 
					                            ((8), Self::sub(left, right, &mut flags))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, diff);
 | 
					                    cpu.set_register(CpuRegister::A, diff);
 | 
				
			||||||
@@ -684,13 +681,13 @@ impl Instruction {
 | 
				
			|||||||
                        B | C | D | E | H | L | A => {
 | 
					                        B | C | D | E | H | L | A => {
 | 
				
			||||||
                            let right = cpu.register(reg.cpu_register());
 | 
					                            let right = cpu.register(reg.cpu_register());
 | 
				
			||||||
                            let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
 | 
					                            let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
 | 
				
			||||||
                            (4, diff)
 | 
					                            ((4), diff)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
 | 
					                            let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
 | 
				
			||||||
                            (8, diff)
 | 
					                            ((8), diff)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, diff);
 | 
					                    cpu.set_register(CpuRegister::A, diff);
 | 
				
			||||||
@@ -720,7 +717,7 @@ impl Instruction {
 | 
				
			|||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            (8, left & right)
 | 
					                            ((8), left & right)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, acc);
 | 
					                    cpu.set_register(CpuRegister::A, acc);
 | 
				
			||||||
@@ -746,7 +743,7 @@ impl Instruction {
 | 
				
			|||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            (8, left ^ right)
 | 
					                            ((8), left ^ right)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, acc);
 | 
					                    cpu.set_register(CpuRegister::A, acc);
 | 
				
			||||||
@@ -772,7 +769,7 @@ impl Instruction {
 | 
				
			|||||||
                        IndirectHL => {
 | 
					                        IndirectHL => {
 | 
				
			||||||
                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                            let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
					                            let right = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                            (8, left | right)
 | 
					                            ((8), left | right)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    cpu.set_register(CpuRegister::A, acc);
 | 
					                    cpu.set_register(CpuRegister::A, acc);
 | 
				
			||||||
@@ -1056,14 +1053,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let rotated = byte.rotate_left(1);
 | 
					                        let rotated = byte.rotate_left(1);
 | 
				
			||||||
                        cpu.set_register(reg, rotated);
 | 
					                        cpu.set_register(reg, rotated);
 | 
				
			||||||
                        (8, byte >> 7, rotated)
 | 
					                        ((8), byte >> 7, rotated)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let rotated = byte.rotate_left(1);
 | 
					                        let rotated = byte.rotate_left(1);
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
				
			||||||
                        (16, byte >> 7, rotated)
 | 
					                        ((16), byte >> 7, rotated)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01);
 | 
					                cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01);
 | 
				
			||||||
@@ -1079,14 +1076,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let rotated = byte.rotate_right(1);
 | 
					                        let rotated = byte.rotate_right(1);
 | 
				
			||||||
                        cpu.set_register(reg, rotated);
 | 
					                        cpu.set_register(reg, rotated);
 | 
				
			||||||
                        (8, byte & 0x01, rotated)
 | 
					                        ((8), byte & 0x01, rotated)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let rotated = byte.rotate_right(1);
 | 
					                        let rotated = byte.rotate_right(1);
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
				
			||||||
                        (16, byte & 0x01, rotated)
 | 
					                        ((16), byte & 0x01, rotated)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01);
 | 
					                cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01);
 | 
				
			||||||
@@ -1104,14 +1101,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
 | 
					                        let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
 | 
				
			||||||
                        cpu.set_register(reg, rotated);
 | 
					                        cpu.set_register(reg, rotated);
 | 
				
			||||||
                        (8, rotated, carry)
 | 
					                        ((8), rotated, carry)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
 | 
					                        let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
				
			||||||
                        (16, rotated, carry)
 | 
					                        ((16), rotated, carry)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(rotated == 0, false, false, carry);
 | 
					                cpu.update_flags(rotated == 0, false, false, carry);
 | 
				
			||||||
@@ -1129,14 +1126,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
 | 
					                        let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
 | 
				
			||||||
                        cpu.set_register(reg, rotated);
 | 
					                        cpu.set_register(reg, rotated);
 | 
				
			||||||
                        (8, rotated, carry)
 | 
					                        ((8), rotated, carry)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
 | 
					                        let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, rotated);
 | 
				
			||||||
                        (16, rotated, carry)
 | 
					                        ((16), rotated, carry)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(rotated == 0, false, false, carry);
 | 
					                cpu.update_flags(rotated == 0, false, false, carry);
 | 
				
			||||||
@@ -1152,14 +1149,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let shifted = byte << 1;
 | 
					                        let shifted = byte << 1;
 | 
				
			||||||
                        cpu.set_register(reg, shifted);
 | 
					                        cpu.set_register(reg, shifted);
 | 
				
			||||||
                        (8, (byte >> 7) & 0x01, shifted)
 | 
					                        ((8), (byte >> 7) & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let shifted = byte << 1;
 | 
					                        let shifted = byte << 1;
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
				
			||||||
                        (16, (byte >> 7) & 0x01, shifted)
 | 
					                        ((16), (byte >> 7) & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01);
 | 
					                cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01);
 | 
				
			||||||
@@ -1175,14 +1172,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
 | 
					                        let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
 | 
				
			||||||
                        cpu.set_register(reg, shifted);
 | 
					                        cpu.set_register(reg, shifted);
 | 
				
			||||||
                        (8, byte & 0x01, shifted)
 | 
					                        ((8), byte & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
 | 
					                        let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
				
			||||||
                        (16, byte & 0x01, shifted)
 | 
					                        ((16), byte & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01);
 | 
					                cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01);
 | 
				
			||||||
@@ -1197,14 +1194,14 @@ impl Instruction {
 | 
				
			|||||||
                        let reg = reg.cpu_register();
 | 
					                        let reg = reg.cpu_register();
 | 
				
			||||||
                        let swapped = Self::swap_bits(cpu.register(reg));
 | 
					                        let swapped = Self::swap_bits(cpu.register(reg));
 | 
				
			||||||
                        cpu.set_register(reg, swapped);
 | 
					                        cpu.set_register(reg, swapped);
 | 
				
			||||||
                        (8, swapped)
 | 
					                        ((8), swapped)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr));
 | 
					                        let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr));
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, swapped);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, swapped);
 | 
				
			||||||
                        (16, swapped)
 | 
					                        ((16), swapped)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(swapped == 0, false, false, false);
 | 
					                cpu.update_flags(swapped == 0, false, false, false);
 | 
				
			||||||
@@ -1220,14 +1217,14 @@ impl Instruction {
 | 
				
			|||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        let shifted = byte >> 1;
 | 
					                        let shifted = byte >> 1;
 | 
				
			||||||
                        cpu.set_register(reg, shifted);
 | 
					                        cpu.set_register(reg, shifted);
 | 
				
			||||||
                        (8, byte & 0x01, shifted)
 | 
					                        ((8), byte & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        let shifted = byte >> 1;
 | 
					                        let shifted = byte >> 1;
 | 
				
			||||||
                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
					                        Self::write_byte(&mut cpu.bus, addr, shifted);
 | 
				
			||||||
                        (16, byte & 0x01, shifted)
 | 
					                        ((16), byte & 0x01, shifted)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01);
 | 
					                cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01);
 | 
				
			||||||
@@ -1242,12 +1239,12 @@ impl Instruction {
 | 
				
			|||||||
                    B | C | D | E | H | L | A => {
 | 
					                    B | C | D | E | H | L | A => {
 | 
				
			||||||
                        let reg = reg.cpu_register();
 | 
					                        let reg = reg.cpu_register();
 | 
				
			||||||
                        let byte = cpu.register(reg);
 | 
					                        let byte = cpu.register(reg);
 | 
				
			||||||
                        (8, ((byte >> bit) & 0x01) == 0x01)
 | 
					                        ((8), ((byte >> bit) & 0x01) == 0x01)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    IndirectHL => {
 | 
					                    IndirectHL => {
 | 
				
			||||||
                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
					                        let addr = cpu.register_pair(RegisterPair::HL);
 | 
				
			||||||
                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
					                        let byte = Self::read_byte(&mut cpu.bus, addr);
 | 
				
			||||||
                        (12, ((byte >> bit) & 0x01) == 0x01)
 | 
					                        ((12), ((byte >> bit) & 0x01) == 0x01)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                flags.set_z(!is_set);
 | 
					                flags.set_z(!is_set);
 | 
				
			||||||
@@ -1896,7 +1893,7 @@ mod table {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Group1RegisterPair {
 | 
					    impl Group1RegisterPair {
 | 
				
			||||||
        pub(crate) fn as_register_pair(&self) -> RegisterPair {
 | 
					        pub fn as_register_pair(&self) -> RegisterPair {
 | 
				
			||||||
            use Group1RegisterPair::*;
 | 
					            use Group1RegisterPair::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self {
 | 
					            match self {
 | 
				
			||||||
@@ -1930,7 +1927,7 @@ mod table {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Group2RegisterPair {
 | 
					    impl Group2RegisterPair {
 | 
				
			||||||
        pub(crate) fn as_register_pair(&self) -> RegisterPair {
 | 
					        pub fn as_register_pair(&self) -> RegisterPair {
 | 
				
			||||||
            use Group2RegisterPair::*;
 | 
					            use Group2RegisterPair::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self {
 | 
					            match self {
 | 
				
			||||||
@@ -1964,7 +1961,7 @@ mod table {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Group3RegisterPair {
 | 
					    impl Group3RegisterPair {
 | 
				
			||||||
        pub(crate) fn as_register_pair(&self) -> RegisterPair {
 | 
					        pub fn as_register_pair(&self) -> RegisterPair {
 | 
				
			||||||
            use Group3RegisterPair::*;
 | 
					            use Group3RegisterPair::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self {
 | 
					            match self {
 | 
				
			||||||
@@ -2006,7 +2003,7 @@ mod table {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Register {
 | 
					    impl Register {
 | 
				
			||||||
        pub(crate) fn cpu_register(&self) -> CpuRegister {
 | 
					        pub fn cpu_register(&self) -> CpuRegister {
 | 
				
			||||||
            use Register::*;
 | 
					            use Register::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match self {
 | 
					            match self {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,8 +110,7 @@ impl ButtonEvent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[inline]
 | 
					pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) {
 | 
				
			||||||
pub(crate) fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) {
 | 
					 | 
				
			||||||
    use winit::event::VirtualKeyCode;
 | 
					    use winit::event::VirtualKeyCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: What do I have to do to get a match statement here?
 | 
					    // TODO: What do I have to do to get a match statement here?
 | 
				
			||||||
@@ -147,17 +146,17 @@ pub(crate) fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper)
 | 
				
			|||||||
        state.dpad_right.update(false, irq);
 | 
					        state.dpad_right.update(false, irq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if input.key_pressed(VirtualKeyCode::Return) {
 | 
					    if input.key_pressed(VirtualKeyCode::T) {
 | 
				
			||||||
        state.start.update(true, irq);
 | 
					        state.start.update(true, irq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if input.key_released(VirtualKeyCode::Return) {
 | 
					    if input.key_released(VirtualKeyCode::T) {
 | 
				
			||||||
        state.start.update(false, irq);
 | 
					        state.start.update(false, irq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if input.key_pressed(VirtualKeyCode::RShift) {
 | 
					    if input.key_pressed(VirtualKeyCode::Y) {
 | 
				
			||||||
        state.select.update(true, irq);
 | 
					        state.select.update(true, irq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if input.key_released(VirtualKeyCode::RShift) {
 | 
					    if input.key_released(VirtualKeyCode::Y) {
 | 
				
			||||||
        state.select.update(false, irq);
 | 
					        state.select.update(false, irq);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -176,8 +175,7 @@ pub(crate) fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[inline]
 | 
					pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) {
 | 
				
			||||||
pub(crate) fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) {
 | 
					 | 
				
			||||||
    use Button::*;
 | 
					    use Button::*;
 | 
				
			||||||
    use GamepadEventType::*;
 | 
					    use GamepadEventType::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
pub use apu::gen::init as spsc_init;
 | 
					pub use apu::gen::AudioSPSC;
 | 
				
			||||||
pub type Cycle = u64;
 | 
					pub type Cycle = u64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const GB_WIDTH: usize = 160;
 | 
					pub const GB_WIDTH: usize = 160;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,11 +1,12 @@
 | 
				
			|||||||
use anyhow::Result;
 | 
					use std::convert::TryInto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{anyhow, Result};
 | 
				
			||||||
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
 | 
					use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
 | 
				
			||||||
use gb::emu::{Emulator, CYCLES_IN_FRAME};
 | 
					use gb::emu::build::EmulatorBuilder;
 | 
				
			||||||
use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
 | 
					use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH};
 | 
				
			||||||
use gilrs::Gilrs;
 | 
					use gilrs::Gilrs;
 | 
				
			||||||
use pixels::{PixelsBuilder, SurfaceTexture};
 | 
					use pixels::{PixelsBuilder, SurfaceTexture};
 | 
				
			||||||
use rodio::{OutputStream, Sink};
 | 
					use rodio::{OutputStream, Sink};
 | 
				
			||||||
use tracing_subscriber::EnvFilter;
 | 
					 | 
				
			||||||
use winit::dpi::{LogicalSize, PhysicalSize};
 | 
					use winit::dpi::{LogicalSize, PhysicalSize};
 | 
				
			||||||
use winit::event::{Event, VirtualKeyCode};
 | 
					use winit::event::{Event, VirtualKeyCode};
 | 
				
			||||||
use winit::event_loop::{ControlFlow, EventLoop};
 | 
					use winit::event_loop::{ControlFlow, EventLoop};
 | 
				
			||||||
@@ -13,7 +14,7 @@ use winit::window::{Window, WindowBuilder};
 | 
				
			|||||||
use winit_input_helper::WinitInputHelper;
 | 
					use winit_input_helper::WinitInputHelper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const WINDOW_SCALE: usize = 3;
 | 
					const WINDOW_SCALE: usize = 3;
 | 
				
			||||||
const AUDIO_ENABLED: bool = true;
 | 
					const AUDIO_ENABLED: bool = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() -> Result<()> {
 | 
					fn main() -> Result<()> {
 | 
				
			||||||
    let app = App::new(crate_name!())
 | 
					    let app = App::new(crate_name!())
 | 
				
			||||||
@@ -26,6 +27,7 @@ fn main() -> Result<()> {
 | 
				
			|||||||
            Arg::with_name("rom")
 | 
					            Arg::with_name("rom")
 | 
				
			||||||
                .value_name("ROM_FILE")
 | 
					                .value_name("ROM_FILE")
 | 
				
			||||||
                .takes_value(true)
 | 
					                .takes_value(true)
 | 
				
			||||||
 | 
					                .required(true)
 | 
				
			||||||
                .index(1)
 | 
					                .index(1)
 | 
				
			||||||
                .help("Path to the Game ROM"),
 | 
					                .help("Path to the Game ROM"),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@@ -39,36 +41,16 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        .get_matches();
 | 
					        .get_matches();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Set up subscriber
 | 
					    let mut emu_build =
 | 
				
			||||||
    if std::env::var("RUST_LOG").is_err() {
 | 
					        EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?;
 | 
				
			||||||
        std::env::set_var("RUST_LOG", "gb=info");
 | 
					
 | 
				
			||||||
 | 
					    if let Some(path) = m.value_of("boot") {
 | 
				
			||||||
 | 
					        emu_build = emu_build.with_boot(path)?;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tracing_subscriber::fmt::fmt()
 | 
					    let mut emu = emu_build.finish();
 | 
				
			||||||
        .with_env_filter(EnvFilter::from_default_env())
 | 
					 | 
				
			||||||
        .init();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mut emu = match m.value_of("boot") {
 | 
					 | 
				
			||||||
        Some(path) => {
 | 
					 | 
				
			||||||
            tracing::info!("User-provided boot ROM");
 | 
					 | 
				
			||||||
            Emulator::from_boot_rom(path)?
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        None => {
 | 
					 | 
				
			||||||
            tracing::info!("Built-in boot ROM");
 | 
					 | 
				
			||||||
            Emulator::new()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Some(path) = m.value_of("rom") {
 | 
					 | 
				
			||||||
        tracing::info!("User-provided cartridge ROM");
 | 
					 | 
				
			||||||
        emu.read_game_rom(path)?;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Load Save file if it exists
 | 
					 | 
				
			||||||
    emu.try_load_sav().expect("Load save if exists");
 | 
					 | 
				
			||||||
    let rom_title = emu.title();
 | 
					    let rom_title = emu.title();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tracing::info!("Initialize Gamepad");
 | 
					 | 
				
			||||||
    let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
 | 
					    let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize GUI
 | 
					    // Initialize GUI
 | 
				
			||||||
@@ -89,7 +71,8 @@ fn main() -> Result<()> {
 | 
				
			|||||||
    let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio");
 | 
					    let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if AUDIO_ENABLED {
 | 
					    if AUDIO_ENABLED {
 | 
				
			||||||
        let (prod, cons) = gb::spsc_init();
 | 
					        let spsc: AudioSPSC<f32> = Default::default();
 | 
				
			||||||
 | 
					        let (prod, cons) = spsc.init();
 | 
				
			||||||
        let sink = {
 | 
					        let sink = {
 | 
				
			||||||
            let s = Sink::try_new(&stream_handle)?;
 | 
					            let s = Sink::try_new(&stream_handle)?;
 | 
				
			||||||
            s.append(cons);
 | 
					            s.append(cons);
 | 
				
			||||||
@@ -99,7 +82,6 @@ fn main() -> Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        emu.set_prod(prod);
 | 
					        emu.set_prod(prod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tracing::info!("Spawn Audio Thread");
 | 
					 | 
				
			||||||
        std::thread::spawn(move || {
 | 
					        std::thread::spawn(move || {
 | 
				
			||||||
            sink.sleep_until_end();
 | 
					            sink.sleep_until_end();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@@ -109,9 +91,11 @@ fn main() -> Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    event_loop.run(move |event, _, control_flow| {
 | 
					    event_loop.run(move |event, _, control_flow| {
 | 
				
			||||||
        if let Event::RedrawRequested(_) = event {
 | 
					        if let Event::RedrawRequested(_) = event {
 | 
				
			||||||
            if pixels.render().is_err() {
 | 
					            if pixels
 | 
				
			||||||
                emu.try_write_sav().expect("Write game save if need be");
 | 
					                .render()
 | 
				
			||||||
 | 
					                .map_err(|e| anyhow!("pixels.render() failed: {}", e))
 | 
				
			||||||
 | 
					                .is_err()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                *control_flow = ControlFlow::Exit;
 | 
					                *control_flow = ControlFlow::Exit;
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -119,8 +103,6 @@ fn main() -> Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if input.update(&event) {
 | 
					        if input.update(&event) {
 | 
				
			||||||
            if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
 | 
					            if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
 | 
				
			||||||
                emu.try_write_sav().expect("Write game save if need be");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                *control_flow = ControlFlow::Exit;
 | 
					                *control_flow = ControlFlow::Exit;
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -131,8 +113,8 @@ fn main() -> Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
 | 
					            cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if cycle_count >= CYCLES_IN_FRAME {
 | 
					            if cycle_count >= gb::emu::CYCLES_IN_FRAME {
 | 
				
			||||||
                cycle_count %= CYCLES_IN_FRAME;
 | 
					                cycle_count %= gb::emu::CYCLES_IN_FRAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
 | 
					                let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
 | 
				
			||||||
                    .get_frame()
 | 
					                    .get_frame()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										556
									
								
								src/ppu.rs
									
									
									
									
									
								
							
							
						
						
									
										556
									
								
								src/ppu.rs
									
									
									
									
									
								
							@@ -4,6 +4,7 @@ use crate::GB_HEIGHT;
 | 
				
			|||||||
use crate::GB_WIDTH;
 | 
					use crate::GB_WIDTH;
 | 
				
			||||||
use dma::DirectMemoryAccess;
 | 
					use dma::DirectMemoryAccess;
 | 
				
			||||||
use std::collections::VecDeque;
 | 
					use std::collections::VecDeque;
 | 
				
			||||||
 | 
					use std::convert::TryInto;
 | 
				
			||||||
pub(crate) use types::PpuMode;
 | 
					pub(crate) use types::PpuMode;
 | 
				
			||||||
use types::{
 | 
					use types::{
 | 
				
			||||||
    BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette,
 | 
					    BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette,
 | 
				
			||||||
@@ -44,18 +45,18 @@ pub struct Ppu {
 | 
				
			|||||||
    vram: Box<[u8; VRAM_SIZE]>,
 | 
					    vram: Box<[u8; VRAM_SIZE]>,
 | 
				
			||||||
    pub(crate) oam: ObjectAttributeTable,
 | 
					    pub(crate) oam: ObjectAttributeTable,
 | 
				
			||||||
    pub(crate) dma: DirectMemoryAccess,
 | 
					    pub(crate) dma: DirectMemoryAccess,
 | 
				
			||||||
    scan_dot: Cycle,
 | 
					    scan_state: OamScanState,
 | 
				
			||||||
    fetch: PixelFetcher,
 | 
					    fetch: PixelFetcher,
 | 
				
			||||||
    fifo: PixelFifo,
 | 
					    fifo: PixelFifo,
 | 
				
			||||||
    obj_buffer: ObjectBuffer,
 | 
					    obj_buffer: ObjectBuffer,
 | 
				
			||||||
    frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>,
 | 
					    frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>,
 | 
				
			||||||
    win_stat: WindowStatus,
 | 
					    window_stat: WindowStatus,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scanline_start: bool,
 | 
					    scanline_start: bool,
 | 
				
			||||||
    to_discard: u8,
 | 
					    to_discard: u8,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    x_pos: u8,
 | 
					    x_pos: u8,
 | 
				
			||||||
    dot: Cycle,
 | 
					    cycle: Cycle,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BusIo for Ppu {
 | 
					impl BusIo for Ppu {
 | 
				
			||||||
@@ -70,7 +71,7 @@ impl BusIo for Ppu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl Ppu {
 | 
					impl Ppu {
 | 
				
			||||||
    pub(crate) fn tick(&mut self) {
 | 
					    pub(crate) fn tick(&mut self) {
 | 
				
			||||||
        self.dot += 1;
 | 
					        self.cycle += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if !self.ctrl.lcd_enabled() {
 | 
					        if !self.ctrl.lcd_enabled() {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@@ -78,26 +79,7 @@ impl Ppu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        match self.stat.mode() {
 | 
					        match self.stat.mode() {
 | 
				
			||||||
            PpuMode::OamScan => {
 | 
					            PpuMode::OamScan => {
 | 
				
			||||||
                // Cycles 1 -> 80
 | 
					                if self.cycle >= 80 {
 | 
				
			||||||
                if self.dot >= 80 {
 | 
					 | 
				
			||||||
                    self.x_pos = 0;
 | 
					 | 
				
			||||||
                    self.scanline_start = true;
 | 
					 | 
				
			||||||
                    self.fetch.back.tile_high_reset = true;
 | 
					 | 
				
			||||||
                    self.to_discard = 0;
 | 
					 | 
				
			||||||
                    self.fifo.back.clear();
 | 
					 | 
				
			||||||
                    self.fifo.obj.clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Sort Sprites
 | 
					 | 
				
			||||||
                    self.obj_buffer.inner.sort_by(|left, right| {
 | 
					 | 
				
			||||||
                        left.zip(*right)
 | 
					 | 
				
			||||||
                            .map(|(left, right)| right.x.cmp(&left.x))
 | 
					 | 
				
			||||||
                            .unwrap_or(std::cmp::Ordering::Greater)
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // if self.obj_buffer.len != 0 {
 | 
					 | 
				
			||||||
                    //     dbg!(&self.obj_buffer);
 | 
					 | 
				
			||||||
                    // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    self.stat.set_mode(PpuMode::Drawing);
 | 
					                    self.stat.set_mode(PpuMode::Drawing);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -106,7 +88,7 @@ impl Ppu {
 | 
				
			|||||||
            PpuMode::Drawing => {
 | 
					            PpuMode::Drawing => {
 | 
				
			||||||
                if self.ctrl.lcd_enabled() {
 | 
					                if self.ctrl.lcd_enabled() {
 | 
				
			||||||
                    // Only Draw when the LCD Is Enabled
 | 
					                    // Only Draw when the LCD Is Enabled
 | 
				
			||||||
                    self.draw();
 | 
					                    self.draw(self.cycle);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    self.reset();
 | 
					                    self.reset();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -122,22 +104,29 @@ impl Ppu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    // Increment Window line counter if scanline had any window pixels on it
 | 
					                    // Increment Window line counter if scanline had any window pixels on it
 | 
				
			||||||
                    // only increment once per scanline though
 | 
					                    // only increment once per scanline though
 | 
				
			||||||
                    if self.win_stat.enabled {
 | 
					                    if self.window_stat.should_draw() {
 | 
				
			||||||
                        self.fetch.back.wl_count += 1;
 | 
					                        self.fetch.back.window_line.increment();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.hblank_reset();
 | 
					                    self.x_pos = 0;
 | 
				
			||||||
                    self.win_stat.enabled = false;
 | 
					                    self.scanline_start = true;
 | 
				
			||||||
 | 
					                    self.to_discard = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.fetch.hblank_reset();
 | 
				
			||||||
 | 
					                    self.window_stat.hblank_reset();
 | 
				
			||||||
                    self.obj_buffer.clear();
 | 
					                    self.obj_buffer.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.fifo.back.clear();
 | 
				
			||||||
 | 
					                    self.fifo.obj.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.stat.set_mode(PpuMode::HBlank);
 | 
					                    self.stat.set_mode(PpuMode::HBlank);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            PpuMode::HBlank => {
 | 
					            PpuMode::HBlank => {
 | 
				
			||||||
                // This mode will always end at 456 cycles
 | 
					                // This mode will always end at 456 cycles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if self.dot >= 456 {
 | 
					                if self.cycle >= 456 {
 | 
				
			||||||
                    self.dot %= 456;
 | 
					                    self.cycle %= 456;
 | 
				
			||||||
                    self.pos.line_y += 1;
 | 
					                    self.pos.line_y += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Update LY==LYC bit
 | 
					                    // Update LY==LYC bit
 | 
				
			||||||
@@ -154,10 +143,9 @@ impl Ppu {
 | 
				
			|||||||
                        self.int.set_vblank(true);
 | 
					                        self.int.set_vblank(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // Reset Window Line Counter in Fetcher
 | 
					                        // Reset Window Line Counter in Fetcher
 | 
				
			||||||
                        self.fetch.back.wl_count = 0;
 | 
					                        self.fetch.vblank_reset();
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Reset WY=LY coincidence flag
 | 
					                        // Reset WY=LY coincidence flag
 | 
				
			||||||
                        self.win_stat.coincidence = false;
 | 
					                        self.window_stat.vblank_reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if self.stat.vblank_int() {
 | 
					                        if self.stat.vblank_int() {
 | 
				
			||||||
                            // Enable Vblank LCDStat Interrupt
 | 
					                            // Enable Vblank LCDStat Interrupt
 | 
				
			||||||
@@ -171,7 +159,7 @@ impl Ppu {
 | 
				
			|||||||
                            self.int.set_lcd_stat(true);
 | 
					                            self.int.set_lcd_stat(true);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        self.scan_dot = Default::default();
 | 
					                        self.scan_state.reset();
 | 
				
			||||||
                        PpuMode::OamScan
 | 
					                        PpuMode::OamScan
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -179,8 +167,8 @@ impl Ppu {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            PpuMode::VBlank => {
 | 
					            PpuMode::VBlank => {
 | 
				
			||||||
                if self.dot >= 456 {
 | 
					                if self.cycle > 456 {
 | 
				
			||||||
                    self.dot %= 456;
 | 
					                    self.cycle %= 456;
 | 
				
			||||||
                    self.pos.line_y += 1;
 | 
					                    self.pos.line_y += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // Update LY==LYC bit
 | 
					                    // Update LY==LYC bit
 | 
				
			||||||
@@ -200,7 +188,8 @@ impl Ppu {
 | 
				
			|||||||
                            self.int.set_lcd_stat(true);
 | 
					                            self.int.set_lcd_stat(true);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        self.scan_dot = Default::default();
 | 
					                        self.scan_state.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        self.stat.set_mode(PpuMode::OamScan);
 | 
					                        self.stat.set_mode(PpuMode::OamScan);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -209,81 +198,89 @@ impl Ppu {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn scan_oam(&mut self) {
 | 
					    fn scan_oam(&mut self) {
 | 
				
			||||||
        if self.scan_dot % 2 == 0 {
 | 
					        match self.scan_state.mode() {
 | 
				
			||||||
            if self.dma.is_active() {
 | 
					            OamScanMode::Scan if !self.dma.is_active() => {
 | 
				
			||||||
                return;
 | 
					                if !self.window_stat.coincidence() && self.scan_state.count() == 0 {
 | 
				
			||||||
 | 
					                    // Determine whether we should draw the window next frame
 | 
				
			||||||
 | 
					                    self.window_stat
 | 
				
			||||||
 | 
					                        .set_coincidence(self.pos.line_y == self.pos.window_y);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if !self.win_stat.coincidence && self.scan_dot == 0 {
 | 
					                let sprite_height = self.ctrl.obj_size().as_u8();
 | 
				
			||||||
                self.win_stat.coincidence = self.pos.line_y == self.pos.window_y;
 | 
					                let index = self.scan_state.count();
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let obj_height = self.ctrl.obj_size().size();
 | 
					                let attr = self.oam.attribute(index as usize);
 | 
				
			||||||
            let attr = self.oam.attribute(self.scan_dot as usize / 2);
 | 
					 | 
				
			||||||
                let line_y = self.pos.line_y + 16;
 | 
					                let line_y = self.pos.line_y + 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if attr.x > 0
 | 
					                if attr.x > 0
 | 
				
			||||||
                    && line_y >= attr.y
 | 
					                    && line_y >= attr.y
 | 
				
			||||||
                && line_y < (attr.y + obj_height)
 | 
					                    && line_y < (attr.y + sprite_height)
 | 
				
			||||||
                    && !self.obj_buffer.is_full()
 | 
					                    && !self.obj_buffer.is_full()
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    self.obj_buffer.add(attr);
 | 
					                    self.obj_buffer.add(attr);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.scan_state.increase();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        self.scan_dot += 1;
 | 
					            _ => {}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn draw(&mut self) {
 | 
					        self.scan_state.next();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn draw(&mut self, _cycle: Cycle) {
 | 
				
			||||||
        use FetcherState::*;
 | 
					        use FetcherState::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut obj_attr = &mut None;
 | 
					        let mut iter = self.obj_buffer.iter_mut();
 | 
				
			||||||
 | 
					        let default = &mut None;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for maybe_attr in &mut self.obj_buffer.inner {
 | 
					        let obj_attr = loop {
 | 
				
			||||||
            match maybe_attr {
 | 
					            match iter.next() {
 | 
				
			||||||
                Some(attr) if self.ctrl.obj_enabled() => {
 | 
					                Some(attr_opt) => {
 | 
				
			||||||
 | 
					                    if let Some(attr) = attr_opt {
 | 
				
			||||||
                        if attr.x <= (self.x_pos + 8) {
 | 
					                        if attr.x <= (self.x_pos + 8) {
 | 
				
			||||||
                            self.fetch.back.reset();
 | 
					                            self.fetch.back.reset();
 | 
				
			||||||
                        self.fetch.back.enabled = false;
 | 
					                            self.fetch.back.pause();
 | 
				
			||||||
                            self.fifo.pause();
 | 
					                            self.fifo.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        obj_attr = maybe_attr;
 | 
					                            break attr_opt;
 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                _ => break,
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                None => break default,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(attr) = obj_attr {
 | 
					        if let Some(attr) = obj_attr {
 | 
				
			||||||
            match self.fetch.obj.state {
 | 
					            match self.fetch.obj.state {
 | 
				
			||||||
                TileNumberA => self.fetch.obj.state = TileNumberB,
 | 
					                TileNumber => {
 | 
				
			||||||
                TileNumberB => {
 | 
					 | 
				
			||||||
                    self.fetch.obj.tile.with_id(attr.tile_index);
 | 
					                    self.fetch.obj.tile.with_id(attr.tile_index);
 | 
				
			||||||
                    self.fetch.obj.state = TileLowA;
 | 
					                    self.fetch.obj.next(SleepOne);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TileLowA => self.fetch.obj.state = TileLowB,
 | 
					                SleepOne => self.fetch.obj.next(TileLow),
 | 
				
			||||||
                TileLowB => {
 | 
					                TileLow => {
 | 
				
			||||||
                    let obj_size = self.ctrl.obj_size();
 | 
					                    let obj_size = self.ctrl.obj_size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
 | 
					                    let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let byte = self.read_byte(addr);
 | 
					                    let byte = self.read_byte(addr);
 | 
				
			||||||
                    self.fetch.obj.tile.with_low(byte);
 | 
					                    self.fetch.obj.tile.with_low_byte(byte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.obj.state = TileHighA;
 | 
					                    self.fetch.obj.next(SleepTwo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TileHighA => self.fetch.obj.state = TileHighB,
 | 
					                SleepTwo => self.fetch.obj.next(TileHigh),
 | 
				
			||||||
                TileHighB => {
 | 
					                TileHigh => {
 | 
				
			||||||
                    let obj_size = self.ctrl.obj_size();
 | 
					                    let obj_size = self.ctrl.obj_size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
 | 
					                    let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let byte = self.read_byte(addr + 1);
 | 
					                    let byte = self.read_byte(addr + 1);
 | 
				
			||||||
                    self.fetch.obj.tile.with_high(byte);
 | 
					                    self.fetch.obj.tile.with_high_byte(byte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.obj.state = ToFifoA;
 | 
					                    self.fetch.obj.next(SleepThree);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ToFifoA => {
 | 
					                SleepThree => self.fetch.obj.next(ToFifoOne),
 | 
				
			||||||
 | 
					                ToFifoOne => {
 | 
				
			||||||
                    // Load into Fifo
 | 
					                    // Load into Fifo
 | 
				
			||||||
                    let (high, low) = self
 | 
					                    let (high, low) = self
 | 
				
			||||||
                        .fetch
 | 
					                        .fetch
 | 
				
			||||||
@@ -315,65 +312,73 @@ impl Ppu {
 | 
				
			|||||||
                        self.fifo.obj.push_back(fifo_info);
 | 
					                        self.fifo.obj.push_back(fifo_info);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.back.enabled = true;
 | 
					                    self.fetch.back.resume();
 | 
				
			||||||
                    self.fifo.resume();
 | 
					                    self.fifo.resume();
 | 
				
			||||||
                    let _ = std::mem::take(obj_attr);
 | 
					                    let _ = std::mem::take(obj_attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.obj.state = ToFifoB;
 | 
					                    self.fetch.obj.next(ToFifoTwo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ToFifoB => self.fetch.obj.reset(),
 | 
					                ToFifoTwo => self.fetch.obj.reset(),
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.fetch.back.enabled {
 | 
					        if self.ctrl.window_enabled()
 | 
				
			||||||
 | 
					            && !self.window_stat.should_draw()
 | 
				
			||||||
 | 
					            && self.window_stat.coincidence()
 | 
				
			||||||
 | 
					            && self.x_pos as i16 >= self.pos.window_x as i16 - 7
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            self.window_stat.set_should_draw(true);
 | 
				
			||||||
 | 
					            self.fetch.back.reset();
 | 
				
			||||||
 | 
					            self.fetch.x_pos = 0;
 | 
				
			||||||
 | 
					            self.fifo.back.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.fetch.back.is_enabled() {
 | 
				
			||||||
            match self.fetch.back.state {
 | 
					            match self.fetch.back.state {
 | 
				
			||||||
                TileNumberA => self.fetch.back.state = TileNumberB,
 | 
					                TileNumber => {
 | 
				
			||||||
                TileNumberB => {
 | 
					                    let x_pos = self.fetch.x_pos;
 | 
				
			||||||
                    // Are we rendering the Window currently?
 | 
					 | 
				
			||||||
                    self.fetch.back.draw_window = self.win_stat.enabled;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let addr =
 | 
					 | 
				
			||||||
                    self.fetch
 | 
					                    self.fetch
 | 
				
			||||||
                        .back
 | 
					                        .back
 | 
				
			||||||
                            .tile_id_addr(&self.ctrl, &self.pos, self.fetch.x_pos);
 | 
					                        .should_render_window(self.window_stat.should_draw());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let id = self.read_byte(addr);
 | 
					                    let id = self.read_byte(addr);
 | 
				
			||||||
                    self.fetch.back.tile.with_id(id);
 | 
					                    self.fetch.back.tile.with_id(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.back.state = TileLowA;
 | 
					                    // Move on to the Next state in 2 T-cycles
 | 
				
			||||||
 | 
					                    self.fetch.back.next(SleepOne);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TileLowA => self.fetch.back.state = TileLowB,
 | 
					                SleepOne => self.fetch.back.next(TileLow),
 | 
				
			||||||
                TileLowB => {
 | 
					                TileLow => {
 | 
				
			||||||
                    let id = self.fetch.back.tile.id.expect("Tile ID present");
 | 
					                    let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id);
 | 
					                    let low = self.read_byte(addr);
 | 
				
			||||||
                    let byte = self.read_byte(addr);
 | 
					                    self.fetch.back.tile.with_low_byte(low);
 | 
				
			||||||
                    self.fetch.back.tile.with_low(byte);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    self.fetch.back.state = TileHighA;
 | 
					                    self.fetch.back.next(SleepTwo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TileHighA => self.fetch.back.state = TileHighB,
 | 
					                SleepTwo => self.fetch.back.next(TileHigh),
 | 
				
			||||||
                TileHighB => {
 | 
					                TileHigh => {
 | 
				
			||||||
                    let id = self.fetch.back.tile.id.expect("Tile ID present");
 | 
					                    let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id);
 | 
					                    let high = self.read_byte(addr + 1);
 | 
				
			||||||
                    let byte = self.read_byte(addr + 1);
 | 
					                    self.fetch.back.tile.with_high_byte(high);
 | 
				
			||||||
                    self.fetch.back.tile.with_high(byte);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if self.fetch.back.tile_high_reset {
 | 
					                    self.fetch.back.next(SleepThree);
 | 
				
			||||||
                        self.fetch.back.reset();
 | 
					 | 
				
			||||||
                        self.fetch.back.tile_high_reset = false;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        self.fetch.back.state = ToFifoA;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                SleepThree => self.fetch.back.next(ToFifoOne),
 | 
				
			||||||
 | 
					                ToFifoOne => {
 | 
				
			||||||
 | 
					                    self.fetch.back.next(ToFifoTwo);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ToFifoA => {
 | 
					                ToFifoTwo => {
 | 
				
			||||||
                    if self.fetch.send_to_fifo(&mut self.fifo).is_ok() {
 | 
					                    if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) {
 | 
				
			||||||
                        self.fetch.x_pos += 1;
 | 
					                        self.fetch.x_pos += 1;
 | 
				
			||||||
                        self.fetch.back.state = ToFifoB;
 | 
					                        self.fetch.back.next(TileNumber);
 | 
				
			||||||
 | 
					                        self.fetch.back.tile = Default::default();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ToFifoB => self.fetch.back.reset(),
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -383,7 +388,7 @@ impl Ppu {
 | 
				
			|||||||
                self.scanline_start = false;
 | 
					                self.scanline_start = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if !self.win_stat.enabled && self.to_discard > 0 && !self.fifo.back.is_empty() {
 | 
					            if self.to_discard > 0 && !self.fifo.back.is_empty() {
 | 
				
			||||||
                let _ = self.fifo.back.pop_front();
 | 
					                let _ = self.fifo.back.pop_front();
 | 
				
			||||||
                self.to_discard -= 1;
 | 
					                self.to_discard -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -401,17 +406,6 @@ impl Ppu {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                self.x_pos += 1;
 | 
					                self.x_pos += 1;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if self.ctrl.window_enabled()
 | 
					 | 
				
			||||||
                && !self.win_stat.enabled
 | 
					 | 
				
			||||||
                && self.win_stat.coincidence
 | 
					 | 
				
			||||||
                && self.x_pos as i16 >= self.pos.window_x as i16 - 7
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                self.win_stat.enabled = true;
 | 
					 | 
				
			||||||
                self.fetch.back.reset();
 | 
					 | 
				
			||||||
                self.fetch.x_pos = 0;
 | 
					 | 
				
			||||||
                self.fifo.back.clear();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -419,49 +413,53 @@ impl Ppu {
 | 
				
			|||||||
        self.pos.line_y = 0;
 | 
					        self.pos.line_y = 0;
 | 
				
			||||||
        self.stat.set_mode(PpuMode::HBlank);
 | 
					        self.stat.set_mode(PpuMode::HBlank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: Is this an unnecessary performance hit?
 | 
				
			||||||
        let mut blank = WHITE.repeat(self.frame_buf.len() / 4);
 | 
					        let mut blank = WHITE.repeat(self.frame_buf.len() / 4);
 | 
				
			||||||
        self.frame_buf.swap_with_slice(&mut blank);
 | 
					        self.frame_buf.swap_with_slice(&mut blank);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[inline]
 | 
					 | 
				
			||||||
    pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
 | 
					    pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
 | 
				
			||||||
        &self.frame_buf
 | 
					        &self.frame_buf
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn clock_fifo(&mut self) -> Option<GrayShade> {
 | 
					    fn clock_fifo(&mut self) -> Option<GrayShade> {
 | 
				
			||||||
 | 
					        use ObjectPaletteKind::*;
 | 
				
			||||||
        use RenderPriority::*;
 | 
					        use RenderPriority::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.fifo
 | 
					        let obj_palette_0 = &self.monochrome.obj_palette_0;
 | 
				
			||||||
            .back
 | 
					        let obj_palette_1 = &self.monochrome.obj_palette_1;
 | 
				
			||||||
            .pop_front()
 | 
					
 | 
				
			||||||
            .map(|bg| match self.fifo.obj.pop_front() {
 | 
					        match self.fifo.back.pop_front() {
 | 
				
			||||||
                Some(obj) => match obj.priority {
 | 
					            Some(bg_pixel) => match self.fifo.obj.pop_front() {
 | 
				
			||||||
                    _ if obj.shade_id == 0 => self.bg_pixel(bg),
 | 
					                Some(obj_pixel) if self.ctrl.obj_enabled() => match obj_pixel.priority {
 | 
				
			||||||
                    BackgroundAndWindow if bg.shade_id != 0 => self.bg_pixel(bg),
 | 
					                    Object | BackgroundAndWindow if obj_pixel.shade_id == 0 => {
 | 
				
			||||||
                    _ => self.obj_pixel(obj),
 | 
					                        Some(self.bg_pixel(bg_pixel.shade_id))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    BackgroundAndWindow if bg_pixel.shade_id != 0 => {
 | 
				
			||||||
 | 
					                        Some(self.bg_pixel(bg_pixel.shade_id))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Object | BackgroundAndWindow => {
 | 
				
			||||||
 | 
					                        let maybe_sprite = match obj_pixel.palette_kind {
 | 
				
			||||||
 | 
					                            Zero => obj_palette_0.shade(obj_pixel.shade_id),
 | 
				
			||||||
 | 
					                            One => obj_palette_1.shade(obj_pixel.shade_id),
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let sprite = maybe_sprite
 | 
				
			||||||
 | 
					                            .expect("Sprite w/ a colour id of 0 has already been handled");
 | 
				
			||||||
 | 
					                        Some(sprite)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                None => self.bg_pixel(bg),
 | 
					                _ => Some(self.bg_pixel(bg_pixel.shade_id)),
 | 
				
			||||||
            })
 | 
					            },
 | 
				
			||||||
    }
 | 
					            None => None,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn obj_pixel(&self, obj: ObjPixelProperty) -> GrayShade {
 | 
					 | 
				
			||||||
        use ObjectPaletteKind::*;
 | 
					 | 
				
			||||||
        assert!(obj.shade_id != 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let p0 = &self.monochrome.obj_palette_0;
 | 
					 | 
				
			||||||
        let p1 = &self.monochrome.obj_palette_1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match obj.palette_kind {
 | 
					 | 
				
			||||||
            Zero => p0.shade(obj.shade_id).expect("Object shade id is non-zero"),
 | 
					 | 
				
			||||||
            One => p1.shade(obj.shade_id).expect("Object shade id is non-zero"),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn bg_pixel(&self, bg: BgPixelProperty) -> GrayShade {
 | 
					    fn bg_pixel(&self, shade_id: u8) -> GrayShade {
 | 
				
			||||||
        let bg_palette = &self.monochrome.bg_palette;
 | 
					        let bg_palette = &self.monochrome.bg_palette;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.ctrl.bg_win_enabled() {
 | 
					        if self.ctrl.bg_win_enabled() {
 | 
				
			||||||
            bg_palette.shade(bg.shade_id)
 | 
					            bg_palette.shade(shade_id)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            bg_palette.shade(0)
 | 
					            bg_palette.shade(0)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -472,7 +470,7 @@ impl Default for Ppu {
 | 
				
			|||||||
    fn default() -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            vram: Box::new([0u8; VRAM_SIZE]),
 | 
					            vram: Box::new([0u8; VRAM_SIZE]),
 | 
				
			||||||
            dot: Default::default(),
 | 
					            cycle: Default::default(),
 | 
				
			||||||
            frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
 | 
					            frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
 | 
				
			||||||
            int: Default::default(),
 | 
					            int: Default::default(),
 | 
				
			||||||
            ctrl: Default::default(),
 | 
					            ctrl: Default::default(),
 | 
				
			||||||
@@ -480,11 +478,11 @@ impl Default for Ppu {
 | 
				
			|||||||
            pos: Default::default(),
 | 
					            pos: Default::default(),
 | 
				
			||||||
            stat: Default::default(),
 | 
					            stat: Default::default(),
 | 
				
			||||||
            oam: Default::default(),
 | 
					            oam: Default::default(),
 | 
				
			||||||
            scan_dot: Default::default(),
 | 
					            scan_state: Default::default(),
 | 
				
			||||||
            fetch: Default::default(),
 | 
					            fetch: Default::default(),
 | 
				
			||||||
            fifo: Default::default(),
 | 
					            fifo: Default::default(),
 | 
				
			||||||
            obj_buffer: Default::default(),
 | 
					            obj_buffer: Default::default(),
 | 
				
			||||||
            win_stat: Default::default(),
 | 
					            window_stat: Default::default(),
 | 
				
			||||||
            dma: Default::default(),
 | 
					            dma: Default::default(),
 | 
				
			||||||
            x_pos: 0,
 | 
					            x_pos: 0,
 | 
				
			||||||
            scanline_start: true,
 | 
					            scanline_start: true,
 | 
				
			||||||
@@ -580,7 +578,7 @@ impl Default for ObjectAttributeTable {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy, Default)]
 | 
					#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
 | 
				
			||||||
struct ObjectAttribute {
 | 
					struct ObjectAttribute {
 | 
				
			||||||
    y: u8,
 | 
					    y: u8,
 | 
				
			||||||
    x: u8,
 | 
					    x: u8,
 | 
				
			||||||
@@ -630,6 +628,10 @@ impl ObjectBuffer {
 | 
				
			|||||||
        self.inner[self.len] = Some(attr);
 | 
					        self.inner[self.len] = Some(attr);
 | 
				
			||||||
        self.len += 1;
 | 
					        self.len += 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn iter_mut(&mut self) -> std::slice::IterMut<'_, Option<ObjectAttribute>> {
 | 
				
			||||||
 | 
					        self.inner.iter_mut()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for ObjectBuffer {
 | 
					impl Default for ObjectBuffer {
 | 
				
			||||||
@@ -655,6 +657,61 @@ impl PixelFetcher {
 | 
				
			|||||||
        self.x_pos = 0;
 | 
					        self.x_pos = 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn vblank_reset(&mut self) {
 | 
				
			||||||
 | 
					        self.back.vblank_reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn bg_tile_num_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
 | 
				
			||||||
 | 
					        let line_y = pos.line_y;
 | 
				
			||||||
 | 
					        let scroll_y = pos.scroll_y;
 | 
				
			||||||
 | 
					        let scroll_x = pos.scroll_x;
 | 
				
			||||||
 | 
					        let is_window = self.back.is_window_tile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Determine which tile map is being used
 | 
				
			||||||
 | 
					        let tile_map = if is_window {
 | 
				
			||||||
 | 
					            control.win_tile_map_addr()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            control.bg_tile_map_addr()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let tile_map_addr = tile_map.into_address();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Both Offsets are used to offset the tile map address we found above
 | 
				
			||||||
 | 
					        // Offsets are ANDed wih 0x3FF so that we stay in bounds of tile map memory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let scx_offset = if is_window { 0 } else { scroll_x / 8 };
 | 
				
			||||||
 | 
					        let y_offset = if is_window {
 | 
				
			||||||
 | 
					            self.back.window_line.count() as u16 / 8
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ((line_y as u16 + scroll_y as u16) & 0xFF) / 8
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let x_offset = (scx_offset + x_pos) & 0x1F;
 | 
				
			||||||
 | 
					        let offset = (32 * y_offset) + (x_offset as u16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tile_map_addr + (offset & 0x3FF)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn bg_byte_addr(&mut self, control: &LCDControl, pos: &ScreenPosition) -> u16 {
 | 
				
			||||||
 | 
					        let line_y = pos.line_y;
 | 
				
			||||||
 | 
					        let scroll_y = pos.scroll_y;
 | 
				
			||||||
 | 
					        let is_window = self.back.is_window_tile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let id = self.back.tile.id.expect("Tile Number is present");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tile_data_addr = match control.tile_data_addr() {
 | 
				
			||||||
 | 
					            TileDataAddress::X8800 => 0x9000u16.wrapping_add(((id as i8) as i16 * 16) as u16),
 | 
				
			||||||
 | 
					            TileDataAddress::X8000 => 0x8000 + (id as u16 * 16),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let offset = if is_window {
 | 
				
			||||||
 | 
					            self.back.window_line.count() as u16 % 8
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            (line_y as u16 + scroll_y as u16) % 8
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tile_data_addr + (offset * 2)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> {
 | 
					    fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> {
 | 
				
			||||||
        if !fifo.back.is_empty() {
 | 
					        if !fifo.back.is_empty() {
 | 
				
			||||||
            return Err(());
 | 
					            return Err(());
 | 
				
			||||||
@@ -700,6 +757,7 @@ impl PixelFetcher {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trait Fetcher {
 | 
					trait Fetcher {
 | 
				
			||||||
 | 
					    fn next(&mut self, state: FetcherState);
 | 
				
			||||||
    fn reset(&mut self);
 | 
					    fn reset(&mut self);
 | 
				
			||||||
    fn hblank_reset(&mut self);
 | 
					    fn hblank_reset(&mut self);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -708,63 +766,42 @@ trait Fetcher {
 | 
				
			|||||||
struct BackgroundFetcher {
 | 
					struct BackgroundFetcher {
 | 
				
			||||||
    state: FetcherState,
 | 
					    state: FetcherState,
 | 
				
			||||||
    tile: TileBuilder,
 | 
					    tile: TileBuilder,
 | 
				
			||||||
    wl_count: u8,
 | 
					    window_line: WindowLineCounter,
 | 
				
			||||||
    draw_window: bool,
 | 
					    is_window_tile: bool,
 | 
				
			||||||
    enabled: bool,
 | 
					    enabled: bool,
 | 
				
			||||||
    tile_high_reset: bool,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BackgroundFetcher {
 | 
					impl BackgroundFetcher {
 | 
				
			||||||
    fn tile_id_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
 | 
					    fn should_render_window(&mut self, value: bool) {
 | 
				
			||||||
        let line_y = pos.line_y;
 | 
					        self.is_window_tile = value;
 | 
				
			||||||
        let scroll_y = pos.scroll_y;
 | 
					 | 
				
			||||||
        let scroll_x = pos.scroll_x;
 | 
					 | 
				
			||||||
        let is_window = self.draw_window;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Determine which tile map is being used
 | 
					 | 
				
			||||||
        let tile_map = if is_window {
 | 
					 | 
				
			||||||
            control.win_tile_map_addr()
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            control.bg_tile_map_addr()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        let tile_map_addr = tile_map.into_address();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Both Offsets are used to offset the tile map address we found above
 | 
					 | 
				
			||||||
        // Offsets are ANDed wih 0x3FF so that we stay in bounds of tile map memory
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let scx_offset = if is_window { 0 } else { scroll_x / 8 };
 | 
					 | 
				
			||||||
        let y_offset = if is_window {
 | 
					 | 
				
			||||||
            self.wl_count as u16 / 8
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            ((line_y as u16 + scroll_y as u16) & 0xFF) / 8
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let x_offset = (scx_offset + x_pos) & 0x1F;
 | 
					 | 
				
			||||||
        let offset = (32 * y_offset) + (x_offset as u16);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        tile_map_addr + (offset & 0x3FF)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn tile_addr(&mut self, control: &LCDControl, pos: &ScreenPosition, id: u8) -> u16 {
 | 
					    fn is_window_tile(&self) -> bool {
 | 
				
			||||||
        let line_y = pos.line_y;
 | 
					        self.is_window_tile
 | 
				
			||||||
        let scroll_y = pos.scroll_y;
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let tile_data_addr = match control.tile_data_addr() {
 | 
					    fn pause(&mut self) {
 | 
				
			||||||
            TileDataAddress::X8800 => 0x9000u16.wrapping_add((id as i8 as i16 * 16) as u16),
 | 
					        self.enabled = false;
 | 
				
			||||||
            TileDataAddress::X8000 => 0x8000 + (id as u16 * 16),
 | 
					    }
 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let offset = if self.draw_window {
 | 
					    fn resume(&mut self) {
 | 
				
			||||||
            self.wl_count as u16 % 8
 | 
					        self.enabled = true;
 | 
				
			||||||
        } else {
 | 
					    }
 | 
				
			||||||
            (line_y as u16 + scroll_y as u16) % 8
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tile_data_addr + (offset * 2)
 | 
					    fn is_enabled(&self) -> bool {
 | 
				
			||||||
 | 
					        self.enabled
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn vblank_reset(&mut self) {
 | 
				
			||||||
 | 
					        self.window_line.vblank_reset();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Fetcher for BackgroundFetcher {
 | 
					impl Fetcher for BackgroundFetcher {
 | 
				
			||||||
 | 
					    fn next(&mut self, state: FetcherState) {
 | 
				
			||||||
 | 
					        self.state = state
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn reset(&mut self) {
 | 
					    fn reset(&mut self) {
 | 
				
			||||||
        self.state = Default::default();
 | 
					        self.state = Default::default();
 | 
				
			||||||
        self.tile = Default::default();
 | 
					        self.tile = Default::default();
 | 
				
			||||||
@@ -773,7 +810,7 @@ impl Fetcher for BackgroundFetcher {
 | 
				
			|||||||
    fn hblank_reset(&mut self) {
 | 
					    fn hblank_reset(&mut self) {
 | 
				
			||||||
        self.reset();
 | 
					        self.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.draw_window = false;
 | 
					        self.is_window_tile = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.enabled = true;
 | 
					        self.enabled = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -784,55 +821,69 @@ impl Default for BackgroundFetcher {
 | 
				
			|||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            state: Default::default(),
 | 
					            state: Default::default(),
 | 
				
			||||||
            tile: Default::default(),
 | 
					            tile: Default::default(),
 | 
				
			||||||
            draw_window: Default::default(),
 | 
					            is_window_tile: Default::default(),
 | 
				
			||||||
            wl_count: Default::default(),
 | 
					            window_line: Default::default(),
 | 
				
			||||||
            enabled: true,
 | 
					            enabled: true,
 | 
				
			||||||
            tile_high_reset: true,
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
struct ObjectFetcher {
 | 
					struct ObjectFetcher {
 | 
				
			||||||
    state: FetcherState,
 | 
					    state: FetcherState,
 | 
				
			||||||
    tile: TileBuilder,
 | 
					    tile: TileBuilder,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for ObjectFetcher {
 | 
					 | 
				
			||||||
    fn default() -> Self {
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            state: Default::default(),
 | 
					 | 
				
			||||||
            tile: Default::default(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Fetcher for ObjectFetcher {
 | 
					impl Fetcher for ObjectFetcher {
 | 
				
			||||||
 | 
					    fn next(&mut self, state: FetcherState) {
 | 
				
			||||||
 | 
					        self.state = state
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn reset(&mut self) {
 | 
					    fn reset(&mut self) {
 | 
				
			||||||
        self.state = Default::default();
 | 
					        self.state = Default::default();
 | 
				
			||||||
        self.tile = Default::default();
 | 
					        self.tile = Default::default();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn hblank_reset(&mut self) {
 | 
					    fn hblank_reset(&mut self) {
 | 
				
			||||||
        self.reset()
 | 
					        self.state = Default::default();
 | 
				
			||||||
 | 
					        self.tile = Default::default();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					struct WindowLineCounter {
 | 
				
			||||||
 | 
					    count: u8,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowLineCounter {
 | 
				
			||||||
 | 
					    fn increment(&mut self) {
 | 
				
			||||||
 | 
					        self.count += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn vblank_reset(&mut self) {
 | 
				
			||||||
 | 
					        self.count = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn count(&self) -> u8 {
 | 
				
			||||||
 | 
					        self.count
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy)]
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
enum FetcherState {
 | 
					enum FetcherState {
 | 
				
			||||||
    TileNumberA,
 | 
					    TileNumber,
 | 
				
			||||||
    TileNumberB,
 | 
					    SleepOne,
 | 
				
			||||||
    TileLowA,
 | 
					    TileLow,
 | 
				
			||||||
    TileLowB,
 | 
					    SleepTwo,
 | 
				
			||||||
    TileHighA,
 | 
					    TileHigh,
 | 
				
			||||||
    TileHighB,
 | 
					    SleepThree,
 | 
				
			||||||
    ToFifoA,
 | 
					    ToFifoOne,
 | 
				
			||||||
    ToFifoB,
 | 
					    ToFifoTwo,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for FetcherState {
 | 
					impl Default for FetcherState {
 | 
				
			||||||
    fn default() -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
        Self::TileNumberA
 | 
					        Self::TileNumber
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -893,11 +944,11 @@ impl TileBuilder {
 | 
				
			|||||||
        self.id = Some(id);
 | 
					        self.id = Some(id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn with_low(&mut self, data: u8) {
 | 
					    fn with_low_byte(&mut self, data: u8) {
 | 
				
			||||||
        self.low = Some(data);
 | 
					        self.low = Some(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn with_high(&mut self, data: u8) {
 | 
					    fn with_high_byte(&mut self, data: u8) {
 | 
				
			||||||
        self.high = Some(data);
 | 
					        self.high = Some(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -906,11 +957,84 @@ impl TileBuilder {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					struct OamScanState {
 | 
				
			||||||
 | 
					    count: u8,
 | 
				
			||||||
 | 
					    mode: OamScanMode,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OamScanState {
 | 
				
			||||||
 | 
					    fn increase(&mut self) {
 | 
				
			||||||
 | 
					        self.count += 1;
 | 
				
			||||||
 | 
					        self.count %= 40;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn reset(&mut self) {
 | 
				
			||||||
 | 
					        self.count = Default::default();
 | 
				
			||||||
 | 
					        self.mode = Default::default();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn count(&self) -> u8 {
 | 
				
			||||||
 | 
					        self.count
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn mode(&self) -> &OamScanMode {
 | 
				
			||||||
 | 
					        &self.mode
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next(&mut self) {
 | 
				
			||||||
 | 
					        use OamScanMode::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.mode = match self.mode {
 | 
				
			||||||
 | 
					            Scan => Sleep,
 | 
				
			||||||
 | 
					            Sleep => Scan,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq)]
 | 
				
			||||||
 | 
					enum OamScanMode {
 | 
				
			||||||
 | 
					    Scan,
 | 
				
			||||||
 | 
					    Sleep,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Default for OamScanMode {
 | 
				
			||||||
 | 
					    fn default() -> Self {
 | 
				
			||||||
 | 
					        Self::Scan
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
struct WindowStatus {
 | 
					struct WindowStatus {
 | 
				
			||||||
    /// This will be true if WY == LY at any point in the frame thus far
 | 
					    /// This will be true if WY == LY at any point in the frame thus far
 | 
				
			||||||
    coincidence: bool,
 | 
					    coincidence: bool,
 | 
				
			||||||
    /// This will be true if the conditions which tell the PPU to start
 | 
					    /// This will be true if the conditions which tell the PPU to start
 | 
				
			||||||
    /// drawing from the window tile map is true
 | 
					    /// drawing from the window tile map is true
 | 
				
			||||||
    enabled: bool,
 | 
					    should_draw: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl WindowStatus {
 | 
				
			||||||
 | 
					    fn should_draw(&self) -> bool {
 | 
				
			||||||
 | 
					        self.should_draw
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn coincidence(&self) -> bool {
 | 
				
			||||||
 | 
					        self.coincidence
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_should_draw(&mut self, value: bool) {
 | 
				
			||||||
 | 
					        self.should_draw = value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_coincidence(&mut self, value: bool) {
 | 
				
			||||||
 | 
					        self.coincidence = value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn hblank_reset(&mut self) {
 | 
				
			||||||
 | 
					        self.should_draw = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn vblank_reset(&mut self) {
 | 
				
			||||||
 | 
					        self.coincidence = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -183,7 +183,7 @@ pub enum ObjectSize {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ObjectSize {
 | 
					impl ObjectSize {
 | 
				
			||||||
    pub(crate) fn size(&self) -> u8 {
 | 
					    pub(crate) fn as_u8(&self) -> u8 {
 | 
				
			||||||
        use ObjectSize::*;
 | 
					        use ObjectSize::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src/timer.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/timer.rs
									
									
									
									
									
								
							@@ -21,6 +21,17 @@ impl Timer {
 | 
				
			|||||||
        use State::*;
 | 
					        use State::*;
 | 
				
			||||||
        use TimerSpeed::*;
 | 
					        use TimerSpeed::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match self.state {
 | 
				
			||||||
 | 
					            TIMAOverflow(_) | AbortedTIMAOverflow(_) => self.next(),
 | 
				
			||||||
 | 
					            LoadTMA => {
 | 
				
			||||||
 | 
					                self.counter = self.modulo;
 | 
				
			||||||
 | 
					                self.interrupt = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Normal => {}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.divider = self.divider.wrapping_add(1);
 | 
					        self.divider = self.divider.wrapping_add(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Get Bit Position
 | 
					        // Get Bit Position
 | 
				
			||||||
@@ -42,17 +53,6 @@ impl Timer {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.and_result = Some(new_result);
 | 
					        self.and_result = Some(new_result);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        match self.state {
 | 
					 | 
				
			||||||
            TIMAOverflow(_) | AbortedTIMAOverflow(_) => self.next(),
 | 
					 | 
				
			||||||
            LoadTIMA => {
 | 
					 | 
				
			||||||
                self.counter = self.modulo;
 | 
					 | 
				
			||||||
                self.interrupt = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.next();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Normal => {}
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// 0xFF05 | TIMA - Timer Counter
 | 
					    /// 0xFF05 | TIMA - Timer Counter
 | 
				
			||||||
@@ -70,7 +70,7 @@ impl Timer {
 | 
				
			|||||||
                self.counter = byte;
 | 
					                self.counter = byte;
 | 
				
			||||||
                self.state = AbortedTIMAOverflow(step);
 | 
					                self.state = AbortedTIMAOverflow(step);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            LoadTIMA => { /* Ignored */ }
 | 
					            LoadTMA => {}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,8 +95,9 @@ impl Timer {
 | 
				
			|||||||
        use State::*;
 | 
					        use State::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.state = match self.state {
 | 
					        self.state = match self.state {
 | 
				
			||||||
            Normal | LoadTIMA | AbortedTIMAOverflow(3) => Normal,
 | 
					            Normal | LoadTMA => Normal,
 | 
				
			||||||
            TIMAOverflow(3) => LoadTIMA,
 | 
					            AbortedTIMAOverflow(4) => Normal,
 | 
				
			||||||
 | 
					            TIMAOverflow(4) => LoadTMA,
 | 
				
			||||||
            AbortedTIMAOverflow(step) => AbortedTIMAOverflow(step + 1),
 | 
					            AbortedTIMAOverflow(step) => AbortedTIMAOverflow(step + 1),
 | 
				
			||||||
            TIMAOverflow(step) => TIMAOverflow(step + 1),
 | 
					            TIMAOverflow(step) => TIMAOverflow(step + 1),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -180,5 +181,5 @@ enum State {
 | 
				
			|||||||
    TIMAOverflow(u8),
 | 
					    TIMAOverflow(u8),
 | 
				
			||||||
    AbortedTIMAOverflow(u8),
 | 
					    AbortedTIMAOverflow(u8),
 | 
				
			||||||
    Normal,
 | 
					    Normal,
 | 
				
			||||||
    LoadTIMA,
 | 
					    LoadTMA,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user