Compare commits
	
		
			39 Commits
		
	
	
		
			refactor
			...
			80a15039e0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 80a15039e0 | |||
| 32405c0734 | |||
| 2ef8fefbb7 | |||
| 548945a4ee | |||
| 1526b4a09d | |||
| 4125ea5c74 | |||
| b9519d9b7a | |||
| 01278ca83f | |||
| 2bb8bd6d8f | |||
| 080c1e7518 | |||
| 765f9d8288 | |||
| 8780c4a59b | |||
| e19a540650 | |||
| 4cfd951ede | |||
| de8fe1a367 | |||
| c5fa41b20d | |||
| 552cfd4a18 | |||
| 4c516804e4 | |||
| 9b3ab73bb1 | |||
| d9f1d661ae | |||
| da83032e24 | |||
| 67e2447d3f | |||
| 37cf3d92e4 | |||
| 293e5762c3 | |||
| 999f661e6b | |||
| e573411890 | |||
| 352a65b705 | |||
| 64230973f1 | |||
| dbbf87af52 | |||
| 1440cd1fc7 | |||
| 9964b49ce1 | |||
| 142231d355 | |||
| 227928e8ca | |||
| 1001b0b124 | |||
| 71ce3f43e0 | |||
| ce121864d2 | |||
| e1fe00ab64 | |||
| 5882678bc5 | |||
| bcd67cb317 | 
							
								
								
									
										422
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										422
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -23,7 +23,7 @@ dependencies = [ | ||||
|  "alsa-sys", | ||||
|  "bitflags", | ||||
|  "libc", | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -59,10 +59,19 @@ dependencies = [ | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "anyhow" | ||||
| version = "1.0.43" | ||||
| name = "ansi_term" | ||||
| version = "0.12.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" | ||||
| checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" | ||||
| dependencies = [ | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "anyhow" | ||||
| version = "1.0.44" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" | ||||
|  | ||||
| [[package]] | ||||
| name = "arrayvec" | ||||
| @@ -76,7 +85,7 @@ version = "0.33.3+1.2.191" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219" | ||||
| dependencies = [ | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -144,9 +153,9 @@ checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" | ||||
|  | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.2.1" | ||||
| version = "1.3.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | ||||
| checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||||
|  | ||||
| [[package]] | ||||
| name = "block" | ||||
| @@ -156,9 +165,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" | ||||
|  | ||||
| [[package]] | ||||
| name = "bumpalo" | ||||
| version = "3.7.0" | ||||
| version = "3.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" | ||||
| checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" | ||||
|  | ||||
| [[package]] | ||||
| name = "bytemuck" | ||||
| @@ -196,9 +205,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "cc" | ||||
| version = "1.0.70" | ||||
| version = "1.0.71" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" | ||||
| checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" | ||||
| dependencies = [ | ||||
|  "jobserver", | ||||
| ] | ||||
| @@ -244,7 +253,7 @@ checksum = "10612c0ec0e0a1ff0e97980647cb058a6e7aedb913d01d009c406b8b7d0b26ee" | ||||
| dependencies = [ | ||||
|  "glob", | ||||
|  "libc", | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -253,7 +262,7 @@ version = "2.33.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" | ||||
| dependencies = [ | ||||
|  "ansi_term", | ||||
|  "ansi_term 0.11.0", | ||||
|  "atty", | ||||
|  "bitflags", | ||||
|  "strsim 0.8.0", | ||||
| @@ -277,7 +286,7 @@ dependencies = [ | ||||
|  "bitflags", | ||||
|  "block", | ||||
|  "cocoa-foundation", | ||||
|  "core-foundation 0.9.1", | ||||
|  "core-foundation 0.9.2", | ||||
|  "core-graphics 0.22.2", | ||||
|  "foreign-types", | ||||
|  "libc", | ||||
| @@ -292,7 +301,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "block", | ||||
|  "core-foundation 0.9.1", | ||||
|  "core-foundation 0.9.2", | ||||
|  "core-graphics-types", | ||||
|  "foreign-types", | ||||
|  "libc", | ||||
| @@ -347,11 +356,11 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "core-foundation" | ||||
| version = "0.9.1" | ||||
| version = "0.9.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" | ||||
| checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" | ||||
| dependencies = [ | ||||
|  "core-foundation-sys 0.8.2", | ||||
|  "core-foundation-sys 0.8.3", | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| @@ -369,9 +378,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" | ||||
|  | ||||
| [[package]] | ||||
| name = "core-foundation-sys" | ||||
| version = "0.8.2" | ||||
| version = "0.8.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" | ||||
| checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" | ||||
|  | ||||
| [[package]] | ||||
| name = "core-graphics" | ||||
| @@ -392,7 +401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "core-foundation 0.9.1", | ||||
|  "core-foundation 0.9.2", | ||||
|  "core-graphics-types", | ||||
|  "foreign-types", | ||||
|  "libc", | ||||
| @@ -405,7 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "core-foundation 0.9.1", | ||||
|  "core-foundation 0.9.2", | ||||
|  "foreign-types", | ||||
|  "libc", | ||||
| ] | ||||
| @@ -449,7 +458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418" | ||||
| dependencies = [ | ||||
|  "alsa", | ||||
|  "core-foundation-sys 0.8.2", | ||||
|  "core-foundation-sys 0.8.3", | ||||
|  "coreaudio-rs", | ||||
|  "jni", | ||||
|  "js-sys", | ||||
| @@ -458,7 +467,7 @@ dependencies = [ | ||||
|  "mach 0.3.2", | ||||
|  "ndk 0.3.0", | ||||
|  "ndk-glue 0.3.0", | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
|  "oboe", | ||||
|  "parking_lot", | ||||
|  "stdweb 0.1.3", | ||||
| @@ -542,7 +551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| @@ -592,6 +601,47 @@ dependencies = [ | ||||
|  "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]] | ||||
| name = "discard" | ||||
| version = "1.0.4" | ||||
| @@ -619,7 +669,7 @@ version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" | ||||
| dependencies = [ | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -628,12 +678,6 @@ version = "1.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" | ||||
|  | ||||
| [[package]] | ||||
| name = "fixedbitset" | ||||
| version = "0.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" | ||||
|  | ||||
| [[package]] | ||||
| name = "fnv" | ||||
| version = "1.0.7" | ||||
| @@ -671,14 +715,28 @@ dependencies = [ | ||||
|  "anyhow", | ||||
|  "bitfield", | ||||
|  "clap", | ||||
|  "directories-next", | ||||
|  "gilrs", | ||||
|  "pixels", | ||||
|  "rodio", | ||||
|  "rtrb", | ||||
|  "tracing", | ||||
|  "tracing-subscriber", | ||||
|  "winit", | ||||
|  "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]] | ||||
| name = "gilrs" | ||||
| version = "0.8.1" | ||||
| @@ -703,7 +761,7 @@ dependencies = [ | ||||
|  "libc", | ||||
|  "libudev-sys", | ||||
|  "log", | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
|  "rusty-xinput", | ||||
|  "stdweb 0.4.20", | ||||
|  "uuid", | ||||
| @@ -731,9 +789,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "gpu-alloc" | ||||
| version = "0.5.1" | ||||
| version = "0.5.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ab8524eac5fc9d05625c891adf78fcf64dc0ee9f8d0882874b9f220f42b442bf" | ||||
| checksum = "0e64cbb8d36508d3e19da95e56e196a84f674fc190881f2cc010000798838aa6" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "gpu-alloc-types", | ||||
| @@ -792,6 +850,12 @@ dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "hexf-parse" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" | ||||
|  | ||||
| [[package]] | ||||
| name = "hound" | ||||
| version = "3.4.0" | ||||
| @@ -822,11 +886,14 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" | ||||
|  | ||||
| [[package]] | ||||
| name = "instant" | ||||
| version = "0.1.10" | ||||
| version = "0.1.11" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" | ||||
| checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd" | ||||
| dependencies = [ | ||||
|  "cfg-if 1.0.0", | ||||
|  "js-sys", | ||||
|  "wasm-bindgen", | ||||
|  "web-sys", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -876,9 +943,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "js-sys" | ||||
| version = "0.3.54" | ||||
| version = "0.3.55" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" | ||||
| checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" | ||||
| dependencies = [ | ||||
|  "wasm-bindgen", | ||||
| ] | ||||
| @@ -890,7 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -918,9 +985,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.101" | ||||
| version = "0.2.103" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" | ||||
| checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" | ||||
|  | ||||
| [[package]] | ||||
| name = "libloading" | ||||
| @@ -934,9 +1001,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "libloading" | ||||
| version = "0.7.0" | ||||
| version = "0.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" | ||||
| checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0" | ||||
| dependencies = [ | ||||
|  "cfg-if 1.0.0", | ||||
|  "winapi", | ||||
| @@ -998,10 +1065,13 @@ dependencies = [ | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "maybe-uninit" | ||||
| version = "2.0.0" | ||||
| name = "matchers" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" | ||||
| checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" | ||||
| dependencies = [ | ||||
|  "regex-automata", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| @@ -1043,9 +1113,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "minimal-lexical" | ||||
| version = "0.1.3" | ||||
| version = "0.1.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d" | ||||
| checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" | ||||
|  | ||||
| [[package]] | ||||
| name = "minimp3" | ||||
| @@ -1103,17 +1173,18 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "naga" | ||||
| version = "0.6.3" | ||||
| version = "0.7.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8c5859e55c51da10b98e7a73068e0a0c5da7bbcae4fc38f86043d0c6d1b917cf" | ||||
| checksum = "eda66d09f712e1f0a6ab436137da4fac312f78301f6d4ac7cb8bfe96e988734f" | ||||
| dependencies = [ | ||||
|  "bit-set", | ||||
|  "bitflags", | ||||
|  "codespan-reporting", | ||||
|  "fxhash", | ||||
|  "hexf-parse", | ||||
|  "indexmap", | ||||
|  "log", | ||||
|  "num-traits", | ||||
|  "petgraph", | ||||
|  "spirv", | ||||
|  "thiserror", | ||||
| ] | ||||
| @@ -1204,15 +1275,14 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "nix" | ||||
| version = "0.20.1" | ||||
| version = "0.20.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "df8e5e343312e7fbeb2a52139114e9e702991ef9c2aea6817ff2440b35647d56" | ||||
| checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "cc", | ||||
|  "cfg-if 1.0.0", | ||||
|  "libc", | ||||
|  "memoffset", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -1281,7 +1351,7 @@ version = "0.5.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" | ||||
| dependencies = [ | ||||
|  "proc-macro-crate 1.0.0", | ||||
|  "proc-macro-crate 1.1.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| @@ -1391,20 +1461,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" | ||||
|  | ||||
| [[package]] | ||||
| name = "petgraph" | ||||
| version = "0.6.0" | ||||
| name = "pin-project-lite" | ||||
| version = "0.2.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" | ||||
| dependencies = [ | ||||
|  "fixedbitset", | ||||
|  "indexmap", | ||||
| ] | ||||
| checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" | ||||
|  | ||||
| [[package]] | ||||
| name = "pixels" | ||||
| version = "0.6.0" | ||||
| version = "0.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6e794d4eab6519352618cca630ce4970a65576b9df54eca2068d4aad4b143903" | ||||
| checksum = "94ab1e297051c39cc7b7511e7e2b3ab151f14aff6a44e73bdde652b1e2190950" | ||||
| dependencies = [ | ||||
|  "bytemuck", | ||||
|  "pollster", | ||||
| @@ -1416,9 +1482,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "pkg-config" | ||||
| version = "0.3.19" | ||||
| version = "0.3.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" | ||||
| checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" | ||||
|  | ||||
| [[package]] | ||||
| name = "pollster" | ||||
| @@ -1437,9 +1503,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "proc-macro-crate" | ||||
| version = "1.0.0" | ||||
| version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" | ||||
| checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" | ||||
| dependencies = [ | ||||
|  "thiserror", | ||||
|  "toml", | ||||
| @@ -1447,9 +1513,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.29" | ||||
| version = "1.0.30" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" | ||||
| checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" | ||||
| dependencies = [ | ||||
|  "unicode-xid", | ||||
| ] | ||||
| @@ -1462,9 +1528,9 @@ checksum = "87dfd5592a8eed7e74f56ad7b125f8234763b805c30f0c7c95c486920026a6ec" | ||||
|  | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.9" | ||||
| version = "1.0.10" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" | ||||
| checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
| @@ -1493,6 +1559,16 @@ dependencies = [ | ||||
|  "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]] | ||||
| name = "regex" | ||||
| version = "1.5.4" | ||||
| @@ -1502,6 +1578,15 @@ dependencies = [ | ||||
|  "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]] | ||||
| name = "regex-syntax" | ||||
| version = "0.6.25" | ||||
| @@ -1529,9 +1614,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "rtrb" | ||||
| version = "0.1.4" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f" | ||||
| checksum = "7d911d82e9745a598dd118e6bda2165531e316cf8eae65240776637ae8168458" | ||||
| dependencies = [ | ||||
|  "cache-padded", | ||||
| ] | ||||
| @@ -1642,9 +1727,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.67" | ||||
| version = "1.0.68" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" | ||||
| checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "ryu", | ||||
| @@ -1657,6 +1742,15 @@ version = "0.6.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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]] | ||||
| name = "shlex" | ||||
| version = "0.1.1" | ||||
| @@ -1685,9 +1779,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "smallvec" | ||||
| version = "1.6.1" | ||||
| version = "1.7.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" | ||||
| checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" | ||||
|  | ||||
| [[package]] | ||||
| name = "smithay-client-toolkit" | ||||
| @@ -1789,9 +1883,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "1.0.76" | ||||
| version = "1.0.80" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" | ||||
| checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
| @@ -1818,18 +1912,18 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror" | ||||
| version = "1.0.29" | ||||
| version = "1.0.30" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" | ||||
| checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" | ||||
| dependencies = [ | ||||
|  "thiserror-impl", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror-impl" | ||||
| version = "1.0.29" | ||||
| version = "1.0.30" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" | ||||
| checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
| @@ -1837,10 +1931,19 @@ dependencies = [ | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tinyvec" | ||||
| version = "1.3.1" | ||||
| name = "thread_local" | ||||
| version = "1.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" | ||||
| checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" | ||||
| dependencies = [ | ||||
|  "once_cell", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tinyvec" | ||||
| version = "1.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" | ||||
| dependencies = [ | ||||
|  "tinyvec_macros", | ||||
| ] | ||||
| @@ -1860,6 +1963,67 @@ dependencies = [ | ||||
|  "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]] | ||||
| name = "ttf-parser" | ||||
| version = "0.6.2" | ||||
| @@ -1877,9 +2041,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "unicode-width" | ||||
| version = "0.1.8" | ||||
| version = "0.1.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" | ||||
| checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" | ||||
|  | ||||
| [[package]] | ||||
| name = "unicode-xid" | ||||
| @@ -1917,10 +2081,16 @@ dependencies = [ | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen" | ||||
| version = "0.2.77" | ||||
| name = "wasi" | ||||
| version = "0.10.2+wasi-snapshot-preview1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" | ||||
| checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen" | ||||
| version = "0.2.78" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" | ||||
| dependencies = [ | ||||
|  "cfg-if 1.0.0", | ||||
|  "wasm-bindgen-macro", | ||||
| @@ -1928,9 +2098,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen-backend" | ||||
| version = "0.2.77" | ||||
| version = "0.2.78" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" | ||||
| checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" | ||||
| dependencies = [ | ||||
|  "bumpalo", | ||||
|  "lazy_static", | ||||
| @@ -1943,9 +2113,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen-futures" | ||||
| version = "0.4.27" | ||||
| version = "0.4.28" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060" | ||||
| checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" | ||||
| dependencies = [ | ||||
|  "cfg-if 1.0.0", | ||||
|  "js-sys", | ||||
| @@ -1955,9 +2125,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen-macro" | ||||
| version = "0.2.77" | ||||
| version = "0.2.78" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" | ||||
| checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "wasm-bindgen-macro-support", | ||||
| @@ -1965,9 +2135,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen-macro-support" | ||||
| version = "0.2.77" | ||||
| version = "0.2.78" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" | ||||
| checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
| @@ -1978,9 +2148,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wasm-bindgen-shared" | ||||
| version = "0.2.77" | ||||
| version = "0.2.78" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" | ||||
| checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" | ||||
|  | ||||
| [[package]] | ||||
| name = "wayland-client" | ||||
| @@ -1991,7 +2161,7 @@ dependencies = [ | ||||
|  "bitflags", | ||||
|  "downcast-rs", | ||||
|  "libc", | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
|  "scoped-tls", | ||||
|  "wayland-commons", | ||||
|  "wayland-scanner", | ||||
| @@ -2004,7 +2174,7 @@ version = "0.28.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" | ||||
| dependencies = [ | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
|  "once_cell", | ||||
|  "smallvec", | ||||
|  "wayland-sys", | ||||
| @@ -2016,7 +2186,7 @@ version = "0.28.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" | ||||
| dependencies = [ | ||||
|  "nix 0.20.1", | ||||
|  "nix 0.20.0", | ||||
|  "wayland-client", | ||||
|  "xcursor", | ||||
| ] | ||||
| @@ -2057,9 +2227,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "web-sys" | ||||
| version = "0.3.51" | ||||
| version = "0.3.55" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" | ||||
| checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" | ||||
| dependencies = [ | ||||
|  "js-sys", | ||||
|  "wasm-bindgen", | ||||
| @@ -2067,9 +2237,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wgpu" | ||||
| version = "0.10.1" | ||||
| version = "0.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3d92a4fe73b1e7d7ef99938dacd49258cbf1ad87cdb5bf6efa20c27447442b45" | ||||
| checksum = "d1577ecc4f6992b9e965878ac594efb24eed2bdf089c11f45b3d1c5f216e2e30" | ||||
| dependencies = [ | ||||
|  "arrayvec", | ||||
|  "js-sys", | ||||
| @@ -2087,9 +2257,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wgpu-core" | ||||
| version = "0.10.2" | ||||
| version = "0.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "425b975c319d311e051bf3afb54120a34b187f9d889edc68e347567e512774c8" | ||||
| checksum = "45af76ba5545b61a6904f26cdcf4287329144ae9e12f0c23ec4c9be982d675a6" | ||||
| dependencies = [ | ||||
|  "arrayvec", | ||||
|  "bitflags", | ||||
| @@ -2109,9 +2279,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "wgpu-hal" | ||||
| version = "0.10.4" | ||||
| version = "0.11.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b33daf5eff68118c0aad030886655824052f82a164fd2e257211f742c6cade53" | ||||
| checksum = "11095a81f4406b1e594dab7dc35d6508409d364e62458f2e5b07b3edc7aca517" | ||||
| dependencies = [ | ||||
|  "arrayvec", | ||||
|  "ash", | ||||
| @@ -2126,26 +2296,30 @@ dependencies = [ | ||||
|  "gpu-alloc", | ||||
|  "gpu-descriptor", | ||||
|  "inplace_it", | ||||
|  "js-sys", | ||||
|  "khronos-egl", | ||||
|  "libloading 0.7.0", | ||||
|  "libloading 0.7.1", | ||||
|  "log", | ||||
|  "metal", | ||||
|  "naga", | ||||
|  "objc", | ||||
|  "parking_lot", | ||||
|  "profiling", | ||||
|  "range-alloc", | ||||
|  "raw-window-handle", | ||||
|  "renderdoc-sys", | ||||
|  "thiserror", | ||||
|  "wasm-bindgen", | ||||
|  "web-sys", | ||||
|  "wgpu-types", | ||||
|  "winapi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "wgpu-types" | ||||
| version = "0.10.0" | ||||
| version = "0.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "25feb2fbf24ab3219a9f10890ceb8e1ef02b13314ed89d64a9ae99dcad883e18" | ||||
| checksum = "e15e44ba88ec415466e18e91881319e7c9e96cb905dc623305168aea65b85ccc" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
| ] | ||||
| @@ -2199,7 +2373,7 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" | ||||
| dependencies = [ | ||||
|  "bitflags", | ||||
|  "cocoa", | ||||
|  "core-foundation 0.9.1", | ||||
|  "core-foundation 0.9.2", | ||||
|  "core-graphics 0.22.2", | ||||
|  "core-video-sys", | ||||
|  "dispatch", | ||||
| @@ -2234,13 +2408,12 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "x11-dl" | ||||
| version = "2.18.5" | ||||
| version = "2.19.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" | ||||
| checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" | ||||
| dependencies = [ | ||||
|  "lazy_static", | ||||
|  "libc", | ||||
|  "maybe-uninit", | ||||
|  "pkg-config", | ||||
| ] | ||||
|  | ||||
| @@ -2255,9 +2428,12 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "xdg" | ||||
| version = "2.2.0" | ||||
| version = "2.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" | ||||
| checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" | ||||
| dependencies = [ | ||||
|  "dirs", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "xml-rs" | ||||
|   | ||||
							
								
								
									
										24
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								Cargo.toml
									
									
									
									
									
								
							| @@ -2,21 +2,23 @@ | ||||
| name = "gb" | ||||
| version = "0.1.0" | ||||
| authors = ["Rekai Musuka <rekai@musuka.dev>"] | ||||
| edition = "2018" | ||||
| resolver = "2" | ||||
| edition = "2021" | ||||
|  | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [dependencies] | ||||
| anyhow = "^1.0" | ||||
| bitfield = "^0.13" | ||||
| clap = "^2.33" | ||||
| gilrs = "^0.8" | ||||
| pixels = "^0.6" | ||||
| winit = "^0.25" | ||||
| winit_input_helper = "^0.10" | ||||
| rodio = "^0.14" | ||||
| rtrb = "^0.1.4" | ||||
| anyhow = "1.0" | ||||
| bitfield = "0.13" | ||||
| clap = "2.33" | ||||
| gilrs = "0.8" | ||||
| pixels = "0.7" | ||||
| winit = "0.25" | ||||
| winit_input_helper = "0.10" | ||||
| rodio = "0.14" | ||||
| rtrb = "0.2" | ||||
| directories-next = "2.0" | ||||
| tracing = "0.1" | ||||
| tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] } | ||||
|  | ||||
| [profile.release] | ||||
| debug = true | ||||
|   | ||||
							
								
								
									
										40
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,13 +2,37 @@ | ||||
| [](https://ci.paoda.moe/paoda/gb) | ||||
|  | ||||
| ### Status | ||||
| * Passes Blargg's cpu_instrs Test | ||||
| * Renders Background & Window Tiles  | ||||
| * Implements a PPU FIFO | ||||
| * From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/) | ||||
|     * [x] cpu_instrs | ||||
|     * [x] instr_timing | ||||
|     * [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 | ||||
| * [gameboy-logs](https://github.com/wheremyfoodat/Gameboy-logs) suggests that there are still some underlying problems with the cpu implementation | ||||
| * 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.  | ||||
| * Code cleanup is pending completion of some minimum viable product of the emulator | ||||
|  | ||||
| ### Controls | ||||
| Controls are defined [here](https://git.musuka.dev/paoda/gb/src/branch/main/src/joypad.rs#L114) | ||||
|  | ||||
| Key | Button | ||||
| --- | --- | ||||
| <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) | ||||
							
								
								
									
										
											BIN
										
									
								
								bin/bootix_dmg.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								bin/bootix_dmg.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										298
									
								
								src/apu.rs
									
									
									
									
									
								
							
							
						
						
									
										298
									
								
								src/apu.rs
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| use crate::apu::gen::SAMPLE_RATE; | ||||
| use crate::bus::BusIo; | ||||
| use crate::emu::SM83_CLOCK_SPEED; | ||||
| use gen::SampleProducer; | ||||
| @@ -5,8 +6,8 @@ use types::ch1::{Sweep, SweepDirection}; | ||||
| use types::ch3::Volume as Ch3Volume; | ||||
| use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter}; | ||||
| use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope}; | ||||
| use types::fs::{FrameSequencer, State as FrameSequencerState}; | ||||
| use types::{ChannelControl, SoundOutput}; | ||||
| use types::fs::{FrameSequencer, State as FSState}; | ||||
| use types::{ChannelControl, NRx4, SoundOutput}; | ||||
|  | ||||
| pub mod gen; | ||||
| mod types; | ||||
| @@ -26,11 +27,13 @@ pub struct Apu { | ||||
|     /// Noise | ||||
|     ch4: Channel4, | ||||
|  | ||||
|     sequencer: FrameSequencer, | ||||
|     fs: FrameSequencer, | ||||
|     div_prev: Option<u16>, | ||||
|  | ||||
|     prod: Option<SampleProducer<f32>>, | ||||
|     sample_counter: u64, | ||||
|  | ||||
|     cap: f32, | ||||
| } | ||||
|  | ||||
| impl BusIo for Apu { | ||||
| @@ -54,7 +57,7 @@ impl BusIo for Apu { | ||||
|             0x26 => self.ctrl.status(self), | ||||
|             0x30..=0x3F => self.ch3.read_byte(addr), | ||||
|             _ => { | ||||
|                 eprintln!("Read 0xFF from unused IO register {:#06X} [APU]", addr); | ||||
|                 tracing::warn!("Attempted read from {:#06X}", addr); | ||||
|                 0xFF | ||||
|             } | ||||
|         } | ||||
| @@ -66,29 +69,26 @@ impl BusIo for Apu { | ||||
|             0x11 if self.ctrl.enabled => self.ch1.set_duty(byte), | ||||
|             0x12 if self.ctrl.enabled => self.ch1.set_envelope(byte), | ||||
|             0x13 if self.ctrl.enabled => self.ch1.set_freq_lo(byte), | ||||
|             0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(byte), | ||||
|             0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(&self.fs, byte), | ||||
|             0x16 if self.ctrl.enabled => self.ch2.set_duty(byte), | ||||
|             0x17 if self.ctrl.enabled => self.ch2.set_envelope(byte), | ||||
|             0x18 if self.ctrl.enabled => self.ch2.set_freq_lo(byte), | ||||
|             0x19 if self.ctrl.enabled => self.ch2.set_freq_hi(byte), | ||||
|             0x19 if self.ctrl.enabled => self.ch2.set_freq_hi(&self.fs, byte), | ||||
|             0x1A if self.ctrl.enabled => self.ch3.set_dac_enabled(byte), | ||||
|             0x1B if self.ctrl.enabled => self.ch3.set_len(byte), | ||||
|             0x1C if self.ctrl.enabled => self.ch3.set_volume(byte), | ||||
|             0x1D if self.ctrl.enabled => self.ch3.set_freq_lo(byte), | ||||
|             0x1E if self.ctrl.enabled => self.ch3.set_freq_hi(byte), | ||||
|             0x1E if self.ctrl.enabled => self.ch3.set_freq_hi(&self.fs, byte), | ||||
|             0x20 if self.ctrl.enabled => self.ch4.set_len(byte), | ||||
|             0x21 if self.ctrl.enabled => self.ch4.set_envelope(byte), | ||||
|             0x22 if self.ctrl.enabled => self.ch4.set_poly(byte), | ||||
|             0x23 if self.ctrl.enabled => self.ch4.set_frequency(byte), | ||||
|             0x23 if self.ctrl.enabled => self.ch4.set_frequency(&self.fs, byte), | ||||
|             0x24 if self.ctrl.enabled => self.ctrl.set_channel(byte), | ||||
|             0x25 if self.ctrl.enabled => self.ctrl.set_output(byte), | ||||
|             0x26 => self.set_status(byte), | ||||
|             0x30..=0x3F => self.ch3.write_byte(addr, byte), | ||||
|             _ if !self.ctrl.enabled => {} | ||||
|             _ => eprintln!( | ||||
|                 "Wrote {:#04X} to unused IO register {:#06X} [APU]", | ||||
|                 byte, addr | ||||
|             ), | ||||
|             _ => tracing::warn!("Attempted write of {:#04X} to {:#06X}", byte, addr), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -99,9 +99,9 @@ impl Apu { | ||||
|  | ||||
|         // Frame Sequencer (512Hz) | ||||
|         if self.is_falling_edge(12, div) { | ||||
|             use FrameSequencerState::*; | ||||
|             use FSState::*; | ||||
|  | ||||
|             match self.sequencer.state() { | ||||
|             match self.fs.state() { | ||||
|                 Length => self.clock_length(), | ||||
|                 LengthAndSweep => { | ||||
|                     self.clock_length(); | ||||
| @@ -111,7 +111,7 @@ impl Apu { | ||||
|                 Nothing => {} | ||||
|             } | ||||
|  | ||||
|             self.sequencer.next(); | ||||
|             self.fs.next(); | ||||
|         } | ||||
|  | ||||
|         self.div_prev = Some(div); | ||||
| @@ -124,25 +124,33 @@ impl Apu { | ||||
|         if self.sample_counter >= SM83_CLOCK_SPEED { | ||||
|             self.sample_counter %= SM83_CLOCK_SPEED; | ||||
|  | ||||
|             if let Some(ref mut prod) = self.prod { | ||||
|             if let Some(prod) = self.prod.as_mut() { | ||||
|                 if prod.available_blocking() { | ||||
|                     // 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 ch1_left = if left { self.ch1.amplitude() } else { 0.0 }; | ||||
|                     let ch1_right = if right { self.ch1.amplitude() } else { 0.0 }; | ||||
|                     let ch1_left = if left { ch1_amplitude } else { 0.0 }; | ||||
|                     let ch1_right = if right { 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 ch2_left = if left { self.ch2.amplitude() } else { 0.0 }; | ||||
|                     let ch2_right = if right { self.ch2.amplitude() } else { 0.0 }; | ||||
|                     let ch2_left = if left { ch2_amplitude } else { 0.0 }; | ||||
|                     let ch2_right = if right { 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 ch3_left = if left { self.ch3.amplitude() } else { 0.0 }; | ||||
|                     let ch3_right = if right { self.ch3.amplitude() } else { 0.0 }; | ||||
|                     let ch3_left = if left { ch3_amplitude } else { 0.0 }; | ||||
|                     let ch3_right = if right { 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 ch4_left = if left { self.ch4.amplitude() } else { 0.0 }; | ||||
|                     let ch4_right = if right { self.ch4.amplitude() } else { 0.0 }; | ||||
|                     let ch4_left = if left { ch4_amplitude } else { 0.0 }; | ||||
|                     let ch4_right = if right { ch4_amplitude } else { 0.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; | ||||
| @@ -164,7 +172,7 @@ impl Apu { | ||||
|  | ||||
|         if self.ctrl.enabled { | ||||
|             // Frame Sequencer reset to Step 0 | ||||
|             self.sequencer.reset(); | ||||
|             self.fs.reset(); | ||||
|  | ||||
|             // Square Duty units are reset to first step | ||||
|             self.ch1.duty_pos = 0; | ||||
| @@ -217,25 +225,13 @@ impl Apu { | ||||
|         self.ch4.enabled = Default::default(); | ||||
|     } | ||||
|  | ||||
|     fn process_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) { | ||||
|         if freq_hi.length_disable() && *length_timer > 0 { | ||||
|             *length_timer -= 1; | ||||
|     fn process_length(freq: &impl NRx4, counter: &mut u16, enabled: &mut bool) { | ||||
|         if freq.length_enable() && *counter > 0 { | ||||
|             *counter -= 1; | ||||
|  | ||||
|             // Check in this scope ensures (only) the above subtraction | ||||
|             // made length_timer 0 | ||||
|             if *length_timer == 0 { | ||||
|                 *enabled = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn ch4_process_length(freq: &Ch4Frequency, length_timer: &mut u16, enabled: &mut bool) { | ||||
|         if freq.length_disable() && *length_timer > 0 { | ||||
|             *length_timer -= 1; | ||||
|  | ||||
|             // Check in this scope ensures (only) the above subtraction | ||||
|             // made length_timer 0 | ||||
|             if *length_timer == 0 { | ||||
|             if *counter == 0 { | ||||
|                 *enabled = false; | ||||
|             } | ||||
|         } | ||||
| @@ -244,25 +240,25 @@ impl Apu { | ||||
|     fn clock_length(&mut self) { | ||||
|         Self::process_length( | ||||
|             &self.ch1.freq_hi, | ||||
|             &mut self.ch1.length_timer, | ||||
|             &mut self.ch1.length_counter, | ||||
|             &mut self.ch1.enabled, | ||||
|         ); | ||||
|  | ||||
|         Self::process_length( | ||||
|             &self.ch2.freq_hi, | ||||
|             &mut self.ch2.length_timer, | ||||
|             &mut self.ch2.length_counter, | ||||
|             &mut self.ch2.enabled, | ||||
|         ); | ||||
|  | ||||
|         Self::process_length( | ||||
|             &self.ch3.freq_hi, | ||||
|             &mut self.ch3.length_timer, | ||||
|             &mut self.ch3.length_counter, | ||||
|             &mut self.ch3.enabled, | ||||
|         ); | ||||
|  | ||||
|         Self::ch4_process_length( | ||||
|         Self::process_length( | ||||
|             &self.ch4.freq, | ||||
|             &mut self.ch4.length_timer, | ||||
|             &mut self.ch4.length_counter, | ||||
|             &mut self.ch4.enabled, | ||||
|         ); | ||||
|     } | ||||
| @@ -279,7 +275,7 @@ impl Apu { | ||||
|             if self.ch1.sweep_enabled && period != 0 { | ||||
|                 let new_freq = self.ch1.calc_sweep_freq(); | ||||
|  | ||||
|                 if new_freq <= 2047 && self.ch1.sweep.shift_count() != 0 { | ||||
|                 if new_freq <= 0x7FF && self.ch1.sweep.shift_count() != 0 { | ||||
|                     self.ch1.set_frequency(new_freq); | ||||
|                     self.ch1.shadow_freq = new_freq; | ||||
|  | ||||
| @@ -337,6 +333,19 @@ impl Apu { | ||||
|             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)] | ||||
| @@ -408,7 +417,7 @@ pub(crate) struct Channel1 { | ||||
|     sweep_enabled: bool, | ||||
|  | ||||
|     // Length Functionality | ||||
|     length_timer: u16, | ||||
|     length_counter: u16, | ||||
|  | ||||
|     freq_timer: u16, | ||||
|     duty_pos: u8, | ||||
| @@ -435,11 +444,11 @@ impl Channel1 { | ||||
|     /// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty | ||||
|     pub(crate) fn set_duty(&mut self, byte: u8) { | ||||
|         self.duty = byte.into(); | ||||
|         self.length_timer = 64 - self.duty.sound_length() as u16; | ||||
|         self.length_counter = 64 - self.duty.sound_length() as u16; | ||||
|     } | ||||
|  | ||||
|     /// 0xFF12 | NR12 - Channel 1 Volume Envelope | ||||
|     pub fn envelope(&self) -> u8 { | ||||
|     pub(crate) fn envelope(&self) -> u8 { | ||||
|         u8::from(self.envelope) | ||||
|     } | ||||
|  | ||||
| @@ -463,25 +472,30 @@ impl Channel1 { | ||||
|     } | ||||
|  | ||||
|     /// 0xFF14 | NR14 - Channel 1 Frequency high | ||||
|     pub(crate) fn set_freq_hi(&mut self, byte: u8) { | ||||
|         self.freq_hi = byte.into(); | ||||
|     pub(crate) fn set_freq_hi(&mut self, fs: &FrameSequencer, byte: u8) { | ||||
|         let mut new_freq: FrequencyHigh = byte.into(); | ||||
|  | ||||
|         // If this bit is set, a trigger event occurs | ||||
|         if self.freq_hi.initial() { | ||||
|             if self.is_dac_enabled() { | ||||
|                 self.enabled = true; | ||||
|             } | ||||
|         if new_freq.trigger() { | ||||
|             self.enabled = true; | ||||
|  | ||||
|             // Length behaviour during trigger event | ||||
|             if self.length_timer == 0 { | ||||
|                 self.length_timer = 64; | ||||
|             } | ||||
|             if self.length_counter == 0 { | ||||
|                 self.length_counter = 64; | ||||
|  | ||||
|             // Envelope Behaviour during trigger event | ||||
|                 // length_counter was 0 so length evidently wasn't enabled | ||||
|                 self.freq_hi.set_length_enable(false); | ||||
|             }; | ||||
|  | ||||
|             // reload freq_timer but last two bits are unmodified | ||||
|             self.freq_timer = obscure::square::freq_timer_reload(self.freq_timer, self.frequency()); | ||||
|  | ||||
|             // Volume Envelope loaded w/ period | ||||
|             self.period_timer = self.envelope.period(); | ||||
|  | ||||
|             // Channel Volume reloaded | ||||
|             self.current_volume = self.envelope.init_vol(); | ||||
|  | ||||
|             // Sweep behaviour during trigger event | ||||
|             // Channel 1 Sweep Behaviour | ||||
|             let sweep_period = self.sweep.period(); | ||||
|             let sweep_shift = self.sweep.shift_count(); | ||||
|  | ||||
| @@ -493,7 +507,19 @@ impl Channel1 { | ||||
|             if sweep_shift != 0 { | ||||
|                 let _ = self.calc_sweep_freq(); | ||||
|             } | ||||
|  | ||||
|             self.enabled = self.is_dac_enabled(); | ||||
|         } | ||||
|  | ||||
|         obscure::nrx4::length_update( | ||||
|             &mut new_freq, | ||||
|             fs, | ||||
|             &mut self.length_counter, | ||||
|             &mut self.enabled, | ||||
|             self.freq_hi.length_enable(), | ||||
|         ); | ||||
|  | ||||
|         self.freq_hi = new_freq; | ||||
|     } | ||||
|  | ||||
|     fn tick(&mut self) { | ||||
| @@ -567,7 +593,7 @@ pub(crate) struct Channel2 { | ||||
|     current_volume: u8, | ||||
|  | ||||
|     // Length Functionality | ||||
|     length_timer: u16, | ||||
|     length_counter: u16, | ||||
|  | ||||
|     freq_timer: u16, | ||||
|     duty_pos: u8, | ||||
| @@ -584,7 +610,7 @@ impl Channel2 { | ||||
|     /// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty | ||||
|     pub(crate) fn set_duty(&mut self, byte: u8) { | ||||
|         self.duty = byte.into(); | ||||
|         self.length_timer = 64 - self.duty.sound_length() as u16; | ||||
|         self.length_counter = 64 - self.duty.sound_length() as u16; | ||||
|     } | ||||
|  | ||||
|     /// 0xFF17 | NR22 - Channel 2 Volume ENvelope | ||||
| @@ -612,22 +638,36 @@ impl Channel2 { | ||||
|     } | ||||
|  | ||||
|     /// 0xFF19 | NR24 - Channel 2 Frequency high | ||||
|     pub(crate) fn set_freq_hi(&mut self, byte: u8) { | ||||
|     pub(crate) fn set_freq_hi(&mut self, fs: &FrameSequencer, byte: u8) { | ||||
|         let prev_le = self.freq_hi.length_enable(); | ||||
|         self.freq_hi = byte.into(); | ||||
|  | ||||
|         if self.freq_hi.initial() { | ||||
|             // Envelope behaviour during trigger event | ||||
|         obscure::nrx4::length_update( | ||||
|             &mut self.freq_hi, | ||||
|             fs, | ||||
|             &mut self.length_counter, | ||||
|             &mut self.enabled, | ||||
|             prev_le, | ||||
|         ); | ||||
|  | ||||
|         if self.freq_hi.trigger() { | ||||
|             self.enabled = true; | ||||
|  | ||||
|             // Reload length counter if need be | ||||
|             if self.length_counter == 0 { | ||||
|                 self.length_counter = 64; | ||||
|             } | ||||
|  | ||||
|             // reload frequency timer | ||||
|             self.freq_timer = obscure::square::freq_timer_reload(self.freq_timer, self.frequency()); | ||||
|  | ||||
|             // reload envelope | ||||
|             self.period_timer = self.envelope.period(); | ||||
|  | ||||
|             // reload volume | ||||
|             self.current_volume = self.envelope.init_vol(); | ||||
|  | ||||
|             // Length behaviour during trigger event | ||||
|             if self.length_timer == 0 { | ||||
|                 self.length_timer = 64; | ||||
|             } | ||||
|  | ||||
|             if self.is_dac_enabled() { | ||||
|                 self.enabled = true; | ||||
|             } | ||||
|             self.enabled = self.is_dac_enabled(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -678,7 +718,7 @@ pub(crate) struct Channel3 { | ||||
|     wave_ram: [u8; WAVE_PATTERN_RAM_LEN], | ||||
|  | ||||
|     // Length Functionality | ||||
|     length_timer: u16, | ||||
|     length_counter: u16, | ||||
|  | ||||
|     freq_timer: u16, | ||||
|     offset: u8, | ||||
| @@ -724,7 +764,7 @@ impl Channel3 { | ||||
|     /// 0xFF1B | NR31 - Sound Length | ||||
|     pub(crate) fn set_len(&mut self, byte: u8) { | ||||
|         self.len = byte; | ||||
|         self.length_timer = 256 - self.len as u16; | ||||
|         self.length_counter = 256 - self.len as u16; | ||||
|     } | ||||
|  | ||||
|     /// 0xFF1C | NR32 - Channel 3 Volume | ||||
| @@ -756,18 +796,32 @@ impl Channel3 { | ||||
|     } | ||||
|  | ||||
|     /// 0xFF1E | NR34 - Channel 3 Frequency high | ||||
|     pub(crate) fn set_freq_hi(&mut self, byte: u8) { | ||||
|     pub(crate) fn set_freq_hi(&mut self, fs: &FrameSequencer, byte: u8) { | ||||
|         let prev_le = self.freq_hi.length_enable(); | ||||
|         self.freq_hi = byte.into(); | ||||
|  | ||||
|         if self.freq_hi.initial() { | ||||
|         obscure::nrx4::length_update( | ||||
|             &mut self.freq_hi, | ||||
|             fs, | ||||
|             &mut self.length_counter, | ||||
|             &mut self.enabled, | ||||
|             prev_le, | ||||
|         ); | ||||
|  | ||||
|         if self.freq_hi.trigger() { | ||||
|             self.enabled = true; | ||||
|  | ||||
|             // Length behaviour during trigger event | ||||
|             if self.length_timer == 0 { | ||||
|                 self.length_timer = 256; | ||||
|             if self.length_counter == 0 { | ||||
|                 self.length_counter = 256; | ||||
|             } | ||||
|  | ||||
|             if self.dac_enabled { | ||||
|                 self.enabled = true; | ||||
|             } | ||||
|             self.freq_timer = (2048 - self.frequency()) * 2; | ||||
|  | ||||
|             // reset wave channel's ptr into wave RAM | ||||
|             self.offset = 0; | ||||
|  | ||||
|             self.enabled = self.dac_enabled; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -826,7 +880,7 @@ pub(crate) struct Channel4 { | ||||
|     current_volume: u8, | ||||
|  | ||||
|     // Length Functionality | ||||
|     length_timer: u16, | ||||
|     length_counter: u16, | ||||
|  | ||||
|     /// Linear Feedback Shift Register (15-bit) | ||||
|     lf_shift: u16, | ||||
| @@ -840,7 +894,7 @@ impl Channel4 { | ||||
|     /// 0xFF20 | NR41 - Channel 4 Sound Length | ||||
|     pub(crate) fn set_len(&mut self, byte: u8) { | ||||
|         self.len = byte & 0x3F; | ||||
|         self.length_timer = 64 - self.len as u16; | ||||
|         self.length_counter = 64 - self.len as u16; | ||||
|     } | ||||
|  | ||||
|     /// 0xFF21 | NR42 - Channel 4 Volume Envelope | ||||
| @@ -873,25 +927,37 @@ impl Channel4 { | ||||
|     } | ||||
|  | ||||
|     /// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart | ||||
|     pub(crate) fn set_frequency(&mut self, byte: u8) { | ||||
|     pub(crate) fn set_frequency(&mut self, fs: &FrameSequencer, byte: u8) { | ||||
|         let prev_le = self.freq.length_enable(); | ||||
|         self.freq = byte.into(); | ||||
|  | ||||
|         if self.freq.initial() { | ||||
|             // Envelope behaviour during trigger event | ||||
|         obscure::nrx4::length_update( | ||||
|             &mut self.freq, | ||||
|             fs, | ||||
|             &mut self.length_counter, | ||||
|             &mut self.enabled, | ||||
|             prev_le, | ||||
|         ); | ||||
|  | ||||
|         if self.freq.trigger() { | ||||
|             self.enabled = true; | ||||
|  | ||||
|             if self.length_counter == 0 { | ||||
|                 self.length_counter = 64; | ||||
|             } | ||||
|  | ||||
|             // FIXME: Frequency Timer reloaded? | ||||
|  | ||||
|             // reload envelope | ||||
|             self.period_timer = self.envelope.period(); | ||||
|  | ||||
|             //reload volume | ||||
|             self.current_volume = self.envelope.init_vol(); | ||||
|  | ||||
|             // Length behaviour during trigger event | ||||
|             if self.length_timer == 0 { | ||||
|                 self.length_timer = 64; | ||||
|             } | ||||
|  | ||||
|             // LFSR behaviour during trigger event | ||||
|             // LFSR reset | ||||
|             self.lf_shift = 0x7FFF; | ||||
|  | ||||
|             if self.is_dac_enabled() { | ||||
|                 self.enabled = true; | ||||
|             } | ||||
|             self.enabled = self.is_dac_enabled(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -936,3 +1002,39 @@ impl Channel4 { | ||||
|         code << 4 | ||||
|     } | ||||
| } | ||||
|  | ||||
| mod obscure { | ||||
|     pub(super) mod square { | ||||
|         pub(crate) fn freq_timer_reload(freq_timer: u16, frequency: u16) -> u16 { | ||||
|             (freq_timer & 0x0003) | (((2048 - frequency) * 4) & 0xFFFC) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(super) mod nrx4 { | ||||
|         use super::super::{FrameSequencer, NRx4}; | ||||
|  | ||||
|         /// Implements the obscure behaviour when writing to NRX4 under certain | ||||
|         /// conditions | ||||
|         /// | ||||
|         /// # Arguments | ||||
|         /// * `freq_hi` - mutable reference to a channel's frequency high register | ||||
|         /// * `fs` - reference to the APU's frame sequencer | ||||
|         /// * `counter` - mutable reference to a channel's internal enabled flag | ||||
|         /// * `prev_le` - what length_enable was before NRx4 was written with a new value | ||||
|         pub(crate) fn length_update( | ||||
|             freq: &mut impl NRx4, | ||||
|             fs: &FrameSequencer, | ||||
|             counter: &mut u16, | ||||
|             enabled: &mut bool, | ||||
|             prev_le: bool, | ||||
|         ) { | ||||
|             if !fs.next_clocks_length() && !prev_le && freq.length_enable() && *counter != 0 { | ||||
|                 *counter -= 1; | ||||
|  | ||||
|                 if *counter == 0 && !freq.trigger() { | ||||
|                     *enabled = false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,33 +5,12 @@ pub(crate) const SAMPLE_RATE: u32 = 48000; // Hz | ||||
| const CHANNEL_COUNT: usize = 2; | ||||
| const BUFFER_CAPACITY: usize = 2048 * CHANNEL_COUNT; // # of samples * the # of channels | ||||
|  | ||||
| pub struct AudioSPSC<T> { | ||||
|     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 }, | ||||
|             SampleConsumer { inner: cons }, | ||||
|         ) | ||||
|     } | ||||
| pub fn init<T>() -> (SampleProducer<T>, SampleConsumer<T>) { | ||||
|     let (prod, cons) = RingBuffer::new(BUFFER_CAPACITY); | ||||
|     ( | ||||
|         SampleProducer { inner: prod }, | ||||
|         SampleConsumer { inner: cons }, | ||||
|     ) | ||||
| } | ||||
|  | ||||
| pub struct SampleProducer<T> { | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| use bitfield::bitfield; | ||||
|  | ||||
| pub(crate) trait NRx4 { | ||||
|     fn trigger(&self) -> bool; | ||||
|     fn length_enable(&self) -> bool; | ||||
|     fn set_length_enable(&mut self, value: bool); | ||||
| } | ||||
|  | ||||
| pub(crate) mod ch1 { | ||||
|     use super::bitfield; | ||||
|  | ||||
| @@ -103,7 +109,7 @@ pub(super) mod ch3 { | ||||
| } | ||||
|  | ||||
| pub(super) mod ch4 { | ||||
|     use super::bitfield; | ||||
|     use super::{bitfield, NRx4}; | ||||
|  | ||||
|     bitfield! { | ||||
|         pub struct PolynomialCounter(u8); | ||||
| @@ -177,17 +183,21 @@ pub(super) mod ch4 { | ||||
|     bitfield! { | ||||
|         pub struct Frequency(u8); | ||||
|         impl Debug; | ||||
|         _initial, _: 7; | ||||
|         _length_disable, _: 6; | ||||
|         _trigger, _: 7; | ||||
|         _length_enable, _set_length_enable: 6; | ||||
|     } | ||||
|  | ||||
|     impl Frequency { | ||||
|         pub(crate) fn length_disable(&self) -> bool { | ||||
|             self._length_disable() | ||||
|     impl NRx4 for Frequency { | ||||
|         fn trigger(&self) -> bool { | ||||
|             self._trigger() | ||||
|         } | ||||
|  | ||||
|         pub(crate) fn initial(&self) -> bool { | ||||
|             self._initial() | ||||
|         fn length_enable(&self) -> bool { | ||||
|             self._length_enable() | ||||
|         } | ||||
|  | ||||
|         fn set_length_enable(&mut self, value: bool) { | ||||
|             self._set_length_enable(value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -218,23 +228,27 @@ pub(super) mod ch4 { | ||||
| } | ||||
|  | ||||
| pub(super) mod common { | ||||
|     use super::bitfield; | ||||
|     use super::{bitfield, NRx4}; | ||||
|  | ||||
|     bitfield! { | ||||
|         pub struct FrequencyHigh(u8); | ||||
|         impl Debug; | ||||
|         _initial, _: 7; | ||||
|         _length_disable, _: 6; | ||||
|         _trigger, _: 7; | ||||
|         _length_enable, _set_length_enable: 6; | ||||
|         pub freq_bits, set_freq_bits: 2, 0; | ||||
|     } | ||||
|  | ||||
|     impl FrequencyHigh { | ||||
|         pub(crate) fn initial(&self) -> bool { | ||||
|             self._initial() | ||||
|     impl NRx4 for FrequencyHigh { | ||||
|         fn trigger(&self) -> bool { | ||||
|             self._trigger() | ||||
|         } | ||||
|  | ||||
|         pub(crate) fn length_disable(&self) -> bool { | ||||
|             self._length_disable() | ||||
|         fn length_enable(&self) -> bool { | ||||
|             self._length_enable() | ||||
|         } | ||||
|  | ||||
|         fn set_length_enable(&mut self, value: bool) { | ||||
|             self._set_length_enable(value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -557,6 +571,15 @@ pub(super) mod fs { | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         pub(crate) fn next_clocks_length(&self) -> bool { | ||||
|             use State::*; | ||||
|  | ||||
|             match self.state { | ||||
|                 Length | LengthAndSweep => true, | ||||
|                 Nothing | Envelope => false, | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pub(crate) fn state(&self) -> State { | ||||
|             self.state | ||||
|         } | ||||
| @@ -567,7 +590,7 @@ pub(super) mod fs { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[derive(Debug, Clone, Copy)] | ||||
|     #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
|     pub(crate) enum State { | ||||
|         Length, | ||||
|         Nothing, | ||||
|   | ||||
							
								
								
									
										103
									
								
								src/bus.rs
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								src/bus.rs
									
									
									
									
									
								
							| @@ -7,30 +7,29 @@ use crate::ppu::{Ppu, PpuMode}; | ||||
| use crate::serial::Serial; | ||||
| use crate::timer::Timer; | ||||
| use crate::work_ram::{VariableWorkRam, WorkRam}; | ||||
| use std::{fs::File, io::Read}; | ||||
|  | ||||
| const BOOT_ROM_SIZE: usize = 0x100; | ||||
| pub(crate) const BOOT_SIZE: usize = 0x100; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Bus { | ||||
|     boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long | ||||
|     cartridge: Option<Cartridge>, | ||||
|     pub(crate) ppu: Ppu, | ||||
|     boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long | ||||
|     cart: Option<Cartridge>, | ||||
|     ppu: Ppu, | ||||
|     work_ram: WorkRam, | ||||
|     var_ram: VariableWorkRam, | ||||
|     pub(crate) timer: Timer, | ||||
|     timer: Timer, | ||||
|     int: Interrupt, | ||||
|     pub(crate) apu: Apu, | ||||
|     apu: Apu, | ||||
|     high_ram: HighRam, | ||||
|     serial: Serial, | ||||
|     pub(crate) joypad: Joypad, | ||||
|     joypad: Joypad, | ||||
| } | ||||
|  | ||||
| impl Default for Bus { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             boot: None, | ||||
|             cartridge: None, | ||||
|             cart: None, | ||||
|             ppu: Default::default(), | ||||
|             work_ram: Default::default(), | ||||
|             var_ram: Default::default(), | ||||
| @@ -45,25 +44,19 @@ impl Default for Bus { | ||||
| } | ||||
|  | ||||
| impl Bus { | ||||
|     pub(crate) fn with_boot(path: &str) -> anyhow::Result<Self> { | ||||
|         let mut file = File::open(path)?; | ||||
|         let mut boot_rom = [0u8; 256]; | ||||
|  | ||||
|         file.read_exact(&mut boot_rom)?; | ||||
|  | ||||
|         Ok(Self { | ||||
|             boot: Some(boot_rom), | ||||
|     pub(crate) fn with_boot(rom: [u8; 256]) -> Self { | ||||
|         Self { | ||||
|             boot: Some(rom), | ||||
|             ..Default::default() | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> { | ||||
|         self.cartridge = Some(Cartridge::new(path)?); | ||||
|         Ok(()) | ||||
|     pub(crate) fn load_cart(&mut self, rom: Vec<u8>) { | ||||
|         self.cart = Some(Cartridge::new(rom)); | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn rom_title(&self) -> Option<&str> { | ||||
|         self.cartridge.as_ref()?.title() | ||||
|     pub(crate) fn cart_title(&self) -> Option<&str> { | ||||
|         self.cart.as_ref()?.title() | ||||
|     } | ||||
|  | ||||
|     #[allow(dead_code)] | ||||
| @@ -71,10 +64,12 @@ impl Bus { | ||||
|         self.boot.is_some() | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     pub(crate) fn clock(&mut self) { | ||||
|         self.tick(4); | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn tick(&mut self, limit: u8) { | ||||
|         for _ in 0..limit { | ||||
|             self.timer.tick(); | ||||
| @@ -90,10 +85,35 @@ impl Bus { | ||||
|             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 { | ||||
|     pub fn oam_read_byte(&self, addr: u16) -> u8 { | ||||
|     pub(crate) fn oam_read_byte(&self, addr: u16) -> u8 { | ||||
|         match addr { | ||||
|             0x0000..=0x7FFF => { | ||||
|                 // 16KB ROM bank 00 (ends at 0x3FFF) | ||||
| @@ -104,16 +124,16 @@ impl Bus { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 match self.cartridge.as_ref() { | ||||
|                 match self.cart.as_ref() { | ||||
|                     Some(cart) => cart.read_byte(addr), | ||||
|                     None => panic!("Tried to read from a non-existent cartridge"), | ||||
|                     None => 0xFF, | ||||
|                 } | ||||
|             } | ||||
|             0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM | ||||
|             0xA000..=0xBFFF => match self.cartridge.as_ref() { | ||||
|             0xA000..=0xBFFF => match self.cart.as_ref() { | ||||
|                 // 8KB External RAM | ||||
|                 Some(cart) => cart.read_byte(addr), | ||||
|                 None => panic!("Tried to read from a non-existent cartridge"), | ||||
|                 None => 0xFF, | ||||
|             }, | ||||
|             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 | ||||
| @@ -140,7 +160,7 @@ impl Bus { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn oam_write_byte(&mut self, addr: u16, byte: u8) { | ||||
|     pub(crate) fn oam_write_byte(&mut self, addr: u16, byte: u8) { | ||||
|         self.ppu.oam.write_byte(addr, byte); | ||||
|     } | ||||
| } | ||||
| @@ -157,9 +177,9 @@ impl BusIo for Bus { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 match self.cartridge.as_ref() { | ||||
|                 match self.cart.as_ref() { | ||||
|                     Some(cart) => cart.read_byte(addr), | ||||
|                     None => panic!("Tried to read from a non-existent cartridge"), | ||||
|                     None => 0xFF, | ||||
|                 } | ||||
|             } | ||||
|             0x8000..=0x9FFF => { | ||||
| @@ -169,10 +189,10 @@ impl BusIo for Bus { | ||||
|                     _ => self.ppu.read_byte(addr), | ||||
|                 } | ||||
|             } | ||||
|             0xA000..=0xBFFF => match self.cartridge.as_ref() { | ||||
|             0xA000..=0xBFFF => match self.cart.as_ref() { | ||||
|                 // 8KB External RAM | ||||
|                 Some(cart) => cart.read_byte(addr), | ||||
|                 None => panic!("Tried to read from a non-existent cartridge"), | ||||
|                 None => 0xFF, | ||||
|             }, | ||||
|             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 | ||||
| @@ -240,8 +260,9 @@ impl BusIo for Bus { | ||||
|                     0x49 => self.ppu.monochrome.obj_palette_1.into(), | ||||
|                     0x4A => self.ppu.pos.window_y, | ||||
|                     0x4B => self.ppu.pos.window_x, | ||||
|                     0x4F => 0xFF, // CGB VRAM Bank Select | ||||
|                     _ => { | ||||
|                         eprintln!("Read 0xFF from unused IO register {:#06X}.", addr); | ||||
|                         tracing::warn!("Attempted read from {:#06X} on IO", addr); | ||||
|                         0xFF | ||||
|                     } | ||||
|                 } | ||||
| @@ -262,9 +283,8 @@ impl BusIo for Bus { | ||||
|             0x0000..=0x7FFF => { | ||||
|                 // 16KB ROM bank 00 (ends at 0x3FFF) | ||||
|                 // and 16KB ROM Bank 01 -> NN (switchable via MB) | ||||
|                 match self.cartridge.as_mut() { | ||||
|                     Some(cart) => cart.write_byte(addr, byte), | ||||
|                     None => panic!("Tried to write into non-existent cartridge"), | ||||
|                 if let Some(cart) = self.cart.as_mut() { | ||||
|                     cart.write_byte(addr, byte); | ||||
|                 } | ||||
|             } | ||||
|             0x8000..=0x9FFF => { | ||||
| @@ -276,9 +296,8 @@ impl BusIo for Bus { | ||||
|             } | ||||
|             0xA000..=0xBFFF => { | ||||
|                 // 8KB External RAM | ||||
|                 match self.cartridge.as_mut() { | ||||
|                     Some(cart) => cart.write_byte(addr, byte), | ||||
|                     None => panic!("Tried to write into non-existent cartridge"), | ||||
|                 if let Some(cart) = self.cart.as_mut() { | ||||
|                     cart.write_byte(addr, byte); | ||||
|                 } | ||||
|             } | ||||
|             0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0 | ||||
| @@ -352,13 +371,15 @@ impl BusIo for Bus { | ||||
|                     0x4A => self.ppu.pos.window_y = byte, | ||||
|                     0x4B => self.ppu.pos.window_x = byte, | ||||
|                     0x4D => {} // CGB Specific Register | ||||
|                     0x4F => {} // CGB VRAM Bank Select | ||||
|                     0x50 => { | ||||
|                         // Disable Boot ROM | ||||
|                         if byte != 0 { | ||||
|                             self.boot = None; | ||||
|                         } | ||||
|                     } | ||||
|                     _ => eprintln!("Wrote {:#04X} to unused IO register {:#06X}.", byte, addr), | ||||
|                     0x70 => {} // CGB WRAM Bank Select | ||||
|                     _ => tracing::warn!("Attempted write of {:#04X} to {:#06X} on IO", byte, addr), | ||||
|                 }; | ||||
|             } | ||||
|             0xFF80..=0xFFFE => { | ||||
|   | ||||
							
								
								
									
										196
									
								
								src/cartridge.rs
									
									
									
									
									
								
							
							
						
						
									
										196
									
								
								src/cartridge.rs
									
									
									
									
									
								
							| @@ -1,13 +1,10 @@ | ||||
| use std::fs::File; | ||||
| use std::io::{self, Read}; | ||||
| use std::path::Path; | ||||
|  | ||||
| use crate::bus::BusIo; | ||||
|  | ||||
| const RAM_SIZE_ADDRESS: usize = 0x0149; | ||||
| const ROM_SIZE_ADDRESS: usize = 0x0148; | ||||
| const MBC_TYPE_ADDRESS: usize = 0x0147; | ||||
| const ROM_TITLE_RANGE: std::ops::RangeInclusive<usize> = 0x0134..=0x0143; | ||||
| const ROM_TITLE_START: usize = 0x134; | ||||
| const ROM_TITLE_MAX_SIZE: usize = 16; | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| pub(crate) struct Cartridge { | ||||
| @@ -17,19 +14,23 @@ pub(crate) struct Cartridge { | ||||
| } | ||||
|  | ||||
| impl Cartridge { | ||||
|     pub(crate) fn new<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<Self> { | ||||
|         let mut memory = vec![]; | ||||
|         let mut rom = File::open(path)?; | ||||
|         rom.read_to_end(&mut memory)?; | ||||
|  | ||||
|     pub(crate) fn new(memory: Vec<u8>) -> Self { | ||||
|         let title = Self::find_title(&memory); | ||||
|         eprintln!("Cartridge Title: {:?}", title); | ||||
|         tracing::info!("Title: {:?}", title); | ||||
|  | ||||
|         Ok(Self { | ||||
|         Self { | ||||
|             mbc: Self::detect_mbc(&memory), | ||||
|             title, | ||||
|             memory, | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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> { | ||||
| @@ -39,31 +40,35 @@ impl Cartridge { | ||||
|         let ram_cap = ram_size.capacity(); | ||||
|         let rom_cap = rom_size.capacity(); | ||||
|  | ||||
|         eprintln!("Cartridge Ram Size: {} bytes", ram_cap); | ||||
|         eprintln!("Cartridge ROM Size: {} bytes", rom_size.capacity()); | ||||
|         eprintln!("MBC Type: {:?}", mbc_kind); | ||||
|         tracing::info!("RAM size: {} bytes", ram_cap); | ||||
|         tracing::info!("ROM size: {} bytes", rom_size.capacity()); | ||||
|         tracing::info!("MBC kind: {:?}", mbc_kind); | ||||
|  | ||||
|         match mbc_kind { | ||||
|             MBCKind::None => Box::new(NoMBC), | ||||
|             MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)), | ||||
|             MBCKind::MBC1WithBattery => Box::new(MBC1::new(ram_size, rom_size)), // TODO: Implement Saving | ||||
|             MBCKind::MBC1WithBattery => Box::new(MBC1::with_battery(ram_size, rom_size)), | ||||
|             MBCKind::MBC2 => Box::new(MBC2::new(rom_cap)), | ||||
|             MBCKind::MBC2WithBattery => Box::new(MBC2::new(rom_cap)), // TODO: Implement Saving | ||||
|             MBCKind::MBC2WithBattery => Box::new(MBC2::with_battery(rom_cap)), | ||||
|             MBCKind::MBC3 => Box::new(MBC3::new(ram_cap)), | ||||
|             MBCKind::MBC3WithBattery => Box::new(MBC3::new(ram_cap)), // TODO: Implement Saving | ||||
|             MBCKind::MBC3WithBattery => Box::new(MBC3::with_battery(ram_cap)), | ||||
|             MBCKind::MBC5 => Box::new(MBC5::new(ram_cap, rom_cap)), | ||||
|             MBCKind::MBC5WithBattery => Box::new(MBC5::new(ram_cap, rom_cap)), // TDO: Implement Saving | ||||
|             MBCKind::MBC5WithBattery => Box::new(MBC5::with_battery(ram_cap, rom_cap)), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn find_title(memory: &[u8]) -> Option<String> { | ||||
|         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()); | ||||
|         let title_bytes = &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE)]; | ||||
|  | ||||
|         match trimmed { | ||||
|         // ASCII Byte array purposely does not have null terminator | ||||
|         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(_) => trimmed.map(String::from), | ||||
|             Some(title) => Some(String::from(title)), | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -125,6 +130,8 @@ struct MBC1 { | ||||
|     memory: Vec<u8>, | ||||
|     rom_size: RomSize, | ||||
|     mem_enabled: bool, | ||||
|  | ||||
|     has_battery: bool, | ||||
| } | ||||
|  | ||||
| impl MBC1 { | ||||
| @@ -137,6 +144,20 @@ impl MBC1 { | ||||
|             ram_bank: Default::default(), | ||||
|             mode: 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, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -205,6 +226,21 @@ 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 { | ||||
|     fn handle_read(&self, addr: u16) -> MBCResult { | ||||
|         use MBCResult::*; | ||||
| @@ -264,6 +300,8 @@ struct MBC3 { | ||||
|  | ||||
|     // RTC Data Latch Previous Write | ||||
|     prev_latch_write: Option<u8>, | ||||
|  | ||||
|     has_battery: bool, | ||||
| } | ||||
|  | ||||
| impl MBC3 { | ||||
| @@ -275,6 +313,34 @@ impl MBC3 { | ||||
|             devs_enabled: Default::default(), | ||||
|             mapped: 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -350,9 +416,10 @@ struct MBC5 { | ||||
|     ram_bank: u8, | ||||
|  | ||||
|     rom_cap: usize, | ||||
|  | ||||
|     memory: Vec<u8>, | ||||
|     mem_enabled: bool, | ||||
|  | ||||
|     has_battery: bool, | ||||
| } | ||||
|  | ||||
| impl MBC5 { | ||||
| @@ -363,6 +430,18 @@ impl MBC5 { | ||||
|             rom_cap, | ||||
|             ram_bank: 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, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -371,6 +450,21 @@ 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 { | ||||
|     fn handle_read(&self, addr: u16) -> MBCResult { | ||||
|         use MBCResult::*; | ||||
| @@ -409,6 +503,7 @@ struct MBC2 { | ||||
|     mem_enabled: bool, | ||||
|  | ||||
|     rom_cap: usize, | ||||
|     has_battery: bool, | ||||
| } | ||||
|  | ||||
| impl MBC2 { | ||||
| @@ -418,8 +513,19 @@ impl MBC2 { | ||||
|         Self { | ||||
|             rom_bank: 0x01, | ||||
|             memory: Box::new([0; Self::RAM_SIZE]), | ||||
|             mem_enabled: Default::default(), | ||||
|             rom_cap, | ||||
|             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, | ||||
|             mem_enabled: Default::default(), | ||||
|             has_battery: true, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -428,6 +534,21 @@ 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 { | ||||
|     fn handle_read(&self, addr: u16) -> MBCResult { | ||||
|         use MBCResult::*; | ||||
| @@ -465,17 +586,27 @@ impl MBCIo for MBC2 { | ||||
| #[derive(Debug)] | ||||
| 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 { | ||||
|     fn handle_read(&self, addr: u16) -> MBCResult { | ||||
|         MBCResult::Address(addr as usize) | ||||
|     } | ||||
|  | ||||
|     fn handle_write(&mut self, _addr: u16, _byte: u8) { | ||||
|         // eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); | ||||
|     fn handle_write(&mut self, _: u16, byte: u8) { | ||||
|         tracing::warn!("Attempted write of {:#04X} to cartridge w/out MBC", byte); | ||||
|     } | ||||
| } | ||||
|  | ||||
| trait MBCIo { | ||||
| trait MBCIo: Savable { | ||||
|     fn handle_read(&self, addr: u16) -> MBCResult; | ||||
|     fn handle_write(&mut self, addr: u16, byte: u8); | ||||
| } | ||||
| @@ -615,3 +746,8 @@ impl Default for Box<dyn MBCIo> { | ||||
|         Box::new(NoMBC) | ||||
|     } | ||||
| } | ||||
|  | ||||
| trait Savable { | ||||
|     fn ext_ram(&self) -> Option<&[u8]>; | ||||
|     fn write_ext_ram(&mut self, memory: Vec<u8>); | ||||
| } | ||||
|   | ||||
							
								
								
									
										62
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								src/cpu.rs
									
									
									
									
									
								
							| @@ -1,16 +1,13 @@ | ||||
| use crate::apu::Apu; | ||||
| use crate::bus::{Bus, BusIo}; | ||||
| use crate::bus::{Bus, BusIo, BOOT_SIZE}; | ||||
| use crate::instruction::Instruction; | ||||
| use crate::interrupt::{InterruptEnable, InterruptFlag}; | ||||
| use crate::joypad::Joypad; | ||||
| use crate::ppu::Ppu; | ||||
| use crate::Cycle; | ||||
| use bitfield::bitfield; | ||||
| use std::fmt::{Display, Formatter, Result as FmtResult}; | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| pub struct Cpu { | ||||
|     pub bus: Bus, | ||||
|     pub(crate) bus: Bus, | ||||
|     reg: Registers, | ||||
|     flags: Flags, | ||||
|     ime: ImeState, | ||||
| @@ -18,7 +15,8 @@ pub struct Cpu { | ||||
| } | ||||
|  | ||||
| impl Cpu { | ||||
|     pub fn new() -> Self { | ||||
|     #[allow(dead_code)] | ||||
|     pub(crate) fn without_boot() -> Self { | ||||
|         Self { | ||||
|             reg: Registers { | ||||
|                 a: 0x01, | ||||
| @@ -36,11 +34,11 @@ impl Cpu { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn boot_new(path: &str) -> anyhow::Result<Self> { | ||||
|         Ok(Self { | ||||
|             bus: Bus::with_boot(path)?, | ||||
|     pub(crate) fn with_boot(rom: [u8; BOOT_SIZE]) -> Self { | ||||
|         Self { | ||||
|             bus: Bus::with_boot(rom), | ||||
|             ..Default::default() | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn ime(&self) -> ImeState { | ||||
| @@ -60,10 +58,7 @@ impl Cpu { | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn is_halted(&self) -> bool { | ||||
|         match self.state { | ||||
|             State::Halt(_) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|         matches!(self.state, State::Halt(_)) | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn halt_kind(&self) -> Option<HaltKind> { | ||||
| @@ -72,14 +67,6 @@ impl Cpu { | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> { | ||||
|         self.bus.load_cartridge(path) | ||||
|     } | ||||
|  | ||||
|     pub fn rom_title(&self) -> Option<&str> { | ||||
|         self.bus.rom_title() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Cpu { | ||||
| @@ -116,7 +103,7 @@ impl Cpu { | ||||
|     /// routine. | ||||
|     /// | ||||
|     /// Handle HALT and interrupts. | ||||
|     pub fn step(&mut self) -> Cycle { | ||||
|     pub(crate) fn step(&mut self) -> Cycle { | ||||
|         // Log instructions | ||||
|         // if self.reg.pc > 0xFF { | ||||
|         //     let out = std::io::stdout(); | ||||
| @@ -131,13 +118,10 @@ impl Cpu { | ||||
|             use HaltKind::*; | ||||
|  | ||||
|             self.bus.clock(); | ||||
|  | ||||
|             let elapsed = match kind { | ||||
|             return match kind { | ||||
|                 ImeEnabled | NonePending => 4, | ||||
|                 SomePending => todo!("Implement HALT bug"), | ||||
|             }; | ||||
|  | ||||
|             return elapsed; | ||||
|         } | ||||
|  | ||||
|         let opcode = self.fetch(); | ||||
| @@ -146,11 +130,11 @@ impl Cpu { | ||||
|         self.handle_ei(); | ||||
|  | ||||
|         // For use in Blargg's Test ROMs | ||||
|         if self.read_byte(0xFF02) == 0x81 { | ||||
|             let c = self.read_byte(0xFF01) as char; | ||||
|             self.write_byte(0xFF02, 0x00); | ||||
|             eprint!("{}", c); | ||||
|         } | ||||
|         // if self.read_byte(0xFF02) == 0x81 { | ||||
|         //     let c = self.read_byte(0xFF01) as char; | ||||
|         //     self.write_byte(0xFF02, 0x00); | ||||
|         //     eprint!("{}", c); | ||||
|         // } | ||||
|  | ||||
|         elapsed | ||||
|     } | ||||
| @@ -167,16 +151,14 @@ impl BusIo for Cpu { | ||||
| } | ||||
|  | ||||
| impl Cpu { | ||||
|     pub fn ppu(&mut self) -> &Ppu { | ||||
|         &self.bus.ppu | ||||
|     #[inline] | ||||
|     pub(crate) fn bus(&self) -> &Bus { | ||||
|         &self.bus | ||||
|     } | ||||
|  | ||||
|     pub fn apu_mut(&mut self) -> &mut Apu { | ||||
|         &mut self.bus.apu | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn joypad_mut(&mut self) -> &mut Joypad { | ||||
|         &mut self.bus.joypad | ||||
|     #[inline] | ||||
|     pub(crate) fn bus_mut(&mut self) -> &mut Bus { | ||||
|         &mut self.bus | ||||
|     } | ||||
|  | ||||
|     fn handle_ei(&mut self) { | ||||
|   | ||||
							
								
								
									
										170
									
								
								src/emu.rs
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								src/emu.rs
									
									
									
									
									
								
							| @@ -1,9 +1,13 @@ | ||||
| use crate::cpu::Cpu as SM83; | ||||
| use crate::joypad; | ||||
| use crate::ppu::Ppu; | ||||
| use crate::Cycle; | ||||
| use anyhow::Result; | ||||
| use crate::apu::gen::SampleProducer; | ||||
| use crate::bus::BOOT_SIZE; | ||||
| use crate::cpu::Cpu; | ||||
| use crate::joypad::{self, Joypad}; | ||||
| use crate::{Cycle, GB_HEIGHT, GB_WIDTH}; | ||||
| use clap::crate_name; | ||||
| use gilrs::Gilrs; | ||||
| use std::fs::File; | ||||
| use std::io::{Read, Write}; | ||||
| use std::path::{Path, PathBuf}; | ||||
| use std::time::Duration; | ||||
| use winit_input_helper::WinitInputHelper; | ||||
|  | ||||
| @@ -12,56 +16,130 @@ pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines | ||||
| pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz | ||||
| const DEFAULT_TITLE: &str = "DMG-01 Emulator"; | ||||
|  | ||||
| pub fn init(boot_path: Option<&str>, rom_path: &str) -> Result<SM83> { | ||||
|     let mut cpu = match boot_path { | ||||
|         Some(path) => SM83::boot_new(path)?, | ||||
|         None => SM83::new(), | ||||
|     }; | ||||
|  | ||||
|     eprintln!("Initialized GB Emulator"); | ||||
|  | ||||
|     cpu.load_cartridge(rom_path)?; | ||||
|     Ok(cpu) | ||||
| } | ||||
|  | ||||
| pub fn rom_title(game_boy: &SM83) -> &str { | ||||
|     game_boy.rom_title().unwrap_or(DEFAULT_TITLE) | ||||
| } | ||||
|  | ||||
| pub fn run( | ||||
|     game_boy: &mut SM83, | ||||
|     gamepad: &mut Gilrs, | ||||
|     input: &WinitInputHelper, | ||||
|     target: Cycle, | ||||
| ) -> Cycle { | ||||
| pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper) -> Cycle { | ||||
|     let mut elapsed = 0; | ||||
|  | ||||
|     if let Some(event) = gamepad.next_event() { | ||||
|         joypad::handle_gamepad_input(game_boy.joypad_mut(), event); | ||||
|         joypad::handle_gamepad_input(emu.joyp_mut(), event); | ||||
|     } | ||||
|     joypad::handle_keyboard_input(game_boy.joypad_mut(), input); | ||||
|     joypad::handle_keyboard_input(emu.joyp_mut(), key); | ||||
|  | ||||
|     while elapsed < target { | ||||
|         elapsed += game_boy.step(); | ||||
|     } | ||||
|  | ||||
|     elapsed | ||||
| } | ||||
|  | ||||
| pub fn run_frame(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle { | ||||
|     let mut elapsed = 0; | ||||
|  | ||||
|     if let Some(event) = gamepad.next_event() { | ||||
|         joypad::handle_gamepad_input(game_boy.joypad_mut(), event); | ||||
|     } | ||||
|     joypad::handle_keyboard_input(game_boy.joypad_mut(), input); | ||||
|     while elapsed < CYCLES_IN_FRAME { | ||||
|         elapsed += game_boy.step(); | ||||
|         elapsed += emu.step(); | ||||
|     } | ||||
|  | ||||
|     elapsed | ||||
| } | ||||
|  | ||||
| pub fn draw(ppu: &Ppu, frame: &mut [u8]) { | ||||
|     ppu.copy_to_gui(frame); | ||||
| pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) { | ||||
|     buf.copy_from_slice(emu.cpu.bus().ppu().frame_buf()); | ||||
| } | ||||
|  | ||||
| pub struct Emulator { | ||||
|     cpu: Cpu, | ||||
|     timestamp: Cycle, | ||||
| } | ||||
|  | ||||
| impl Default for Emulator { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Emulator { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             cpu: Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")), | ||||
|             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 { | ||||
|         let cycles = self.cpu.step(); | ||||
|         self.timestamp += cycles; | ||||
|         cycles | ||||
|     } | ||||
|  | ||||
|     pub fn read_game_rom<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> { | ||||
|         self.load_rom(std::fs::read(path.as_ref())?); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn load_rom(&mut self, rom: Vec<u8>) { | ||||
|         self.cpu.bus_mut().load_cart(rom); | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     fn joyp_mut(&mut self) -> &mut Joypad { | ||||
|         self.cpu.bus_mut().joyp_mut() | ||||
|     } | ||||
|  | ||||
|     pub fn set_prod(&mut self, prod: SampleProducer<f32>) { | ||||
|         self.cpu.bus_mut().apu_mut().attach_producer(prod) | ||||
|     } | ||||
|  | ||||
|     pub fn title(&self) -> &str { | ||||
|         self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE) | ||||
|     } | ||||
|  | ||||
|     pub fn try_write_sav(&self) -> std::io::Result<()> { | ||||
|         if let Some(ext_ram) = self.cpu.bus().cart().map(|c| c.ext_ram()).flatten() { | ||||
|             if let Some(title) = self.cpu.bus().cart_title() { | ||||
|                 let mut save_path = Self::data_path().unwrap_or_else(|| PathBuf::from(".")); | ||||
|                 save_path.push(title); | ||||
|                 save_path.set_extension("sav"); | ||||
|  | ||||
|                 let mut file = File::create(save_path)?; | ||||
|                 file.write_all(ext_ram)?; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn try_load_sav(&mut self) -> std::io::Result<()> { | ||||
|         if let Some(cart) = self.cpu.bus_mut().cart_mut() { | ||||
|             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) { | ||||
|                     tracing::info!("Load {:?}", save_path); | ||||
|  | ||||
|                     let mut memory = Vec::new(); | ||||
|                     file.read_to_end(&mut memory)?; | ||||
|                     cart.write_ext_ram(memory); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn data_path() -> Option<PathBuf> { | ||||
|         match directories_next::ProjectDirs::from("dev", "musuka", crate_name!()) { | ||||
|             Some(dirs) => { | ||||
|                 let data_local = dirs.data_local_dir(); | ||||
|                 std::fs::create_dir_all(data_local).ok()?; | ||||
|                 Some(data_local.to_path_buf()) | ||||
|             } | ||||
|             None => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -116,7 +116,7 @@ impl std::fmt::Debug for Instruction { | ||||
| impl Instruction { | ||||
|     pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle { | ||||
|         match instruction { | ||||
|             Instruction::NOP => (4), | ||||
|             Instruction::NOP => 4, | ||||
|             Instruction::LD(target, src) => match (target, src) { | ||||
|                 (LDTarget::IndirectImmediateWord, LDSource::SP) => { | ||||
|                     // LD (u16), SP | Store stack pointer in byte at 16-bit register | ||||
| @@ -334,7 +334,6 @@ impl Instruction { | ||||
|             Instruction::ADD(target, src) => match (target, src) { | ||||
|                 (AddTarget::HL, AddSource::Group1(pair)) => { | ||||
|                     // ADD HL, r16 | Add 16-bit register to HL | ||||
|                     // FIXME: Memory Timings are not properly emulated for this instruction | ||||
|                     use Group1RegisterPair::*; | ||||
|                     let mut flags: Flags = *cpu.flags(); | ||||
|  | ||||
| @@ -342,10 +341,11 @@ impl Instruction { | ||||
|                         BC | DE | HL | SP => { | ||||
|                             let left = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = cpu.register_pair(pair.as_register_pair()); | ||||
|                             cpu.set_register_pair( | ||||
|                                 RegisterPair::HL, | ||||
|                                 Self::add_u16(left, right, &mut flags), | ||||
|                             ); | ||||
|                             let result = Self::add_u16(left, right, &mut flags); | ||||
|  | ||||
|                             cpu.set_register(CpuRegister::L, result as u8); | ||||
|                             cpu.bus.clock(); | ||||
|                             cpu.set_register(CpuRegister::H, (result >> 8) as u8); | ||||
|                         } | ||||
|                     } | ||||
|                     cpu.set_flags(flags); | ||||
| @@ -361,12 +361,12 @@ impl Instruction { | ||||
|                     let (cycles, sum) = match reg { | ||||
|                         B | C | D | E | H | L | A => { | ||||
|                             let right = cpu.register(reg.cpu_register()); | ||||
|                             ((4), Self::add(left, right, &mut flags)) | ||||
|                             (4, Self::add(left, right, &mut flags)) | ||||
|                         } | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             ((8), Self::add(left, right, &mut flags)) | ||||
|                             (8, Self::add(left, right, &mut flags)) | ||||
|                         } | ||||
|                     }; | ||||
|  | ||||
| @@ -376,12 +376,15 @@ impl Instruction { | ||||
|                 } | ||||
|                 (AddTarget::SP, AddSource::ImmediateSignedByte) => { | ||||
|                     // 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 left = cpu.register_pair(RegisterPair::SP); | ||||
|                     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.bus.clock(); | ||||
|  | ||||
|                     cpu.set_flags(flags); | ||||
|                     16 | ||||
|                 } | ||||
| @@ -423,7 +426,6 @@ impl Instruction { | ||||
|                     AllRegisters::Group1(pair) => { | ||||
|                         // INC r16 | Increment 16-bit register | ||||
|                         // Note: No flags are set with this version of the INC instruction | ||||
|                         // FIXME: Memory Timings are not properly emulated for this instruction | ||||
|                         use Group1RegisterPair::*; | ||||
|  | ||||
|                         match pair { | ||||
| @@ -431,6 +433,7 @@ impl Instruction { | ||||
|                                 let pair = pair.as_register_pair(); | ||||
|                                 let left = cpu.register_pair(pair); | ||||
|                                 cpu.set_register_pair(pair, left.wrapping_add(1)); | ||||
|                                 cpu.bus.clock(); // internal | ||||
|                             } | ||||
|                         } | ||||
|                         8 | ||||
| @@ -462,7 +465,6 @@ impl Instruction { | ||||
|                     } | ||||
|                     AllRegisters::Group1(pair) => { | ||||
|                         // DEC r16 | Decrement Register Pair | ||||
|                         // FIXME: Memory Timings are not properly emulated for this instruction | ||||
|                         use Group1RegisterPair::*; | ||||
|  | ||||
|                         match pair { | ||||
| @@ -470,6 +472,7 @@ impl Instruction { | ||||
|                                 let pair = pair.as_register_pair(); | ||||
|                                 let left = cpu.register_pair(pair); | ||||
|                                 cpu.set_register_pair(pair, left.wrapping_sub(1)); | ||||
|                                 cpu.bus.clock(); // internal | ||||
|                             } | ||||
|                         }; | ||||
|                         8 | ||||
| @@ -610,13 +613,13 @@ impl Instruction { | ||||
|                         B | C | D | E | H | L | A => { | ||||
|                             let right = cpu.register(reg.cpu_register()); | ||||
|                             let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); | ||||
|                             ((4), sum) | ||||
|                             (4, sum) | ||||
|                         } | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); | ||||
|                             ((8), sum) | ||||
|                             (8, sum) | ||||
|                         } | ||||
|                     }; | ||||
|                     cpu.set_register(CpuRegister::A, sum); | ||||
| @@ -646,12 +649,12 @@ impl Instruction { | ||||
|                     let (cycles, diff) = match reg { | ||||
|                         B | C | D | E | H | L | A => { | ||||
|                             let right = cpu.register(reg.cpu_register()); | ||||
|                             ((4), Self::sub(left, right, &mut flags)) | ||||
|                             (4, Self::sub(left, right, &mut flags)) | ||||
|                         } | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             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); | ||||
| @@ -681,13 +684,13 @@ impl Instruction { | ||||
|                         B | C | D | E | H | L | A => { | ||||
|                             let right = cpu.register(reg.cpu_register()); | ||||
|                             let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); | ||||
|                             ((4), diff) | ||||
|                             (4, diff) | ||||
|                         } | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); | ||||
|                             ((8), diff) | ||||
|                             (8, diff) | ||||
|                         } | ||||
|                     }; | ||||
|                     cpu.set_register(CpuRegister::A, diff); | ||||
| @@ -717,7 +720,7 @@ impl Instruction { | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             ((8), left & right) | ||||
|                             (8, left & right) | ||||
|                         } | ||||
|                     }; | ||||
|                     cpu.set_register(CpuRegister::A, acc); | ||||
| @@ -743,7 +746,7 @@ impl Instruction { | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             ((8), left ^ right) | ||||
|                             (8, left ^ right) | ||||
|                         } | ||||
|                     }; | ||||
|                     cpu.set_register(CpuRegister::A, acc); | ||||
| @@ -769,7 +772,7 @@ impl Instruction { | ||||
|                         IndirectHL => { | ||||
|                             let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                             let right = Self::read_byte(&mut cpu.bus, addr); | ||||
|                             ((8), left | right) | ||||
|                             (8, left | right) | ||||
|                         } | ||||
|                     }; | ||||
|                     cpu.set_register(CpuRegister::A, acc); | ||||
| @@ -1053,14 +1056,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let rotated = byte.rotate_left(1); | ||||
|                         cpu.set_register(reg, rotated); | ||||
|                         ((8), byte >> 7, rotated) | ||||
|                         (8, byte >> 7, rotated) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let rotated = byte.rotate_left(1); | ||||
|                         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); | ||||
| @@ -1076,14 +1079,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let rotated = byte.rotate_right(1); | ||||
|                         cpu.set_register(reg, rotated); | ||||
|                         ((8), byte & 0x01, rotated) | ||||
|                         (8, byte & 0x01, rotated) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let rotated = byte.rotate_right(1); | ||||
|                         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); | ||||
| @@ -1101,14 +1104,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); | ||||
|                         cpu.set_register(reg, rotated); | ||||
|                         ((8), rotated, carry) | ||||
|                         (8, rotated, carry) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); | ||||
|                         Self::write_byte(&mut cpu.bus, addr, rotated); | ||||
|                         ((16), rotated, carry) | ||||
|                         (16, rotated, carry) | ||||
|                     } | ||||
|                 }; | ||||
|                 cpu.update_flags(rotated == 0, false, false, carry); | ||||
| @@ -1126,14 +1129,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); | ||||
|                         cpu.set_register(reg, rotated); | ||||
|                         ((8), rotated, carry) | ||||
|                         (8, rotated, carry) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); | ||||
|                         Self::write_byte(&mut cpu.bus, addr, rotated); | ||||
|                         ((16), rotated, carry) | ||||
|                         (16, rotated, carry) | ||||
|                     } | ||||
|                 }; | ||||
|                 cpu.update_flags(rotated == 0, false, false, carry); | ||||
| @@ -1149,14 +1152,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let shifted = byte << 1; | ||||
|                         cpu.set_register(reg, shifted); | ||||
|                         ((8), (byte >> 7) & 0x01, shifted) | ||||
|                         (8, (byte >> 7) & 0x01, shifted) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let shifted = byte << 1; | ||||
|                         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); | ||||
| @@ -1172,14 +1175,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; | ||||
|                         cpu.set_register(reg, shifted); | ||||
|                         ((8), byte & 0x01, shifted) | ||||
|                         (8, byte & 0x01, shifted) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; | ||||
|                         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); | ||||
| @@ -1194,14 +1197,14 @@ impl Instruction { | ||||
|                         let reg = reg.cpu_register(); | ||||
|                         let swapped = Self::swap_bits(cpu.register(reg)); | ||||
|                         cpu.set_register(reg, swapped); | ||||
|                         ((8), swapped) | ||||
|                         (8, swapped) | ||||
|                     } | ||||
|  | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr)); | ||||
|                         Self::write_byte(&mut cpu.bus, addr, swapped); | ||||
|                         ((16), swapped) | ||||
|                         (16, swapped) | ||||
|                     } | ||||
|                 }; | ||||
|                 cpu.update_flags(swapped == 0, false, false, false); | ||||
| @@ -1217,14 +1220,14 @@ impl Instruction { | ||||
|                         let byte = cpu.register(reg); | ||||
|                         let shifted = byte >> 1; | ||||
|                         cpu.set_register(reg, shifted); | ||||
|                         ((8), byte & 0x01, shifted) | ||||
|                         (8, byte & 0x01, shifted) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         let shifted = byte >> 1; | ||||
|                         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); | ||||
| @@ -1239,12 +1242,12 @@ impl Instruction { | ||||
|                     B | C | D | E | H | L | A => { | ||||
|                         let reg = reg.cpu_register(); | ||||
|                         let byte = cpu.register(reg); | ||||
|                         ((8), ((byte >> bit) & 0x01) == 0x01) | ||||
|                         (8, ((byte >> bit) & 0x01) == 0x01) | ||||
|                     } | ||||
|                     IndirectHL => { | ||||
|                         let addr = cpu.register_pair(RegisterPair::HL); | ||||
|                         let byte = Self::read_byte(&mut cpu.bus, addr); | ||||
|                         ((12), ((byte >> bit) & 0x01) == 0x01) | ||||
|                         (12, ((byte >> bit) & 0x01) == 0x01) | ||||
|                     } | ||||
|                 }; | ||||
|                 flags.set_z(!is_set); | ||||
| @@ -1893,7 +1896,7 @@ mod table { | ||||
|     } | ||||
|  | ||||
|     impl Group1RegisterPair { | ||||
|         pub fn as_register_pair(&self) -> RegisterPair { | ||||
|         pub(crate) fn as_register_pair(&self) -> RegisterPair { | ||||
|             use Group1RegisterPair::*; | ||||
|  | ||||
|             match self { | ||||
| @@ -1927,7 +1930,7 @@ mod table { | ||||
|     } | ||||
|  | ||||
|     impl Group2RegisterPair { | ||||
|         pub fn as_register_pair(&self) -> RegisterPair { | ||||
|         pub(crate) fn as_register_pair(&self) -> RegisterPair { | ||||
|             use Group2RegisterPair::*; | ||||
|  | ||||
|             match self { | ||||
| @@ -1961,7 +1964,7 @@ mod table { | ||||
|     } | ||||
|  | ||||
|     impl Group3RegisterPair { | ||||
|         pub fn as_register_pair(&self) -> RegisterPair { | ||||
|         pub(crate) fn as_register_pair(&self) -> RegisterPair { | ||||
|             use Group3RegisterPair::*; | ||||
|  | ||||
|             match self { | ||||
| @@ -2003,7 +2006,7 @@ mod table { | ||||
|     } | ||||
|  | ||||
|     impl Register { | ||||
|         pub fn cpu_register(&self) -> CpuRegister { | ||||
|         pub(crate) fn cpu_register(&self) -> CpuRegister { | ||||
|             use Register::*; | ||||
|  | ||||
|             match self { | ||||
|   | ||||
| @@ -110,7 +110,8 @@ impl ButtonEvent { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) { | ||||
| #[inline] | ||||
| pub(crate) fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) { | ||||
|     use winit::event::VirtualKeyCode; | ||||
|  | ||||
|     // TODO: What do I have to do to get a match statement here? | ||||
| @@ -146,17 +147,17 @@ pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) { | ||||
|         state.dpad_right.update(false, irq); | ||||
|     } | ||||
|  | ||||
|     if input.key_pressed(VirtualKeyCode::T) { | ||||
|     if input.key_pressed(VirtualKeyCode::Return) { | ||||
|         state.start.update(true, irq); | ||||
|     } | ||||
|     if input.key_released(VirtualKeyCode::T) { | ||||
|     if input.key_released(VirtualKeyCode::Return) { | ||||
|         state.start.update(false, irq); | ||||
|     } | ||||
|  | ||||
|     if input.key_pressed(VirtualKeyCode::Y) { | ||||
|     if input.key_pressed(VirtualKeyCode::RShift) { | ||||
|         state.select.update(true, irq); | ||||
|     } | ||||
|     if input.key_released(VirtualKeyCode::Y) { | ||||
|     if input.key_released(VirtualKeyCode::RShift) { | ||||
|         state.select.update(false, irq); | ||||
|     } | ||||
|  | ||||
| @@ -175,7 +176,8 @@ pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) { | ||||
| #[inline] | ||||
| pub(crate) fn handle_gamepad_input(pad: &mut Joypad, event: GamepadEvent) { | ||||
|     use Button::*; | ||||
|     use GamepadEventType::*; | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| pub use apu::gen::AudioSPSC; | ||||
| pub use apu::gen::init as spsc_init; | ||||
| pub type Cycle = u64; | ||||
|  | ||||
| pub const GB_WIDTH: usize = 160; | ||||
|   | ||||
							
								
								
									
										82
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,9 +1,11 @@ | ||||
| use anyhow::{anyhow, Result}; | ||||
| use anyhow::Result; | ||||
| use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; | ||||
| use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH}; | ||||
| use gb::emu::{Emulator, CYCLES_IN_FRAME}; | ||||
| use gb::{Cycle, GB_HEIGHT, GB_WIDTH}; | ||||
| use gilrs::Gilrs; | ||||
| use pixels::{PixelsBuilder, SurfaceTexture}; | ||||
| use rodio::{OutputStream, Sink}; | ||||
| use tracing_subscriber::EnvFilter; | ||||
| use winit::dpi::{LogicalSize, PhysicalSize}; | ||||
| use winit::event::{Event, VirtualKeyCode}; | ||||
| use winit::event_loop::{ControlFlow, EventLoop}; | ||||
| @@ -11,7 +13,7 @@ use winit::window::{Window, WindowBuilder}; | ||||
| use winit_input_helper::WinitInputHelper; | ||||
|  | ||||
| const WINDOW_SCALE: usize = 3; | ||||
| const AUDIO_ENABLED: bool = false; | ||||
| const AUDIO_ENABLED: bool = true; | ||||
|  | ||||
| fn main() -> Result<()> { | ||||
|     let app = App::new(crate_name!()) | ||||
| @@ -24,7 +26,6 @@ fn main() -> Result<()> { | ||||
|             Arg::with_name("rom") | ||||
|                 .value_name("ROM_FILE") | ||||
|                 .takes_value(true) | ||||
|                 .required(true) | ||||
|                 .index(1) | ||||
|                 .help("Path to the Game ROM"), | ||||
|         ) | ||||
| @@ -38,14 +39,36 @@ fn main() -> Result<()> { | ||||
|         ) | ||||
|         .get_matches(); | ||||
|  | ||||
|     let rom_path = m | ||||
|         .value_of("rom") | ||||
|         .expect("Required value 'rom' was provided"); | ||||
|     // Set up subscriber | ||||
|     if std::env::var("RUST_LOG").is_err() { | ||||
|         std::env::set_var("RUST_LOG", "gb=info"); | ||||
|     } | ||||
|  | ||||
|     let mut game_boy = | ||||
|         gb::emu::init(m.value_of("boot"), rom_path).expect("Initialize DMG-01 Emulator"); | ||||
|     let rom_title = gb::emu::rom_title(&game_boy); | ||||
|     tracing_subscriber::fmt::fmt() | ||||
|         .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(); | ||||
|  | ||||
|     tracing::info!("Initialize Gamepad"); | ||||
|     let mut gamepad = Gilrs::new().expect("Initialize Controller Support"); | ||||
|  | ||||
|     // Initialize GUI | ||||
| @@ -66,13 +89,17 @@ fn main() -> Result<()> { | ||||
|     let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio"); | ||||
|  | ||||
|     if AUDIO_ENABLED { | ||||
|         let spsc: AudioSPSC<f32> = Default::default(); | ||||
|         let (prod, cons) = spsc.init(); | ||||
|         let sink = Sink::try_new(&stream_handle)?; | ||||
|         sink.append(cons); | ||||
|         sink.set_volume(0.1); // TODO: Is this the right way to go about this? | ||||
|         game_boy.apu_mut().attach_producer(prod); | ||||
|         let (prod, cons) = gb::spsc_init(); | ||||
|         let sink = { | ||||
|             let s = Sink::try_new(&stream_handle)?; | ||||
|             s.append(cons); | ||||
|             s.set_volume(0.1); | ||||
|             s | ||||
|         }; | ||||
|  | ||||
|         emu.set_prod(prod); | ||||
|  | ||||
|         tracing::info!("Spawn Audio Thread"); | ||||
|         std::thread::spawn(move || { | ||||
|             sink.sleep_until_end(); | ||||
|         }); | ||||
| @@ -82,11 +109,9 @@ fn main() -> Result<()> { | ||||
|  | ||||
|     event_loop.run(move |event, _, control_flow| { | ||||
|         if let Event::RedrawRequested(_) = event { | ||||
|             if pixels | ||||
|                 .render() | ||||
|                 .map_err(|e| anyhow!("pixels.render() failed: {}", e)) | ||||
|                 .is_err() | ||||
|             { | ||||
|             if pixels.render().is_err() { | ||||
|                 emu.try_write_sav().expect("Write game save if need be"); | ||||
|  | ||||
|                 *control_flow = ControlFlow::Exit; | ||||
|                 return; | ||||
|             } | ||||
| @@ -94,6 +119,8 @@ fn main() -> Result<()> { | ||||
|  | ||||
|         if input.update(&event) { | ||||
|             if input.key_pressed(VirtualKeyCode::Escape) || input.quit() { | ||||
|                 emu.try_write_sav().expect("Write game save if need be"); | ||||
|  | ||||
|                 *control_flow = ControlFlow::Exit; | ||||
|                 return; | ||||
|             } | ||||
| @@ -102,12 +129,17 @@ fn main() -> Result<()> { | ||||
|                 pixels.resize_surface(size.width, size.height); | ||||
|             } | ||||
|  | ||||
|             cycle_count += gb::emu::run_frame(&mut game_boy, &mut gamepad, &input); | ||||
|             cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input); | ||||
|  | ||||
|             if cycle_count >= gb::emu::CYCLES_IN_FRAME { | ||||
|                 cycle_count %= gb::emu::CYCLES_IN_FRAME; | ||||
|             if cycle_count >= CYCLES_IN_FRAME { | ||||
|                 cycle_count %= CYCLES_IN_FRAME; | ||||
|  | ||||
|                 gb::emu::draw(game_boy.ppu(), pixels.get_frame()); | ||||
|                 let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels | ||||
|                     .get_frame() | ||||
|                     .try_into() | ||||
|                     .expect("Size of Pixel Buffer is GB_WIDTH * GB_HEIGHT * 4"); | ||||
|  | ||||
|                 gb::emu::draw_frame(&emu, buf); | ||||
|                 window.request_redraw(); | ||||
|             } | ||||
|         } | ||||
|   | ||||
							
								
								
									
										596
									
								
								src/ppu.rs
									
									
									
									
									
								
							
							
						
						
									
										596
									
								
								src/ppu.rs
									
									
									
									
									
								
							| @@ -4,7 +4,6 @@ use crate::GB_HEIGHT; | ||||
| use crate::GB_WIDTH; | ||||
| use dma::DirectMemoryAccess; | ||||
| use std::collections::VecDeque; | ||||
| use std::convert::TryInto; | ||||
| pub(crate) use types::PpuMode; | ||||
| use types::{ | ||||
|     BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette, | ||||
| @@ -45,18 +44,18 @@ pub struct Ppu { | ||||
|     vram: Box<[u8; VRAM_SIZE]>, | ||||
|     pub(crate) oam: ObjectAttributeTable, | ||||
|     pub(crate) dma: DirectMemoryAccess, | ||||
|     scan_state: OamScanState, | ||||
|     scan_dot: Cycle, | ||||
|     fetch: PixelFetcher, | ||||
|     fifo: PixelFifo, | ||||
|     obj_buffer: ObjectBuffer, | ||||
|     frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>, | ||||
|     window_stat: WindowStatus, | ||||
|     win_stat: WindowStatus, | ||||
|  | ||||
|     scanline_start: bool, | ||||
|     to_discard: u8, | ||||
|  | ||||
|     x_pos: u8, | ||||
|     cycle: Cycle, | ||||
|     dot: Cycle, | ||||
| } | ||||
|  | ||||
| impl BusIo for Ppu { | ||||
| @@ -71,7 +70,7 @@ impl BusIo for Ppu { | ||||
|  | ||||
| impl Ppu { | ||||
|     pub(crate) fn tick(&mut self) { | ||||
|         self.cycle += 1; | ||||
|         self.dot += 1; | ||||
|  | ||||
|         if !self.ctrl.lcd_enabled() { | ||||
|             return; | ||||
| @@ -79,7 +78,26 @@ impl Ppu { | ||||
|  | ||||
|         match self.stat.mode() { | ||||
|             PpuMode::OamScan => { | ||||
|                 if self.cycle >= 80 { | ||||
|                 // Cycles 1 -> 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); | ||||
|                 } | ||||
|  | ||||
| @@ -88,7 +106,7 @@ impl Ppu { | ||||
|             PpuMode::Drawing => { | ||||
|                 if self.ctrl.lcd_enabled() { | ||||
|                     // Only Draw when the LCD Is Enabled | ||||
|                     self.draw(self.cycle); | ||||
|                     self.draw(); | ||||
|                 } else { | ||||
|                     self.reset(); | ||||
|                 } | ||||
| @@ -104,29 +122,22 @@ impl Ppu { | ||||
|  | ||||
|                     // Increment Window line counter if scanline had any window pixels on it | ||||
|                     // only increment once per scanline though | ||||
|                     if self.window_stat.should_draw() { | ||||
|                         self.fetch.back.window_line.increment(); | ||||
|                     if self.win_stat.enabled { | ||||
|                         self.fetch.back.wl_count += 1; | ||||
|                     } | ||||
|  | ||||
|                     self.x_pos = 0; | ||||
|                     self.scanline_start = true; | ||||
|                     self.to_discard = 0; | ||||
|  | ||||
|                     self.fetch.hblank_reset(); | ||||
|                     self.window_stat.hblank_reset(); | ||||
|                     self.win_stat.enabled = false; | ||||
|  | ||||
|                     self.obj_buffer.clear(); | ||||
|  | ||||
|                     self.fifo.back.clear(); | ||||
|                     self.fifo.obj.clear(); | ||||
|  | ||||
|                     self.stat.set_mode(PpuMode::HBlank); | ||||
|                 } | ||||
|             } | ||||
|             PpuMode::HBlank => { | ||||
|                 // This mode will always end at 456 cycles | ||||
|  | ||||
|                 if self.cycle >= 456 { | ||||
|                     self.cycle %= 456; | ||||
|                 if self.dot >= 456 { | ||||
|                     self.dot %= 456; | ||||
|                     self.pos.line_y += 1; | ||||
|  | ||||
|                     // Update LY==LYC bit | ||||
| @@ -143,9 +154,10 @@ impl Ppu { | ||||
|                         self.int.set_vblank(true); | ||||
|  | ||||
|                         // Reset Window Line Counter in Fetcher | ||||
|                         self.fetch.vblank_reset(); | ||||
|                         self.fetch.back.wl_count = 0; | ||||
|  | ||||
|                         // Reset WY=LY coincidence flag | ||||
|                         self.window_stat.vblank_reset(); | ||||
|                         self.win_stat.coincidence = false; | ||||
|  | ||||
|                         if self.stat.vblank_int() { | ||||
|                             // Enable Vblank LCDStat Interrupt | ||||
| @@ -159,7 +171,7 @@ impl Ppu { | ||||
|                             self.int.set_lcd_stat(true); | ||||
|                         } | ||||
|  | ||||
|                         self.scan_state.reset(); | ||||
|                         self.scan_dot = Default::default(); | ||||
|                         PpuMode::OamScan | ||||
|                     }; | ||||
|  | ||||
| @@ -167,8 +179,8 @@ impl Ppu { | ||||
|                 } | ||||
|             } | ||||
|             PpuMode::VBlank => { | ||||
|                 if self.cycle > 456 { | ||||
|                     self.cycle %= 456; | ||||
|                 if self.dot >= 456 { | ||||
|                     self.dot %= 456; | ||||
|                     self.pos.line_y += 1; | ||||
|  | ||||
|                     // Update LY==LYC bit | ||||
| @@ -188,8 +200,7 @@ impl Ppu { | ||||
|                             self.int.set_lcd_stat(true); | ||||
|                         } | ||||
|  | ||||
|                         self.scan_state.reset(); | ||||
|  | ||||
|                         self.scan_dot = Default::default(); | ||||
|                         self.stat.set_mode(PpuMode::OamScan); | ||||
|                     } | ||||
|                 } | ||||
| @@ -198,89 +209,81 @@ impl Ppu { | ||||
|     } | ||||
|  | ||||
|     fn scan_oam(&mut self) { | ||||
|         match self.scan_state.mode() { | ||||
|             OamScanMode::Scan if !self.dma.is_active() => { | ||||
|                 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); | ||||
|                 } | ||||
|  | ||||
|                 let sprite_height = self.ctrl.obj_size().as_u8(); | ||||
|                 let index = self.scan_state.count(); | ||||
|  | ||||
|                 let attr = self.oam.attribute(index as usize); | ||||
|                 let line_y = self.pos.line_y + 16; | ||||
|  | ||||
|                 if attr.x > 0 | ||||
|                     && line_y >= attr.y | ||||
|                     && line_y < (attr.y + sprite_height) | ||||
|                     && !self.obj_buffer.is_full() | ||||
|                 { | ||||
|                     self.obj_buffer.add(attr); | ||||
|                 } | ||||
|  | ||||
|                 self.scan_state.increase(); | ||||
|         if self.scan_dot % 2 == 0 { | ||||
|             if self.dma.is_active() { | ||||
|                 return; | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|  | ||||
|         self.scan_state.next(); | ||||
|             if !self.win_stat.coincidence && self.scan_dot == 0 { | ||||
|                 self.win_stat.coincidence = self.pos.line_y == self.pos.window_y; | ||||
|             } | ||||
|  | ||||
|             let obj_height = self.ctrl.obj_size().size(); | ||||
|             let attr = self.oam.attribute(self.scan_dot as usize / 2); | ||||
|             let line_y = self.pos.line_y + 16; | ||||
|  | ||||
|             if attr.x > 0 | ||||
|                 && line_y >= attr.y | ||||
|                 && line_y < (attr.y + obj_height) | ||||
|                 && !self.obj_buffer.is_full() | ||||
|             { | ||||
|                 self.obj_buffer.add(attr); | ||||
|             } | ||||
|         } | ||||
|         self.scan_dot += 1; | ||||
|     } | ||||
|  | ||||
|     fn draw(&mut self, _cycle: Cycle) { | ||||
|     fn draw(&mut self) { | ||||
|         use FetcherState::*; | ||||
|  | ||||
|         let mut iter = self.obj_buffer.iter_mut(); | ||||
|         let default = &mut None; | ||||
|         let mut obj_attr = &mut None; | ||||
|  | ||||
|         let obj_attr = loop { | ||||
|             match iter.next() { | ||||
|                 Some(attr_opt) => { | ||||
|                     if let Some(attr) = attr_opt { | ||||
|                         if attr.x <= (self.x_pos + 8) { | ||||
|                             self.fetch.back.reset(); | ||||
|                             self.fetch.back.pause(); | ||||
|                             self.fifo.pause(); | ||||
|         for maybe_attr in &mut self.obj_buffer.inner { | ||||
|             match maybe_attr { | ||||
|                 Some(attr) if self.ctrl.obj_enabled() => { | ||||
|                     if attr.x <= (self.x_pos + 8) { | ||||
|                         self.fetch.back.reset(); | ||||
|                         self.fetch.back.enabled = false; | ||||
|                         self.fifo.pause(); | ||||
|  | ||||
|                             break attr_opt; | ||||
|                         } | ||||
|                         obj_attr = maybe_attr; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 None => break default, | ||||
|                 _ => break, | ||||
|             } | ||||
|         }; | ||||
|         } | ||||
|  | ||||
|         if let Some(attr) = obj_attr { | ||||
|             match self.fetch.obj.state { | ||||
|                 TileNumber => { | ||||
|                 TileNumberA => self.fetch.obj.state = TileNumberB, | ||||
|                 TileNumberB => { | ||||
|                     self.fetch.obj.tile.with_id(attr.tile_index); | ||||
|                     self.fetch.obj.next(SleepOne); | ||||
|                     self.fetch.obj.state = TileLowA; | ||||
|                 } | ||||
|                 SleepOne => self.fetch.obj.next(TileLow), | ||||
|                 TileLow => { | ||||
|                 TileLowA => self.fetch.obj.state = TileLowB, | ||||
|                 TileLowB => { | ||||
|                     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); | ||||
|                     self.fetch.obj.tile.with_low_byte(byte); | ||||
|                     self.fetch.obj.tile.with_low(byte); | ||||
|  | ||||
|                     self.fetch.obj.next(SleepTwo); | ||||
|                     self.fetch.obj.state = TileHighA; | ||||
|                 } | ||||
|                 SleepTwo => self.fetch.obj.next(TileHigh), | ||||
|                 TileHigh => { | ||||
|                 TileHighA => self.fetch.obj.state = TileHighB, | ||||
|                 TileHighB => { | ||||
|                     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); | ||||
|                     self.fetch.obj.tile.with_high_byte(byte); | ||||
|                     self.fetch.obj.tile.with_high(byte); | ||||
|  | ||||
|                     self.fetch.obj.next(SleepThree); | ||||
|                     self.fetch.obj.state = ToFifoA; | ||||
|                 } | ||||
|                 SleepThree => self.fetch.obj.next(ToFifoOne), | ||||
|                 ToFifoOne => { | ||||
|                 ToFifoA => { | ||||
|                     // Load into Fifo | ||||
|                     let (high, low) = self | ||||
|                         .fetch | ||||
| @@ -312,73 +315,65 @@ impl Ppu { | ||||
|                         self.fifo.obj.push_back(fifo_info); | ||||
|                     } | ||||
|  | ||||
|                     self.fetch.back.resume(); | ||||
|                     self.fetch.back.enabled = true; | ||||
|                     self.fifo.resume(); | ||||
|                     let _ = std::mem::take(obj_attr); | ||||
|  | ||||
|                     self.fetch.obj.next(ToFifoTwo); | ||||
|                     self.fetch.obj.state = ToFifoB; | ||||
|                 } | ||||
|                 ToFifoTwo => self.fetch.obj.reset(), | ||||
|                 ToFifoB => self.fetch.obj.reset(), | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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() { | ||||
|         if self.fetch.back.enabled { | ||||
|             match self.fetch.back.state { | ||||
|                 TileNumber => { | ||||
|                     let x_pos = self.fetch.x_pos; | ||||
|                 TileNumberA => self.fetch.back.state = TileNumberB, | ||||
|                 TileNumberB => { | ||||
|                     // Are we rendering the Window currently? | ||||
|                     self.fetch.back.draw_window = self.win_stat.enabled; | ||||
|  | ||||
|                     self.fetch | ||||
|                         .back | ||||
|                         .should_render_window(self.window_stat.should_draw()); | ||||
|  | ||||
|                     let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos); | ||||
|                     let addr = | ||||
|                         self.fetch | ||||
|                             .back | ||||
|                             .tile_id_addr(&self.ctrl, &self.pos, self.fetch.x_pos); | ||||
|  | ||||
|                     let id = self.read_byte(addr); | ||||
|                     self.fetch.back.tile.with_id(id); | ||||
|  | ||||
|                     // Move on to the Next state in 2 T-cycles | ||||
|                     self.fetch.back.next(SleepOne); | ||||
|                     self.fetch.back.state = TileLowA; | ||||
|                 } | ||||
|                 SleepOne => self.fetch.back.next(TileLow), | ||||
|                 TileLow => { | ||||
|                     let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); | ||||
|                 TileLowA => self.fetch.back.state = TileLowB, | ||||
|                 TileLowB => { | ||||
|                     let id = self.fetch.back.tile.id.expect("Tile ID present"); | ||||
|  | ||||
|                     let low = self.read_byte(addr); | ||||
|                     self.fetch.back.tile.with_low_byte(low); | ||||
|                     let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id); | ||||
|                     let byte = self.read_byte(addr); | ||||
|                     self.fetch.back.tile.with_low(byte); | ||||
|  | ||||
|                     self.fetch.back.next(SleepTwo); | ||||
|                     self.fetch.back.state = TileHighA; | ||||
|                 } | ||||
|                 SleepTwo => self.fetch.back.next(TileHigh), | ||||
|                 TileHigh => { | ||||
|                     let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); | ||||
|                 TileHighA => self.fetch.back.state = TileHighB, | ||||
|                 TileHighB => { | ||||
|                     let id = self.fetch.back.tile.id.expect("Tile ID present"); | ||||
|  | ||||
|                     let high = self.read_byte(addr + 1); | ||||
|                     self.fetch.back.tile.with_high_byte(high); | ||||
|                     let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id); | ||||
|                     let byte = self.read_byte(addr + 1); | ||||
|                     self.fetch.back.tile.with_high(byte); | ||||
|  | ||||
|                     self.fetch.back.next(SleepThree); | ||||
|                 } | ||||
|                 SleepThree => self.fetch.back.next(ToFifoOne), | ||||
|                 ToFifoOne => { | ||||
|                     self.fetch.back.next(ToFifoTwo); | ||||
|                 } | ||||
|                 ToFifoTwo => { | ||||
|                     if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) { | ||||
|                         self.fetch.x_pos += 1; | ||||
|                         self.fetch.back.next(TileNumber); | ||||
|                         self.fetch.back.tile = Default::default(); | ||||
|                     if self.fetch.back.tile_high_reset { | ||||
|                         self.fetch.back.reset(); | ||||
|                         self.fetch.back.tile_high_reset = false; | ||||
|                     } else { | ||||
|                         self.fetch.back.state = ToFifoA; | ||||
|                     } | ||||
|                 } | ||||
|                 ToFifoA => { | ||||
|                     if self.fetch.send_to_fifo(&mut self.fifo).is_ok() { | ||||
|                         self.fetch.x_pos += 1; | ||||
|                         self.fetch.back.state = ToFifoB; | ||||
|                     } | ||||
|                 } | ||||
|                 ToFifoB => self.fetch.back.reset(), | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -388,7 +383,7 @@ impl Ppu { | ||||
|                 self.scanline_start = false; | ||||
|             } | ||||
|  | ||||
|             if self.to_discard > 0 && !self.fifo.back.is_empty() { | ||||
|             if !self.win_stat.enabled && self.to_discard > 0 && !self.fifo.back.is_empty() { | ||||
|                 let _ = self.fifo.back.pop_front(); | ||||
|                 self.to_discard -= 1; | ||||
|  | ||||
| @@ -406,6 +401,17 @@ impl Ppu { | ||||
|  | ||||
|                 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(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -413,53 +419,49 @@ impl Ppu { | ||||
|         self.pos.line_y = 0; | ||||
|         self.stat.set_mode(PpuMode::HBlank); | ||||
|  | ||||
|         // TODO: Is this an unnecessary performance hit? | ||||
|         let mut blank = WHITE.repeat(self.frame_buf.len() / 4); | ||||
|         self.frame_buf.swap_with_slice(&mut blank); | ||||
|     } | ||||
|  | ||||
|     pub fn copy_to_gui(&self, frame: &mut [u8]) { | ||||
|         frame.copy_from_slice(self.frame_buf.as_ref()); | ||||
|     #[inline] | ||||
|     pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] { | ||||
|         &self.frame_buf | ||||
|     } | ||||
|  | ||||
|     fn clock_fifo(&mut self) -> Option<GrayShade> { | ||||
|         use ObjectPaletteKind::*; | ||||
|         use RenderPriority::*; | ||||
|  | ||||
|         let obj_palette_0 = &self.monochrome.obj_palette_0; | ||||
|         let obj_palette_1 = &self.monochrome.obj_palette_1; | ||||
|  | ||||
|         match self.fifo.back.pop_front() { | ||||
|             Some(bg_pixel) => match self.fifo.obj.pop_front() { | ||||
|                 Some(obj_pixel) if self.ctrl.obj_enabled() => match obj_pixel.priority { | ||||
|                     Object | BackgroundAndWindow if obj_pixel.shade_id == 0 => { | ||||
|                         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) | ||||
|                     } | ||||
|         self.fifo | ||||
|             .back | ||||
|             .pop_front() | ||||
|             .map(|bg| match self.fifo.obj.pop_front() { | ||||
|                 Some(obj) => match obj.priority { | ||||
|                     _ if obj.shade_id == 0 => self.bg_pixel(bg), | ||||
|                     BackgroundAndWindow if bg.shade_id != 0 => self.bg_pixel(bg), | ||||
|                     _ => self.obj_pixel(obj), | ||||
|                 }, | ||||
|                 _ => Some(self.bg_pixel(bg_pixel.shade_id)), | ||||
|             }, | ||||
|             None => None, | ||||
|                 None => self.bg_pixel(bg), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
|     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, shade_id: u8) -> GrayShade { | ||||
|     fn bg_pixel(&self, bg: BgPixelProperty) -> GrayShade { | ||||
|         let bg_palette = &self.monochrome.bg_palette; | ||||
|  | ||||
|         if self.ctrl.bg_win_enabled() { | ||||
|             bg_palette.shade(shade_id) | ||||
|             bg_palette.shade(bg.shade_id) | ||||
|         } else { | ||||
|             bg_palette.shade(0) | ||||
|         } | ||||
| @@ -470,7 +472,7 @@ impl Default for Ppu { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             vram: Box::new([0u8; VRAM_SIZE]), | ||||
|             cycle: Default::default(), | ||||
|             dot: Default::default(), | ||||
|             frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]), | ||||
|             int: Default::default(), | ||||
|             ctrl: Default::default(), | ||||
| @@ -478,11 +480,11 @@ impl Default for Ppu { | ||||
|             pos: Default::default(), | ||||
|             stat: Default::default(), | ||||
|             oam: Default::default(), | ||||
|             scan_state: Default::default(), | ||||
|             scan_dot: Default::default(), | ||||
|             fetch: Default::default(), | ||||
|             fifo: Default::default(), | ||||
|             obj_buffer: Default::default(), | ||||
|             window_stat: Default::default(), | ||||
|             win_stat: Default::default(), | ||||
|             dma: Default::default(), | ||||
|             x_pos: 0, | ||||
|             scanline_start: true, | ||||
| @@ -578,7 +580,7 @@ impl Default for ObjectAttributeTable { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] | ||||
| #[derive(Debug, Clone, Copy, Default)] | ||||
| struct ObjectAttribute { | ||||
|     y: u8, | ||||
|     x: u8, | ||||
| @@ -628,10 +630,6 @@ impl ObjectBuffer { | ||||
|         self.inner[self.len] = Some(attr); | ||||
|         self.len += 1; | ||||
|     } | ||||
|  | ||||
|     fn iter_mut(&mut self) -> std::slice::IterMut<'_, Option<ObjectAttribute>> { | ||||
|         self.inner.iter_mut() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for ObjectBuffer { | ||||
| @@ -657,61 +655,6 @@ impl PixelFetcher { | ||||
|         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<(), ()> { | ||||
|         if !fifo.back.is_empty() { | ||||
|             return Err(()); | ||||
| @@ -757,7 +700,6 @@ impl PixelFetcher { | ||||
| } | ||||
|  | ||||
| trait Fetcher { | ||||
|     fn next(&mut self, state: FetcherState); | ||||
|     fn reset(&mut self); | ||||
|     fn hblank_reset(&mut self); | ||||
| } | ||||
| @@ -766,42 +708,63 @@ trait Fetcher { | ||||
| struct BackgroundFetcher { | ||||
|     state: FetcherState, | ||||
|     tile: TileBuilder, | ||||
|     window_line: WindowLineCounter, | ||||
|     is_window_tile: bool, | ||||
|     wl_count: u8, | ||||
|     draw_window: bool, | ||||
|     enabled: bool, | ||||
|     tile_high_reset: bool, | ||||
| } | ||||
|  | ||||
| impl BackgroundFetcher { | ||||
|     fn should_render_window(&mut self, value: bool) { | ||||
|         self.is_window_tile = value; | ||||
|     fn tile_id_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.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 is_window_tile(&self) -> bool { | ||||
|         self.is_window_tile | ||||
|     } | ||||
|     fn tile_addr(&mut self, control: &LCDControl, pos: &ScreenPosition, id: u8) -> u16 { | ||||
|         let line_y = pos.line_y; | ||||
|         let scroll_y = pos.scroll_y; | ||||
|  | ||||
|     fn pause(&mut self) { | ||||
|         self.enabled = false; | ||||
|     } | ||||
|         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), | ||||
|         }; | ||||
|  | ||||
|     fn resume(&mut self) { | ||||
|         self.enabled = true; | ||||
|     } | ||||
|         let offset = if self.draw_window { | ||||
|             self.wl_count as u16 % 8 | ||||
|         } else { | ||||
|             (line_y as u16 + scroll_y as u16) % 8 | ||||
|         }; | ||||
|  | ||||
|     fn is_enabled(&self) -> bool { | ||||
|         self.enabled | ||||
|     } | ||||
|  | ||||
|     fn vblank_reset(&mut self) { | ||||
|         self.window_line.vblank_reset(); | ||||
|         tile_data_addr + (offset * 2) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Fetcher for BackgroundFetcher { | ||||
|     fn next(&mut self, state: FetcherState) { | ||||
|         self.state = state | ||||
|     } | ||||
|  | ||||
|     fn reset(&mut self) { | ||||
|         self.state = Default::default(); | ||||
|         self.tile = Default::default(); | ||||
| @@ -810,7 +773,7 @@ impl Fetcher for BackgroundFetcher { | ||||
|     fn hblank_reset(&mut self) { | ||||
|         self.reset(); | ||||
|  | ||||
|         self.is_window_tile = false; | ||||
|         self.draw_window = false; | ||||
|  | ||||
|         self.enabled = true; | ||||
|     } | ||||
| @@ -821,69 +784,55 @@ impl Default for BackgroundFetcher { | ||||
|         Self { | ||||
|             state: Default::default(), | ||||
|             tile: Default::default(), | ||||
|             is_window_tile: Default::default(), | ||||
|             window_line: Default::default(), | ||||
|             draw_window: Default::default(), | ||||
|             wl_count: Default::default(), | ||||
|             enabled: true, | ||||
|             tile_high_reset: true, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Default)] | ||||
| #[derive(Debug)] | ||||
| struct ObjectFetcher { | ||||
|     state: FetcherState, | ||||
|     tile: TileBuilder, | ||||
| } | ||||
|  | ||||
| impl Fetcher for ObjectFetcher { | ||||
|     fn next(&mut self, state: FetcherState) { | ||||
|         self.state = state | ||||
| impl Default for ObjectFetcher { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             state: Default::default(), | ||||
|             tile: Default::default(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Fetcher for ObjectFetcher { | ||||
|     fn reset(&mut self) { | ||||
|         self.state = Default::default(); | ||||
|         self.tile = Default::default(); | ||||
|     } | ||||
|  | ||||
|     fn hblank_reset(&mut self) { | ||||
|         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 | ||||
|         self.reset() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| enum FetcherState { | ||||
|     TileNumber, | ||||
|     SleepOne, | ||||
|     TileLow, | ||||
|     SleepTwo, | ||||
|     TileHigh, | ||||
|     SleepThree, | ||||
|     ToFifoOne, | ||||
|     ToFifoTwo, | ||||
|     TileNumberA, | ||||
|     TileNumberB, | ||||
|     TileLowA, | ||||
|     TileLowB, | ||||
|     TileHighA, | ||||
|     TileHighB, | ||||
|     ToFifoA, | ||||
|     ToFifoB, | ||||
| } | ||||
|  | ||||
| impl Default for FetcherState { | ||||
|     fn default() -> Self { | ||||
|         Self::TileNumber | ||||
|         Self::TileNumberA | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -944,11 +893,11 @@ impl TileBuilder { | ||||
|         self.id = Some(id); | ||||
|     } | ||||
|  | ||||
|     fn with_low_byte(&mut self, data: u8) { | ||||
|     fn with_low(&mut self, data: u8) { | ||||
|         self.low = Some(data); | ||||
|     } | ||||
|  | ||||
|     fn with_high_byte(&mut self, data: u8) { | ||||
|     fn with_high(&mut self, data: u8) { | ||||
|         self.high = Some(data); | ||||
|     } | ||||
|  | ||||
| @@ -957,84 +906,11 @@ 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)] | ||||
| struct WindowStatus { | ||||
|     /// This will be true if WY == LY at any point in the frame thus far | ||||
|     coincidence: bool, | ||||
|     /// This will be true if the conditions which tell the PPU to start | ||||
|     /// drawing from the window tile map is true | ||||
|     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; | ||||
|     } | ||||
|     enabled: bool, | ||||
| } | ||||
|   | ||||
| @@ -183,7 +183,7 @@ pub enum ObjectSize { | ||||
| } | ||||
|  | ||||
| impl ObjectSize { | ||||
|     pub(crate) fn as_u8(&self) -> u8 { | ||||
|     pub(crate) fn size(&self) -> u8 { | ||||
|         use ObjectSize::*; | ||||
|  | ||||
|         match self { | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/timer.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/timer.rs
									
									
									
									
									
								
							| @@ -21,17 +21,6 @@ impl Timer { | ||||
|         use State::*; | ||||
|         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); | ||||
|  | ||||
|         // Get Bit Position | ||||
| @@ -53,6 +42,17 @@ impl Timer { | ||||
|         } | ||||
|  | ||||
|         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 | ||||
| @@ -70,7 +70,7 @@ impl Timer { | ||||
|                 self.counter = byte; | ||||
|                 self.state = AbortedTIMAOverflow(step); | ||||
|             } | ||||
|             LoadTMA => {} | ||||
|             LoadTIMA => { /* Ignored */ } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -95,9 +95,8 @@ impl Timer { | ||||
|         use State::*; | ||||
|  | ||||
|         self.state = match self.state { | ||||
|             Normal | LoadTMA => Normal, | ||||
|             AbortedTIMAOverflow(4) => Normal, | ||||
|             TIMAOverflow(4) => LoadTMA, | ||||
|             Normal | LoadTIMA | AbortedTIMAOverflow(3) => Normal, | ||||
|             TIMAOverflow(3) => LoadTIMA, | ||||
|             AbortedTIMAOverflow(step) => AbortedTIMAOverflow(step + 1), | ||||
|             TIMAOverflow(step) => TIMAOverflow(step + 1), | ||||
|         } | ||||
| @@ -181,5 +180,5 @@ enum State { | ||||
|     TIMAOverflow(u8), | ||||
|     AbortedTIMAOverflow(u8), | ||||
|     Normal, | ||||
|     LoadTMA, | ||||
|     LoadTIMA, | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user