Compare commits

..

43 Commits

Author SHA1 Message Date
f2c49b398c chore(cart): improve code quality of RtcClock 2021-11-05 23:05:36 -03:00
6f76571d6c chore: update README 2021-11-05 22:40:31 -03:00
55da5a29d8 chore: remove more getters 2021-11-01 18:04:22 +09:00
7dfcd44820 chore(emu): remove bus and bus_mut from cpu 2021-11-01 17:56:42 +09:00
80a15039e0 chore(apu): improve accuracy of Channel 1 2021-10-30 21:53:08 +09:00
32405c0734 fix(apu): implement some obscure behaviour for ch1,2 & 3 2021-10-30 20:04:16 +09:00
2ef8fefbb7 chore(apu): implement more obscure behaviour
implement capacitor
2021-10-30 18:41:31 +09:00
548945a4ee chore: remove imports of tracing macros 2021-10-30 16:41:06 +09:00
1526b4a09d fix(cart): improve title parsin
Now both Tokimeki Memorial titles are read properly
2021-10-30 15:48:47 +09:00
4125ea5c74 fix(instr): fix timing issues with select instructions
instr_timing now runs (and fails on everything)
2021-10-30 14:02:28 +09:00
b9519d9b7a chore(bus): stub some CGB IO registers 2021-10-30 14:02:05 +09:00
01278ca83f chore: reduce size of public interface 2021-10-30 10:28:20 +09:00
2bb8bd6d8f chore: update dependencies 2021-10-30 09:37:24 +09:00
080c1e7518 fix(bus): don't panic on non-existent cartridge 2021-10-28 22:19:38 -03:00
765f9d8288 Merge branch 'main' of ssh://musuka.dev:2222/paoda/gb 2021-10-28 22:08:04 -03:00
8780c4a59b chore(emu): remove and replace EmulatorBuilder 2021-10-28 22:07:36 -03:00
e19a540650 chore: upgrade to rust 2021 2021-10-22 01:42:40 -03:00
4cfd951ede chore(ppu): improve bg-sprite priority functionality 2021-10-20 20:08:09 -03:00
de8fe1a367 chore(ppu): remove object buffer iter_mut method 2021-10-20 19:41:27 -03:00
c5fa41b20d chore(ppu): refactor FetcherState 2021-10-20 19:40:29 -03:00
552cfd4a18 chore(ppu): sort OAM Memory 2021-10-20 17:36:38 -03:00
4c516804e4 fix(ppu): refactor and improve accuracy of background fetcher 2021-10-20 16:58:19 -03:00
9b3ab73bb1 fix(ppu): incremental improvements to accuracy of ppu fetcher 2021-10-20 05:21:49 -03:00
d9f1d661ae chore(ppu): refactor behaviour w.r.t window enabling 2021-10-20 04:48:20 -03:00
da83032e24 chore(ppu): use "dot" intead of "cycle" 2021-10-20 03:34:09 -03:00
67e2447d3f chore(ppu): rename window_stat to win_stat 2021-10-20 03:16:37 -03:00
37cf3d92e4 chore(ppu): refactor OAM Scan implementation 2021-10-20 03:14:30 -03:00
293e5762c3 chore: small code-cleanup changes 2021-10-20 02:48:44 -03:00
999f661e6b chore: update cargo.lock 2021-10-14 14:58:25 -03:00
e573411890 chore: update dependencies 2021-10-11 00:06:51 -03:00
352a65b705 chore(apu): update spsc depenency 2021-10-07 15:27:51 -03:00
64230973f1 chore: suggest inline for hot code 2021-09-24 16:16:14 -03:00
dbbf87af52 chore: add a creative-commons boot rom for compat 2021-09-24 16:15:55 -03:00
1440cd1fc7 chore: update README.md 2021-09-24 12:34:48 -03:00
9964b49ce1 fix(ppu): improve accuracy of timer 2021-09-21 12:50:31 -03:00
142231d355 chore(ppu): remoe unnecessary brackets 2021-09-21 12:13:55 -03:00
227928e8ca Revert "fix(ppu): explicity choose to use sign extension"
This reverts commit 1001b0b124.
2021-09-21 12:09:35 -03:00
1001b0b124 fix(ppu): explicity choose to use sign extension 2021-09-21 09:52:12 -03:00
71ce3f43e0 chore: satisfy clippy 2021-09-20 04:15:05 -03:00
ce121864d2 feat: implement cartridge saving
Implemented for MBC1, MBC2, MBC3 and MBC5
2021-09-20 04:13:25 -03:00
e1fe00ab64 fix: have start and select keybinds match other emulators 2021-09-20 03:26:06 -03:00
5882678bc5 chore: inline some functions
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-09-20 01:34:41 -03:00
bcd67cb317 chore: begin refactor of public api 2021-09-20 01:34:21 -03:00
18 changed files with 1392 additions and 874 deletions

422
Cargo.lock generated
View File

@@ -23,7 +23,7 @@ dependencies = [
"alsa-sys", "alsa-sys",
"bitflags", "bitflags",
"libc", "libc",
"nix 0.20.1", "nix 0.20.0",
] ]
[[package]] [[package]]
@@ -59,10 +59,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "anyhow" name = "ansi_term"
version = "1.0.43" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "arrayvec" name = "arrayvec"
@@ -76,7 +85,7 @@ version = "0.33.3+1.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219" checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
dependencies = [ dependencies = [
"libloading 0.7.0", "libloading 0.7.1",
] ]
[[package]] [[package]]
@@ -144,9 +153,9 @@ checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "block" name = "block"
@@ -156,9 +165,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.7.0" version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
@@ -196,9 +205,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.70" version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
dependencies = [ dependencies = [
"jobserver", "jobserver",
] ]
@@ -244,7 +253,7 @@ checksum = "10612c0ec0e0a1ff0e97980647cb058a6e7aedb913d01d009c406b8b7d0b26ee"
dependencies = [ dependencies = [
"glob", "glob",
"libc", "libc",
"libloading 0.7.0", "libloading 0.7.1",
] ]
[[package]] [[package]]
@@ -253,7 +262,7 @@ version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [ dependencies = [
"ansi_term", "ansi_term 0.11.0",
"atty", "atty",
"bitflags", "bitflags",
"strsim 0.8.0", "strsim 0.8.0",
@@ -277,7 +286,7 @@ dependencies = [
"bitflags", "bitflags",
"block", "block",
"cocoa-foundation", "cocoa-foundation",
"core-foundation 0.9.1", "core-foundation 0.9.2",
"core-graphics 0.22.2", "core-graphics 0.22.2",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -292,7 +301,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"block", "block",
"core-foundation 0.9.1", "core-foundation 0.9.2",
"core-graphics-types", "core-graphics-types",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -347,11 +356,11 @@ dependencies = [
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.1" version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
dependencies = [ dependencies = [
"core-foundation-sys 0.8.2", "core-foundation-sys 0.8.3",
"libc", "libc",
] ]
@@ -369,9 +378,9 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.2" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]] [[package]]
name = "core-graphics" name = "core-graphics"
@@ -392,7 +401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation 0.9.1", "core-foundation 0.9.2",
"core-graphics-types", "core-graphics-types",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -405,7 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation 0.9.1", "core-foundation 0.9.2",
"foreign-types", "foreign-types",
"libc", "libc",
] ]
@@ -449,7 +458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418" checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418"
dependencies = [ dependencies = [
"alsa", "alsa",
"core-foundation-sys 0.8.2", "core-foundation-sys 0.8.3",
"coreaudio-rs", "coreaudio-rs",
"jni", "jni",
"js-sys", "js-sys",
@@ -458,7 +467,7 @@ dependencies = [
"mach 0.3.2", "mach 0.3.2",
"ndk 0.3.0", "ndk 0.3.0",
"ndk-glue 0.3.0", "ndk-glue 0.3.0",
"nix 0.20.1", "nix 0.20.0",
"oboe", "oboe",
"parking_lot", "parking_lot",
"stdweb 0.1.3", "stdweb 0.1.3",
@@ -542,7 +551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c" checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libloading 0.7.0", "libloading 0.7.1",
"winapi", "winapi",
] ]
@@ -592,6 +601,47 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
dependencies = [
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "discard" name = "discard"
version = "1.0.4" version = "1.0.4"
@@ -619,7 +669,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
dependencies = [ dependencies = [
"libloading 0.7.0", "libloading 0.7.1",
] ]
[[package]] [[package]]
@@ -628,12 +678,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "fixedbitset"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@@ -671,14 +715,28 @@ dependencies = [
"anyhow", "anyhow",
"bitfield", "bitfield",
"clap", "clap",
"directories-next",
"gilrs", "gilrs",
"pixels", "pixels",
"rodio", "rodio",
"rtrb", "rtrb",
"tracing",
"tracing-subscriber",
"winit", "winit",
"winit_input_helper", "winit_input_helper",
] ]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi",
]
[[package]] [[package]]
name = "gilrs" name = "gilrs"
version = "0.8.1" version = "0.8.1"
@@ -703,7 +761,7 @@ dependencies = [
"libc", "libc",
"libudev-sys", "libudev-sys",
"log", "log",
"nix 0.20.1", "nix 0.20.0",
"rusty-xinput", "rusty-xinput",
"stdweb 0.4.20", "stdweb 0.4.20",
"uuid", "uuid",
@@ -731,9 +789,9 @@ dependencies = [
[[package]] [[package]]
name = "gpu-alloc" name = "gpu-alloc"
version = "0.5.1" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab8524eac5fc9d05625c891adf78fcf64dc0ee9f8d0882874b9f220f42b442bf" checksum = "0e64cbb8d36508d3e19da95e56e196a84f674fc190881f2cc010000798838aa6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"gpu-alloc-types", "gpu-alloc-types",
@@ -792,6 +850,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hexf-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]] [[package]]
name = "hound" name = "hound"
version = "3.4.0" version = "3.4.0"
@@ -822,11 +886,14 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca"
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
@@ -876,9 +943,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.54" version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -890,7 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
dependencies = [ dependencies = [
"libc", "libc",
"libloading 0.7.0", "libloading 0.7.1",
] ]
[[package]] [[package]]
@@ -918,9 +985,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.101" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]] [[package]]
name = "libloading" name = "libloading"
@@ -934,9 +1001,9 @@ dependencies = [
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"winapi", "winapi",
@@ -998,10 +1065,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "maybe-uninit" name = "matchers"
version = "2.0.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
@@ -1043,9 +1113,9 @@ dependencies = [
[[package]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d" checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677"
[[package]] [[package]]
name = "minimp3" name = "minimp3"
@@ -1103,17 +1173,18 @@ dependencies = [
[[package]] [[package]]
name = "naga" name = "naga"
version = "0.6.3" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c5859e55c51da10b98e7a73068e0a0c5da7bbcae4fc38f86043d0c6d1b917cf" checksum = "eda66d09f712e1f0a6ab436137da4fac312f78301f6d4ac7cb8bfe96e988734f"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bitflags", "bitflags",
"codespan-reporting", "codespan-reporting",
"fxhash", "fxhash",
"hexf-parse",
"indexmap",
"log", "log",
"num-traits", "num-traits",
"petgraph",
"spirv", "spirv",
"thiserror", "thiserror",
] ]
@@ -1204,15 +1275,14 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.20.1" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8e5e343312e7fbeb2a52139114e9e702991ef9c2aea6817ff2440b35647d56" checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cc", "cc",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"memoffset",
] ]
[[package]] [[package]]
@@ -1281,7 +1351,7 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
dependencies = [ dependencies = [
"proc-macro-crate 1.0.0", "proc-macro-crate 1.1.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -1391,20 +1461,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]] [[package]]
name = "petgraph" name = "pin-project-lite"
version = "0.6.0" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]] [[package]]
name = "pixels" name = "pixels"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e794d4eab6519352618cca630ce4970a65576b9df54eca2068d4aad4b143903" checksum = "94ab1e297051c39cc7b7511e7e2b3ab151f14aff6a44e73bdde652b1e2190950"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"pollster", "pollster",
@@ -1416,9 +1482,9 @@ dependencies = [
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.19" version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
[[package]] [[package]]
name = "pollster" name = "pollster"
@@ -1437,9 +1503,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "1.0.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
dependencies = [ dependencies = [
"thiserror", "thiserror",
"toml", "toml",
@@ -1447,9 +1513,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.29" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@@ -1462,9 +1528,9 @@ checksum = "87dfd5592a8eed7e74f56ad7b125f8234763b805c30f0c7c95c486920026a6ec"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.9" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -1493,6 +1559,16 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.5.4" version = "1.5.4"
@@ -1502,6 +1578,15 @@ dependencies = [
"regex-syntax", "regex-syntax",
] ]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax",
]
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.25" version = "0.6.25"
@@ -1529,9 +1614,9 @@ dependencies = [
[[package]] [[package]]
name = "rtrb" name = "rtrb"
version = "0.1.4" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f" checksum = "7d911d82e9745a598dd118e6bda2165531e316cf8eae65240776637ae8168458"
dependencies = [ dependencies = [
"cache-padded", "cache-padded",
] ]
@@ -1642,9 +1727,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.67" version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -1657,6 +1742,15 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "0.1.1" version = "0.1.1"
@@ -1685,9 +1779,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.6.1" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"
@@ -1789,9 +1883,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.76" version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1818,18 +1912,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.29" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.29" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1837,10 +1931,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tinyvec" name = "thread_local"
version = "1.3.1" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@@ -1860,6 +1963,67 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "tracing"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
dependencies = [
"lazy_static",
]
[[package]]
name = "tracing-log"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52"
dependencies = [
"ansi_term 0.12.1",
"lazy_static",
"matchers",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.6.2" version = "0.6.2"
@@ -1877,9 +2041,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.8" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
@@ -1917,10 +2081,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasi"
version = "0.2.77" version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@@ -1928,9 +2098,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.77" version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@@ -1943,9 +2113,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.27" version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060" checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"js-sys", "js-sys",
@@ -1955,9 +2125,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.77" version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -1965,9 +2135,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.77" version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1978,9 +2148,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.77" version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"
@@ -1991,7 +2161,7 @@ dependencies = [
"bitflags", "bitflags",
"downcast-rs", "downcast-rs",
"libc", "libc",
"nix 0.20.1", "nix 0.20.0",
"scoped-tls", "scoped-tls",
"wayland-commons", "wayland-commons",
"wayland-scanner", "wayland-scanner",
@@ -2004,7 +2174,7 @@ version = "0.28.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda"
dependencies = [ dependencies = [
"nix 0.20.1", "nix 0.20.0",
"once_cell", "once_cell",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys",
@@ -2016,7 +2186,7 @@ version = "0.28.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a"
dependencies = [ dependencies = [
"nix 0.20.1", "nix 0.20.0",
"wayland-client", "wayland-client",
"xcursor", "xcursor",
] ]
@@ -2057,9 +2227,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.51" version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -2067,9 +2237,9 @@ dependencies = [
[[package]] [[package]]
name = "wgpu" name = "wgpu"
version = "0.10.1" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d92a4fe73b1e7d7ef99938dacd49258cbf1ad87cdb5bf6efa20c27447442b45" checksum = "d1577ecc4f6992b9e965878ac594efb24eed2bdf089c11f45b3d1c5f216e2e30"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"js-sys", "js-sys",
@@ -2087,9 +2257,9 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core" name = "wgpu-core"
version = "0.10.2" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "425b975c319d311e051bf3afb54120a34b187f9d889edc68e347567e512774c8" checksum = "45af76ba5545b61a6904f26cdcf4287329144ae9e12f0c23ec4c9be982d675a6"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bitflags", "bitflags",
@@ -2109,9 +2279,9 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-hal" name = "wgpu-hal"
version = "0.10.4" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b33daf5eff68118c0aad030886655824052f82a164fd2e257211f742c6cade53" checksum = "11095a81f4406b1e594dab7dc35d6508409d364e62458f2e5b07b3edc7aca517"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"ash", "ash",
@@ -2126,26 +2296,30 @@ dependencies = [
"gpu-alloc", "gpu-alloc",
"gpu-descriptor", "gpu-descriptor",
"inplace_it", "inplace_it",
"js-sys",
"khronos-egl", "khronos-egl",
"libloading 0.7.0", "libloading 0.7.1",
"log", "log",
"metal", "metal",
"naga", "naga",
"objc", "objc",
"parking_lot", "parking_lot",
"profiling",
"range-alloc", "range-alloc",
"raw-window-handle", "raw-window-handle",
"renderdoc-sys", "renderdoc-sys",
"thiserror", "thiserror",
"wasm-bindgen",
"web-sys",
"wgpu-types", "wgpu-types",
"winapi", "winapi",
] ]
[[package]] [[package]]
name = "wgpu-types" name = "wgpu-types"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25feb2fbf24ab3219a9f10890ceb8e1ef02b13314ed89d64a9ae99dcad883e18" checksum = "e15e44ba88ec415466e18e91881319e7c9e96cb905dc623305168aea65b85ccc"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@@ -2199,7 +2373,7 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cocoa", "cocoa",
"core-foundation 0.9.1", "core-foundation 0.9.2",
"core-graphics 0.22.2", "core-graphics 0.22.2",
"core-video-sys", "core-video-sys",
"dispatch", "dispatch",
@@ -2234,13 +2408,12 @@ dependencies = [
[[package]] [[package]]
name = "x11-dl" name = "x11-dl"
version = "2.18.5" version = "2.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
"maybe-uninit",
"pkg-config", "pkg-config",
] ]
@@ -2255,9 +2428,12 @@ dependencies = [
[[package]] [[package]]
name = "xdg" name = "xdg"
version = "2.2.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803"
dependencies = [
"dirs",
]
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"

View File

@@ -2,21 +2,23 @@
name = "gb" name = "gb"
version = "0.1.0" version = "0.1.0"
authors = ["Rekai Musuka <rekai@musuka.dev>"] authors = ["Rekai Musuka <rekai@musuka.dev>"]
edition = "2018" edition = "2021"
resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "^1.0" anyhow = "1.0"
bitfield = "^0.13" bitfield = "0.13"
clap = "^2.33" clap = "2.33"
gilrs = "^0.8" gilrs = "0.8"
pixels = "^0.6" pixels = "0.7"
winit = "^0.25" winit = "0.25"
winit_input_helper = "^0.10" winit_input_helper = "0.10"
rodio = "^0.14" rodio = "0.14"
rtrb = "^0.1.4" rtrb = "0.2"
directories-next = "2.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["std", "env-filter"] }
[profile.release] [profile.release]
debug = true debug = true

View File

@@ -2,13 +2,37 @@
[![Build Status](https://ci.paoda.moe/api/badges/paoda/gb/status.svg)](https://ci.paoda.moe/paoda/gb) [![Build Status](https://ci.paoda.moe/api/badges/paoda/gb/status.svg)](https://ci.paoda.moe/paoda/gb)
### Status ### Status
* Passes Blargg's cpu_instrs Test * From [Blargg Test ROMs](https://github.com/L-P/blargg-test-roms/)
* Renders Background & Window Tiles * [x] cpu_instrs
* Implements a PPU FIFO * [ ] instr_timing (kind of)
* [x] mem_timing
* [x] mem_timing-2
* [ ] dmg_sound (partial)
* [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 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 ### Controls
* The Sprite FIFO does not work as expected yet Controls are defined [here](https://git.musuka.dev/paoda/gb/src/branch/main/src/joypad.rs#L114)
* 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 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

Binary file not shown.

View File

@@ -1,3 +1,4 @@
use crate::apu::gen::SAMPLE_RATE;
use crate::bus::BusIo; use crate::bus::BusIo;
use crate::emu::SM83_CLOCK_SPEED; use crate::emu::SM83_CLOCK_SPEED;
use gen::SampleProducer; use gen::SampleProducer;
@@ -5,8 +6,8 @@ use types::ch1::{Sweep, SweepDirection};
use types::ch3::Volume as Ch3Volume; use types::ch3::Volume as Ch3Volume;
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter}; use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope}; use types::common::{EnvelopeDirection, FrequencyHigh, SoundDuty, VolumeEnvelope};
use types::fs::{FrameSequencer, State as FrameSequencerState}; use types::fs::{FrameSequencer, State as FSState};
use types::{ChannelControl, SoundOutput}; use types::{ChannelControl, NRx4, SoundOutput};
pub mod gen; pub mod gen;
mod types; mod types;
@@ -26,11 +27,13 @@ pub struct Apu {
/// Noise /// Noise
ch4: Channel4, ch4: Channel4,
sequencer: FrameSequencer, fs: FrameSequencer,
div_prev: Option<u16>, div_prev: Option<u16>,
prod: Option<SampleProducer<f32>>, prod: Option<SampleProducer<f32>>,
sample_counter: u64, sample_counter: u64,
cap: f32,
} }
impl BusIo for Apu { impl BusIo for Apu {
@@ -54,7 +57,7 @@ impl BusIo for Apu {
0x26 => self.ctrl.status(self), 0x26 => self.ctrl.status(self),
0x30..=0x3F => self.ch3.read_byte(addr), 0x30..=0x3F => self.ch3.read_byte(addr),
_ => { _ => {
eprintln!("Read 0xFF from unused IO register {:#06X} [APU]", addr); tracing::warn!("Attempted read from {:#06X}", addr);
0xFF 0xFF
} }
} }
@@ -66,29 +69,26 @@ impl BusIo for Apu {
0x11 if self.ctrl.enabled => self.ch1.set_duty(byte), 0x11 if self.ctrl.enabled => self.ch1.set_duty(byte),
0x12 if self.ctrl.enabled => self.ch1.set_envelope(byte), 0x12 if self.ctrl.enabled => self.ch1.set_envelope(byte),
0x13 if self.ctrl.enabled => self.ch1.set_freq_lo(byte), 0x13 if self.ctrl.enabled => self.ch1.set_freq_lo(byte),
0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(byte), 0x14 if self.ctrl.enabled => self.ch1.set_freq_hi(&self.fs, byte),
0x16 if self.ctrl.enabled => self.ch2.set_duty(byte), 0x16 if self.ctrl.enabled => self.ch2.set_duty(byte),
0x17 if self.ctrl.enabled => self.ch2.set_envelope(byte), 0x17 if self.ctrl.enabled => self.ch2.set_envelope(byte),
0x18 if self.ctrl.enabled => self.ch2.set_freq_lo(byte), 0x18 if self.ctrl.enabled => self.ch2.set_freq_lo(byte),
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), 0x1A if self.ctrl.enabled => self.ch3.set_dac_enabled(byte),
0x1B if self.ctrl.enabled => self.ch3.set_len(byte), 0x1B if self.ctrl.enabled => self.ch3.set_len(byte),
0x1C if self.ctrl.enabled => self.ch3.set_volume(byte), 0x1C if self.ctrl.enabled => self.ch3.set_volume(byte),
0x1D if self.ctrl.enabled => self.ch3.set_freq_lo(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), 0x20 if self.ctrl.enabled => self.ch4.set_len(byte),
0x21 if self.ctrl.enabled => self.ch4.set_envelope(byte), 0x21 if self.ctrl.enabled => self.ch4.set_envelope(byte),
0x22 if self.ctrl.enabled => self.ch4.set_poly(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), 0x24 if self.ctrl.enabled => self.ctrl.set_channel(byte),
0x25 if self.ctrl.enabled => self.ctrl.set_output(byte), 0x25 if self.ctrl.enabled => self.ctrl.set_output(byte),
0x26 => self.set_status(byte), 0x26 => self.set_status(byte),
0x30..=0x3F => self.ch3.write_byte(addr, byte), 0x30..=0x3F => self.ch3.write_byte(addr, byte),
_ if !self.ctrl.enabled => {} _ if !self.ctrl.enabled => {}
_ => eprintln!( _ => tracing::warn!("Attempted write of {:#04X} to {:#06X}", byte, addr),
"Wrote {:#04X} to unused IO register {:#06X} [APU]",
byte, addr
),
} }
} }
} }
@@ -99,9 +99,9 @@ impl Apu {
// Frame Sequencer (512Hz) // Frame Sequencer (512Hz)
if self.is_falling_edge(12, div) { if self.is_falling_edge(12, div) {
use FrameSequencerState::*; use FSState::*;
match self.sequencer.state() { match self.fs.state() {
Length => self.clock_length(), Length => self.clock_length(),
LengthAndSweep => { LengthAndSweep => {
self.clock_length(); self.clock_length();
@@ -111,7 +111,7 @@ impl Apu {
Nothing => {} Nothing => {}
} }
self.sequencer.next(); self.fs.next();
} }
self.div_prev = Some(div); self.div_prev = Some(div);
@@ -124,25 +124,33 @@ impl Apu {
if self.sample_counter >= SM83_CLOCK_SPEED { if self.sample_counter >= SM83_CLOCK_SPEED {
self.sample_counter %= SM83_CLOCK_SPEED; self.sample_counter %= SM83_CLOCK_SPEED;
if let Some(ref mut prod) = self.prod { if let Some(prod) = self.prod.as_mut() {
if prod.available_blocking() { if prod.available_blocking() {
// Sample the APU // Sample the APU
let ch1_amplitude =
Self::high_pass(&mut self.cap, self.ch1.amplitude(), self.ch1.enabled);
let (left, right) = self.ctrl.out.ch1(); let (left, right) = self.ctrl.out.ch1();
let ch1_left = if left { self.ch1.amplitude() } else { 0.0 }; let ch1_left = if left { ch1_amplitude } else { 0.0 };
let ch1_right = if right { self.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 (left, right) = self.ctrl.out.ch2();
let ch2_left = if left { self.ch2.amplitude() } else { 0.0 }; let ch2_left = if left { ch2_amplitude } else { 0.0 };
let ch2_right = if right { self.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 (left, right) = self.ctrl.out.ch3();
let ch3_left = if left { self.ch3.amplitude() } else { 0.0 }; let ch3_left = if left { ch3_amplitude } else { 0.0 };
let ch3_right = if right { self.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 (left, right) = self.ctrl.out.ch4();
let ch4_left = if left { self.ch4.amplitude() } else { 0.0 }; let ch4_left = if left { ch4_amplitude } else { 0.0 };
let ch4_right = if right { self.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 left_mixed = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
let right_mixed = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0; let right_mixed = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
@@ -164,7 +172,7 @@ impl Apu {
if self.ctrl.enabled { if self.ctrl.enabled {
// Frame Sequencer reset to Step 0 // Frame Sequencer reset to Step 0
self.sequencer.reset(); self.fs.reset();
// Square Duty units are reset to first step // Square Duty units are reset to first step
self.ch1.duty_pos = 0; self.ch1.duty_pos = 0;
@@ -217,25 +225,13 @@ impl Apu {
self.ch4.enabled = Default::default(); self.ch4.enabled = Default::default();
} }
fn process_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) { fn process_length(freq: &impl NRx4, counter: &mut u16, enabled: &mut bool) {
if freq_hi.length_disable() && *length_timer > 0 { if freq.length_enable() && *counter > 0 {
*length_timer -= 1; *counter -= 1;
// Check in this scope ensures (only) the above subtraction // Check in this scope ensures (only) the above subtraction
// made length_timer 0 // made length_timer 0
if *length_timer == 0 { if *counter == 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 {
*enabled = false; *enabled = false;
} }
} }
@@ -244,25 +240,25 @@ impl Apu {
fn clock_length(&mut self) { fn clock_length(&mut self) {
Self::process_length( Self::process_length(
&self.ch1.freq_hi, &self.ch1.freq_hi,
&mut self.ch1.length_timer, &mut self.ch1.length_counter,
&mut self.ch1.enabled, &mut self.ch1.enabled,
); );
Self::process_length( Self::process_length(
&self.ch2.freq_hi, &self.ch2.freq_hi,
&mut self.ch2.length_timer, &mut self.ch2.length_counter,
&mut self.ch2.enabled, &mut self.ch2.enabled,
); );
Self::process_length( Self::process_length(
&self.ch3.freq_hi, &self.ch3.freq_hi,
&mut self.ch3.length_timer, &mut self.ch3.length_counter,
&mut self.ch3.enabled, &mut self.ch3.enabled,
); );
Self::ch4_process_length( Self::process_length(
&self.ch4.freq, &self.ch4.freq,
&mut self.ch4.length_timer, &mut self.ch4.length_counter,
&mut self.ch4.enabled, &mut self.ch4.enabled,
); );
} }
@@ -279,7 +275,7 @@ impl Apu {
if self.ch1.sweep_enabled && period != 0 { if self.ch1.sweep_enabled && period != 0 {
let new_freq = self.ch1.calc_sweep_freq(); 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.set_frequency(new_freq);
self.ch1.shadow_freq = new_freq; self.ch1.shadow_freq = new_freq;
@@ -337,6 +333,19 @@ impl Apu {
None => false, None => false,
} }
} }
fn high_pass(capacitor: &mut f32, input: f32, enabled: bool) -> f32 {
const CHARGE_FACTOR: f32 = 0.999958;
let mut output = 0.0;
if enabled {
output = input - *capacitor;
*capacitor =
input - output * CHARGE_FACTOR.powi(SM83_CLOCK_SPEED as i32 / SAMPLE_RATE as i32);
}
output
}
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@@ -408,7 +417,7 @@ pub(crate) struct Channel1 {
sweep_enabled: bool, sweep_enabled: bool,
// Length Functionality // Length Functionality
length_timer: u16, length_counter: u16,
freq_timer: u16, freq_timer: u16,
duty_pos: u8, duty_pos: u8,
@@ -435,11 +444,11 @@ impl Channel1 {
/// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty /// 0xFF11 | NR11 - Channel 1 Sound length / Wave pattern duty
pub(crate) fn set_duty(&mut self, byte: u8) { pub(crate) fn set_duty(&mut self, byte: u8) {
self.duty = byte.into(); self.duty = byte.into();
self.length_timer = 64 - self.duty.sound_length() as u16; self.length_counter = 64 - self.duty.sound_length() as u16;
} }
/// 0xFF12 | NR12 - Channel 1 Volume Envelope /// 0xFF12 | NR12 - Channel 1 Volume Envelope
pub fn envelope(&self) -> u8 { pub(crate) fn envelope(&self) -> u8 {
u8::from(self.envelope) u8::from(self.envelope)
} }
@@ -463,25 +472,30 @@ impl Channel1 {
} }
/// 0xFF14 | NR14 - Channel 1 Frequency high /// 0xFF14 | NR14 - Channel 1 Frequency high
pub(crate) fn set_freq_hi(&mut self, byte: u8) { pub(crate) fn set_freq_hi(&mut self, fs: &FrameSequencer, byte: u8) {
self.freq_hi = byte.into(); let mut new_freq: FrequencyHigh = byte.into();
// If this bit is set, a trigger event occurs // If this bit is set, a trigger event occurs
if self.freq_hi.initial() { if new_freq.trigger() {
if self.is_dac_enabled() {
self.enabled = true; self.enabled = true;
}
// Length behaviour during trigger event if self.length_counter == 0 {
if self.length_timer == 0 { self.length_counter = 64;
self.length_timer = 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(); self.period_timer = self.envelope.period();
// Channel Volume reloaded
self.current_volume = self.envelope.init_vol(); self.current_volume = self.envelope.init_vol();
// Sweep behaviour during trigger event // Channel 1 Sweep Behaviour
let sweep_period = self.sweep.period(); let sweep_period = self.sweep.period();
let sweep_shift = self.sweep.shift_count(); let sweep_shift = self.sweep.shift_count();
@@ -493,7 +507,19 @@ impl Channel1 {
if sweep_shift != 0 { if sweep_shift != 0 {
let _ = self.calc_sweep_freq(); 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) { fn tick(&mut self) {
@@ -567,7 +593,7 @@ pub(crate) struct Channel2 {
current_volume: u8, current_volume: u8,
// Length Functionality // Length Functionality
length_timer: u16, length_counter: u16,
freq_timer: u16, freq_timer: u16,
duty_pos: u8, duty_pos: u8,
@@ -584,7 +610,7 @@ impl Channel2 {
/// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty /// 0xFF16 | NR21 - Channel 2 Sound length / Wave Pattern Duty
pub(crate) fn set_duty(&mut self, byte: u8) { pub(crate) fn set_duty(&mut self, byte: u8) {
self.duty = byte.into(); self.duty = byte.into();
self.length_timer = 64 - self.duty.sound_length() as u16; self.length_counter = 64 - self.duty.sound_length() as u16;
} }
/// 0xFF17 | NR22 - Channel 2 Volume ENvelope /// 0xFF17 | NR22 - Channel 2 Volume ENvelope
@@ -612,22 +638,36 @@ impl Channel2 {
} }
/// 0xFF19 | NR24 - Channel 2 Frequency high /// 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(); self.freq_hi = byte.into();
if self.freq_hi.initial() { obscure::nrx4::length_update(
// Envelope behaviour during trigger event &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(); self.period_timer = self.envelope.period();
// reload volume
self.current_volume = self.envelope.init_vol(); self.current_volume = self.envelope.init_vol();
// Length behaviour during trigger event self.enabled = self.is_dac_enabled();
if self.length_timer == 0 {
self.length_timer = 64;
}
if self.is_dac_enabled() {
self.enabled = true;
}
} }
} }
@@ -678,7 +718,7 @@ pub(crate) struct Channel3 {
wave_ram: [u8; WAVE_PATTERN_RAM_LEN], wave_ram: [u8; WAVE_PATTERN_RAM_LEN],
// Length Functionality // Length Functionality
length_timer: u16, length_counter: u16,
freq_timer: u16, freq_timer: u16,
offset: u8, offset: u8,
@@ -724,7 +764,7 @@ impl Channel3 {
/// 0xFF1B | NR31 - Sound Length /// 0xFF1B | NR31 - Sound Length
pub(crate) fn set_len(&mut self, byte: u8) { pub(crate) fn set_len(&mut self, byte: u8) {
self.len = byte; self.len = byte;
self.length_timer = 256 - self.len as u16; self.length_counter = 256 - self.len as u16;
} }
/// 0xFF1C | NR32 - Channel 3 Volume /// 0xFF1C | NR32 - Channel 3 Volume
@@ -756,18 +796,32 @@ impl Channel3 {
} }
/// 0xFF1E | NR34 - Channel 3 Frequency high /// 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(); 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 // Length behaviour during trigger event
if self.length_timer == 0 { if self.length_counter == 0 {
self.length_timer = 256; self.length_counter = 256;
} }
if self.dac_enabled { self.freq_timer = (2048 - self.frequency()) * 2;
self.enabled = true;
} // 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, current_volume: u8,
// Length Functionality // Length Functionality
length_timer: u16, length_counter: u16,
/// Linear Feedback Shift Register (15-bit) /// Linear Feedback Shift Register (15-bit)
lf_shift: u16, lf_shift: u16,
@@ -840,7 +894,7 @@ impl Channel4 {
/// 0xFF20 | NR41 - Channel 4 Sound Length /// 0xFF20 | NR41 - Channel 4 Sound Length
pub(crate) fn set_len(&mut self, byte: u8) { pub(crate) fn set_len(&mut self, byte: u8) {
self.len = byte & 0x3F; 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 /// 0xFF21 | NR42 - Channel 4 Volume Envelope
@@ -873,25 +927,37 @@ impl Channel4 {
} }
/// 0xFF23 | NR44 - Channel 4 Counter / Consecutive Selector and Restart /// 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(); self.freq = byte.into();
if self.freq.initial() { obscure::nrx4::length_update(
// Envelope behaviour during trigger event &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(); self.period_timer = self.envelope.period();
//reload volume
self.current_volume = self.envelope.init_vol(); self.current_volume = self.envelope.init_vol();
// Length behaviour during trigger event // LFSR reset
if self.length_timer == 0 {
self.length_timer = 64;
}
// LFSR behaviour during trigger event
self.lf_shift = 0x7FFF; self.lf_shift = 0x7FFF;
if self.is_dac_enabled() { self.enabled = self.is_dac_enabled();
self.enabled = true;
}
} }
} }
@@ -936,3 +1002,39 @@ impl Channel4 {
code << 4 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;
}
}
}
}
}

View File

@@ -5,34 +5,13 @@ pub(crate) const SAMPLE_RATE: u32 = 48000; // Hz
const CHANNEL_COUNT: usize = 2; const CHANNEL_COUNT: usize = 2;
const BUFFER_CAPACITY: usize = 2048 * CHANNEL_COUNT; // # of samples * the # of channels const BUFFER_CAPACITY: usize = 2048 * CHANNEL_COUNT; // # of samples * the # of channels
pub struct AudioSPSC<T> { pub fn init<T>() -> (SampleProducer<T>, SampleConsumer<T>) {
inner: RingBuffer<T>, let (prod, cons) = RingBuffer::new(BUFFER_CAPACITY);
}
impl<T> Default for AudioSPSC<T> {
fn default() -> Self {
Self {
inner: RingBuffer::new(BUFFER_CAPACITY),
}
}
}
impl<T> AudioSPSC<T> {
pub fn new(capacity: usize) -> Self {
Self {
inner: RingBuffer::new(capacity),
}
}
pub fn init(self) -> (SampleProducer<T>, SampleConsumer<T>) {
let (prod, cons) = self.inner.split();
( (
SampleProducer { inner: prod }, SampleProducer { inner: prod },
SampleConsumer { inner: cons }, SampleConsumer { inner: cons },
) )
} }
}
pub struct SampleProducer<T> { pub struct SampleProducer<T> {
inner: Producer<T>, inner: Producer<T>,

View File

@@ -1,5 +1,11 @@
use bitfield::bitfield; 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 { pub(crate) mod ch1 {
use super::bitfield; use super::bitfield;
@@ -103,7 +109,7 @@ pub(super) mod ch3 {
} }
pub(super) mod ch4 { pub(super) mod ch4 {
use super::bitfield; use super::{bitfield, NRx4};
bitfield! { bitfield! {
pub struct PolynomialCounter(u8); pub struct PolynomialCounter(u8);
@@ -177,17 +183,21 @@ pub(super) mod ch4 {
bitfield! { bitfield! {
pub struct Frequency(u8); pub struct Frequency(u8);
impl Debug; impl Debug;
_initial, _: 7; _trigger, _: 7;
_length_disable, _: 6; _length_enable, _set_length_enable: 6;
} }
impl Frequency { impl NRx4 for Frequency {
pub(crate) fn length_disable(&self) -> bool { fn trigger(&self) -> bool {
self._length_disable() self._trigger()
} }
pub(crate) fn initial(&self) -> bool { fn length_enable(&self) -> bool {
self._initial() 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 { pub(super) mod common {
use super::bitfield; use super::{bitfield, NRx4};
bitfield! { bitfield! {
pub struct FrequencyHigh(u8); pub struct FrequencyHigh(u8);
impl Debug; impl Debug;
_initial, _: 7; _trigger, _: 7;
_length_disable, _: 6; _length_enable, _set_length_enable: 6;
pub freq_bits, set_freq_bits: 2, 0; pub freq_bits, set_freq_bits: 2, 0;
} }
impl FrequencyHigh { impl NRx4 for FrequencyHigh {
pub(crate) fn initial(&self) -> bool { fn trigger(&self) -> bool {
self._initial() self._trigger()
} }
pub(crate) fn length_disable(&self) -> bool { fn length_enable(&self) -> bool {
self._length_disable() 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 { pub(crate) fn state(&self) -> 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 { pub(crate) enum State {
Length, Length,
Nothing, Nothing,

View File

@@ -13,16 +13,16 @@ pub(crate) const BOOT_SIZE: usize = 0x100;
#[derive(Debug)] #[derive(Debug)]
pub struct Bus { pub struct Bus {
boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
cart: Option<Cartridge>, pub(crate) cart: Option<Cartridge>,
pub(crate) ppu: Ppu, pub(crate) ppu: Ppu,
work_ram: WorkRam, work_ram: WorkRam,
var_ram: VariableWorkRam, var_ram: VariableWorkRam,
pub(crate) timer: Timer, timer: Timer,
int: Interrupt, int: Interrupt,
pub(crate) apu: Apu, apu: Apu,
high_ram: HighRam, high_ram: HighRam,
serial: Serial, serial: Serial,
pub(crate) joypad: Joypad, pub(crate) joyp: Joypad,
} }
impl Default for Bus { impl Default for Bus {
@@ -38,7 +38,7 @@ impl Default for Bus {
apu: Default::default(), apu: Default::default(),
high_ram: Default::default(), high_ram: Default::default(),
serial: Default::default(), serial: Default::default(),
joypad: Default::default(), joyp: Default::default(),
} }
} }
} }
@@ -64,13 +64,16 @@ impl Bus {
self.boot.is_some() self.boot.is_some()
} }
#[inline]
pub(crate) fn clock(&mut self) { pub(crate) fn clock(&mut self) {
self.tick(4); self.tick(4);
} }
#[inline]
fn tick(&mut self, limit: u8) { fn tick(&mut self, limit: u8) {
for _ in 0..limit { for _ in 0..limit {
self.timer.tick(); self.timer.tick();
self.cart.as_mut().map(|cart| cart.tick());
self.ppu.tick(); self.ppu.tick();
self.apu.tick(self.timer.divider); self.apu.tick(self.timer.divider);
self.dma_tick() self.dma_tick()
@@ -83,10 +86,15 @@ impl Bus {
self.oam_write_byte(dest_addr, byte); self.oam_write_byte(dest_addr, byte);
} }
} }
#[inline]
pub(crate) fn apu_mut(&mut self) -> &mut Apu {
&mut self.apu
}
} }
impl Bus { impl Bus {
pub fn oam_read_byte(&self, addr: u16) -> u8 { pub(crate) fn oam_read_byte(&self, addr: u16) -> u8 {
match addr { match addr {
0x0000..=0x7FFF => { 0x0000..=0x7FFF => {
// 16KB ROM bank 00 (ends at 0x3FFF) // 16KB ROM bank 00 (ends at 0x3FFF)
@@ -99,14 +107,14 @@ impl Bus {
match self.cart.as_ref() { match self.cart.as_ref() {
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => 0xFF,
} }
} }
0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM 0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
0xA000..=0xBFFF => match self.cart.as_ref() { 0xA000..=0xBFFF => match self.cart.as_ref() {
// 8KB External RAM // 8KB External RAM
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => 0xFF,
}, },
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0 0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N 0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
@@ -133,7 +141,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); self.ppu.oam.write_byte(addr, byte);
} }
} }
@@ -152,7 +160,7 @@ impl BusIo for Bus {
match self.cart.as_ref() { match self.cart.as_ref() {
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => 0xFF,
} }
} }
0x8000..=0x9FFF => { 0x8000..=0x9FFF => {
@@ -165,7 +173,7 @@ impl BusIo for Bus {
0xA000..=0xBFFF => match self.cart.as_ref() { 0xA000..=0xBFFF => match self.cart.as_ref() {
// 8KB External RAM // 8KB External RAM
Some(cart) => cart.read_byte(addr), Some(cart) => cart.read_byte(addr),
None => panic!("Tried to read from a non-existent cartridge"), None => 0xFF,
}, },
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0 0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N 0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
@@ -212,7 +220,7 @@ impl BusIo for Bus {
// Every address here starts with 0xFF so we can just check the // Every address here starts with 0xFF so we can just check the
// low byte to figure out which register it is // low byte to figure out which register it is
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.p1, 0x00 => self.joyp.p1,
0x01 => self.serial.next, 0x01 => self.serial.next,
0x02 => self.serial.ctrl.into(), 0x02 => self.serial.ctrl.into(),
0x04 => (self.timer.divider >> 8) as u8, 0x04 => (self.timer.divider >> 8) as u8,
@@ -233,8 +241,9 @@ impl BusIo for Bus {
0x49 => self.ppu.monochrome.obj_palette_1.into(), 0x49 => self.ppu.monochrome.obj_palette_1.into(),
0x4A => self.ppu.pos.window_y, 0x4A => self.ppu.pos.window_y,
0x4B => self.ppu.pos.window_x, 0x4B => self.ppu.pos.window_x,
0x4F => 0xFF, // CGB VRAM Bank Select
_ => { _ => {
eprintln!("Read 0xFF from unused IO register {:#06X}.", addr); tracing::warn!("Attempted read from {:#06X} on IO", addr);
0xFF 0xFF
} }
} }
@@ -255,9 +264,8 @@ impl BusIo for Bus {
0x0000..=0x7FFF => { 0x0000..=0x7FFF => {
// 16KB ROM bank 00 (ends at 0x3FFF) // 16KB ROM bank 00 (ends at 0x3FFF)
// and 16KB ROM Bank 01 -> NN (switchable via MB) // and 16KB ROM Bank 01 -> NN (switchable via MB)
match self.cart.as_mut() { if let Some(cart) = self.cart.as_mut() {
Some(cart) => cart.write_byte(addr, byte), cart.write_byte(addr, byte);
None => panic!("Tried to write into non-existent cartridge"),
} }
} }
0x8000..=0x9FFF => { 0x8000..=0x9FFF => {
@@ -269,9 +277,8 @@ impl BusIo for Bus {
} }
0xA000..=0xBFFF => { 0xA000..=0xBFFF => {
// 8KB External RAM // 8KB External RAM
match self.cart.as_mut() { if let Some(cart) = self.cart.as_mut() {
Some(cart) => cart.write_byte(addr, byte), cart.write_byte(addr, byte);
None => panic!("Tried to write into non-existent cartridge"),
} }
} }
0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0 0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
@@ -311,7 +318,7 @@ impl BusIo for Bus {
// Every address here starts with 0xFF so we can just check the // Every address here starts with 0xFF so we can just check the
// low byte to figure out which register it is // low byte to figure out which register it is
match addr & 0x00FF { match addr & 0x00FF {
0x00 => self.joypad.update(byte), 0x00 => self.joyp.update(byte),
0x01 => self.serial.next = byte, 0x01 => self.serial.next = byte,
0x02 => self.serial.ctrl = byte.into(), 0x02 => self.serial.ctrl = byte.into(),
0x04 => self.timer.divider = 0x0000, 0x04 => self.timer.divider = 0x0000,
@@ -345,13 +352,15 @@ impl BusIo for Bus {
0x4A => self.ppu.pos.window_y = byte, 0x4A => self.ppu.pos.window_y = byte,
0x4B => self.ppu.pos.window_x = byte, 0x4B => self.ppu.pos.window_x = byte,
0x4D => {} // CGB Specific Register 0x4D => {} // CGB Specific Register
0x4F => {} // CGB VRAM Bank Select
0x50 => { 0x50 => {
// Disable Boot ROM // Disable Boot ROM
if byte != 0 { if byte != 0 {
self.boot = None; self.boot = None;
} }
} }
_ => 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 => { 0xFF80..=0xFFFE => {
@@ -373,7 +382,7 @@ impl Bus {
let lcd_stat = self.ppu.int.lcd_stat(); let lcd_stat = self.ppu.int.lcd_stat();
// Read the current interrupt information from the Joypad // Read the current interrupt information from the Joypad
let joypad = self.joypad.interrupt(); let joypad = self.joyp.interrupt();
// Read the current interrupt information from the Timer // Read the current interrupt information from the Timer
let timer = self.timer.interrupt(); let timer = self.timer.interrupt();
@@ -403,7 +412,7 @@ impl Bus {
self.ppu.int.set_lcd_stat(lcd_stat); self.ppu.int.set_lcd_stat(lcd_stat);
// Update the Joypad's instance of the following interrupts // Update the Joypad's instance of the following interrupts
self.joypad.set_interrupt(joypad); self.joyp.set_interrupt(joypad);
// Update the Timer's instance of the following interrupts // Update the Timer's instance of the following interrupts
self.timer.set_interrupt(timer); self.timer.set_interrupt(timer);

View File

@@ -1,9 +1,14 @@
use bitfield::bitfield;
use crate::bus::BusIo; use crate::bus::BusIo;
use crate::emu::SM83_CLOCK_SPEED;
use crate::Cycle;
const RAM_SIZE_ADDRESS: usize = 0x0149; const RAM_SIZE_ADDRESS: usize = 0x0149;
const ROM_SIZE_ADDRESS: usize = 0x0148; const ROM_SIZE_ADDRESS: usize = 0x0148;
const MBC_TYPE_ADDRESS: usize = 0x0147; const MBC_TYPE_ADDRESS: usize = 0x0147;
const ROM_TITLE_RANGE: std::ops::RangeInclusive<usize> = 0x0134..=0x0143; const ROM_TITLE_START: usize = 0x134;
const ROM_TITLE_MAX_SIZE: usize = 16;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Cartridge { pub(crate) struct Cartridge {
@@ -15,7 +20,7 @@ pub(crate) struct Cartridge {
impl Cartridge { impl Cartridge {
pub(crate) fn new(memory: Vec<u8>) -> Self { pub(crate) fn new(memory: Vec<u8>) -> Self {
let title = Self::find_title(&memory); let title = Self::find_title(&memory);
eprintln!("Cartridge Title: {:?}", title); tracing::info!("Title: {:?}", title);
Self { Self {
mbc: Self::detect_mbc(&memory), mbc: Self::detect_mbc(&memory),
@@ -24,6 +29,18 @@ impl Cartridge {
} }
} }
pub(crate) fn ext_ram(&self) -> Option<&[u8]> {
self.mbc.ext_ram()
}
pub(crate) fn write_ext_ram(&mut self, memory: Vec<u8>) {
self.mbc.write_ext_ram(memory)
}
pub(crate) fn tick(&mut self) {
self.mbc.tick()
}
fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> { fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
let ram_size = Self::detect_ram_info(memory); let ram_size = Self::detect_ram_info(memory);
let rom_size = Self::detect_rom_info(memory); let rom_size = Self::detect_rom_info(memory);
@@ -31,31 +48,35 @@ impl Cartridge {
let ram_cap = ram_size.capacity(); let ram_cap = ram_size.capacity();
let rom_cap = rom_size.capacity(); let rom_cap = rom_size.capacity();
eprintln!("Cartridge Ram Size: {} bytes", ram_cap); tracing::info!("RAM size: {} bytes", ram_cap);
eprintln!("Cartridge ROM Size: {} bytes", rom_size.capacity()); tracing::info!("ROM size: {} bytes", rom_size.capacity());
eprintln!("MBC Type: {:?}", mbc_kind); tracing::info!("MBC kind: {:?}", mbc_kind);
match mbc_kind { match mbc_kind {
MBCKind::None => Box::new(NoMBC), MBCKind::None => Box::new(NoMBC),
MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)), MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)),
MBCKind::MBC1WithBattery => Box::new(MBC1::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::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::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::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> { fn find_title(memory: &[u8]) -> Option<String> {
let slice = &memory[ROM_TITLE_RANGE]; let title_bytes = &memory[ROM_TITLE_START..(ROM_TITLE_START + ROM_TITLE_MAX_SIZE)];
let with_nulls = std::str::from_utf8(slice).ok();
let trimmed = with_nulls.map(|s| s.trim_matches('\0').trim());
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("") | None => None,
Some(_) => trimmed.map(String::from), Some(title) => Some(String::from(title)),
} }
} }
@@ -117,6 +138,8 @@ struct MBC1 {
memory: Vec<u8>, memory: Vec<u8>,
rom_size: RomSize, rom_size: RomSize,
mem_enabled: bool, mem_enabled: bool,
has_battery: bool,
} }
impl MBC1 { impl MBC1 {
@@ -129,6 +152,20 @@ impl MBC1 {
ram_bank: Default::default(), ram_bank: Default::default(),
mode: Default::default(), mode: Default::default(),
mem_enabled: Default::default(), mem_enabled: Default::default(),
has_battery: Default::default(),
}
}
fn with_battery(ram_size: RamSize, rom_size: RomSize) -> Self {
Self {
rom_bank: 0x01,
memory: vec![0; ram_size.capacity() as usize],
ram_size,
rom_size,
ram_bank: Default::default(),
mode: Default::default(),
mem_enabled: Default::default(),
has_battery: true,
} }
} }
@@ -197,6 +234,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 { impl MBCIo for MBC1 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MBCResult {
use MBCResult::*; use MBCResult::*;
@@ -237,10 +289,120 @@ impl MBCIo for MBC1 {
} }
} }
impl RtClockTick for MBC1 {
fn tick(&mut self) {}
}
#[derive(Debug, Default, Clone, Copy)]
struct RtClock {
/// 6-bit unsigned integer
sec: u8,
/// 6-bit unsigned integer
min: u8,
/// 6-bit unsigned integer
hr: u8,
day_low: u8,
day_high: DayHigh,
cycles: Cycle,
}
impl RtClock {
fn inc_day(&mut self) {
// TODO: Figure out order of operations, the brackets are a bit too defenseive here
let days: u16 = (((self.day_high.ninth() as u16) << 8) | self.day_low as u16) + 1;
if days > 0x1FF {
self.day_high.set_carry(true);
}
self.day_high.set_ninth(((days >> 8) & 0x01) == 0x01);
self.day_low = days as u8;
}
}
impl RtClockTick for RtClock {
fn tick(&mut self) {
// This is the sort of situation where you'd want to use a scheduler.
if self.day_high.halt() {
return;
}
self.cycles += 1;
if self.cycles >= SM83_CLOCK_SPEED {
self.cycles %= SM83_CLOCK_SPEED;
self.sec += 1;
if self.sec == 60 {
self.sec = 0;
self.min += 1;
}
if self.min == 60 {
self.min = 0;
self.hr += 1;
}
if self.hr == 24 {
self.hr = 0;
self.inc_day();
}
}
}
}
trait RtClockTick {
fn tick(&mut self);
}
bitfield! {
struct DayHigh(u8);
impl Debug;
_, set_carry: 7;
halt, _: 6;
ninth, set_ninth: 0;
}
impl Copy for DayHigh {}
impl Clone for DayHigh {
fn clone(&self) -> Self {
*self
}
}
impl Default for DayHigh {
fn default() -> Self {
Self(0)
}
}
impl From<u8> for DayHigh {
fn from(byte: u8) -> Self {
Self(byte)
}
}
impl From<DayHigh> for u8 {
fn from(dh: DayHigh) -> Self {
dh.0
}
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum MBC3Device { enum MBC3Device {
ExternalRam, ExternalRam,
RealTimeClock, Clock(RtcRegister),
}
#[derive(Debug, Clone, Copy)]
enum RtcRegister {
Second,
Minute,
Hour,
DayLow,
DayHigh,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -256,6 +418,10 @@ struct MBC3 {
// RTC Data Latch Previous Write // RTC Data Latch Previous Write
prev_latch_write: Option<u8>, prev_latch_write: Option<u8>,
has_battery: bool,
rtc: RtClock,
rtc_latch: Option<RtClock>,
} }
impl MBC3 { impl MBC3 {
@@ -267,6 +433,38 @@ impl MBC3 {
devs_enabled: Default::default(), devs_enabled: Default::default(),
mapped: Default::default(), mapped: Default::default(),
prev_latch_write: Default::default(), prev_latch_write: Default::default(),
has_battery: Default::default(),
rtc: Default::default(),
rtc_latch: 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(),
rtc: Default::default(),
rtc_latch: 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);
} }
} }
} }
@@ -274,6 +472,7 @@ impl MBC3 {
impl MBCIo for MBC3 { impl MBCIo for MBC3 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MBCResult {
use MBCResult::*; use MBCResult::*;
use RtcRegister::*;
let res = match addr { let res = match addr {
0x0000..=0x3FFF => Address(addr as usize), 0x0000..=0x3FFF => Address(addr as usize),
@@ -282,9 +481,18 @@ impl MBCIo for MBC3 {
Some(MBC3Device::ExternalRam) if self.devs_enabled => { Some(MBC3Device::ExternalRam) if self.devs_enabled => {
Value(self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)]) Value(self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)])
} }
Some(MBC3Device::RealTimeClock) if self.devs_enabled => { Some(MBC3Device::Clock(reg)) if self.devs_enabled => Value(
todo!("Return Latched value of register") self.rtc_latch
} .as_ref()
.map(|rtc| match reg {
Second => rtc.sec,
Minute => rtc.min,
Hour => rtc.hr,
DayLow => rtc.day_low,
DayHigh => rtc.day_high.into(),
})
.unwrap_or(0xFF),
),
_ => Value(0xFF), _ => Value(0xFF),
}, },
_ => unreachable!("A read from {:#06X} should not be handled by MBC3", addr), _ => unreachable!("A read from {:#06X} should not be handled by MBC3", addr),
@@ -294,6 +502,8 @@ impl MBCIo for MBC3 {
} }
fn handle_write(&mut self, addr: u16, byte: u8) { fn handle_write(&mut self, addr: u16, byte: u8) {
use RtcRegister::*;
match addr { match addr {
0x000..=0x1FFF => self.devs_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one 0x000..=0x1FFF => self.devs_enabled = (byte & 0x0F) == 0x0A, // Enable External RAM and Access to RTC if there is one
0x2000..=0x3FFF => { 0x2000..=0x3FFF => {
@@ -307,15 +517,18 @@ impl MBCIo for MBC3 {
self.ram_bank = byte & 0x03; self.ram_bank = byte & 0x03;
self.mapped = Some(MBC3Device::ExternalRam); self.mapped = Some(MBC3Device::ExternalRam);
} }
0x08 | 0x09 | 0x0A | 0x0B | 0x0C => { 0x08 => self.mapped = Some(MBC3Device::Clock(Second)),
self.mapped = Some(MBC3Device::RealTimeClock); 0x09 => self.mapped = Some(MBC3Device::Clock(Minute)),
} 0x0A => self.mapped = Some(MBC3Device::Clock(Hour)),
0x0B => self.mapped = Some(MBC3Device::Clock(DayLow)),
0x0C => self.mapped = Some(MBC3Device::Clock(DayHigh)),
_ => {} _ => {}
}, },
0x6000..=0x7FFF => { 0x6000..=0x7FFF => {
if let Some(0x00) = self.prev_latch_write { if let Some(0x00) = self.prev_latch_write {
if byte == 0x01 { if byte == 0x01 {
todo!("Perform Data Latch") self.rtc_latch = Some(self.rtc);
} }
} }
self.prev_latch_write = Some(byte); self.prev_latch_write = Some(byte);
@@ -324,9 +537,13 @@ impl MBCIo for MBC3 {
Some(MBC3Device::ExternalRam) if self.devs_enabled => { Some(MBC3Device::ExternalRam) if self.devs_enabled => {
self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte self.memory[0x2000 * self.ram_bank as usize + (addr as usize - 0xA000)] = byte
} }
Some(MBC3Device::RealTimeClock) if self.devs_enabled => { Some(MBC3Device::Clock(rtc_reg)) if self.devs_enabled => match rtc_reg {
todo!("Write to RTC") Second => self.rtc.sec = byte & 0x3F,
} Minute => self.rtc.min = byte & 0x3F,
Hour => self.rtc.hr = byte & 0x1F,
DayLow => self.rtc.day_low = byte & 0xFF,
DayHigh => self.rtc.day_high = (byte & 0xC1).into(),
},
_ => {} _ => {}
}, },
_ => unreachable!("A write to {:#06X} should not be handled by MBC3", addr), _ => unreachable!("A write to {:#06X} should not be handled by MBC3", addr),
@@ -334,6 +551,12 @@ impl MBCIo for MBC3 {
} }
} }
impl RtClockTick for MBC3 {
fn tick(&mut self) {
self.rtc.tick();
}
}
#[derive(Debug)] #[derive(Debug)]
struct MBC5 { struct MBC5 {
/// 9-bit number /// 9-bit number
@@ -342,9 +565,10 @@ struct MBC5 {
ram_bank: u8, ram_bank: u8,
rom_cap: usize, rom_cap: usize,
memory: Vec<u8>, memory: Vec<u8>,
mem_enabled: bool, mem_enabled: bool,
has_battery: bool,
} }
impl MBC5 { impl MBC5 {
@@ -355,6 +579,18 @@ impl MBC5 {
rom_cap, rom_cap,
ram_bank: Default::default(), ram_bank: Default::default(),
mem_enabled: Default::default(), mem_enabled: Default::default(),
has_battery: Default::default(),
}
}
fn with_battery(ram_cap: usize, rom_cap: usize) -> Self {
Self {
rom_bank: 0x01,
memory: vec![0; ram_cap],
rom_cap,
ram_bank: Default::default(),
mem_enabled: Default::default(),
has_battery: true,
} }
} }
@@ -363,6 +599,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 { impl MBCIo for MBC5 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MBCResult {
use MBCResult::*; use MBCResult::*;
@@ -393,6 +644,10 @@ impl MBCIo for MBC5 {
} }
} }
impl RtClockTick for MBC5 {
fn tick(&mut self) {}
}
#[derive(Debug)] #[derive(Debug)]
struct MBC2 { struct MBC2 {
/// 4-bit number /// 4-bit number
@@ -401,6 +656,7 @@ struct MBC2 {
mem_enabled: bool, mem_enabled: bool,
rom_cap: usize, rom_cap: usize,
has_battery: bool,
} }
impl MBC2 { impl MBC2 {
@@ -410,8 +666,19 @@ impl MBC2 {
Self { Self {
rom_bank: 0x01, rom_bank: 0x01,
memory: Box::new([0; Self::RAM_SIZE]), memory: Box::new([0; Self::RAM_SIZE]),
mem_enabled: Default::default(),
rom_cap, 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,
} }
} }
@@ -420,6 +687,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 { impl MBCIo for MBC2 {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MBCResult {
use MBCResult::*; use MBCResult::*;
@@ -454,20 +736,38 @@ impl MBCIo for MBC2 {
} }
} }
impl RtClockTick for MBC2 {
fn tick(&mut self) {}
}
#[derive(Debug)] #[derive(Debug)]
struct NoMBC; struct NoMBC;
impl Savable for NoMBC {
fn ext_ram(&self) -> Option<&[u8]> {
None
}
fn write_ext_ram(&mut self, _memory: Vec<u8>) {
// Nothing Happens Here
}
}
impl MBCIo for NoMBC { impl MBCIo for NoMBC {
fn handle_read(&self, addr: u16) -> MBCResult { fn handle_read(&self, addr: u16) -> MBCResult {
MBCResult::Address(addr as usize) MBCResult::Address(addr as usize)
} }
fn handle_write(&mut self, _addr: u16, _byte: u8) { fn handle_write(&mut self, _: u16, byte: u8) {
// eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); tracing::warn!("Attempted write of {:#04X} to cartridge w/out MBC", byte);
} }
} }
trait MBCIo { impl RtClockTick for NoMBC {
fn tick(&mut self) {}
}
trait MBCIo: Savable + RtClockTick {
fn handle_read(&self, addr: u16) -> MBCResult; fn handle_read(&self, addr: u16) -> MBCResult;
fn handle_write(&mut self, addr: u16, byte: u8); fn handle_write(&mut self, addr: u16, byte: u8);
} }
@@ -607,3 +907,8 @@ impl Default for Box<dyn MBCIo> {
Box::new(NoMBC) Box::new(NoMBC)
} }
} }
trait Savable {
fn ext_ram(&self) -> Option<&[u8]>;
fn write_ext_ram(&mut self, memory: Vec<u8>);
}

View File

@@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Cpu { pub struct Cpu {
pub bus: Bus, pub(crate) bus: Bus,
reg: Registers, reg: Registers,
flags: Flags, flags: Flags,
ime: ImeState, ime: ImeState,
@@ -15,6 +15,7 @@ pub struct Cpu {
} }
impl Cpu { impl Cpu {
#[allow(dead_code)]
pub(crate) fn without_boot() -> Self { pub(crate) fn without_boot() -> Self {
Self { Self {
reg: Registers { reg: Registers {
@@ -57,10 +58,7 @@ impl Cpu {
} }
pub(crate) fn is_halted(&self) -> bool { pub(crate) fn is_halted(&self) -> bool {
match self.state { matches!(self.state, State::Halt(_))
State::Halt(_) => true,
_ => false,
}
} }
pub(crate) fn halt_kind(&self) -> Option<HaltKind> { pub(crate) fn halt_kind(&self) -> Option<HaltKind> {
@@ -105,7 +103,7 @@ impl Cpu {
/// routine. /// routine.
/// ///
/// Handle HALT and interrupts. /// Handle HALT and interrupts.
pub fn step(&mut self) -> Cycle { pub(crate) fn step(&mut self) -> Cycle {
// Log instructions // Log instructions
// if self.reg.pc > 0xFF { // if self.reg.pc > 0xFF {
// let out = std::io::stdout(); // let out = std::io::stdout();
@@ -120,13 +118,10 @@ impl Cpu {
use HaltKind::*; use HaltKind::*;
self.bus.clock(); self.bus.clock();
return match kind {
let elapsed = match kind {
ImeEnabled | NonePending => 4, ImeEnabled | NonePending => 4,
SomePending => todo!("Implement HALT bug"), SomePending => todo!("Implement HALT bug"),
}; };
return elapsed;
} }
let opcode = self.fetch(); let opcode = self.fetch();
@@ -135,11 +130,11 @@ impl Cpu {
self.handle_ei(); self.handle_ei();
// For use in Blargg's Test ROMs // For use in Blargg's Test ROMs
if self.read_byte(0xFF02) == 0x81 { // if self.read_byte(0xFF02) == 0x81 {
let c = self.read_byte(0xFF01) as char; // let c = self.read_byte(0xFF01) as char;
self.write_byte(0xFF02, 0x00); // self.write_byte(0xFF02, 0x00);
eprint!("{}", c); // eprint!("{}", c);
} // }
elapsed elapsed
} }
@@ -156,14 +151,6 @@ impl BusIo for Cpu {
} }
impl Cpu { impl Cpu {
pub(crate) fn bus(&self) -> &Bus {
&self.bus
}
pub(crate) fn bus_mut(&mut self) -> &mut Bus {
&mut self.bus
}
fn handle_ei(&mut self) { fn handle_ei(&mut self) {
match self.ime { match self.ime {
ImeState::EiExecuted => self.ime = ImeState::Pending, ImeState::EiExecuted => self.ime = ImeState::Pending,

View File

@@ -1,8 +1,12 @@
use crate::apu::gen::SampleProducer; use crate::apu::gen::SampleProducer;
use crate::bus::BOOT_SIZE;
use crate::cpu::Cpu; use crate::cpu::Cpu;
use crate::joypad::{self, Joypad};
use crate::{Cycle, GB_HEIGHT, GB_WIDTH}; use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
use clap::crate_name;
use gilrs::Gilrs; use gilrs::Gilrs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
@@ -15,10 +19,10 @@ pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper
let mut elapsed = 0; let mut elapsed = 0;
if let Some(event) = gamepad.next_event() { if let Some(event) = gamepad.next_event() {
joypad::handle_gamepad_input(emu.joyp_mut(), event); crate::joypad::handle_gamepad_input(&mut emu.cpu.bus.joyp, event);
} }
crate::joypad::handle_keyboard_input(&mut emu.cpu.bus.joyp, key);
joypad::handle_keyboard_input(emu.joyp_mut(), key);
while elapsed < CYCLES_IN_FRAME { while elapsed < CYCLES_IN_FRAME {
elapsed += emu.step(); elapsed += emu.step();
} }
@@ -27,7 +31,7 @@ pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper
} }
pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) { pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
buf.copy_from_slice(emu.cpu.bus().ppu.frame_buf()); buf.copy_from_slice(emu.cpu.bus.ppu.frame_buf.as_ref());
} }
pub struct Emulator { pub struct Emulator {
@@ -35,87 +39,101 @@ pub struct Emulator {
timestamp: Cycle, timestamp: Cycle,
} }
impl Default for Emulator {
fn default() -> Self {
Self::new()
}
}
impl Emulator { impl Emulator {
fn new(cpu: Cpu) -> Self { pub fn new() -> Self {
Self { Self {
cpu, cpu: Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")),
timestamp: Default::default(), timestamp: Default::default(),
} }
} }
pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
Ok(Self {
cpu: Cpu::with_boot(Self::read_boot(path)?),
timestamp: Default::default(),
})
}
fn read_boot<P: AsRef<Path>>(path: P) -> std::io::Result<[u8; BOOT_SIZE]> {
let mut buf = [0; BOOT_SIZE];
let mut file = File::open(path.as_ref())?;
file.read_exact(&mut buf)?;
Ok(buf)
}
fn step(&mut self) -> Cycle { fn step(&mut self) -> Cycle {
self.cpu.step() let cycles = self.cpu.step();
self.timestamp += cycles;
cycles
} }
fn load_cart(&mut self, rom: Vec<u8>) { pub fn read_game_rom<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
self.cpu.bus_mut().load_cart(rom) self.load_rom(std::fs::read(path.as_ref())?);
Ok(())
} }
fn joyp_mut(&mut self) -> &mut Joypad { fn load_rom(&mut self, rom: Vec<u8>) {
&mut self.cpu.bus_mut().joypad self.cpu.bus.load_cart(rom);
} }
pub fn set_prod(&mut self, prod: SampleProducer<f32>) { pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
self.cpu.bus_mut().apu.attach_producer(prod) self.cpu.bus.apu_mut().attach_producer(prod)
} }
pub fn title(&self) -> &str { pub fn title(&self) -> &str {
self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE) self.cpu.bus.cart_title().unwrap_or(DEFAULT_TITLE)
}
pub fn try_write_sav(&self) -> std::io::Result<()> {
if let Some(ext_ram) = self.cpu.bus.cart.as_ref().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)?;
} }
} }
pub mod build { Ok(())
use std::fs::File;
use std::io::{Read, Result};
use std::path::Path;
use crate::bus::BOOT_SIZE;
use crate::cpu::Cpu;
use super::Emulator;
#[derive(Debug, Default)]
pub struct EmulatorBuilder {
boot: Option<[u8; BOOT_SIZE]>,
cart: Option<Vec<u8>>,
} }
impl EmulatorBuilder { pub fn try_load_sav(&mut self) -> std::io::Result<()> {
pub fn new() -> Self { if let Some(cart) = &mut self.cpu.bus.cart {
Default::default() 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");
pub fn with_boot<P: AsRef<Path>>(mut self, path: P) -> Result<Self> { if let Ok(mut file) = File::open(&save_path) {
let mut file = File::open(path.as_ref())?; tracing::info!("Load {:?}", save_path);
let mut buf = [0x00; BOOT_SIZE]; let mut memory = Vec::new();
file.read_exact(&mut buf)?; file.read_to_end(&mut memory)?;
cart.write_ext_ram(memory);
self.boot = Some(buf); }
Ok(self) }
} }
pub fn with_cart<P: AsRef<Path>>(mut self, path: P) -> Result<Self> { Ok(())
let mut file = File::open(path.as_ref())?; }
let mut buf = Vec::new(); fn data_path() -> Option<PathBuf> {
file.read_to_end(&mut buf)?; match directories_next::ProjectDirs::from("dev", "musuka", crate_name!()) {
Some(dirs) => {
self.cart = Some(buf); let data_local = dirs.data_local_dir();
Ok(self) std::fs::create_dir_all(data_local).ok()?;
} Some(data_local.to_path_buf())
}
pub fn finish(mut self) -> Emulator { None => None,
let mut emu = Emulator::new(match self.boot {
Some(rom) => Cpu::with_boot(rom),
None => Cpu::without_boot(),
});
if let Some(rom) = self.cart.take() {
emu.load_cart(rom)
}
emu
} }
} }
} }

View File

@@ -116,7 +116,7 @@ impl std::fmt::Debug for Instruction {
impl Instruction { impl Instruction {
pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle { pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
match instruction { match instruction {
Instruction::NOP => (4), Instruction::NOP => 4,
Instruction::LD(target, src) => match (target, src) { Instruction::LD(target, src) => match (target, src) {
(LDTarget::IndirectImmediateWord, LDSource::SP) => { (LDTarget::IndirectImmediateWord, LDSource::SP) => {
// LD (u16), SP | Store stack pointer in byte at 16-bit register // LD (u16), SP | Store stack pointer in byte at 16-bit register
@@ -334,7 +334,6 @@ impl Instruction {
Instruction::ADD(target, src) => match (target, src) { Instruction::ADD(target, src) => match (target, src) {
(AddTarget::HL, AddSource::Group1(pair)) => { (AddTarget::HL, AddSource::Group1(pair)) => {
// ADD HL, r16 | Add 16-bit register to HL // ADD HL, r16 | Add 16-bit register to HL
// FIXME: Memory Timings are not properly emulated for this instruction
use Group1RegisterPair::*; use Group1RegisterPair::*;
let mut flags: Flags = *cpu.flags(); let mut flags: Flags = *cpu.flags();
@@ -342,10 +341,11 @@ impl Instruction {
BC | DE | HL | SP => { BC | DE | HL | SP => {
let left = cpu.register_pair(RegisterPair::HL); let left = cpu.register_pair(RegisterPair::HL);
let right = cpu.register_pair(pair.as_register_pair()); let right = cpu.register_pair(pair.as_register_pair());
cpu.set_register_pair( let result = Self::add_u16(left, right, &mut flags);
RegisterPair::HL,
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); cpu.set_flags(flags);
@@ -361,12 +361,12 @@ impl Instruction {
let (cycles, sum) = match reg { let (cycles, sum) = match reg {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let right = cpu.register(reg.cpu_register()); let right = cpu.register(reg.cpu_register());
((4), Self::add(left, right, &mut flags)) (4, Self::add(left, right, &mut flags))
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
((8), Self::add(left, right, &mut flags)) (8, Self::add(left, right, &mut flags))
} }
}; };
@@ -376,12 +376,15 @@ impl Instruction {
} }
(AddTarget::SP, AddSource::ImmediateSignedByte) => { (AddTarget::SP, AddSource::ImmediateSignedByte) => {
// ADD SP, i8 | Add i8 to stack pointer // ADD SP, i8 | Add i8 to stack pointer
// FIXME: Memory Timings are not properly emulated for this instruction
let mut flags: Flags = *cpu.flags(); let mut flags: Flags = *cpu.flags();
let left = cpu.register_pair(RegisterPair::SP); let left = cpu.register_pair(RegisterPair::SP);
let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags); let sum = Self::add_u16_i8(left, Self::imm_byte(cpu) as i8, &mut flags);
cpu.bus.clock(); // internal
cpu.set_register_pair(RegisterPair::SP, sum); cpu.set_register_pair(RegisterPair::SP, sum);
cpu.bus.clock();
cpu.set_flags(flags); cpu.set_flags(flags);
16 16
} }
@@ -423,7 +426,6 @@ impl Instruction {
AllRegisters::Group1(pair) => { AllRegisters::Group1(pair) => {
// INC r16 | Increment 16-bit register // INC r16 | Increment 16-bit register
// Note: No flags are set with this version of the INC instruction // Note: No flags are set with this version of the INC instruction
// FIXME: Memory Timings are not properly emulated for this instruction
use Group1RegisterPair::*; use Group1RegisterPair::*;
match pair { match pair {
@@ -431,6 +433,7 @@ impl Instruction {
let pair = pair.as_register_pair(); let pair = pair.as_register_pair();
let left = cpu.register_pair(pair); let left = cpu.register_pair(pair);
cpu.set_register_pair(pair, left.wrapping_add(1)); cpu.set_register_pair(pair, left.wrapping_add(1));
cpu.bus.clock(); // internal
} }
} }
8 8
@@ -462,7 +465,6 @@ impl Instruction {
} }
AllRegisters::Group1(pair) => { AllRegisters::Group1(pair) => {
// DEC r16 | Decrement Register Pair // DEC r16 | Decrement Register Pair
// FIXME: Memory Timings are not properly emulated for this instruction
use Group1RegisterPair::*; use Group1RegisterPair::*;
match pair { match pair {
@@ -470,6 +472,7 @@ impl Instruction {
let pair = pair.as_register_pair(); let pair = pair.as_register_pair();
let left = cpu.register_pair(pair); let left = cpu.register_pair(pair);
cpu.set_register_pair(pair, left.wrapping_sub(1)); cpu.set_register_pair(pair, left.wrapping_sub(1));
cpu.bus.clock(); // internal
} }
}; };
8 8
@@ -610,13 +613,13 @@ impl Instruction {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let right = cpu.register(reg.cpu_register()); let right = cpu.register(reg.cpu_register());
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
((4), sum) (4, sum)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
((8), sum) (8, sum)
} }
}; };
cpu.set_register(CpuRegister::A, sum); cpu.set_register(CpuRegister::A, sum);
@@ -646,12 +649,12 @@ impl Instruction {
let (cycles, diff) = match reg { let (cycles, diff) = match reg {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let right = cpu.register(reg.cpu_register()); let right = cpu.register(reg.cpu_register());
((4), Self::sub(left, right, &mut flags)) (4, Self::sub(left, right, &mut flags))
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
((8), Self::sub(left, right, &mut flags)) (8, Self::sub(left, right, &mut flags))
} }
}; };
cpu.set_register(CpuRegister::A, diff); cpu.set_register(CpuRegister::A, diff);
@@ -681,13 +684,13 @@ impl Instruction {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let right = cpu.register(reg.cpu_register()); let right = cpu.register(reg.cpu_register());
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
((4), diff) (4, diff)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
((8), diff) (8, diff)
} }
}; };
cpu.set_register(CpuRegister::A, diff); cpu.set_register(CpuRegister::A, diff);
@@ -717,7 +720,7 @@ impl Instruction {
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
((8), left & right) (8, left & right)
} }
}; };
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
@@ -743,7 +746,7 @@ impl Instruction {
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
((8), left ^ right) (8, left ^ right)
} }
}; };
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
@@ -769,7 +772,7 @@ impl Instruction {
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let right = Self::read_byte(&mut cpu.bus, addr); let right = Self::read_byte(&mut cpu.bus, addr);
((8), left | right) (8, left | right)
} }
}; };
cpu.set_register(CpuRegister::A, acc); cpu.set_register(CpuRegister::A, acc);
@@ -1053,14 +1056,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let rotated = byte.rotate_left(1); let rotated = byte.rotate_left(1);
cpu.set_register(reg, rotated); cpu.set_register(reg, rotated);
((8), byte >> 7, rotated) (8, byte >> 7, rotated)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let rotated = byte.rotate_left(1); let rotated = byte.rotate_left(1);
Self::write_byte(&mut cpu.bus, addr, rotated); Self::write_byte(&mut cpu.bus, addr, rotated);
((16), byte >> 7, rotated) (16, byte >> 7, rotated)
} }
}; };
cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01); cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01);
@@ -1076,14 +1079,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let rotated = byte.rotate_right(1); let rotated = byte.rotate_right(1);
cpu.set_register(reg, rotated); cpu.set_register(reg, rotated);
((8), byte & 0x01, rotated) (8, byte & 0x01, rotated)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let rotated = byte.rotate_right(1); let rotated = byte.rotate_right(1);
Self::write_byte(&mut cpu.bus, addr, rotated); Self::write_byte(&mut cpu.bus, addr, rotated);
((16), byte & 0x01, rotated) (16, byte & 0x01, rotated)
} }
}; };
cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01); cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01);
@@ -1101,14 +1104,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
cpu.set_register(reg, rotated); cpu.set_register(reg, rotated);
((8), rotated, carry) (8, rotated, carry)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
Self::write_byte(&mut cpu.bus, addr, rotated); Self::write_byte(&mut cpu.bus, addr, rotated);
((16), rotated, carry) (16, rotated, carry)
} }
}; };
cpu.update_flags(rotated == 0, false, false, carry); cpu.update_flags(rotated == 0, false, false, carry);
@@ -1126,14 +1129,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
cpu.set_register(reg, rotated); cpu.set_register(reg, rotated);
((8), rotated, carry) (8, rotated, carry)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
Self::write_byte(&mut cpu.bus, addr, rotated); Self::write_byte(&mut cpu.bus, addr, rotated);
((16), rotated, carry) (16, rotated, carry)
} }
}; };
cpu.update_flags(rotated == 0, false, false, carry); cpu.update_flags(rotated == 0, false, false, carry);
@@ -1149,14 +1152,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let shifted = byte << 1; let shifted = byte << 1;
cpu.set_register(reg, shifted); cpu.set_register(reg, shifted);
((8), (byte >> 7) & 0x01, shifted) (8, (byte >> 7) & 0x01, shifted)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let shifted = byte << 1; let shifted = byte << 1;
Self::write_byte(&mut cpu.bus, addr, shifted); Self::write_byte(&mut cpu.bus, addr, shifted);
((16), (byte >> 7) & 0x01, shifted) (16, (byte >> 7) & 0x01, shifted)
} }
}; };
cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01); cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01);
@@ -1172,14 +1175,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
cpu.set_register(reg, shifted); cpu.set_register(reg, shifted);
((8), byte & 0x01, shifted) (8, byte & 0x01, shifted)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
Self::write_byte(&mut cpu.bus, addr, shifted); Self::write_byte(&mut cpu.bus, addr, shifted);
((16), byte & 0x01, shifted) (16, byte & 0x01, shifted)
} }
}; };
cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01); cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01);
@@ -1194,14 +1197,14 @@ impl Instruction {
let reg = reg.cpu_register(); let reg = reg.cpu_register();
let swapped = Self::swap_bits(cpu.register(reg)); let swapped = Self::swap_bits(cpu.register(reg));
cpu.set_register(reg, swapped); cpu.set_register(reg, swapped);
((8), swapped) (8, swapped)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr)); let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr));
Self::write_byte(&mut cpu.bus, addr, swapped); Self::write_byte(&mut cpu.bus, addr, swapped);
((16), swapped) (16, swapped)
} }
}; };
cpu.update_flags(swapped == 0, false, false, false); cpu.update_flags(swapped == 0, false, false, false);
@@ -1217,14 +1220,14 @@ impl Instruction {
let byte = cpu.register(reg); let byte = cpu.register(reg);
let shifted = byte >> 1; let shifted = byte >> 1;
cpu.set_register(reg, shifted); cpu.set_register(reg, shifted);
((8), byte & 0x01, shifted) (8, byte & 0x01, shifted)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
let shifted = byte >> 1; let shifted = byte >> 1;
Self::write_byte(&mut cpu.bus, addr, shifted); Self::write_byte(&mut cpu.bus, addr, shifted);
((16), byte & 0x01, shifted) (16, byte & 0x01, shifted)
} }
}; };
cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01); cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01);
@@ -1239,12 +1242,12 @@ impl Instruction {
B | C | D | E | H | L | A => { B | C | D | E | H | L | A => {
let reg = reg.cpu_register(); let reg = reg.cpu_register();
let byte = cpu.register(reg); let byte = cpu.register(reg);
((8), ((byte >> bit) & 0x01) == 0x01) (8, ((byte >> bit) & 0x01) == 0x01)
} }
IndirectHL => { IndirectHL => {
let addr = cpu.register_pair(RegisterPair::HL); let addr = cpu.register_pair(RegisterPair::HL);
let byte = Self::read_byte(&mut cpu.bus, addr); let byte = Self::read_byte(&mut cpu.bus, addr);
((12), ((byte >> bit) & 0x01) == 0x01) (12, ((byte >> bit) & 0x01) == 0x01)
} }
}; };
flags.set_z(!is_set); flags.set_z(!is_set);
@@ -1893,7 +1896,7 @@ mod table {
} }
impl Group1RegisterPair { impl Group1RegisterPair {
pub fn as_register_pair(&self) -> RegisterPair { pub(crate) fn as_register_pair(&self) -> RegisterPair {
use Group1RegisterPair::*; use Group1RegisterPair::*;
match self { match self {
@@ -1927,7 +1930,7 @@ mod table {
} }
impl Group2RegisterPair { impl Group2RegisterPair {
pub fn as_register_pair(&self) -> RegisterPair { pub(crate) fn as_register_pair(&self) -> RegisterPair {
use Group2RegisterPair::*; use Group2RegisterPair::*;
match self { match self {
@@ -1961,7 +1964,7 @@ mod table {
} }
impl Group3RegisterPair { impl Group3RegisterPair {
pub fn as_register_pair(&self) -> RegisterPair { pub(crate) fn as_register_pair(&self) -> RegisterPair {
use Group3RegisterPair::*; use Group3RegisterPair::*;
match self { match self {
@@ -2003,7 +2006,7 @@ mod table {
} }
impl Register { impl Register {
pub fn cpu_register(&self) -> CpuRegister { pub(crate) fn cpu_register(&self) -> CpuRegister {
use Register::*; use Register::*;
match self { match self {

View File

@@ -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; use winit::event::VirtualKeyCode;
// TODO: What do I have to do to get a match statement here? // TODO: What do I have to do to get a match statement here?
@@ -146,17 +147,17 @@ pub fn handle_keyboard_input(pad: &mut Joypad, input: &WinitInputHelper) {
state.dpad_right.update(false, irq); state.dpad_right.update(false, irq);
} }
if input.key_pressed(VirtualKeyCode::T) { if input.key_pressed(VirtualKeyCode::Return) {
state.start.update(true, irq); state.start.update(true, irq);
} }
if input.key_released(VirtualKeyCode::T) { if input.key_released(VirtualKeyCode::Return) {
state.start.update(false, irq); state.start.update(false, irq);
} }
if input.key_pressed(VirtualKeyCode::Y) { if input.key_pressed(VirtualKeyCode::RShift) {
state.select.update(true, irq); state.select.update(true, irq);
} }
if input.key_released(VirtualKeyCode::Y) { if input.key_released(VirtualKeyCode::RShift) {
state.select.update(false, irq); 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 Button::*;
use GamepadEventType::*; use GamepadEventType::*;

View File

@@ -1,4 +1,4 @@
pub use apu::gen::AudioSPSC; pub use apu::gen::init as spsc_init;
pub type Cycle = u64; pub type Cycle = u64;
pub const GB_WIDTH: usize = 160; pub const GB_WIDTH: usize = 160;

View File

@@ -1,12 +1,11 @@
use std::convert::TryInto; use anyhow::Result;
use anyhow::{anyhow, Result};
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use gb::emu::build::EmulatorBuilder; use gb::emu::{Emulator, CYCLES_IN_FRAME};
use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH}; use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
use gilrs::Gilrs; use gilrs::Gilrs;
use pixels::{PixelsBuilder, SurfaceTexture}; use pixels::{PixelsBuilder, SurfaceTexture};
use rodio::{OutputStream, Sink}; use rodio::{OutputStream, Sink};
use tracing_subscriber::EnvFilter;
use winit::dpi::{LogicalSize, PhysicalSize}; use winit::dpi::{LogicalSize, PhysicalSize};
use winit::event::{Event, VirtualKeyCode}; use winit::event::{Event, VirtualKeyCode};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
@@ -14,7 +13,7 @@ use winit::window::{Window, WindowBuilder};
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
const WINDOW_SCALE: usize = 3; const WINDOW_SCALE: usize = 3;
const AUDIO_ENABLED: bool = false; const AUDIO_ENABLED: bool = true;
fn main() -> Result<()> { fn main() -> Result<()> {
let app = App::new(crate_name!()) let app = App::new(crate_name!())
@@ -27,7 +26,6 @@ fn main() -> Result<()> {
Arg::with_name("rom") Arg::with_name("rom")
.value_name("ROM_FILE") .value_name("ROM_FILE")
.takes_value(true) .takes_value(true)
.required(true)
.index(1) .index(1)
.help("Path to the Game ROM"), .help("Path to the Game ROM"),
) )
@@ -41,16 +39,36 @@ fn main() -> Result<()> {
) )
.get_matches(); .get_matches();
let mut emu_build = // Set up subscriber
EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?; if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "gb=info");
if let Some(path) = m.value_of("boot") {
emu_build = emu_build.with_boot(path)?;
} }
let mut emu = emu_build.finish(); 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(); let rom_title = emu.title();
tracing::info!("Initialize Gamepad");
let mut gamepad = Gilrs::new().expect("Initialize Controller Support"); let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
// Initialize GUI // Initialize GUI
@@ -71,8 +89,7 @@ fn main() -> Result<()> {
let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio"); let (_stream, stream_handle) = OutputStream::try_default().expect("Initialized Audio");
if AUDIO_ENABLED { if AUDIO_ENABLED {
let spsc: AudioSPSC<f32> = Default::default(); let (prod, cons) = gb::spsc_init();
let (prod, cons) = spsc.init();
let sink = { let sink = {
let s = Sink::try_new(&stream_handle)?; let s = Sink::try_new(&stream_handle)?;
s.append(cons); s.append(cons);
@@ -82,6 +99,7 @@ fn main() -> Result<()> {
emu.set_prod(prod); emu.set_prod(prod);
tracing::info!("Spawn Audio Thread");
std::thread::spawn(move || { std::thread::spawn(move || {
sink.sleep_until_end(); sink.sleep_until_end();
}); });
@@ -91,11 +109,9 @@ fn main() -> Result<()> {
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
if let Event::RedrawRequested(_) = event { if let Event::RedrawRequested(_) = event {
if pixels if pixels.render().is_err() {
.render() emu.try_write_sav().expect("Write game save if need be");
.map_err(|e| anyhow!("pixels.render() failed: {}", e))
.is_err()
{
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
return; return;
} }
@@ -103,6 +119,8 @@ fn main() -> Result<()> {
if input.update(&event) { if input.update(&event) {
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() { if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
emu.try_write_sav().expect("Write game save if need be");
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
return; return;
} }
@@ -113,8 +131,8 @@ fn main() -> Result<()> {
cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input); cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
if cycle_count >= gb::emu::CYCLES_IN_FRAME { if cycle_count >= CYCLES_IN_FRAME {
cycle_count %= gb::emu::CYCLES_IN_FRAME; cycle_count %= CYCLES_IN_FRAME;
let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
.get_frame() .get_frame()

View File

@@ -4,7 +4,6 @@ use crate::GB_HEIGHT;
use crate::GB_WIDTH; use crate::GB_WIDTH;
use dma::DirectMemoryAccess; use dma::DirectMemoryAccess;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryInto;
pub(crate) use types::PpuMode; pub(crate) use types::PpuMode;
use types::{ use types::{
BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette, BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette,
@@ -45,18 +44,18 @@ pub struct Ppu {
vram: Box<[u8; VRAM_SIZE]>, vram: Box<[u8; VRAM_SIZE]>,
pub(crate) oam: ObjectAttributeTable, pub(crate) oam: ObjectAttributeTable,
pub(crate) dma: DirectMemoryAccess, pub(crate) dma: DirectMemoryAccess,
scan_state: OamScanState, scan_dot: Cycle,
fetch: PixelFetcher, fetch: PixelFetcher,
fifo: PixelFifo, fifo: PixelFifo,
obj_buffer: ObjectBuffer, obj_buffer: ObjectBuffer,
frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>, pub(crate) frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>,
window_stat: WindowStatus, win_stat: WindowStatus,
scanline_start: bool, scanline_start: bool,
to_discard: u8, to_discard: u8,
x_pos: u8, x_pos: u8,
cycle: Cycle, dot: Cycle,
} }
impl BusIo for Ppu { impl BusIo for Ppu {
@@ -71,7 +70,7 @@ impl BusIo for Ppu {
impl Ppu { impl Ppu {
pub(crate) fn tick(&mut self) { pub(crate) fn tick(&mut self) {
self.cycle += 1; self.dot += 1;
if !self.ctrl.lcd_enabled() { if !self.ctrl.lcd_enabled() {
return; return;
@@ -79,7 +78,26 @@ impl Ppu {
match self.stat.mode() { match self.stat.mode() {
PpuMode::OamScan => { 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); self.stat.set_mode(PpuMode::Drawing);
} }
@@ -88,7 +106,7 @@ impl Ppu {
PpuMode::Drawing => { PpuMode::Drawing => {
if self.ctrl.lcd_enabled() { if self.ctrl.lcd_enabled() {
// Only Draw when the LCD Is Enabled // Only Draw when the LCD Is Enabled
self.draw(self.cycle); self.draw();
} else { } else {
self.reset(); self.reset();
} }
@@ -104,29 +122,22 @@ impl Ppu {
// Increment Window line counter if scanline had any window pixels on it // Increment Window line counter if scanline had any window pixels on it
// only increment once per scanline though // only increment once per scanline though
if self.window_stat.should_draw() { if self.win_stat.enabled {
self.fetch.back.window_line.increment(); self.fetch.back.wl_count += 1;
} }
self.x_pos = 0;
self.scanline_start = true;
self.to_discard = 0;
self.fetch.hblank_reset(); self.fetch.hblank_reset();
self.window_stat.hblank_reset(); self.win_stat.enabled = false;
self.obj_buffer.clear(); self.obj_buffer.clear();
self.fifo.back.clear();
self.fifo.obj.clear();
self.stat.set_mode(PpuMode::HBlank); self.stat.set_mode(PpuMode::HBlank);
} }
} }
PpuMode::HBlank => { PpuMode::HBlank => {
// This mode will always end at 456 cycles // This mode will always end at 456 cycles
if self.cycle >= 456 { if self.dot >= 456 {
self.cycle %= 456; self.dot %= 456;
self.pos.line_y += 1; self.pos.line_y += 1;
// Update LY==LYC bit // Update LY==LYC bit
@@ -143,9 +154,10 @@ impl Ppu {
self.int.set_vblank(true); self.int.set_vblank(true);
// Reset Window Line Counter in Fetcher // Reset Window Line Counter in Fetcher
self.fetch.vblank_reset(); self.fetch.back.wl_count = 0;
// Reset WY=LY coincidence flag // Reset WY=LY coincidence flag
self.window_stat.vblank_reset(); self.win_stat.coincidence = false;
if self.stat.vblank_int() { if self.stat.vblank_int() {
// Enable Vblank LCDStat Interrupt // Enable Vblank LCDStat Interrupt
@@ -159,7 +171,7 @@ impl Ppu {
self.int.set_lcd_stat(true); self.int.set_lcd_stat(true);
} }
self.scan_state.reset(); self.scan_dot = Default::default();
PpuMode::OamScan PpuMode::OamScan
}; };
@@ -167,8 +179,8 @@ impl Ppu {
} }
} }
PpuMode::VBlank => { PpuMode::VBlank => {
if self.cycle > 456 { if self.dot >= 456 {
self.cycle %= 456; self.dot %= 456;
self.pos.line_y += 1; self.pos.line_y += 1;
// Update LY==LYC bit // Update LY==LYC bit
@@ -188,8 +200,7 @@ impl Ppu {
self.int.set_lcd_stat(true); self.int.set_lcd_stat(true);
} }
self.scan_state.reset(); self.scan_dot = Default::default();
self.stat.set_mode(PpuMode::OamScan); self.stat.set_mode(PpuMode::OamScan);
} }
} }
@@ -198,89 +209,81 @@ impl Ppu {
} }
fn scan_oam(&mut self) { fn scan_oam(&mut self) {
match self.scan_state.mode() { if self.scan_dot % 2 == 0 {
OamScanMode::Scan if !self.dma.is_active() => { if self.dma.is_active() {
if !self.window_stat.coincidence() && self.scan_state.count() == 0 { return;
// 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(); if !self.win_stat.coincidence && self.scan_dot == 0 {
let index = self.scan_state.count(); self.win_stat.coincidence = self.pos.line_y == self.pos.window_y;
}
let attr = self.oam.attribute(index as usize); 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; let line_y = self.pos.line_y + 16;
if attr.x > 0 if attr.x > 0
&& line_y >= attr.y && line_y >= attr.y
&& line_y < (attr.y + sprite_height) && line_y < (attr.y + obj_height)
&& !self.obj_buffer.is_full() && !self.obj_buffer.is_full()
{ {
self.obj_buffer.add(attr); self.obj_buffer.add(attr);
} }
self.scan_state.increase();
} }
_ => {} self.scan_dot += 1;
} }
self.scan_state.next(); fn draw(&mut self) {
}
fn draw(&mut self, _cycle: Cycle) {
use FetcherState::*; use FetcherState::*;
let mut iter = self.obj_buffer.iter_mut(); let mut obj_attr = &mut None;
let default = &mut None;
let obj_attr = loop { for maybe_attr in &mut self.obj_buffer.inner {
match iter.next() { match maybe_attr {
Some(attr_opt) => { Some(attr) if self.ctrl.obj_enabled() => {
if let Some(attr) = attr_opt {
if attr.x <= (self.x_pos + 8) { if attr.x <= (self.x_pos + 8) {
self.fetch.back.reset(); self.fetch.back.reset();
self.fetch.back.pause(); self.fetch.back.enabled = false;
self.fifo.pause(); self.fifo.pause();
break attr_opt; obj_attr = maybe_attr;
break;
} }
} }
_ => break,
} }
None => break default,
} }
};
if let Some(attr) = obj_attr { if let Some(attr) = obj_attr {
match self.fetch.obj.state { match self.fetch.obj.state {
TileNumber => { TileNumberA => self.fetch.obj.state = TileNumberB,
TileNumberB => {
self.fetch.obj.tile.with_id(attr.tile_index); 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), TileLowA => self.fetch.obj.state = TileLowB,
TileLow => { TileLowB => {
let obj_size = self.ctrl.obj_size(); let obj_size = self.ctrl.obj_size();
let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
let byte = self.read_byte(addr); let byte = self.read_byte(addr);
self.fetch.obj.tile.with_low_byte(byte); self.fetch.obj.tile.with_low(byte);
self.fetch.obj.next(SleepTwo); self.fetch.obj.state = TileHighA;
} }
SleepTwo => self.fetch.obj.next(TileHigh), TileHighA => self.fetch.obj.state = TileHighB,
TileHigh => { TileHighB => {
let obj_size = self.ctrl.obj_size(); let obj_size = self.ctrl.obj_size();
let addr = PixelFetcher::get_obj_addr(&attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
let byte = self.read_byte(addr + 1); let byte = self.read_byte(addr + 1);
self.fetch.obj.tile.with_high_byte(byte); self.fetch.obj.tile.with_high(byte);
self.fetch.obj.next(SleepThree); self.fetch.obj.state = ToFifoA;
} }
SleepThree => self.fetch.obj.next(ToFifoOne), ToFifoA => {
ToFifoOne => {
// Load into Fifo // Load into Fifo
let (high, low) = self let (high, low) = self
.fetch .fetch
@@ -312,73 +315,65 @@ impl Ppu {
self.fifo.obj.push_back(fifo_info); self.fifo.obj.push_back(fifo_info);
} }
self.fetch.back.resume(); self.fetch.back.enabled = true;
self.fifo.resume(); self.fifo.resume();
let _ = std::mem::take(obj_attr); 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() if self.fetch.back.enabled {
&& !self.window_stat.should_draw()
&& self.window_stat.coincidence()
&& self.x_pos as i16 >= self.pos.window_x as i16 - 7
{
self.window_stat.set_should_draw(true);
self.fetch.back.reset();
self.fetch.x_pos = 0;
self.fifo.back.clear();
}
if self.fetch.back.is_enabled() {
match self.fetch.back.state { match self.fetch.back.state {
TileNumber => { TileNumberA => self.fetch.back.state = TileNumberB,
let x_pos = self.fetch.x_pos; TileNumberB => {
// Are we rendering the Window currently?
self.fetch.back.draw_window = self.win_stat.enabled;
let addr =
self.fetch self.fetch
.back .back
.should_render_window(self.window_stat.should_draw()); .tile_id_addr(&self.ctrl, &self.pos, self.fetch.x_pos);
let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos);
let id = self.read_byte(addr); let id = self.read_byte(addr);
self.fetch.back.tile.with_id(id); self.fetch.back.tile.with_id(id);
// Move on to the Next state in 2 T-cycles self.fetch.back.state = TileLowA;
self.fetch.back.next(SleepOne);
} }
SleepOne => self.fetch.back.next(TileLow), TileLowA => self.fetch.back.state = TileLowB,
TileLow => { TileLowB => {
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); let id = self.fetch.back.tile.id.expect("Tile ID present");
let low = self.read_byte(addr); let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id);
self.fetch.back.tile.with_low_byte(low); 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), TileHighA => self.fetch.back.state = TileHighB,
TileHigh => { TileHighB => {
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos); let id = self.fetch.back.tile.id.expect("Tile ID present");
let high = self.read_byte(addr + 1); let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id);
self.fetch.back.tile.with_high_byte(high); let byte = self.read_byte(addr + 1);
self.fetch.back.tile.with_high(byte);
self.fetch.back.next(SleepThree); if self.fetch.back.tile_high_reset {
self.fetch.back.reset();
self.fetch.back.tile_high_reset = false;
} else {
self.fetch.back.state = ToFifoA;
} }
SleepThree => self.fetch.back.next(ToFifoOne),
ToFifoOne => {
self.fetch.back.next(ToFifoTwo);
} }
ToFifoTwo => { ToFifoA => {
if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) { if self.fetch.send_to_fifo(&mut self.fifo).is_ok() {
self.fetch.x_pos += 1; self.fetch.x_pos += 1;
self.fetch.back.next(TileNumber); self.fetch.back.state = ToFifoB;
self.fetch.back.tile = Default::default();
} }
} }
ToFifoB => self.fetch.back.reset(),
} }
} }
@@ -388,7 +383,7 @@ impl Ppu {
self.scanline_start = false; 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(); let _ = self.fifo.back.pop_front();
self.to_discard -= 1; self.to_discard -= 1;
@@ -406,6 +401,17 @@ impl Ppu {
self.x_pos += 1; self.x_pos += 1;
} }
if self.ctrl.window_enabled()
&& !self.win_stat.enabled
&& self.win_stat.coincidence
&& self.x_pos as i16 >= self.pos.window_x as i16 - 7
{
self.win_stat.enabled = true;
self.fetch.back.reset();
self.fetch.x_pos = 0;
self.fifo.back.clear();
}
} }
} }
@@ -413,53 +419,44 @@ impl Ppu {
self.pos.line_y = 0; self.pos.line_y = 0;
self.stat.set_mode(PpuMode::HBlank); self.stat.set_mode(PpuMode::HBlank);
// TODO: Is this an unnecessary performance hit?
let mut blank = WHITE.repeat(self.frame_buf.len() / 4); let mut blank = WHITE.repeat(self.frame_buf.len() / 4);
self.frame_buf.swap_with_slice(&mut blank); self.frame_buf.swap_with_slice(&mut blank);
} }
pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
&self.frame_buf
}
fn clock_fifo(&mut self) -> Option<GrayShade> { fn clock_fifo(&mut self) -> Option<GrayShade> {
use ObjectPaletteKind::*;
use RenderPriority::*; use RenderPriority::*;
let obj_palette_0 = &self.monochrome.obj_palette_0; self.fifo
let obj_palette_1 = &self.monochrome.obj_palette_1; .back
.pop_front()
match self.fifo.back.pop_front() { .map(|bg| match self.fifo.obj.pop_front() {
Some(bg_pixel) => match self.fifo.obj.pop_front() { Some(obj) => match obj.priority {
Some(obj_pixel) if self.ctrl.obj_enabled() => match obj_pixel.priority { _ if obj.shade_id == 0 => self.bg_pixel(bg),
Object | BackgroundAndWindow if obj_pixel.shade_id == 0 => { BackgroundAndWindow if bg.shade_id != 0 => self.bg_pixel(bg),
Some(self.bg_pixel(bg_pixel.shade_id)) _ => self.obj_pixel(obj),
}
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)
}
}, },
_ => Some(self.bg_pixel(bg_pixel.shade_id)), None => self.bg_pixel(bg),
}, })
None => None, }
fn obj_pixel(&self, obj: ObjPixelProperty) -> GrayShade {
use ObjectPaletteKind::*;
assert!(obj.shade_id != 0);
let p0 = &self.monochrome.obj_palette_0;
let p1 = &self.monochrome.obj_palette_1;
match obj.palette_kind {
Zero => p0.shade(obj.shade_id).expect("Object shade id is non-zero"),
One => p1.shade(obj.shade_id).expect("Object shade id is non-zero"),
} }
} }
fn bg_pixel(&self, shade_id: u8) -> GrayShade { fn bg_pixel(&self, bg: BgPixelProperty) -> GrayShade {
let bg_palette = &self.monochrome.bg_palette; let bg_palette = &self.monochrome.bg_palette;
if self.ctrl.bg_win_enabled() { if self.ctrl.bg_win_enabled() {
bg_palette.shade(shade_id) bg_palette.shade(bg.shade_id)
} else { } else {
bg_palette.shade(0) bg_palette.shade(0)
} }
@@ -470,7 +467,7 @@ impl Default for Ppu {
fn default() -> Self { fn default() -> Self {
Self { Self {
vram: Box::new([0u8; VRAM_SIZE]), vram: Box::new([0u8; VRAM_SIZE]),
cycle: Default::default(), dot: Default::default(),
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]), frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
int: Default::default(), int: Default::default(),
ctrl: Default::default(), ctrl: Default::default(),
@@ -478,11 +475,11 @@ impl Default for Ppu {
pos: Default::default(), pos: Default::default(),
stat: Default::default(), stat: Default::default(),
oam: Default::default(), oam: Default::default(),
scan_state: Default::default(), scan_dot: Default::default(),
fetch: Default::default(), fetch: Default::default(),
fifo: Default::default(), fifo: Default::default(),
obj_buffer: Default::default(), obj_buffer: Default::default(),
window_stat: Default::default(), win_stat: Default::default(),
dma: Default::default(), dma: Default::default(),
x_pos: 0, x_pos: 0,
scanline_start: true, scanline_start: true,
@@ -578,7 +575,7 @@ impl Default for ObjectAttributeTable {
} }
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Default)]
struct ObjectAttribute { struct ObjectAttribute {
y: u8, y: u8,
x: u8, x: u8,
@@ -628,10 +625,6 @@ impl ObjectBuffer {
self.inner[self.len] = Some(attr); self.inner[self.len] = Some(attr);
self.len += 1; self.len += 1;
} }
fn iter_mut(&mut self) -> std::slice::IterMut<'_, Option<ObjectAttribute>> {
self.inner.iter_mut()
}
} }
impl Default for ObjectBuffer { impl Default for ObjectBuffer {
@@ -657,61 +650,6 @@ impl PixelFetcher {
self.x_pos = 0; self.x_pos = 0;
} }
fn vblank_reset(&mut self) {
self.back.vblank_reset();
}
fn bg_tile_num_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
let line_y = pos.line_y;
let scroll_y = pos.scroll_y;
let scroll_x = pos.scroll_x;
let is_window = self.back.is_window_tile();
// Determine which tile map is being used
let tile_map = if is_window {
control.win_tile_map_addr()
} else {
control.bg_tile_map_addr()
};
let tile_map_addr = tile_map.into_address();
// Both Offsets are used to offset the tile map address we found above
// Offsets are ANDed wih 0x3FF so that we stay in bounds of tile map memory
let scx_offset = if is_window { 0 } else { scroll_x / 8 };
let y_offset = if is_window {
self.back.window_line.count() as u16 / 8
} else {
((line_y as u16 + scroll_y as u16) & 0xFF) / 8
};
let x_offset = (scx_offset + x_pos) & 0x1F;
let offset = (32 * y_offset) + (x_offset as u16);
tile_map_addr + (offset & 0x3FF)
}
fn bg_byte_addr(&mut self, control: &LCDControl, pos: &ScreenPosition) -> u16 {
let line_y = pos.line_y;
let scroll_y = pos.scroll_y;
let is_window = self.back.is_window_tile();
let id = self.back.tile.id.expect("Tile Number is present");
let tile_data_addr = match control.tile_data_addr() {
TileDataAddress::X8800 => 0x9000u16.wrapping_add(((id as i8) as i16 * 16) as u16),
TileDataAddress::X8000 => 0x8000 + (id as u16 * 16),
};
let offset = if is_window {
self.back.window_line.count() as u16 % 8
} else {
(line_y as u16 + scroll_y as u16) % 8
};
tile_data_addr + (offset * 2)
}
fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> { fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> {
if !fifo.back.is_empty() { if !fifo.back.is_empty() {
return Err(()); return Err(());
@@ -757,7 +695,6 @@ impl PixelFetcher {
} }
trait Fetcher { trait Fetcher {
fn next(&mut self, state: FetcherState);
fn reset(&mut self); fn reset(&mut self);
fn hblank_reset(&mut self); fn hblank_reset(&mut self);
} }
@@ -766,42 +703,63 @@ trait Fetcher {
struct BackgroundFetcher { struct BackgroundFetcher {
state: FetcherState, state: FetcherState,
tile: TileBuilder, tile: TileBuilder,
window_line: WindowLineCounter, wl_count: u8,
is_window_tile: bool, draw_window: bool,
enabled: bool, enabled: bool,
tile_high_reset: bool,
} }
impl BackgroundFetcher { impl BackgroundFetcher {
fn should_render_window(&mut self, value: bool) { fn tile_id_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
self.is_window_tile = value; 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 { fn tile_addr(&mut self, control: &LCDControl, pos: &ScreenPosition, id: u8) -> u16 {
self.is_window_tile let line_y = pos.line_y;
} let scroll_y = pos.scroll_y;
fn pause(&mut self) { let tile_data_addr = match control.tile_data_addr() {
self.enabled = false; TileDataAddress::X8800 => 0x9000u16.wrapping_add((id as i8 as i16 * 16) as u16),
} TileDataAddress::X8000 => 0x8000 + (id as u16 * 16),
};
fn resume(&mut self) { let offset = if self.draw_window {
self.enabled = true; self.wl_count as u16 % 8
} } else {
(line_y as u16 + scroll_y as u16) % 8
};
fn is_enabled(&self) -> bool { tile_data_addr + (offset * 2)
self.enabled
}
fn vblank_reset(&mut self) {
self.window_line.vblank_reset();
} }
} }
impl Fetcher for BackgroundFetcher { impl Fetcher for BackgroundFetcher {
fn next(&mut self, state: FetcherState) {
self.state = state
}
fn reset(&mut self) { fn reset(&mut self) {
self.state = Default::default(); self.state = Default::default();
self.tile = Default::default(); self.tile = Default::default();
@@ -810,7 +768,7 @@ impl Fetcher for BackgroundFetcher {
fn hblank_reset(&mut self) { fn hblank_reset(&mut self) {
self.reset(); self.reset();
self.is_window_tile = false; self.draw_window = false;
self.enabled = true; self.enabled = true;
} }
@@ -821,69 +779,55 @@ impl Default for BackgroundFetcher {
Self { Self {
state: Default::default(), state: Default::default(),
tile: Default::default(), tile: Default::default(),
is_window_tile: Default::default(), draw_window: Default::default(),
window_line: Default::default(), wl_count: Default::default(),
enabled: true, enabled: true,
tile_high_reset: true,
} }
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
struct ObjectFetcher { struct ObjectFetcher {
state: FetcherState, state: FetcherState,
tile: TileBuilder, tile: TileBuilder,
} }
impl Fetcher for ObjectFetcher { impl Default for ObjectFetcher {
fn next(&mut self, state: FetcherState) { fn default() -> Self {
self.state = state Self {
state: Default::default(),
tile: Default::default(),
}
}
} }
impl Fetcher for ObjectFetcher {
fn reset(&mut self) { fn reset(&mut self) {
self.state = Default::default(); self.state = Default::default();
self.tile = Default::default(); self.tile = Default::default();
} }
fn hblank_reset(&mut self) { fn hblank_reset(&mut self) {
self.state = Default::default(); self.reset()
self.tile = Default::default();
}
}
#[derive(Debug, Default)]
struct WindowLineCounter {
count: u8,
}
impl WindowLineCounter {
fn increment(&mut self) {
self.count += 1;
}
fn vblank_reset(&mut self) {
self.count = 0;
}
fn count(&self) -> u8 {
self.count
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum FetcherState { enum FetcherState {
TileNumber, TileNumberA,
SleepOne, TileNumberB,
TileLow, TileLowA,
SleepTwo, TileLowB,
TileHigh, TileHighA,
SleepThree, TileHighB,
ToFifoOne, ToFifoA,
ToFifoTwo, ToFifoB,
} }
impl Default for FetcherState { impl Default for FetcherState {
fn default() -> Self { fn default() -> Self {
Self::TileNumber Self::TileNumberA
} }
} }
@@ -944,11 +888,11 @@ impl TileBuilder {
self.id = Some(id); self.id = Some(id);
} }
fn with_low_byte(&mut self, data: u8) { fn with_low(&mut self, data: u8) {
self.low = Some(data); self.low = Some(data);
} }
fn with_high_byte(&mut self, data: u8) { fn with_high(&mut self, data: u8) {
self.high = Some(data); self.high = Some(data);
} }
@@ -957,84 +901,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)] #[derive(Debug, Default)]
struct WindowStatus { struct WindowStatus {
/// This will be true if WY == LY at any point in the frame thus far /// This will be true if WY == LY at any point in the frame thus far
coincidence: bool, coincidence: bool,
/// This will be true if the conditions which tell the PPU to start /// This will be true if the conditions which tell the PPU to start
/// drawing from the window tile map is true /// drawing from the window tile map is true
should_draw: bool, enabled: 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;
}
} }

View File

@@ -183,7 +183,7 @@ pub enum ObjectSize {
} }
impl ObjectSize { impl ObjectSize {
pub(crate) fn as_u8(&self) -> u8 { pub(crate) fn size(&self) -> u8 {
use ObjectSize::*; use ObjectSize::*;
match self { match self {

View File

@@ -21,17 +21,6 @@ impl Timer {
use State::*; use State::*;
use TimerSpeed::*; use TimerSpeed::*;
match self.state {
TIMAOverflow(_) | AbortedTIMAOverflow(_) => self.next(),
LoadTMA => {
self.counter = self.modulo;
self.interrupt = true;
self.next();
}
Normal => {}
}
self.divider = self.divider.wrapping_add(1); self.divider = self.divider.wrapping_add(1);
// Get Bit Position // Get Bit Position
@@ -53,6 +42,17 @@ impl Timer {
} }
self.and_result = Some(new_result); self.and_result = Some(new_result);
match self.state {
TIMAOverflow(_) | AbortedTIMAOverflow(_) => self.next(),
LoadTIMA => {
self.counter = self.modulo;
self.interrupt = true;
self.next();
}
Normal => {}
}
} }
/// 0xFF05 | TIMA - Timer Counter /// 0xFF05 | TIMA - Timer Counter
@@ -70,7 +70,7 @@ impl Timer {
self.counter = byte; self.counter = byte;
self.state = AbortedTIMAOverflow(step); self.state = AbortedTIMAOverflow(step);
} }
LoadTMA => {} LoadTIMA => { /* Ignored */ }
} }
} }
@@ -95,9 +95,8 @@ impl Timer {
use State::*; use State::*;
self.state = match self.state { self.state = match self.state {
Normal | LoadTMA => Normal, Normal | LoadTIMA | AbortedTIMAOverflow(3) => Normal,
AbortedTIMAOverflow(4) => Normal, TIMAOverflow(3) => LoadTIMA,
TIMAOverflow(4) => LoadTMA,
AbortedTIMAOverflow(step) => AbortedTIMAOverflow(step + 1), AbortedTIMAOverflow(step) => AbortedTIMAOverflow(step + 1),
TIMAOverflow(step) => TIMAOverflow(step + 1), TIMAOverflow(step) => TIMAOverflow(step + 1),
} }
@@ -181,5 +180,5 @@ enum State {
TIMAOverflow(u8), TIMAOverflow(u8),
AbortedTIMAOverflow(u8), AbortedTIMAOverflow(u8),
Normal, Normal,
LoadTMA, LoadTIMA,
} }