Compare commits

...

28 Commits

Author SHA1 Message Date
august 5a37caf8b2 prettier moons 2024-09-26 22:53:14 -04:00
august 3de2c63fe0 fix too wide glyphs 2024-09-26 22:11:18 -04:00
august 3856d81c47 make sure GeorgeEmu can't be constructed without a rom 2024-09-26 21:13:57 -04:00
august a621ec235a ignore Cargo.lock 2024-09-26 21:12:55 -04:00
august f464dbfaf1 enabled piping a program from stdin 2024-09-26 21:08:45 -04:00
august 7d0b66f418 build script! no more manually rebuilding cozette! 2024-09-26 21:02:03 -04:00
august 6e55faa2c0 Update georgescii encoding 2024-09-26 19:04:34 -04:00
august 952b79cf91 Big refactor! Better handling of wasm/native targets, and cute demo :) 2024-09-07 22:24:51 -04:00
august 8ac0cbc57b Very messy but we're running on the web now! (not in this crate, but will add web-sys to georgeemu bin in a bit) 2024-09-01 23:13:06 -04:00
august c2aef4f249 asm fiddling and better debug info 2024-08-28 21:32:29 -04:00
august 07d40c05d5 hehe the keyboard finally works 2024-07-05 00:19:49 -04:00
august dcaeece7fa interrupts work now! 2024-07-04 22:53:55 -04:00
august 7f8e00af23 the only thing broken is interrupts lol 2024-07-04 22:01:27 -04:00
august fb4fae430b cleaned up instructions, using termion now but is kinda broken, might switch back to crossterm 2024-07-04 13:04:13 -04:00
august ac4619406d much better cli! more ergonomic screen struct! window screen type still broken but working on it! 2024-06-30 22:49:54 -04:00
august 8890853656 fix rom help 2024-06-30 00:07:30 -04:00
august 4f0615b563 refactoring! 2024-06-30 00:03:21 -04:00
august 001d3e434c terminal mode! i like this better, also functional cli now 2024-06-29 23:38:02 -04:00
august 229b8b450d I really should be using branches for diff experiments, but we can display in the terminal now! 2024-06-23 13:23:29 -04:00
august f9198cd0b1 Architecture changes 2024-06-19 17:26:09 -04:00
august 0ec54d6672 Started work on tui debugger! 2024-06-15 22:45:27 -04:00
august b1b9c64468 nix! flakes! 2024-04-10 18:10:25 -04:00
august 5f65c32e56 Update .gitignore 2024-04-10 17:30:23 -04:00
august c154bdc89a Added interrupts, some restructuring & refactoring 2024-04-10 17:28:34 -04:00
august 705dcd3185 we can type all the letters! 2024-03-26 20:56:04 -04:00
august 40ede17ae1 we can type the letter w! 2024-03-03 13:55:56 -05:00
august 9808616203 asm cleanup 2024-02-21 23:38:33 -05:00
august 10559bde8b added data stack, breakpoints, and character coordinates :) 2024-02-21 23:27:44 -05:00
55 changed files with 20845 additions and 33189 deletions
+4
View File
@@ -1,3 +1,7 @@
/target /target
log log
george.o george.o
Cargo.lock
.DS_Store
*.bin
/result
Generated
-891
View File
@@ -1,891 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bdf"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f550a6818e6f42ccd5883f44e45fff4f68415a0d09abdc81e7d1d78e0780af14"
dependencies = [
"bit-set",
"thiserror",
]
[[package]]
name = "bit-set"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84527c7b0452f22545cc010e72d366a435561d2b28b978035550b3778c4d428d"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "dlib"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading",
]
[[package]]
name = "downcast-rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "georgeemu"
version = "0.1.0"
dependencies = [
"bdf",
"bitvec",
"minifb",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "js-sys"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "libredox"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
dependencies = [
"bitflags 2.4.2",
"libc",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "minifb"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05eddefadb505d3dcb66a89fa77dd0936e72ec84e891cc8fc36e3c05bfe61103"
dependencies = [
"cc",
"dlib",
"futures",
"instant",
"js-sys",
"lazy_static",
"libc",
"orbclient",
"raw-window-handle",
"serde",
"serde_derive",
"tempfile",
"wasm-bindgen-futures",
"wayland-client",
"wayland-cursor",
"wayland-protocols",
"winapi",
"x11-dl",
]
[[package]]
name = "nix"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "orbclient"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166"
dependencies = [
"libc",
"libredox",
"sdl2",
"sdl2-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "raw-window-handle"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41"
dependencies = [
"cty",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rustix"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "sdl2"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a"
dependencies = [
"bitflags 1.3.2",
"lazy_static",
"libc",
"sdl2-sys",
]
[[package]]
name = "sdl2-sys"
version = "0.35.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0"
dependencies = [
"cfg-if",
"libc",
"version-compare",
]
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
[[package]]
name = "wasm-bindgen"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "wayland-client"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
dependencies = [
"bitflags 1.3.2",
"downcast-rs",
"libc",
"nix",
"scoped-tls",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
]
[[package]]
name = "wayland-commons"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
dependencies = [
"nix",
"once_cell",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-cursor"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
dependencies = [
"nix",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-protocols"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
dependencies = [
"bitflags 1.3.2",
"wayland-client",
"wayland-commons",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
dependencies = [
"proc-macro2",
"quote",
"xml-rs",
]
[[package]]
name = "wayland-sys"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
dependencies = [
"dlib",
"lazy_static",
"pkg-config",
]
[[package]]
name = "web-sys"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "x11-dl"
version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
dependencies = [
"libc",
"once_cell",
"pkg-config",
]
[[package]]
name = "xcursor"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911"
[[package]]
name = "xml-rs"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+22 -3
View File
@@ -2,10 +2,29 @@
name = "georgeemu" name = "georgeemu"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
build = "build.rs"
[[target.'cfg(not(target_arch = "wasm32"))'.bin]]
path = "src/bin/main.rs"
name = "george"
[target.'cfg(target_arch = "wasm32")'.lib]
crate-type = ["cdylib", "rlib"]
# 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
[build-dependencies]
bdf-parser = "0.1.0"
[dependencies] [dependencies]
bdf = "0.6.0" anyhow = "1.0.81"
bitvec = "1.0.1" minifb = { git = "https://github.com/emoon/rust_minifb" }
minifb = "0.25.0" serde = { version = "1.0.197", features = ["serde_derive", "derive"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7"
web-sys = "0.3.70"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
termion = "4.0.2"
toml = { version = "0.8.12" }
+12
View File
@@ -9,3 +9,15 @@ plenty of inspiration & guidance taken from [emulator_6502](https://docs.rs/emul
see [the george wiki](https://git.augustkline.com/august/george/wiki) for how george works, why she exists, who she is, etc. see [the george wiki](https://git.augustkline.com/august/george/wiki) for how george works, why she exists, who she is, etc.
the george emulator contains 3 main structs: `Cpu`, `Mem`, and `MemMappedDevice`. `Cpu` represents a 65c02 processor, with functions for interacting with memory and executing instructions. `Mem` is a collection of `MemMappedDevice`'s, which each hold the data for some address space. `Mem` can add `MemMappedDevice`'s, and read from and write to them. A `MemMappedDevice` can have multiple banks at the same address space, and can translate 'global' addresses to 'local' ones (e.g. 0xFFFF in the system's [memory map](https://git.augustkline.com/august/george-hardware/src/branch/main/memory.md) corresponds to 0x1FFF in the ROM). the george emulator contains 3 main structs: `Cpu`, `Mem`, and `MemMappedDevice`. `Cpu` represents a 65c02 processor, with functions for interacting with memory and executing instructions. `Mem` is a collection of `MemMappedDevice`'s, which each hold the data for some address space. `Mem` can add `MemMappedDevice`'s, and read from and write to them. A `MemMappedDevice` can have multiple banks at the same address space, and can translate 'global' addresses to 'local' ones (e.g. 0xFFFF in the system's [memory map](https://git.augustkline.com/august/george-hardware/src/branch/main/memory.md) corresponds to 0x1FFF in the ROM).
## font generation
george uses a modified version of [cozette](https://github.com/slavfox/Cozette) for her main font. the modified version has 8 pixel wide characters for use with the character generator rom, and has a limit of 256 (0xFF) characters. the toolchain to generate a rom binary is still pretty clunky, and someday i might get around to streamlining it, but for now to make any changes:
- open `./src/Cozette.sfd` in fontforge
- make your edits
- to reorder glyphs, edit `./src/georgeencoding.txt` and load it with "Encoding -> Load Encoding -> (select georgeencoding.txt, name it whatever) -> Reencode -> (name of encoding)"
- ensure that there are exactly 256 characters in the font
- generate a bdf font with "File -> Generate Fonts... -> (name of the font, select bdf in the options panel)"
- open the generated bdf file in a text editor and change the line `FONTBOUNDINGBOX 11 13 0 -3` to `FONTBOUNDINGBOX 8 13 0 -3`
- now open the font in [bdf view](https://emurenmrz.github.io/bdf_view/), and export a single-row png
+83
View File
@@ -0,0 +1,83 @@
use std::{
env,
fs::File,
io::{stdin, stdout, IsTerminal, Read, Write},
ops::Neg,
os::unix::process::CommandExt,
path::Path,
process::{exit, Command},
};
// takes all charaters in bdf and returns a vec of each character row byte in order, normalized to
// width & height of the font (only works with 8 or fewer pixel wide fonts, should work for any height)
fn bdf_to_bitmap(mut bdf: File) -> [u8; 0x8000] {
let mut bdf_font_bytes = Vec::new();
bdf.read_to_end(&mut bdf_font_bytes).unwrap();
let bdf_font = bdf_parser::BdfFont::parse(&bdf_font_bytes).unwrap();
let mut bdf_vec = vec![];
for glyph in bdf_font.glyphs.iter() {
let glyph_offset_x = glyph.bounding_box.offset.x;
let glyph_offset_y = glyph.bounding_box.offset.y;
let glyph_height = glyph.bounding_box.size.y;
let font_height = bdf_font.metadata.bounding_box.size.y;
let font_offset_y = bdf_font.metadata.bounding_box.offset.y;
let top_space = font_height + font_offset_y - glyph_height - glyph_offset_y;
for _ in 0..top_space {
bdf_vec.push(0x00);
}
for bitmap in glyph.bitmap.iter() {
bdf_vec.push(bitmap >> glyph_offset_x);
}
let bottom_space = font_offset_y.neg() + glyph_offset_y;
for _ in 0..bottom_space {
bdf_vec.push(0x00);
}
}
let height = bdf_font.metadata.bounding_box.size.y as usize;
reorder_bitmap(&bdf_vec, height)
}
// takes an vec of ordered characters and translates them for use with the character rom
// TODO: make this work for any arbitrary char rom pin format using some kinda interface
fn reorder_bitmap(bitmap: &[u8], font_height: usize) -> [u8; 0x8000] {
let mut rom = [0; 0x8000]; // create vec the size of character rom
for row in 0..font_height {
for ascii_address in 0..u8::MAX {
// first 8 bits of address pick character
// next 5 bits pick row
// TODO: final 2 pick character set
let byte = bitmap[ascii_address as usize * font_height + row];
let rom_index: u16 = ((row as u16) << 8) + ascii_address as u16;
rom[rom_index as usize] = byte;
}
}
rom
}
fn rom_from_file<P>(path: P) -> [u8; 0x8000]
where
P: AsRef<Path>,
{
let file = File::open(path).unwrap();
bdf_to_bitmap(file)
}
fn main() {
let mut regen_font = Command::new("regen-font.sh");
let mut cleanup = Command::new("cleanup.sh");
regen_font.exec();
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("cozette.rom");
let cozette_rom_bytes = rom_from_file("build/cozette.bdf");
let mut cozette_rom = File::create(dest_path).unwrap();
cozette_rom.write_all(&cozette_rom_bytes).unwrap();
cleanup.exec();
}
+2410
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
#! /bin/sh
rm *.bdf*
File diff suppressed because it is too large Load Diff
+3903
View File
File diff suppressed because it is too large Load Diff
+3903
View File
File diff suppressed because it is too large Load Diff
+523
View File
@@ -0,0 +1,523 @@
# this is georgescii, august's version of extended ascii for george <3
# we're limited to 255 characters
# format: ascii byte, unicode hex, # unicode name
#
# format: three tab-separated columns
# column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
# column #2 is the Unicode (in hex as 0xXXXX)
# column #3 the Unicode name (follows a comment sign, '#')
# 0x00 0x00 #
# 0x01 0x2591 # ░
# 0x02 0x2592 # ▒
# 0x03 0x2593 # ▓
# 0x04 0x2661 # ♡
# 0x05 0x2665 # ♥
# 0x06 0x2B50 # ⭐
# 0x07 0x272D # ✭
# 0x08 0xF005 # 
# 0x09 0x2726 # ✦
# 0x0a 0x2728 # ✨
# 0x0b 0x2640 # ♀
# 0x0c 0x2642 # ♂
# 0x0d 0x26A2 # ⚢
# 0x0E 0x26A3 # ⚣
# 0x0F 0x26A5 # ⚥
# 0x10 0x2669 # ♩
# 0x11 0x266A # ♪
# 0x12 0x266B # ♫
# 0x13 0x266C # ♬
# 0x14 0xFC5D # ﱝ
# 0x15 0xF026 # 
# 0x16 0xF027 # 
# 0x17 0xF028 # 
# 0x18 0xFA7E # 奄
# 0x19 0xFA7F # 奔
# 0x1A 0xFA80 # 婢
# 0x1B 0xFC5C # ﱜ
# 0x1C 0xFC5B # ﱛ
# 0x1D 0xF0AC # 
# 0x1E 0xF04B # 
# 0x1F 0xF04D # 
# 0x20 0x0020 #
# 0x21 0x0021 # !
# 0x22 0x0022 # "
# 0x23 0x0023 # #
# 0x24 0x0024 # $
# 0x25 0x0025 # %
# 0x26 0x0026 # &
# 0x27 0x0027 # '
# 0x28 0x0028 # (
# 0x29 0x0029 # )
# 0x2A 0x002A # *
# 0x2B 0x002B # +
# 0x2C 0x002C # ,
# 0x2D 0x002D # -
# 0x2E 0x002E # .
# 0x2F 0x002F # /
# 0x30 0x0030 # 0
# 0x31 0x0031 # 1
# 0x32 0x0032 # 2
# 0x33 0x0033 # 3
# 0x34 0x0034 # 4
# 0x35 0x0035 # 5
# 0x36 0x0036 # 6
# 0x37 0x0037 # 7
# 0x38 0x0038 # 8
# 0x39 0x0039 # 9
# 0x3A 0x003A # :
# 0x3B 0x003B # ;
# 0x3C 0x003C # <
# 0x3D 0x003D # =
# 0x3E 0x003E # >
# 0x3F 0x003F # ?
# 0x40 0x0040 # @
# 0x41 0x0041 # A
# 0x42 0x0042 # B
# 0x43 0x0043 # C
# 0x44 0x0044 # D
# 0x45 0x0045 # E
# 0x46 0x0046 # F
# 0x47 0x0047 # G
# 0x48 0x0048 # H
# 0x49 0x0049 # I
# 0x4A 0x004A # J
# 0x4B 0x004B # K
# 0x4C 0x004C # L
# 0x4D 0x004D # M
# 0x4E 0x004E # N
# 0x4F 0x004F # O
# 0x50 0x0050 # P
# 0x51 0x0051 # Q
# 0x52 0x0052 # R
# 0x53 0x0053 # S
# 0x54 0x0054 # T
# 0x55 0x0055 # U
# 0x56 0x0056 # V
# 0x57 0x0057 # W
# 0x58 0x0058 # X
# 0x59 0x0059 # Y
# 0x5A 0x005A # Z
# 0x5B 0x005B # [
# 0x5C 0x005C # \
# 0x5D 0x005D # ]
# 0x5E 0x005E # ^
# 0x5F 0x005F # _
# 0x60 0x0060 # `
# 0x61 0x0061 # a
# 0x62 0x0062 # b
# 0x63 0x0063 # c
# 0x64 0x0064 # d
# 0x65 0x0065 # e
# 0x66 0x0066 # f
# 0x67 0x0067 # g
# 0x68 0x0068 # h
# 0x69 0x0069 # i
# 0x6A 0x006A # j
# 0x6B 0x006B # k
# 0x6C 0x006C # l
# 0x6D 0x006D # m
# 0x6E 0x006E # n
# 0x6F 0x006F # o
# 0x70 0x0070 # p
# 0x71 0x0071 # q
# 0x72 0x0072 # r
# 0x73 0x0073 # s
# 0x74 0x0074 # t
# 0x75 0x0075 # u
# 0x76 0x0076 # v
# 0x77 0x0077 # w
# 0x78 0x0078 # x
# 0x79 0x0079 # y
# 0x7A 0x007A # z
# 0x7B 0x007B # {
# 0x7C 0x007C # |
# 0x7D 0x007D # }
# 0x7E 0x007E # ~
# 0x7F 0x2500 # ─
# 0x80 0x2502 # │
# 0x81 0x250C # ┌
# 0x82 0x2514 # └
# 0x83 0x251C # ├
# 0x84 0x2524 # ┤
# 0x85 0x252C # ┬
# 0x86 0x2534 # ┴
# 0x87 0x253C # ┼
# 0x88 0x256D # ╭
# 0x89 0x256E # ╮
# 0x8A 0x256F # ╯
# 0x8B 0x2570 # ╰
# 0x8C 0x2571 #
# 0x8D 0x2572 # ╲
# 0x8E 0x2573 #
# 0x8F 0x2550 # ═
# 0x90 0x2551 # ║
# 0x91 0x2554 # ╔
# 0x92 0x2557 # ╗
# 0x93 0x255a # ╚
# 0x94 0x255D # ╝
# 0x95 0x2560 # ╠
# 0x96 0x2563 # ╣
# 0x97 0x2566 # ╦
# 0x98 0x2569 # ╩
# 0x99 0x256C # ╬
# 0x9A 0xF04E # 
# 0x9B 0xF050 # 
# 0x9C 0xF051 # 
# 0x9D 0xF052 # 
# 0x9E 0xF048 # 
# 0x9F 0xE0B0 # 
# 0xA0 0xE0B2 # 
# 0xA1 0xE0B4 # 
# 0xA2 0xE0B6 # 
# 0xA3 0xE0B8 # 
# 0xA4 0xE0BA # 
# 0xA5 0xE0BC # 
# 0xA6 0xE0BE # 
# 0xA7 0x2581 # ▁
# 0xA8 0x2582 # ▂
# 0xA9 0x2583 # ▃
# 0xAA 0x2584 # ▄
# 0xAB 0x2585 # ▅
# 0xAC 0x2586 # ▆
# 0xAD 0x2587 # ▇
# 0xAE 0x2588 # █
# 0xAF 0x2589 # ▉
# 0xB0 0x258A # ▊
# 0xB1 0x258B # ▋
# 0xB2 0x258C # ▌
# 0xB3 0x258D # ▍
# 0xB4 0x258E # ▎
# 0xB5 0x258F # ▏
# 0xB6 0x0295 # ʕ
# 0xB7 0x00B7 # ·
# 0xB8 0x1D25 # ᴥ
# 0xB9 0x0294 # ʔ
# 0xBA 0x2596 # ▖
# 0xBB 0x2597 # ▗
# 0xBC 0x2598 # ▘
# 0xBD 0x2599 # ▙
# 0xBE 0x259A # ▚
# 0xBF 0x259B # ▛
# 0xC0 0x259C # ▜
# 0xC1 0x259D # ▝
# 0xC2 0x259E # ▞
# 0xC3 0x259F # ▟
# 0xC4 0x2190 # ←
# 0xC5 0x2191 # ↑
# 0xC6 0x2192 # →
# 0xC7 0x2193 # ↓
# 0xC8 0x2B60 # ⭠
# 0xC9 0x2B61 # ⭡
# 0xCA 0x2B62 # ⭢
# 0xCB 0x2B63 # ⭣
# 0xCC 0x2B80 # ⮀
# 0xCD 0x2B81 # ⮁
# 0xCE 0x2B82 # ⮂
# 0xCF 0x2B83 # ⮃
# 0xD0 0xF049 # 
# 0xD1 0xF04A # 
# 0xD2 0x23F3 # ⏳
# 0xD3 0xF07B # 
# 0xD4 0xF07C # 
# 0xD5 0xF114 # 
# 0xD6 0xF115 # 
# 0xD7 0xF250 # 
# 0xD8 0xF251 # 
# 0xD9 0xF253 # 
# 0xDA 0xF254 # 
# 0xDB 0xF461 # 
# 0xDC 0xF016 # 
# 0xDD 0xF401 # 
# 0xDE 0x1F52E # 🔮
# 0xDF 0xF2DB # 
# 0xE0 0xF008 # 
# 0xE1 0x25C7 # ◇
# 0xE2 0x25C8 # ◈
# 0xE3 0x1F311 # 🌑
# 0xE4 0x1F312 # 🌒
# 0xE5 0x1F313 # 🌓
# 0xE6 0x1F314 # 🌔
# 0xE7 0x1F315 # 🌕
# 0xE8 0x1F316 # 🌖
# 0xE9 0x1F317 # 🌗
# 0xEA 0x1F318 # 🌘
# 0xEB 0xF04C # 
# 0xEC 0x2714 # ✔
# 0xED 0x2718 # ✘
# 0xEE 0x25C6 # ◆
# 0xEF 0xF15D # 
# 0xF0 0xF15E # 
# 0xF1 0xF071 # 
# 0xF2 0xF449 # 
# 0xF3 0xF529 # 
# 0xF4 0xF658 # 
# 0xF5 0xF659 # 
# 0xF6 0x1f381 # 🎁
# 0xF7 0xf05a # 
# 0xF8 0xf06a # 
# 0xF9 0xf834 # 
# 0xFA 0xf835 # 
# 0xFB 0x2690 # ⚐
# 0xFC 0x2691 # ⚑
# 0xFD 0xf8d7 # 
# 0xFE 0xf0e7 # 
# 0xFF 0xf7d9 # 
0x00 0x00 #
0x01 0x0295 # ʕ
0x02 0x00B7 # ·
0x03 0x1D25 # ᴥ
0x04 0x0294 # ʔ
0x05 0x2661 # ♡
0x06 0x2665 # ♥
0x07 0x2726 # ✦
0x08 0x25C7 # ◇
0x09 0x25C6 # ◆
0x0a 0x272D # ✭
0x0b 0xF005 # 
0x0c 0x2728 # ✨
0x0d 0x000d # CR
0x0e 0x2518 # ┘
0x0f 0x2514 # └
0x10 0x250c # ┌
0x11 0x2510 # ┐
0x12 0x2500 # ─
0x13 0x2502 # │
0x14 0x2524 # ┤
0x15 0x2534 # ┴
0x16 0x251C # ├
0x17 0x252C # ┬
0x18 0x253C # ┼
0x19 0x2571 #
0x1a 0x2572 # ╲
0x1b 0x2573 #
0x1c 0x2591 # ░
0x1d 0x2592 # ▒
0x1e 0x2593 # ▓
0x1f 0x2588 # █
0x20 0x0020 #
0x21 0x0021 # !
0x22 0x0022 # "
0x23 0x0023 # #
0x24 0x0024 # $
0x25 0x0025 # %
0x26 0x0026 # &
0x27 0x0027 # '
0x28 0x0028 # (
0x29 0x0029 # )
0x2a 0x002A # *
0x2b 0x002B # +
0x2c 0x002C # ,
0x2d 0x002D # -
0x2e 0x002E # .
0x2f 0x002F # /
0x30 0x0030 # 0
0x31 0x0031 # 1
0x32 0x0032 # 2
0x33 0x0033 # 3
0x34 0x0034 # 4
0x35 0x0035 # 5
0x36 0x0036 # 6
0x37 0x0037 # 7
0x38 0x0038 # 8
0x39 0x0039 # 9
0x3a 0x003A # :
0x3b 0x003B # ;
0x3c 0x003C # <
0x3d 0x003D # =
0x3e 0x003E # >
0x3f 0x003F # ?
0x40 0x0040 # @
0x41 0x0041 # A
0x42 0x0042 # B
0x43 0x0043 # C
0x44 0x0044 # D
0x45 0x0045 # E
0x46 0x0046 # F
0x47 0x0047 # G
0x48 0x0048 # H
0x49 0x0049 # I
0x4a 0x004A # J
0x4b 0x004B # K
0x4c 0x004C # L
0x4d 0x004D # M
0x4e 0x004E # N
0x4f 0x004F # O
0x50 0x0050 # P
0x51 0x0051 # Q
0x52 0x0052 # R
0x53 0x0053 # S
0x54 0x0054 # T
0x55 0x0055 # U
0x56 0x0056 # V
0x57 0x0057 # W
0x58 0x0058 # X
0x59 0x0059 # Y
0x5a 0x005A # Z
0x5b 0x005B # [
0x5c 0x005C # \
0x5d 0x005D # ]
0x5e 0x005E # ^
0x5f 0x005F # _
0x60 0x0060 # `
0x61 0x0061 # a
0x62 0x0062 # b
0x63 0x0063 # c
0x64 0x0064 # d
0x65 0x0065 # e
0x66 0x0066 # f
0x67 0x0067 # g
0x68 0x0068 # h
0x69 0x0069 # i
0x6a 0x006A # j
0x6b 0x006B # k
0x6c 0x006C # l
0x6d 0x006D # m
0x6e 0x006E # n
0x6f 0x006F # o
0x70 0x0070 # p
0x71 0x0071 # q
0x72 0x0072 # r
0x73 0x0073 # s
0x74 0x0074 # t
0x75 0x0075 # u
0x76 0x0076 # v
0x77 0x0077 # w
0x78 0x0078 # x
0x79 0x0079 # y
0x7a 0x007A # z
0x7b 0x007B # {
0x7c 0x007C # |
0x7d 0x007D # }
0x7e 0x007E # ~
0x7f 0x256F # ╯
0x80 0x2570 # ╰
0x81 0x256D # ╭
0x82 0x256E # ╮
0x83 0x255D # ╝
0x84 0x255a # ╚
0x85 0x2554 # ╔
0x86 0x2557 # ╗
0x87 0x2550 # ═
0x88 0x2551 # ║
0x89 0x2563 # ╣
0x8a 0x2569 # ╩
0x8b 0x2560 # ╠
0x8c 0x2566 # ╦
0x8d 0x256C # ╬
0x8e 0xE0B8 # 
0x8f 0xE0BA # 
0x90 0xE0BC # 
0x91 0xE0BE # 
0x92 0xE0B2 # 
0x93 0xE0B0 # 
0x94 0xE0B6 # 
0x95 0xE0B4 # 
0x96 0x2596 # ▖
0x97 0x2597 # ▗
0x98 0x2598 # ▘
0x99 0x2599 # ▙
0x9a 0x259A # ▚
0x9b 0x259B # ▛
0x9c 0x259C # ▜
0x9d 0x259D # ▝
0x9e 0x259E # ▞
0x9f 0x259F # ▟
0xa0 0x2581 # ▁
0xa1 0x2582 # ▂
0xa2 0x2583 # ▃
0xa3 0x2584 # ▄
0xa4 0x2585 # ▅
0xa5 0x2586 # ▆
0xa6 0x2587 # ▇
0xa7 0x2589 # ▉
0xa8 0x258A # ▊
0xa9 0x258B # ▋
0xaa 0x258C # ▌
0xab 0x258D # ▍
0xac 0x258E # ▎
0xad 0x258F # ▏
0xae 0x1F311 # 🌑
0xaf 0x1F312 # 🌒
0xb0 0x1F313 # 🌓
0xb1 0x1F314 # 🌔
0xb2 0x1F315 # 🌕
0xb3 0x1F316 # 🌖
0xb4 0x1F317 # 🌗
0xb5 0x1F318 # 🌘
0xb6 0xF254 # 
0xb7 0xF251 # 
0xb8 0x23F3 # ⏳
0xb9 0xF253 # 
0xba 0xF250 # 
0xbb 0x2190 # ←
0xbc 0x2191 # ↑
0xbd 0x2192 # →
0xbe 0x2193 # ↓
0xbf 0x2B60 # ⭠
0xc0 0x2B61 # ⭡
0xc1 0x2B62 # ⭢
0xc2 0x2B63 # ⭣
0xc3 0x2B80 # ⮀
0xc4 0x2B81 # ⮁
0xc5 0x2B82 # ⮂
0xc6 0x2B83 # ⮃
0xc7 0xF049 # 
0xc8 0xF04A # 
0xc9 0xF048 # 
0xca 0xF04B # 
0xcb 0xF04C # 
0xcc 0xF04D # 
0xcd 0xF052 # 
0xce 0xF051 # 
0xcf 0xF04E # 
0xd0 0xF050 # 
0xd1 0xFC5D # ﱝ
0xd2 0xF026 # 
0xd3 0xF027 # 
0xd4 0xF028 # 
0xd5 0xFA80 # 婢
0xd6 0xFC5C # ﱜ
0xd7 0xFC5B # ﱛ
0xd8 0x2669 # ♩
0xd9 0x266A # ♪
0xda 0x266B # ♫
0xdb 0x266C # ♬
0xdc 0xF016 # 
0xdd 0xF07B # 
0xde 0xF07C # 
0xdf 0xF114 # 
0xe0 0xF115 # 
0xe1 0xF15D # 
0xe2 0xF15E # 
0xe3 0xF529 # 
0xe4 0xF071 # 
0xe5 0xF449 # 
0xe6 0xf05a # 
0xe7 0xF659 # 
0xe8 0xF658 # 
0xe9 0xf835 # 
0xea 0xf834 # 
0xeb 0x2690 # ⚐
0xec 0x2691 # ⚑
0xed 0xf7d9 # 
0xee 0x2714 # ✔
0xef 0x2718 # ✘
0xf0 0xf0e7 # 
0xf1 0xF2DB # 
0xf2 0xF008 # 
0xf3 0xF461 # 
0xf4 0x2B50 # ⭐
0xf5 0xF401 # 
0xf6 0x1F52E # 🔮
0xf7 0x1f381 # 🎁
0xf8 0xf8d7 # 
0xf9 0xF0AC # 
0xfa 0x25C8 # ◈
0xfb 0x2640 # ♀
0xfc 0x2642 # ♂
0xfd 0x26A2 # ⚢
0xfe 0x26A3 # ⚣
0xff 0x26A5 # ⚥
+5
View File
@@ -0,0 +1,5 @@
#! /bin/sh
fontforge -lang=ff -c 'Open($1); LoadEncodingFile($2, "george"); Reencode("george"); Generate($3)' Cozette.sfd georgeencoding.txt cozette.bdf
sed -i'' -e 's/FONTBOUNDINGBOX 11 13 0 -3/FONTBOUNDINGBOX 8 13 0 -3/' *.bdf
mv cozette-13.bdf cozette.bdf
Generated
+112
View File
@@ -0,0 +1,112 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"owner": "nmattia",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"type": "github"
},
"original": {
"owner": "nmattia",
"ref": "master",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1712696601,
"narHash": "sha256-puFPFSa/RC83JilUgB48/VL387eu2QN066Jv6X971LY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "062fc6cf99d809921ecef47317752fc92468e6ae",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1712608508,
"narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+62
View File
@@ -0,0 +1,62 @@
{
inputs = {
naersk.url = "github:nmattia/naersk/master";
# This must be the stable nixpkgs if you're running the app on a
# stable NixOS install. Mixing EGL library versions doesn't work.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
utils.url = "github:numtide/flake-utils";
flake-compat = {
url = github:edolstra/flake-compat;
flake = false;
};
};
outputs = { self, nixpkgs, utils, naersk, ... }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = pkgs.callPackage naersk { };
libPath = with pkgs; lib.makeLibraryPath [
libxkbcommon
wayland
xorg.libX11
xorg.libXcursor
xorg.libXi
xorg.libXrandr
];
in
{
defaultPackage = naersk-lib.buildPackage {
src = ./.;
doCheck = true;
pname = "georgeemu";
nativeBuildInputs = [ pkgs.makeWrapper ];
buildInputs = with pkgs; [
xorg.libxcb
];
postInstall = ''
wrapProgram "$out/bin/georgeemu" --prefix LD_LIBRARY_PATH : "${libPath}"
'';
};
defaultApp = utils.lib.mkApp {
drv = self.defaultPackage."${system}";
};
devShell = with pkgs; mkShell {
buildInputs = [
cargo
cargo-insta
pre-commit
rust-analyzer
rustPackages.clippy
rustc
rustfmt
xorg.libxcb
];
RUST_SRC_PATH = rustPlatform.rustLibSrc;
LD_LIBRARY_PATH = libPath;
};
});
}
+3
View File
@@ -0,0 +1,3 @@
char_rom = "./src/roms/cozette.rom"
rom = "./src/roms/keyboard_sys.rom"
screen = "Window"
+11
View File
@@ -0,0 +1,11 @@
.org $00
.byte $80
.org $8000
reset:
bbr7 $00, reset
.org $fffc
.word reset
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+238
View File
@@ -0,0 +1,238 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
temp = $20 ; scratchpad page
cursor = $300
cursor_x = cursor
cursor_y = cursor + 1
rand_index = $200
reset:
sei
ldx #0; initialize data stack pointer
init:
cli
main:
jsr print
jsr rand_draw
jmp main
newline: ; sets cursor to start of next line
stz cursor_x
lda cursor_y
cmp #28
bne .end
stz cursor_y
rts
.end:
inc cursor_y
rts
text:
.byte 1,2,3,2,4
.asciiz "- george loves u <3"
random_y:
.byte 25,12,0,20,4,25,5,13
.byte 20, 1, 1, 22, 12, 19, 19, 19
.byte 9, 0, 4, 18, 13, 14, 4, 16
.byte 8, 17, 21, 14, 23, 21, 9, 0
.byte 14, 10, 14, 2, 26, 18, 15, 23
.byte 12, 2, 5, 4, 25, 20, 27, 4
.byte 28, 21, 3, 22, 11, 25, 2, 25
.byte 13, 17, 17, 24, 8, 8, 20, 21
.byte 11, 24, 27, 25, 8, 12, 7, 0
.byte 27, 12, 19, 27, 10, 3, 19, 2
.byte 2, 23, 22, 5, 26, 28, 4, 16
.byte 18, 7, 10, 9, 6, 19, 9, 2
.byte 14, 8, 14, 18, 18, 2, 13, 0
.byte 15, 26, 3, 23, 17, 12, 18, 11
.byte 4, 16, 17, 22, 9, 25, 3, 15
.byte 28, 3, 6, 14, 25, 5, 21, 8
.byte 15, 18, 15, 5, 28, 6, 15, 4
.byte 10, 1, 16, 24, 6, 9, 22, 3
.byte 17, 18, 10, 19, 27, 11, 22, 16
.byte 22, 17, 15, 6, 23, 11, 11, 11
.byte 4, 15, 5, 25, 19, 1, 8, 26
.byte 21, 20, 17, 27, 11, 3, 11, 20
.byte 15, 28, 0, 6, 14, 23, 20, 21
.byte 17, 20, 16, 15, 19, 6, 21, 19
.byte 15, 27, 1, 22, 7, 0, 5, 2
.byte 14, 24, 15, 4, 20, 16, 1, 14
.byte 4, 16, 4, 8, 13, 26, 3, 9
.byte 12, 25, 5, 0, 7, 17, 14, 20
.byte 2, 26, 2, 27, 18, 23, 5, 8
.byte 4, 21, 10, 11, 28, 22, 6, 6
.byte 10, 13, 23, 12, 20, 28, 20, 1
.byte 27, 19, 25, 6, 1, 10, 1
random_x:
.byte 42, 59, 11, 5, 18, 0, 26, 1
.byte 61, 16, 1, 51, 36, 47, 23, 1
.byte 16, 50, 46, 4, 55, 31, 15, 2
.byte 45, 21, 59, 53, 15, 43, 0, 2
.byte 64, 31, 38, 41, 25, 12, 12, 3
.byte 30, 13, 64, 44, 21, 8, 48, 3
.byte 46, 1, 2, 33, 4, 32, 59, 28
.byte 4, 24, 58, 53, 21, 41, 30, 2
.byte 56, 53, 31, 10, 42, 12, 9, 54
.byte 14, 14, 24, 29, 43, 60, 54, 26
.byte 5, 53, 17, 55, 27, 46, 31, 3
.byte 26, 44, 63, 30, 10, 34, 62, 48
.byte 42, 47, 51, 7, 55, 32, 14, 21
.byte 15, 26, 52, 37, 48, 0, 13, 2
.byte 50, 20, 35, 32, 8, 41, 2, 24
.byte 18, 9, 52, 22, 52, 12, 19, 32
.byte 29, 46, 34, 58, 54, 51, 43, 57
.byte 62, 10, 12, 57, 36, 39, 4, 30
.byte 38, 9, 30, 32, 33, 57, 3, 25
.byte 21, 36, 59, 30, 19, 39, 9, 60
.byte 34, 50, 52, 37, 34, 42, 3, 33
.byte 40, 19, 2, 26, 10, 38, 46, 30
.byte 3, 1, 19, 16, 26, 58, 42, 49
.byte 63, 1, 63, 41, 0, 21, 41, 19
.byte 21, 45, 44, 52, 20, 5, 11, 64
.byte 1, 62, 16, 5, 5, 8, 58, 56
.byte 16, 26, 6, 37, 19, 16, 25, 29
.byte 64, 59, 16, 6, 41, 28, 8, 51
.byte 54, 5, 19, 28, 13, 38, 52, 35
.byte 42, 13, 34, 33, 61, 61, 7, 27
.byte 38, 33, 9, 57, 10, 30, 8, 4
.byte 46, 3, 39, 46, 62, 20, 48, 7
; increments the cursor line by line, looping to (0, 0) after (63, 28)
inc_cursor:
lda cursor_x
cmp #63
beq .newline
inc cursor_x
rts
.newline:
lda cursor_y
cmp #28
beq .newscreen
stz cursor_x
inc cursor_y
rts
.newscreen:
stz cursor_y
stz cursor_x
rts
; zeroes out the display, resets cursor to 0,0
clear:
lda #0
ldy #0
.loop:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne .loop
stz cursor
stz cursor + 1
rts
; prints string from cursor position, stopping at end of string or at 256 chars, whichever comes first
; $6000 + (64*Y) + X
; THIS WILL WRITE OUT OF BOUNDS IF THE CURSOR IS OUT OF BOUNDS/STRING IS TOO LONG
; TODO: figure out a simple way of writing arbitrary length strings
print:
jsr cursor_addr
ldy #0
; y_overflow = temp + 5
.loop:
lda text, y
beq .end
sta (temp), y
iny
bra .loop
.end:
rts
; calculates real vram address from cursor (x, y)
cursor_addr:
stz temp
stz temp + 1
lda cursor_y
beq .add_x ; if y's zero just add x
.y_mult:
; multiply by 64
clc
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
sta temp
.add_x:
clc
lda cursor_x
adc temp
sta temp
lda #0
adc temp + 1
sta temp + 1
clc
lda #$60
adc temp + 1
sta temp + 1
rts
rand_draw:
ldx rand_index
lda random_x, x
sta cursor_x
lda random_y, x
sta cursor_y
jsr cursor_addr
inc rand_index
rts
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+189
View File
@@ -0,0 +1,189 @@
; .setcpu "65C02"
.include "./macro.inc"
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
n = $01 ; temporary storage for data stack operations
key_row = $200 ; used for character lookup when key pressed
key_col = $201
cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
reset:
sei
ldx #0; initialize data stack pointer
initdisplay:
lda #0
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
print_test:
lda #0
sta key_row
lda #5
sta key_col
push_coords #5, #5
main:
; jsr printtext
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
jsr print
; jsr print
stp
jmp main
; keyboard: ; reads keyboard registers and stores the column and row of the first key found
; ; TODO: make this routine store up to 8 indices (for 8 key rollover)
; ldy #0
; .check_row: ; loop through each row
; lda kb_row, y
; beq .skip_row ; if row has no key pressed, skip checking which key
; ; jmp key_down
; sta kb_row_cache, y ; if key pressed, cache it
; lda kb_row, y
; cmp kb_row_cache, y ; has key changed?
; beq key_down
; .skip_row:
; iny
; cpy #5
; bne .check_row
; rts
; key_down: ; a is loaded with the row byte
; phy
; sty key_row ; store character row
; ldy #0
; .find_col: ; test each row bit, store column if key pressed
; lsr ; test bit 7
; bcs store_col ; if unset, don't go store character columnb
; .skip:
; iny
; cpy #8
; bne .find_col ; loop until we've checked each bit
; rts
; store_col:
; sty key_col
; jsr print
; rts
print: ; x y -- prints the key indexed with key_col and key_row at position x, y
keymap_index:
push
lda key_col
stz 1, x
sta 0, x
push
lda #8
stz 1, x
sta 0, x
push
lda key_row
stz 1, x
sta 0, x
jsr mult
jsr plus
lda 0, x
tay
lda keymap, y
push
sta 0, x
stz 1, x
jsr draw_char
rts
keymap:
.byte "?outrew?"
.byte "?piygsq?"
.byte "a??khvd?"
.byte "42ljbfz?"
.byte "31?mncx?"
.byte "????? m"
draw:
; push_coords #0, #0
; push_char #$00
; jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
lda 0, x ; load a with character to draw
pop ; and pop it off the stack
jsr get_char_address ; calculate where to put the character in memory
sta (0, x) ; store a at the address pointed to on the stack
rts
get_char_address: ; gets vram address for a character at (x, y),
; (n1: x n2: y -- n: $6000 + x + (64 * y))
;jsr push_lit ; push 64 onto stack, low byte first
;.byte 64
;.byte 0
pha
lda #64
push ; doing this instead until `push_lit` is fixed
sta 0, x
stz 1, x
jsr mult ; multiply 64 with y (n2)
jsr plus ; add result with x (n1)
;jsr push_lit ; push vram address onto the stack
;.byte $00
;.byte $60
lda #$60
push
sta 1, x
stz 0, x
jsr plus ; add vram start address to result
pla
rts
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr keyboard
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+124
View File
@@ -0,0 +1,124 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
key_buffer = $200
reset:
sei
ldx #0; initialize data stack pointer
initdisplay:
lda #0
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
; cli
main:
jsr read_keys
lda key_buffer
sta $6000
; lda $4400
; sta $6000
jmp main
; first, let's do the simplest case of just the letter a being pressed
; 1. check row 2 ($4402, where the a key is) for pressed keys
; 2. if any keys are pressed, check if the key is a (bit 0)
; 3. if the key is not a, store zero in the key buffer
; 4. if the key is a, store the ascii for a in the key buffer
; 5. return
; so u don't have to scroll: key_buffer = $200
; key buffer just shows the current state of the keyboard
; it is not a queue of keys to print
read_keys:
lda $4402 ; check row 2
beq check_key ; if there're any keys, check which
clear_buffer:
stz key_buffer
rts
check_key: ; if there is a key pressed, check if it's a
ror
bmi store_key
rts
store_key:
; in a sec we'll set up ascii table lookup,
; for now let's just load the ascii byte for a
lda #$61
sta key_buffer
rts
draw:
push_coords #0, #0
push_char #$af
jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
lda 0, x ; load a with character to draw
pop ; and pop it off the stack
jsr get_char_address ; calculate where to put the character in memory
sta (0, x) ; store a at the address pointed to on the stack
rts
get_char_address: ; gets vram address for a character at (x, y),
; (n1: x n2: y -- n: $6000 + x + (64 * y))
;jsr push_lit ; push 64 onto stack, low byte first
;.byte 64
;.byte 0
pha
lda #64
push ; doing this instead until `push_lit` is fixed
sta 0, x
stz 1, x
jsr mult ; multiply 64 with y (n2)
jsr plus ; add result with x (n1)
;jsr push_lit ; push vram address onto the stack
;.byte $00
;.byte $60
lda #$60
push
sta 1, x
stz 0, x
jsr plus ; add vram start address to result
pla
rts
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
jsr get_char_address
irq:
rts
isr: ; interrupt service routine
pha
phx
phy
jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+215
View File
@@ -0,0 +1,215 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
temp = $20 ; scratchpad page
str_ptr = $30
cursor = $300
cursor_x = cursor
cursor_y = cursor + 1
char_buf = $302
char_buf_index = char_buf + 8
reset:
sei
ldx #0; initialize data stack pointer
init:
lda #$31
sta str_ptr
lda #$80
sta str_ptr + 1
jsr clear
lda #0
sta cursor_x
lda #0
sta cursor_y
cli
main:
jsr print
jmp main
newline: ; sets cursor to start of next line
stz cursor_x
lda cursor_y
cmp #28
bne .end
stz cursor_y
rts
.end:
inc cursor_y
rts
text:
.asciiz "hello <3"
; increments the cursor line by line, looping to (0, 0) after (63, 28)
inc_cursor:
lda cursor_x
cmp #63
beq .newline
inc cursor_x
rts
.newline:
lda cursor_y
cmp #28
beq .newscreen
stz cursor_x
inc cursor_y
rts
.newscreen:
stz cursor_y
stz cursor_x
rts
; zeroes out the display, resets cursor to 0,0
clear:
lda #0
ldy #0
.loop:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne .loop
stz cursor
stz cursor + 1
rts
; prints string from cursor position, stopping at end of string or at 256 chars, whichever comes first
; $6000 + (64*Y) + X
; THIS WILL WRITE OUT OF BOUNDS IF THE CURSOR IS OUT OF BOUNDS/STRING IS TOO LONG
; TODO: figure out a simple way of writing arbitrary length strings
; and
print:
jsr cursor_addr
ldy #0
; y_overflow = temp + 5
.loop:
lda (str_ptr), y
beq .end
sta (temp), y
iny
bra .loop
.end:
rts
; calculates real vram address from cursor (x, y)
cursor_addr:
stz temp
stz temp + 1
lda cursor_y
beq .add_x ; if y's zero just add x
.y_mult:
; multiply by 64
clc
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
asl
rol temp + 1
sta temp
.add_x:
clc
lda cursor_x
adc temp
sta temp
lda #0
adc temp + 1
sta temp + 1
clc
lda #$60
adc temp + 1
sta temp + 1
rts
; print_text:
; lda text,y
; beq .end
; sta $6000, y
; iny
; bra print_text
; .end:
; ldy #0
; rts
; draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
; lda 0, x ; load a with character to draw
; pop ; and pop it off the stack
; jsr get_char_address ; calculate where to put the character in memory
; sta (0, x) ; store a at the address pointed to on the stack
; rts
; get_char_address: ; gets vram address for a character at (x, y),
; ; (n1: x n2: y -- n: $6000 + x + (64 * y))
; ;jsr push_lit ; push 64 onto stack, low byte first
; ;.byte 64
; ;.byte 0
; pha
; lda #64
; push ; doing this instead until `push_lit` is fixed
; sta 0, x
; stz 1, x
; jsr mult ; multiply 64 with y (n2)
; jsr plus ; add result with x (n1)
; ;jsr push_lit ; push vram address onto the stack
; ;.byte $00
; ;.byte $60
; lda #$60
; push
; sta 1, x
; stz 0, x
; jsr plus ; add vram start address to result
; pla
; rts
; fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
; jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
Binary file not shown.
+62
View File
@@ -0,0 +1,62 @@
.macro breakpoint ; $02 isn't a valid instruction, the emulator will see this and halt, dump memory contents
.byte $02
.endm
.macro pop ; drops a data stack cell
inx
inx
.endm
.macro pop2 ; drops 2 data stack cells
inx
inx
inx
inx
.endm
.macro push ; push a data stack cell
dex
dex
.endm
.macro push2 ; push 2 data stack cells
dex
dex
dex
dex
.endm
.macro push_char, char; pushes an ascii character code onto the stack
lda \char
push
sta 0, x ; char low byte
stz 1, x ; char high byte
.endm
.macro push_coords, coord_x, coord_y ; push a set of (x,y) coordinates onto the data stack
lda \coord_x
push
sta 0, x ; low byte
stz 1,x ; high byte is zero
lda \coord_y
push
sta 0,x ; same here
stz 1,x
.endm
.macro to_r ; pop the top of the stack off and save it in the return (hardware) stack: (n -- )
lda 1, x
pha
lda 0, x
pha
pop
.endm
.macro from_r ; pop the top of the return stack off and put it on the data stack: ( -- n)
push
pla
sta 0, x
pla
sta 1, x
.endm
+81
View File
@@ -0,0 +1,81 @@
; --- Data Stack --- ;
; on this channel we love garth wilson: https://wilsonminesco.com/stacks/StackOps.ASM
; data stack is built up of 2-byte cells
; TODO: this is broken, jumping here does nothing to the stack and skips several instructions, could be an emulator problem tho
; push_lit: ; this bad boy lets you inline a literal (low byte first) right after `jsr push_lit` and put it on the stack, once again, on this channel we love garth wilson
; push2
; phx
; tsx
; txa
; tay
; plx
; lda $102, y
; sta 0, x
; clc
; adc #2
; sta $102, y
; lda $103, y
; sta 1, x
; adc #0
; sta $103, y
; fetch:
; lda (0, x)
; pha
; inc 0, x
; bne .1
; inc 1, x
; .1:
; lda (0, x)
; bra put
; push
; put:
; sta 1, x
; pla
; sta 0, x
; rts
plus: ; add: (n1 n2 -- n1+n2)
clc
lda 0, x
adc 2, x
sta 2, x
lda 1, x
adc 3, x
sta 3, x
pop
rts
mult: ; multiply: (n1 n2 -- n1*n2), frankly, i don't know how this works, but TODO: will try to figure it out later
phy
stz n
ldy #0
.1:
lsr 3, x
ror 2, x
bcc .2
clc
lda n
adc 0, x
sta n
tya
adc 1, x
tay
.2:
asl 0, x
rol 1, x
lda 2, x
ora 3, x
bne .1
lda n
sta 2, x
sty 3, x
pop
ply
rts
+88
View File
@@ -0,0 +1,88 @@
; .setcpu "65C02"
.include "./macro.inc"
.org $8000
n = $01 ; temporary storage for data stack operations
reset:
sei
ldx #0; initialize data stack pointer
initdisplay:
lda #0
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
main:
jsr draw
jmp main
draw:
push_coords #0, #0
push_char #$af
jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
lda 0, x ; load a with character to draw
pop ; and pop it off the stack
jsr get_char_address ; calculate where to put the character in memory
sta (0, x) ; store a at the address pointed to on the stack
rts
get_char_address: ; gets vram address for a character at (x, y),
; (n1: x n2: y -- n: $6000 + x + (64 * y))
;jsr push_lit ; push 64 onto stack, low byte first
;.byte 64
;.byte 0
pha
lda #64
push ; doing this instead until `push_lit` is fixed
sta 0, x
stz 1, x
jsr mult ; multiply 64 with y (n2)
jsr plus ; add result with x (n1)
;jsr push_lit ; push vram address onto the stack
;.byte $00
;.byte $60
lda #$60
push
sta 1, x
stz 0, x
jsr plus ; add vram start address to result
pla
rts
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+173
View File
@@ -0,0 +1,173 @@
; .setcpu "65C02"
.include "./macro.inc"
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
n = $01 ; temporary storage for data stack operations
key_row = $200 ; used for character lookup when key pressed
key_col = $201
cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
reset:
sei
ldx #0; initialize data stack pointer
jmp main
initdisplay:
lda #20
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
main:
; jsr keyboard
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
; jsr kitty_keys
lda #9
sta $6000
jmp main
not_keyboard:
ldy #0
.check_row: ; loop through each row
lda kb_row, y
beq .skip_row ; if row has no key pressed, skip checking which key
sta kb_row_cache, y ; if key pressed, cache it
lda kb_row, y
cmp kb_row_cache, y ; has key changed?
beq key_down
.skip_row:
iny
cpy #5
bne .check_row
rts
key_down: ; a is loaded with the row byte
phy
sty key_row ; store character row
ldy #0
.find_col: ; test each row bit, store column if key pressed
lsr ; test bit 7
bcs store_col ; if unset, don't go store character columnb
.skip:
iny
cpy #8
bne .find_col ; loop until we've checked each bit
store_col:
sty key_col
keymap_index:
push
lda key_col
stz 1, x
sta 0, x
push
lda #8
stz 1, x
sta 0, x
push
lda key_row
stz 1, x
sta 0, x
jsr mult
jsr plus
lda 0, x
tay
print: ; we've stored the character position, now let's
lda keymap, y
ldy cursor
sta $6000, y
inc cursor
ply
rts
keymap:
.byte "?outrew?"
.byte "?piygsq?"
.byte "a??khvd?"
.byte "42ljbfz?"
.byte "31?mncx?"
.byte "????? m"
; draw:
; ; push_coords #0, #0
; ; push_char #$00
; ; jsr draw_char
; rts
; draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
; lda 0, x ; load a with character to draw
; pop ; and pop it off the stack
; jsr get_char_address ; calculate where to put the character in memory
; sta (0, x) ; store a at the address pointed to on the stack
; rts
; get_char_address: ; gets vram address for a character at (x, y),
; ; (n1: x n2: y -- n: $6000 + x + (64 * y))
; ;jsr push_lit ; push 64 onto stack, low byte first
; ;.byte 64
; ;.byte 0
; pha
; lda #64
; push ; doing this instead until `push_lit` is fixed
; sta 0, x
; stz 1, x
; jsr mult ; multiply 64 with y (n2)
; jsr plus ; add result with x (n1)
; ;jsr push_lit ; push vram address onto the stack
; ;.byte $00
; ;.byte $60
; lda #$60
; push
; sta 1, x
; stz 0, x
; jsr plus ; add vram start address to result
; pla
; rts
; fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
; jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr irq
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
BIN
View File
Binary file not shown.
+187
View File
@@ -0,0 +1,187 @@
; .setcpu "65C02"
.include "./macro.inc"
; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates
n = $01 ; temporary storage for data stack operations
key_row = $200 ; used for character lookup when key pressed
key_col = $201
cursor = $202
char_buffer = $300 ; 256 byte character buffer
kb_row = $4400 ; keyboard hardware register
kb_row_cache = $203 ; cache
.org $8000
reset:
sei
ldx #0; initialize data stack pointer
initdisplay:
lda #0
ldy #0
cleardisplay:
sta $6000,y
sta $6100,y
sta $6200,y
sta $6300,y
sta $6400,y
sta $6500,y
sta $6600,y
sta $6700,y ; this goes slightly over but it's fine
iny
bne cleardisplay
cli
print_test:
lda #0
sta key_row
lda #5
sta key_col
push_coords #5, #5
main:
; jsr printtext
; key_zero:
; stz keyboard_cache, x
; dex
; bpl key_zero
; fim:
; cli
; bra fim
jsr print
; jsr print
jmp main
; keyboard: ; reads keyboard registers and stores the column and row of the first key found
; ; TODO: make this routine store up to 8 indices (for 8 key rollover)
; ldy #0
; .check_row: ; loop through each row
; lda kb_row, y
; beq .skip_row ; if row has no key pressed, skip checking which key
; ; jmp key_down
; sta kb_row_cache, y ; if key pressed, cache it
; lda kb_row, y
; cmp kb_row_cache, y ; has key changed?
; beq key_down
; .skip_row:
; iny
; cpy #5
; bne .check_row
; rts
; key_down: ; a is loaded with the row byte
; phy
; sty key_row ; store character row
; ldy #0
; .find_col: ; test each row bit, store column if key pressed
; lsr ; test bit 7
; bcs store_col ; if unset, don't go store character columnb
; .skip:
; iny
; cpy #8
; bne .find_col ; loop until we've checked each bit
; rts
; store_col:
; sty key_col
; jsr print
; rts
print: ; x y -- prints the key indexed with key_col and key_row at position x, y
keymap_index:
push
lda key_col
stz 1, x
sta 0, x
push
lda #8
stz 1, x
sta 0, x
push
lda key_row
stz 1, x
sta 0, x
jsr mult
jsr plus
lda 0, x
tay
lda keymap, y
push
sta 0, x
stz 1, x
jsr draw_char
rts
keymap:
.byte "?outrew?"
.byte "?piygsq?"
.byte "a??khvd?"
.byte "42ljbfz?"
.byte "31?mncx?"
.byte "????? m"
draw:
; push_coords #0, #0
; push_char #$00
; jsr draw_char
rts
draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- )
lda 0, x ; load a with character to draw
pop ; and pop it off the stack
jsr get_char_address ; calculate where to put the character in memory
sta (0, x) ; store a at the address pointed to on the stack
rts
get_char_address: ; gets vram address for a character at (x, y),
; (n1: x n2: y -- n: $6000 + x + (64 * y))
;jsr push_lit ; push 64 onto stack, low byte first
;.byte 64
;.byte 0
pha
lda #64
push ; doing this instead until `push_lit` is fixed
sta 0, x
stz 1, x
jsr mult ; multiply 64 with y (n2)
jsr plus ; add result with x (n1)
;jsr push_lit ; push vram address onto the stack
;.byte $00
;.byte $60
lda #$60
push
sta 1, x
stz 0, x
jsr plus ; add vram start address to result
pla
rts
fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- )
jsr get_char_address
isr: ; interrupt service routine
pha
phx
phy
; jsr keyboard
ply
plx
pla
rti
.include "math.inc"
.org $fffc
.word reset
.word isr
Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
if [ $# -eq 0 ]; then
echo "Provide the name of the rom/asm file to run"
exit 1
fi
set -e
vasm6502_oldstyle roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o roms/$1.rom;
cargo run -- rom "roms/$1.rom";
# hexdump -C ./cpu_dump.bin;
-25585
View File
File diff suppressed because it is too large Load Diff
+179
View File
@@ -0,0 +1,179 @@
use std::{
env,
fs::File,
io::{ErrorKind, Read},
process::exit,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Debug)]
pub enum ScreenType {
Terminal,
#[default]
Window,
}
pub struct Config {
pub rom: Option<String>,
pub screen: ScreenType,
pub char_rom: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ConfigBuilder {
rom: Option<String>,
char_rom: Option<String>,
screen: ScreenType,
}
impl ConfigBuilder {
fn new() -> Self {
Self {
rom: None,
char_rom: None,
screen: ScreenType::default(),
}
}
fn build(self) -> Config {
let rom = match self.rom {
Some(rom) => Some(rom),
None => {
println!("no rom was provided :(");
exit(1);
}
};
let char_rom = self.char_rom;
let screen = self.screen;
Config {
rom,
screen,
char_rom,
}
}
}
fn help(command: Option<String>) {
let executable: String = env::args().next().unwrap();
if let Some(command) = command {
match &command as &str {
"rom" | "--rom" | "-r" => {
println!("{executable} {command} <path>\n\nload a rom/binary from path");
exit(0);
}
"screen" | "--screen" | "-s" => {
println!("{executable} {command} <path>\n\nload a rom/binary from path");
exit(0);
}
"help" | "--help" | "-h" => {
println!("{executable} {command} <command>\n\nshow help info for a given command");
exit(0);
}
_ => {
println!(
"{command:?} isn't a valid command!\n\nuse `{executable} help` to see all valid commands",
);
exit(1);
}
}
} else {
println!("ʕ·ᴥ·ʔ- {executable} is an emulator for george:");
println!("https://git.augustkline.com/august/george\n");
println!("commands:");
println!(" help, -h, --help <command>: print help info for any command");
println!(" rom, -r, --rom <path>: load a rom/binary from path");
println!(" screen, -s, --screen <type>: use the \"terminal\" or \"window\" display type");
println!("\nconfiguration:");
println!(" george-emu searches for a `george.toml` in the current directory.\n in `george.toml` you can specify a path for the character rom\n using the key `char_rom` and the main rom/binary with the key `rom`");
println!("\ndebugging:");
println!(" you can pipe in a rom for george to evaluate.");
println!(" she'll read it until she reaches a breakpoint (`0x02`) or `stp` instruction,");
println!(" then will dump her memory to stdout <3");
exit(0);
}
}
pub fn get_input() -> Config {
let executable: String = env::args().next().unwrap();
let mut config = ConfigBuilder::new();
let len = env::args().len();
if len == 1 {
config = match File::open("./george.toml") {
Ok(mut file) => {
let mut string = String::new();
file.read_to_string(&mut string).unwrap();
toml::from_str(string.as_str()).unwrap()
}
Err(_) => {
println!("couldn't find a `george.toml` in the current directory!");
println!("\nuse `{executable} --help` to see all config options");
exit(1);
}
};
return config.build();
}
let mut args = env::args().skip(1);
while let Some(arg) = args.next() {
match &arg[..] {
"--help" | "-h" | "help" => help(args.next()),
"--rom" | "-r" | "rom" => {
if let Some(path) = args.next() {
match std::fs::File::open(&path) {
Ok(_) => {
config.rom = Some(path);
}
Err(error) => {
match error.kind() {
ErrorKind::NotFound => {
println!("couldn't find the rom at {path:?}");
}
ErrorKind::PermissionDenied => {
println!(
"didn't have sufficient permissions to open the rom at {path:?}"
);
}
_ => {
println!("something went wrong! try again in a moment? really not sure why you're getting this error");
}
}
exit(1);
}
};
} else {
println!("no rom specified!");
exit(1);
}
}
"--screen" | "-s" | "screen" => {
let kind = args.next();
match kind {
Some(kind) => match &kind as &str {
"terminal" | "Terminal" | "TERMINAL" | "t" | "term" => {}
"window" | "Window" | "WINDOW" | "w" | "win" => {
config.screen = ScreenType::Window
}
_ => {
println!("{kind:?} isn't a valid screen type!\n\nuse \"{executable} --help screen\" to see all valid screen types");
exit(1);
}
},
None => {
println!("no screen type was provided!\n\nuse \"{executable} --help screen\" to see all valid screen types");
exit(1);
}
}
}
_ => {
println!("{arg:?} isn't a valid command!\n\nuse \"{executable} help\" to see all valid commands");
exit(1);
}
}
}
config.build()
}
+76
View File
@@ -0,0 +1,76 @@
use std::io::{self, stdin, IsTerminal};
#[cfg(not(target_arch = "wasm32"))]
mod cli;
#[cfg(not(target_arch = "wasm32"))]
use cli::get_input;
use georgeemu::memory::{Mem, MemHandle};
#[cfg(not(target_arch = "wasm32"))]
use georgeemu::GeorgeEmu;
#[cfg(not(target_arch = "wasm32"))]
use minifb::{Scale, ScaleMode, Window, WindowOptions};
use std::{fs::File, io::Read};
#[cfg(not(target_arch = "wasm32"))]
fn main() -> io::Result<()> {
use std::io::{stdout, Write};
use georgeemu::cpu::Cpu;
let mut input = stdin();
if !input.is_terminal() {
let mut bytes = [0u8; 0x8000];
input.read_exact(&mut bytes).unwrap();
let memory = make_memory(&bytes);
let mut cpu = Cpu::new(memory);
let final_output = cpu.run_to_end();
let _ = stdout().write_all(&final_output);
Ok(())
} else {
let window = Window::new(
"ʕ·ᴥ·ʔ-☆",
512,
380,
WindowOptions {
resize: true,
borderless: true,
title: true,
transparency: false,
scale: Scale::FitScreen,
scale_mode: ScaleMode::AspectRatioStretch,
topmost: false,
none: true,
},
)
.unwrap();
let config = get_input();
let rom = match config.rom {
Some(path) => {
let mut file = File::open(path).unwrap();
let mut bin = vec![0; 0x8000];
file.read_exact(&mut bin).unwrap();
bin.try_into().unwrap()
}
None => *include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/roms/george.rom")),
};
let mut emu = GeorgeEmu::builder().rom(rom).window(window).build();
emu.run();
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
fn make_memory(bytes: &[u8]) -> MemHandle {
let mut memory = Mem::new();
memory.load_bytes(bytes);
MemHandle::new(memory)
}
#[cfg(target_arch = "wasm32")]
fn main() {}
BIN
View File
Binary file not shown.
+228 -182
View File
@@ -1,250 +1,296 @@
use crate::error::{ExecutionError, GeorgeError, GeorgeErrorKind, MemoryError}; use crate::instructions::get_instruction;
use crate::instructions::{get_instruction, Instruction}; use crate::memory::{MemHandle, MemoryReader, MemoryWriter};
use crate::memory::Mem; use std::fmt::Display;
use crate::types::{Byte, Word}; use std::sync::mpsc::{channel, Receiver, Sender};
#[cfg(not(target_arch = "wasm32"))]
use std::thread::sleep;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Duration; use std::time::Duration;
use std::{
str::FromStr,
sync::{Arc, RwLock},
thread::sleep,
};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum StatusFlag { pub enum StatusFlag {
Negative = 0b1000_0000, Negative = 0b1000_0000,
Overflow = 0b0100_0000, Overflow = 0b0100_0000,
Brk = 0b0011_0000, Brk = 0b0011_0000,
BrkIrq = 0b0010_0000, //BrkIrq = 0b0010_0000,
Decimal = 0b0000_1000, Decimal = 0b0000_1000,
IrqDisable = 0b0000_0100, IrqDisable = 0b0000_0100,
Zero = 0b000_0010, Zero = 0b000_0010,
Carry = 0b0000_0001, Carry = 0b0000_0001,
} }
#[derive(Clone, Debug)]
pub struct CpuController(Sender<CpuControl>);
#[derive(Clone, Copy)]
pub enum CpuControl {
Irq,
Nmi,
Toggle,
Data,
Cycle,
}
impl CpuController {
pub fn new(sender: Sender<CpuControl>) -> Self {
Self(sender)
}
pub fn irq(&self) {
let _ = self.0.send(CpuControl::Irq);
}
pub fn nmi(&self) {
let _ = self.0.send(CpuControl::Nmi);
}
pub fn toggle(&self) {
let _ = self.0.send(CpuControl::Toggle);
}
pub fn cycle(&self) {
let _ = self.0.send(CpuControl::Cycle);
}
pub fn data(&self) {
let _ = self.0.send(CpuControl::Data);
}
}
#[derive(Debug)]
pub struct CpuReceiver(Receiver<CpuControl>);
impl CpuReceiver {
pub fn new(receiver: Receiver<CpuControl>) -> Self {
Self(receiver)
}
}
#[derive(Debug)]
pub struct Cpu { pub struct Cpu {
pub a: Byte, // Accumulator Register pub a: u8, // Accumulator Register
pub x: Byte, // X Register pub x: u8, // X Register
pub y: Byte, // Y Register pub y: u8, // Y Register
pub pc: Word, // Program Counter pub pc: u16, // Program Counter
pub s: Byte, // Stack Pointer pub s: u8, // Stack Pointer
pub p: Byte, // Status Register pub p: u8, // Status Register
pub irq: bool, pub irq: bool,
pub nmi: bool, pub nmi: bool,
pub memory: Arc<RwLock<Mem>>, pub memory: MemHandle,
pub pending_cycles: usize, pub pending_cycles: usize,
cycle_count: usize, pub debug: bool,
receiver: Option<CpuReceiver>,
stopped: bool,
cycle: bool,
}
impl Display for Cpu {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{},{}", self.a, self.x)
}
}
impl MemoryReader for Cpu {
fn read(&self, address: u16) -> u8 {
self.memory.read(address)
}
}
impl MemoryWriter for Cpu {
fn write(&self, address: u16, data: u8) {
self.memory.write(address, data);
}
} }
impl Cpu { impl Cpu {
pub fn new(memory: Arc<RwLock<Mem>>) -> Self { pub fn new(memory: MemHandle) -> Self {
// reset the cpu on initialization so we don't
// scream and cry for two days trying to figure
// out why george isn't working <3
let low_byte = memory.read(0xFFFC);
let high_byte = memory.read(0xFFFD);
let pc = (high_byte as u16) << 8 | (low_byte as u16);
Cpu { Cpu {
a: 0x00, a: 0x00,
x: 0x00, x: 0x00,
y: 0x00, y: 0x00,
pc: 0x0000, pc,
s: 0xFF, s: 0xFF,
p: 0b0010_0100, p: 0b0010_0100,
irq: false, irq: false,
nmi: false, nmi: false,
receiver: None,
memory, memory,
debug: false,
stopped: false,
pending_cycles: 0, pending_cycles: 0,
cycle_count: 0, cycle: false,
} }
} }
pub fn reset(&mut self) -> Result<(), ExecutionError> { pub fn with_receiver(mut self, receiver: CpuReceiver) -> Self {
let reset_vector_pointer = self.read_word(0xFFFC)?; self.receiver = Some(receiver);
self
}
pub fn new_with_control(memory: MemHandle) -> (Self, CpuController) {
let (tx, rx) = channel::<CpuControl>();
let controller = CpuController(tx);
let receiver = CpuReceiver(rx);
let cpu = Cpu::new(memory).with_receiver(receiver);
(cpu, controller)
}
pub fn reset(&mut self) {
let reset_vector_pointer = self.read_word(0xFFFC);
self.pc = reset_vector_pointer; self.pc = reset_vector_pointer;
self.pending_cycles = 8; self.a = 0;
Ok(()) self.pending_cycles = 0;
} }
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> { pub fn read_word(&self, address: u16) -> u16 {
let memory = match self.memory.try_read() { let low_byte = self.read(address);
Ok(read) => read, let high_byte = self.read(address.wrapping_add(0x1));
Err(_) => { (high_byte as u16) << 8 | (low_byte as u16)
println!("Couldn't acquire read lock on memory in cpu thread");
return Err(MemoryError::NoDataAtAddress);
} // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
};
memory.read(address)
}
pub fn read_word(&self, address: Word) -> Result<Word, MemoryError> {
let low_byte = self.read(address)?;
let high_byte = self.read(address + 0x1)?;
Ok((high_byte as u16) << 8 | (low_byte as u16))
} }
fn stack_addr(&self) -> Word { fn stack_addr(&self) -> u16 {
// Dunno if this is necessary, i just don't like adding the 0x0100 every time // Dunno if this is necessary, i just don't like adding the 0x0100 every time
0x0100 + self.s as u16 0x0100 + self.s as u16
} }
pub fn push_stack(&mut self, data: Byte) -> Result<(), ExecutionError> { pub fn push_stack(&mut self, data: u8) {
self.s -= 0x1; self.s = self.s.wrapping_sub(0x1);
self.write(self.stack_addr(), data)?; self.write(self.stack_addr(), data);
Ok(())
} }
pub fn push_stack_word(&mut self, address: Word) -> Result<(), ExecutionError> { pub fn push_stack_word(&mut self, address: u16) {
self.s -= 0x1; self.s = self.s.wrapping_sub(0x1);
self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first self.write(self.stack_addr(), address.to_le_bytes()[1]); // Upper byte first
self.s -= 0x1; self.s = self.s.wrapping_sub(0x1);
self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second self.write(self.stack_addr(), address.to_le_bytes()[0]); // Lower byte second
Ok(())
} }
pub fn pop_stack(&mut self) -> Result<Byte, ExecutionError> { pub fn pop_stack(&mut self) -> u8 {
let byte = self.read(self.stack_addr())?; let byte = self.read(self.stack_addr());
self.s += 0x1; self.s = self.s.wrapping_add(0x1);
Ok(byte) byte
} }
pub fn pop_stack_word(&mut self) -> Result<Word, ExecutionError> { pub fn pop_stack_word(&mut self) -> u16 {
let low_byte = self.pop_stack()?; let low_byte = self.pop_stack();
let high_byte = self.pop_stack()?; let high_byte = self.pop_stack();
let word = ((high_byte as Word) << 8) + low_byte as u16; ((high_byte as u16) << 8) + low_byte as u16
Ok(word)
} }
pub fn set_flag(&mut self, flag: StatusFlag, value: bool) { pub fn set_flag(&mut self, flag: StatusFlag, value: bool) {
self.p &= !(flag as Byte); self.p &= !(flag as u8);
if value { if value {
self.p |= flag as Byte self.p |= flag as u8
} }
} }
pub fn get_flag(&self, flag: StatusFlag) -> bool { pub fn get_flag(&self, flag: StatusFlag) -> bool {
(self.p & flag as Byte) > 0 (self.p & flag as u8) > 0
} }
pub fn is_negative(&self, value: Byte) -> bool {
pub fn is_negative(&self, value: u8) -> bool {
value & 0b1000_0000 == 0b1000_0000 value & 0b1000_0000 == 0b1000_0000
} }
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> {
let mut memory = match self.memory.write() {
Ok(write) => write,
Err(_) => {
println!("Couldn't acquire write lock on memory in cpu thread");
return Err(MemoryError::NoDataAtAddress);
} // TODO: upgrade this error type to a `GeorgeError` and make an errorkind for thread lock errors
};
memory.write(address, data)?;
Ok(())
}
pub fn execute(&mut self) {
self.cycle().unwrap();
while self.pending_cycles != 0 {
self.cycle().unwrap();
}
}
pub fn wait_for_interrupt(&self) { pub fn wait_for_interrupt(&self) {
unimplemented!() unimplemented!()
} }
// fn handle_interrupt(&mut self) -> Result<(), ExecutionError> { pub fn interrupt(&mut self) {
// match self.get_flag(StatusFlag::IrqDisable) {
// // Check that interrupts aren't disabled
// true => Ok(self.execute()),
// false => {
// if self.irq {
// let irq_vector = 0xFFFE;
// match self.read_word(irq_vector) {
// Err(error) => Err(ExecutionError::MemoryError(error)),
// Ok(address) => {
// let isr_address = address;
// match self.read(isr_address) {
// Ok(_opcode) => Ok(self.execute()),
// Err(error) => Err(ExecutionError::MemoryError(error)),
// }
// }
// }
// } else {
// Ok(())
// }
// }
// }
// }
pub fn cycle(&mut self) -> Result<(), GeorgeError> {
sleep(Duration::from_nanos(500));
if self.pending_cycles == 0 {
if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) {
let _ = self.push_stack_word(self.pc);
self.set_flag(StatusFlag::BrkIrq, true);
let _ = self.push_stack(self.p);
self.set_flag(StatusFlag::IrqDisable, true);
if self.nmi {
match self.read_word(0xFFFA) {
Ok(word) => self.pc = word,
Err(error) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Memory(error),
desc: String::from_str("Couldn't read NMI vector").unwrap(),
})
}
};
self.pending_cycles = 8;
} else {
match self.read_word(0xFFFE) {
Ok(word) => self.pc = word,
Err(error) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Memory(error),
desc: String::from_str("Couldn't read IRQ vector").unwrap(),
})
}
};
self.pending_cycles = 7
}
self.nmi = false;
self.irq = false; self.irq = false;
} self.push_stack_word(self.pc);
} else { self.push_stack(self.p);
let opcode = match self.read(self.pc) { self.set_flag(StatusFlag::IrqDisable, true);
Ok(byte) => byte, self.pc = self.read_word(0xFFFE);
Err(error) => {
return Err(GeorgeError {
desc: String::from_str("Failed to read the program counter!").unwrap(),
kind: GeorgeErrorKind::Memory(error),
})
}
};
let instruction = get_instruction(opcode);
match instruction {
Instruction::Valid(valid_instruction) => {
println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi);
println!(
"Instruction: {:?}, {:#04x}",
valid_instruction.opcode, opcode
);
println!("");
self.pc += 1;
match valid_instruction.opcode.call(self) {
Ok(_) => {
self.pending_cycles += valid_instruction.cycles as usize;
},
Err(error) => return Err(GeorgeError{
desc: format!("An IncompatibleAddrMode was used at address {:#06x} for instruction {:?}",
self.pc, valid_instruction).to_string(), kind: error})
};
}
Instruction::Invalid(invalid_instruction) => {
return Err(GeorgeError {
kind: GeorgeErrorKind::Execution(ExecutionError::InvalidInstruction),
desc: format!(
"An invalid instruction with opcode {:#04x} was called at address {:#06x}",
invalid_instruction.opcode, self.pc
),
})
}
}
}
self.pending_cycles -= 1;
self.cycle_count += 1;
Ok(())
} }
pub fn toggle_stopped(&mut self) {
self.stopped = !self.stopped;
}
fn receive_control(&mut self) {
match &self.receiver {
Some(receiver) => {
let control = match receiver.0.try_recv() {
Ok(control) => control,
Err(_error) => return, // most of the time we won't have any impending control
// messages, just return if that's the case
};
match control {
CpuControl::Nmi => self.nmi = true,
CpuControl::Irq => self.irq = true,
CpuControl::Toggle => {
self.stopped = !self.stopped;
}
CpuControl::Cycle => self.cycle = true,
CpuControl::Data => {
// self
// .state_tx
// .send(CpuState {
// a: self.a.clone(), // Accumulator Register
// x: self.x.clone(), // X Register
// y: self.y.clone(), // Y Register
// pc: self.pc.clone(), // Program Counter
// s: self.s.clone(), // Stack Pointer
// p: self.p.clone(), // Status Register
// irq: self.irq.clone(),
// nmi: self.nmi.clone(),
// })
// .unwrap(),
}
}
}
None => {}
}
}
pub fn cycle(&mut self) {
self.receive_control();
if self.stopped & !self.cycle {
self.set_flag(StatusFlag::IrqDisable, true);
return;
}
self.cycle = false;
// can't sleep in wasm
#[cfg(not(target_arch = "wasm32"))]
while self.pending_cycles != 0 {
// roughly cycle-accurate timing
sleep(Duration::from_nanos(500));
self.pending_cycles -= 1;
}
if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
self.interrupt();
}
let opcode = self.read(self.pc);
let instruction = get_instruction(opcode);
instruction.call(self);
}
pub fn stop(&mut self) { pub fn stop(&mut self) {
unimplemented!() self.stopped = true;
}
pub fn breakpoint(&mut self) {
if self.debug {
self.stop();
}
}
fn quick_cycle(&mut self) {
if !self.get_flag(StatusFlag::IrqDisable) && self.irq {
self.interrupt();
}
let opcode = self.read(self.pc);
let instruction = get_instruction(opcode);
instruction.call(self);
}
pub fn run_to_end(&mut self) -> [u8; 0x10000] {
self.quick_cycle();
while self.pending_cycles != 0 {
let opcode = self.read(self.pc);
let instruction = get_instruction(opcode);
match instruction.name {
"stp" | "breakpoint" => return self.memory.dump(),
_ => self.quick_cycle(),
}
}
unreachable!()
} }
} }
-67
View File
@@ -1,67 +0,0 @@
#[derive(Debug)]
pub enum GeorgeErrorKind {
Memory(MemoryError),
Execution(ExecutionError),
AddrMode(AddressingModeError),
Mapping(MappingError),
}
impl From<MemoryError> for GeorgeErrorKind {
fn from(value: MemoryError) -> Self {
Self::Memory(value)
}
}
impl From<AddressingModeError> for GeorgeErrorKind {
fn from(value: AddressingModeError) -> Self {
Self::AddrMode(value)
}
}
impl From<ExecutionError> for GeorgeErrorKind {
fn from(value: ExecutionError) -> Self {
Self::Execution(value)
}
}
#[derive(Debug)]
pub enum ExecutionError {
InvalidInstruction,
InterruptsDisabled,
StackOverflow,
MemoryError(MemoryError),
}
#[derive(Debug)]
pub enum AddressingModeError {
IncompatibleAddrMode,
}
impl From<ExecutionError> for AddressingModeError {
fn from(_value: ExecutionError) -> Self {
Self::IncompatibleAddrMode
}
}
impl From<MemoryError> for ExecutionError {
fn from(error: MemoryError) -> Self {
ExecutionError::MemoryError(error)
}
}
#[derive(Debug)]
pub struct GeorgeError {
pub desc: String,
pub kind: GeorgeErrorKind,
}
#[derive(Debug)]
pub enum MemoryError {
Unmapped,
NoDataAtAddress,
Unwritable,
}
#[derive(Debug)]
pub enum MappingError {
RegionOccupied,
InvalidPage,
}
-25
View File
@@ -1,25 +0,0 @@
.setcpu "65C02"
.segment "CODE"
LDA #$60
STA $01
LDY #$0
fill:
LDA #$20
STY $00
STA ($00)
INY
CPY #$ff
BNE fill
main:
LDY #$0
STY $6000
LDY #$1
STY $6001
LDY #$2
STY $6002
LDY #$1
STY $6003
LDY #$3
STY $6004
JMP main
-11
View File
@@ -1,11 +0,0 @@
MEMORY {
RAM: start = $0000, size = $0200, type = rw, fill = true;
PROGRAM: start = $0200, size = $3E00, type = rw, fill = true;
CTRL: start = $4000, size = $2000, type = rw, fill = true;
VRAM: start = $6000, size = $8000, type = rw, fill = true;
ROM: start = $E000, size = $2000, type = ro, fill = true;
}
SEGMENTS {
CODE: load = "PROGRAM", type = rw;
}
-265
View File
@@ -1,265 +0,0 @@
# this is august's version of extended ascii for george <3
# we're limited to 255 characters
# format: ascii byte, unicode hex, # unicode name
#
# format: three tab-separated columns
# column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
# column #2 is the Unicode (in hex as 0xXXXX)
# column #3 the Unicode name (follows a comment sign, '#')
0x00 0x0295 # ʕ
0x01 0x00B7 # Middle Dot
0x02 0x1D25 # ᴥ
0x03 0x0294 # ʔ
0x04 0x2661 # White heart
0x05 0x2665 # Black heart
0x06 0x2B50 # White star
0x07 0x272D # Black star
0x08 0xF005 # Alternate Black Star
0x09 0x2726 # Black four pointed star
0x0a 0x2728 # Sparkles
0x0b 0x2640 # Female Sign
0x0c 0x2642 # Male Sign
0x0d 0x26A2 # Doubled female sign
0x0E 0x26A3 # Doubled male sign
0x0F 0x26A5 # Male and Female sign0x000E
0x10 0x2669 # Quarter Note
0x11 0x266A # Eighth note
0x12 0x266B # Beamed eighth notes
0x13 0x266C # Beamed sixteenth notes
0x14 0xFC5D #
0x15 0xF026 #
0x16 0xF027 #
0x17 0xF028 #
0x18 0xFA7E #
0x19 0xFA7F #
0x1A 0xFA80 #
0x1B 0xFC5C #
0x1C 0xFC5B #
0x1D 0xF0AC #
0x1E 0xF04B #
0x1F 0xF04D #
0x20 0x0020 # SPACE
0x21 0x0021 # EXCLAMATION MARK
0x22 0x0022 # QUOTATION MARK
0x23 0x0023 # NUMBER SIGN
0x24 0x0024 # DOLLAR SIGN
0x25 0x0025 # PERCENT SIGN
0x26 0x0026 # AMPERSAND
0x27 0x0027 # APOSTROPHE
0x28 0x0028 # LEFT PARENTHESIS
0x29 0x0029 # RIGHT PARENTHESIS
0x2A 0x002A # ASTERISK
0x2B 0x002B # PLUS SIGN
0x2C 0x002C # COMMA
0x2D 0x002D # HYPHEN-MINUS
0x2E 0x002E # FULL STOP
0x2F 0x002F # SOLIDUS
0x30 0x0030 # DIGIT ZERO
0x31 0x0031 # DIGIT ONE
0x32 0x0032 # DIGIT TWO
0x33 0x0033 # DIGIT THREE
0x34 0x0034 # DIGIT FOUR
0x35 0x0035 # DIGIT FIVE
0x36 0x0036 # DIGIT SIX
0x37 0x0037 # DIGIT SEVEN
0x38 0x0038 # DIGIT EIGHT
0x39 0x0039 # DIGIT NINE
0x3A 0x003A # COLON
0x3B 0x003B # SEMICOLON
0x3C 0x003C # LESS-THAN SIGN
0x3D 0x003D # EQUALS SIGN
0x3E 0x003E # GREATER-THAN SIGN
0x3F 0x003F # QUESTION MARK
0x40 0x0040 # COMMERCIAL AT
0x41 0x0041 # LATIN CAPITAL LETTER A
0x42 0x0042 # LATIN CAPITAL LETTER B
0x43 0x0043 # LATIN CAPITAL LETTER C
0x44 0x0044 # LATIN CAPITAL LETTER D
0x45 0x0045 # LATIN CAPITAL LETTER E
0x46 0x0046 # LATIN CAPITAL LETTER F
0x47 0x0047 # LATIN CAPITAL LETTER G
0x48 0x0048 # LATIN CAPITAL LETTER H
0x49 0x0049 # LATIN CAPITAL LETTER I
0x4A 0x004A # LATIN CAPITAL LETTER J
0x4B 0x004B # LATIN CAPITAL LETTER K
0x4C 0x004C # LATIN CAPITAL LETTER L
0x4D 0x004D # LATIN CAPITAL LETTER M
0x4E 0x004E # LATIN CAPITAL LETTER N
0x4F 0x004F # LATIN CAPITAL LETTER O
0x50 0x0050 # LATIN CAPITAL LETTER P
0x51 0x0051 # LATIN CAPITAL LETTER Q
0x52 0x0052 # LATIN CAPITAL LETTER R
0x53 0x0053 # LATIN CAPITAL LETTER S
0x54 0x0054 # LATIN CAPITAL LETTER T
0x55 0x0055 # LATIN CAPITAL LETTER U
0x56 0x0056 # LATIN CAPITAL LETTER V
0x57 0x0057 # LATIN CAPITAL LETTER W
0x58 0x0058 # LATIN CAPITAL LETTER X
0x59 0x0059 # LATIN CAPITAL LETTER Y
0x5A 0x005A # LATIN CAPITAL LETTER Z
0x5B 0x005B # LEFT SQUARE BRACKET
0x5C 0x005C # REVERSE SOLIDUS
0x5D 0x005D # RIGHT SQUARE BRACKET
0x5E 0x005E # CIRCUMFLEX ACCENT
0x5F 0x005F # LOW LINE
0x60 0x0060 # GRAVE ACCENT
0x61 0x0061 # LATIN SMALL LETTER A
0x62 0x0062 # LATIN SMALL LETTER B
0x63 0x0063 # LATIN SMALL LETTER C
0x64 0x0064 # LATIN SMALL LETTER D
0x65 0x0065 # LATIN SMALL LETTER E
0x66 0x0066 # LATIN SMALL LETTER F
0x67 0x0067 # LATIN SMALL LETTER G
0x68 0x0068 # LATIN SMALL LETTER H
0x69 0x0069 # LATIN SMALL LETTER I
0x6A 0x006A # LATIN SMALL LETTER J
0x6B 0x006B # LATIN SMALL LETTER K
0x6C 0x006C # LATIN SMALL LETTER L
0x6D 0x006D # LATIN SMALL LETTER M
0x6E 0x006E # LATIN SMALL LETTER N
0x6F 0x006F # LATIN SMALL LETTER O
0x70 0x0070 # LATIN SMALL LETTER P
0x71 0x0071 # LATIN SMALL LETTER Q
0x72 0x0072 # LATIN SMALL LETTER R
0x73 0x0073 # LATIN SMALL LETTER S
0x74 0x0074 # LATIN SMALL LETTER T
0x75 0x0075 # LATIN SMALL LETTER U
0x76 0x0076 # LATIN SMALL LETTER V
0x77 0x0077 # LATIN SMALL LETTER W
0x78 0x0078 # LATIN SMALL LETTER X
0x79 0x0079 # LATIN SMALL LETTER Y
0x7A 0x007A # LATIN SMALL LETTER Z
0x7B 0x007B # LEFT CURLY BRACKET
0x7C 0x007C # VERTICAL LINE
0x7D 0x007D # RIGHT CURLY BRACKET
0x7E 0x007E # TILDE
0x7F 0x2500 # Box Drawings light horizontal
0x80 0x2502 # Box drawings light vertical
0x81 0x250C # Box drawing light down and right
0x82 0x2514 # Box drawing light up and right
0x83 0x251C # Box drawings light vertical and right
0x84 0x2524 #
0x85 0x252C #
0x86 0x2534 #
0x87 0x253C #
0x88 0x256D #
0x89 0x256E #
0x8A 0x256F #
0x8B 0x2570 #
0x8C 0x2571 #
0x8D 0x2572 #
0x8E 0x2573 #
0x8F 0x2550 #
0x90 0x2551 #
0x91 0x2554 #
0x92 0x2557 #
0x93 0x255a #
0x94 0x255D #
0x95 0x2560 #
0x96 0x2563 #
0x97 0x2566 #
0x98 0x2569 #
0x99 0x256C #
0x9A 0xF04E #
0x9B 0xF050 #
0x9C 0xF051 #
0x9D 0xF052 #
0x9E 0xF048 #
0x9F 0xE0B0 #
0xA0 0xE0B2 #
0xA1 0xE0B4 #
0xA2 0xE0B6 #
0xA3 0xE0B8 #
0xA4 0xE0BA #
0xA5 0xE0BC #
0xA6 0xE0BE #
0xA7 0x2581 #
0xA8 0x2582 #
0xA9 0x2583 #
0xAA 0x2584 #
0xAB 0x2585 #
0xAC 0x2586 #
0xAD 0x2587 #
0xAE 0x2588 #
0xAF 0x2589 #
0xB0 0x258A #
0xB1 0x258B #
0xB2 0x258C #
0xB3 0x258D #
0xB4 0x258E #
0xB5 0x258F #
0xB6 0x2591 #
0xB7 0x2592 #
0xB8 0x2593 #
0xB9 0x2596 #
0xBA 0x2597 #
0xBB 0x2598 #
0xBC 0x2599 #
0xBD 0x259A #
0xBE 0x259B #
0xBF 0x259C #
0xC0 0x259D #
0xC1 0x259E #
0xC2 0x259F #
0xC3 0x2190 #
0xC4 0x2191 #
0xC5 0x2192 #
0xC6 0x2193 #
0xC7 0x2B60 #
0xC8 0x2B61 #
0xC9 0x2B62 #
0xCA 0x2B63 #
0xCB 0x2B80 #
0xCC 0x2B81 #
0xCD 0x2B82 #
0xCE 0x2B83 #
0xCF 0xF049 #
0xD0 0xF04A #
0xD1 0x23F3 #
0xD2 0xF07B #
0xD3 0xF07C #
0xD4 0xF114 #
0xD5 0xF115 #
0xD6 0xF250 #
0xD7 0xF251 #
0xD8 0xF253 #
0xD9 0xF254 #
0xDA 0xF461 #
0xDB 0xF016 #
0xDC 0xF401 #
0xDD 0x1F52E #
0xDE 0xF2DB #
0xDF 0xF008 #
0xE0 0x25C7 #
0xE1 0x25C8 #
0xE2 0x1F311 #
0xE3 0x1F312 #
0xE4 0x1F313 #
0xE5 0x1F314 #
0xE6 0x1F315 #
0xE7 0x1F316 #
0xE8 0x1F317 #
0xE9 0x1F318 #
0xEA 0xF04C #
0xEB 0x2714 #
0xEC 0x2718 #
0xED 0x25C6 #
0xEE 0xF15D #
0xEF 0xF15E #
0xF0 0xF071 #
0xF1 0xF449 #
0xF2 0xF529 #
0xF3 0xF658 #
0xF4 0xF659 #
0xF5 0x0020 #
0xF6 0x1f381 # Space
0xF7 0xf05a # Space
0xF8 0xf06a # Space
0xF9 0xf834 # Space
0xFA 0xf835 # Space
0xFB 0x2690 # Space
0xFC 0x2691 # Space
0xFD 0xf8d7 # Space
0xFE 0xf0e7 # Space
0xFF 0xf7d9 # Space
+2508 -3144
View File
File diff suppressed because it is too large Load Diff
+256
View File
@@ -0,0 +1,256 @@
use minifb::InputCallback;
use minifb::Key as MKey;
#[cfg(not(target_arch = "wasm32"))]
use termion::event::Key;
use crate::memory::{MemHandle, MemoryWriter};
#[derive(Debug, Clone)]
pub struct Keyboard {
memory: MemHandle,
}
impl Keyboard {
pub fn new(memory: MemHandle) -> Self {
Self { memory }
}
#[cfg(not(target_arch = "wasm32"))]
pub fn clear_keys(&self) {
self.memory.write(0x4400, 0x00);
self.memory.write(0x4401, 0x00);
self.memory.write(0x4402, 0x00);
self.memory.write(0x4403, 0x00);
self.memory.write(0x4404, 0x00);
self.memory.write(0x4405, 0x00);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn read_keys(&self, key: Key) {
let mut row0 = 0;
let mut row1 = 0;
let mut row2 = 0;
let mut row3 = 0;
let mut row4 = 0;
let mut row5 = 0;
// these are just so the match statements are easier to read lol
macro_rules! set_shift {
($char:expr) => {
match $char {
'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
| 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y'
| 'Z' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '(' | ')' | '~'
| '_' | '+' | '|' | '}' | '{' | '"' | ':' | '?' | '>' | '<' => {
row2 ^= 0b1000_0000
}
_ => {}
}
};
}
macro_rules! set_plain {
($char:expr) => {
match $char {
'w' | 'W' => row0 ^= 0b0100_0000,
'e' | 'E' => row0 ^= 0b0010_0000,
'r' | 'R' => row0 ^= 0b0001_0000,
't' | 'T' => row0 ^= 0b0000_1000,
'u' | 'U' => row0 ^= 0b0000_0100,
'o' | 'O' => row0 ^= 0b0000_0010,
'q' | 'Q' => row1 ^= 0b0100_0000,
's' | 'S' => row1 ^= 0b0010_0000,
'g' | 'G' => row1 ^= 0b0001_0000,
'y' | 'Y' => row1 ^= 0b0000_1000,
'i' | 'I' => row1 ^= 0b0000_0100,
'p' | 'P' => row1 ^= 0b0000_0010,
'd' | 'D' => row2 ^= 0b0100_0000,
'v' | 'V' => row2 ^= 0b0010_0000,
'h' | 'H' => row2 ^= 0b0001_0000,
'k' | 'K' => row2 ^= 0b0000_1000,
'\\' | '|' => row2 ^= 0b0000_0100,
'/' | '?' => row2 ^= 0b0000_0010,
'a' | 'A' => row2 ^= 0b0000_0001,
'z' | 'Z' => row3 ^= 0b0100_0000,
'f' | 'F' => row3 ^= 0b0010_0000,
'b' | 'B' => row3 ^= 0b0001_0000,
'j' | 'J' => row3 ^= 0b0000_1000,
'l' | 'L' => row3 ^= 0b0000_0100,
'2' | '@' => row3 ^= 0b0000_0010,
'4' | '$' => row3 ^= 0b0000_0001,
'x' | 'X' => row4 ^= 0b0100_0000,
'c' | 'C' => row4 ^= 0b0010_0000,
'n' | 'N' => row4 ^= 0b0001_0000,
'm' | 'M' => row4 ^= 0b0000_1000,
',' | '<' => row4 ^= 0b0000_0100,
'1' | '!' => row4 ^= 0b0000_0010,
'3' | '#' => row4 ^= 0b0000_0001,
' ' => row5 ^= 0b0100_0000,
_ => {}
}
};
}
match key {
Key::Esc => row0 ^= 0b1000_0000,
Key::Backspace => row0 ^= 0b0000_0001,
Key::Char('\t') => row1 ^= 0b1000_0000,
Key::Char('\n') | Key::Char('\r') => row1 ^= 0b0000_0001,
Key::Char(char) => {
set_shift!(char);
set_plain!(char);
}
Key::ShiftLeft | Key::ShiftUp | Key::ShiftDown | Key::ShiftRight => row2 ^= 0b1000_0000,
Key::Ctrl(char) => {
set_shift!(char);
set_plain!(char);
row3 ^= 0b1000_0000;
}
Key::CtrlUp | Key::CtrlLeft | Key::CtrlDown | Key::CtrlRight => {
row3 ^= 0b1000_0000;
}
Key::Alt(char) => {
set_plain!(char);
set_shift!(char);
row4 ^= 0b1000_0000;
}
Key::AltUp | Key::AltLeft | Key::AltDown | Key::AltRight => {
row4 ^= 0b1000_0000;
}
_ => {}
}
self.memory.write(0x4400, row0);
self.memory.write(0x4401, row1);
self.memory.write(0x4402, row2);
self.memory.write(0x4403, row3);
self.memory.write(0x4404, row4);
self.memory.write(0x4405, row5);
// keeping this list around to make future changes easier
// match key.code {
// KeyCode::Esc => row0 ^= 0b1000_0000,
// KeyCode::Char('w') => row0 ^= 0b0100_0000,
// KeyCode::Char('e') => row0 ^= 0b0010_0000,
// KeyCode::Char('r') => row0 ^= 0b0001_0000,
// KeyCode::Char('t') => row0 ^= 0b0000_1000,
// KeyCode::Char('u') => row0 ^= 0b0000_0100,
// KeyCode::Char('o') => row0 ^= 0b0000_0010,
// KeyCode::Backspace => row0 ^= 0b0000_0001,
// KeyCode::Tab => row1 ^= 0b1000_0000,
// KeyCode::Char('q') => row1 ^= 0b0100_0000,
// KeyCode::Char('s') => row1 ^= 0b0010_0000,
// KeyCode::Char('g') => row1 ^= 0b0001_0000,
// KeyCode::Char('y') => row1 ^= 0b0000_1000,
// KeyCode::Char('i') => row1 ^= 0b0000_0100,
// KeyCode::Char('p') => row1 ^= 0b0000_0010,
// KeyCode::Enter => row1 ^= 0b0000_0001,
// KeyCode::Char('d') => row2 ^= 0b0100_0000,
// KeyCode::Char('v') => row2 ^= 0b0010_0000,
// KeyCode::Char('h') => row2 ^= 0b0001_0000,
// KeyCode::Char('k') => row2 ^= 0b0000_1000,
// KeyCode::Char('\'') => row2 ^= 0b0000_0100,
// KeyCode::Char('/') => row2 ^= 0b0000_0010,
// KeyCode::Char('a') => row2 ^= 0b0000_0001,
// KeyCode::Char('z') => row3 ^= 0b0100_0000,
// KeyCode::Char('f') => row3 ^= 0b0010_0000,
// KeyCode::Char('b') => row3 ^= 0b0001_0000,
// KeyCode::Char('j') => row3 ^= 0b0000_1000,
// KeyCode::Char('l') => row3 ^= 0b0000_0100,
// KeyCode::Char('2') => row3 ^= 0b0000_0010,
// KeyCode::Char('4') => row3 ^= 0b0000_0001,
// KeyCode::Char('x') => row4 ^= 0b0100_0000,
// KeyCode::Char('c') => row4 ^= 0b0010_0000,
// KeyCode::Char('n') => row4 ^= 0b0001_0000,
// KeyCode::Char('m') => row4 ^= 0b0000_1000,
// KeyCode::Char(',') => row4 ^= 0b0000_0100,
// KeyCode::Char('1') => row4 ^= 0b0000_0010,
// KeyCode::Char('3') => row4 ^= 0b0000_0001,
// KeyCode::Char(' ') => row5 ^= 0b0100_0000,
// _ => {
// row0 = 0;
// row1 = 0;
// row2 = 0;
// row3 = 0;
// row4 = 0;
// row5 = 0;
// }
// };
}
}
impl MemoryWriter for Keyboard {
fn write(&self, address: u16, data: u8) {
if data != 0x00 {
// println!("wrote {:02x} to address {:04x}", data, address);
}
self.memory.write(address, data);
}
}
impl InputCallback for Keyboard {
fn add_char(&mut self, _uni_char: u32) {}
fn set_key_state(&mut self, key: MKey, _state: bool) {
let mut row0 = 0;
let mut row1 = 0;
let mut row2 = 0;
let mut row3 = 0;
let mut row4 = 0;
let mut row5 = 0;
match key {
MKey::Escape => row0 ^= 0b1000_0000,
MKey::W => row0 ^= 0b0100_0000,
MKey::E => row0 ^= 0b0010_0000,
MKey::R => row0 ^= 0b0001_0000,
MKey::T => row0 ^= 0b0000_1000,
MKey::U => row0 ^= 0b0000_0100,
MKey::O => row0 ^= 0b0000_0010,
MKey::Backspace => row0 ^= 0b0000_0001,
MKey::Tab => row1 ^= 0b1000_0000,
MKey::Q => row1 ^= 0b0100_0000,
MKey::S => row1 ^= 0b0010_0000,
MKey::G => row1 ^= 0b0001_0000,
MKey::Y => row1 ^= 0b0000_1000,
MKey::I => row1 ^= 0b0000_0100,
MKey::P => row1 ^= 0b0000_0010,
MKey::Enter => row1 ^= 0b0000_0001,
MKey::LeftShift | MKey::RightShift => row2 ^= 0b1000_0000,
MKey::D => row2 ^= 0b0100_0000,
MKey::V => row2 ^= 0b0010_0000,
MKey::H => row2 ^= 0b0001_0000,
MKey::K => row2 ^= 0b0000_1000,
MKey::Apostrophe => row2 ^= 0b0000_0100,
MKey::Slash => row2 ^= 0b0000_0010,
MKey::A => row2 ^= 0b0000_0001,
MKey::LeftCtrl | MKey::RightCtrl => row3 ^= 0b1000_0000,
MKey::Z => row3 ^= 0b0100_0000,
MKey::F => row3 ^= 0b0010_0000,
MKey::B => row3 ^= 0b0001_0000,
MKey::J => row3 ^= 0b0000_1000,
MKey::L => row3 ^= 0b0000_0100,
MKey::Key2 => row3 ^= 0b0000_0010,
MKey::Key4 => row3 ^= 0b0000_0001,
MKey::LeftAlt | MKey::RightAlt => row4 ^= 0b1000_0000,
MKey::X => row4 ^= 0b0100_0000,
MKey::C => row4 ^= 0b0010_0000,
MKey::N => row4 ^= 0b0001_0000,
MKey::M => row4 ^= 0b0000_1000,
MKey::Comma => row4 ^= 0b0000_0100,
MKey::Key1 => row4 ^= 0b0000_0010,
MKey::Key3 => row4 ^= 0b0000_0001,
MKey::LeftSuper => row5 ^= 0b1000_0000,
MKey::Space => row5 ^= 0b0100_0000,
MKey::RightSuper => row5 ^= 0b0010_0000,
_ => {}
};
self.memory.write(0x4400, row0);
self.memory.write(0x4401, row1);
self.memory.write(0x4402, row2);
self.memory.write(0x4403, row3);
self.memory.write(0x4404, row4);
self.memory.write(0x4405, row5);
}
}
+204
View File
@@ -0,0 +1,204 @@
pub mod cpu;
mod instructions;
mod keyboard;
pub mod memory;
mod platform;
pub mod video;
use crate::cpu::Cpu;
use crate::keyboard::Keyboard;
use crate::memory::Mem;
use crate::video::Renderer;
use cpu::CpuController;
use memory::MemHandle;
use minifb::Window;
#[cfg(not(target_arch = "wasm32"))]
use platform::native as imp;
#[cfg(target_arch = "wasm32")]
use platform::wasm as imp;
pub struct GeorgeEmu(imp::GeorgeEmu);
impl GeorgeEmu {
pub fn builder() -> GeorgeEmuBuilder<NoRom, NoWindow> {
GeorgeEmuBuilder::new()
}
pub fn draw(&mut self) {
self.0.draw()
}
pub fn cycle(&mut self) {
self.0.cycle()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn run(&mut self) {
self.0.run()
}
}
pub struct SomeRom();
pub struct NoRom();
pub struct SomeWindow(Window);
pub struct NoWindow();
pub struct GeorgeEmuBuilder<Rom, Window> {
rom: Rom,
cpu: Option<Cpu>,
keyboard: Option<Keyboard>,
renderer: Option<Renderer>,
cpu_controller: Option<CpuController>,
memory_handle: Option<MemHandle>,
window: Window,
}
impl GeorgeEmuBuilder<NoRom, NoWindow> {
fn new() -> Self {
GeorgeEmuBuilder {
rom: NoRom(),
cpu: None,
keyboard: None,
renderer: None,
cpu_controller: None,
memory_handle: None,
window: NoWindow(),
}
}
pub fn rom(self, rom: [u8; 0x8000]) -> GeorgeEmuBuilder<SomeRom, NoWindow> {
let mut memory = Mem::new();
memory.load_bytes(&rom);
let memory_handle = MemHandle::new(memory);
let (cpu, cpu_controller) = Cpu::new_with_control(memory_handle.clone());
let Self {
renderer,
keyboard,
window,
..
} = self;
GeorgeEmuBuilder {
cpu: Some(cpu),
rom: SomeRom(),
keyboard,
renderer,
cpu_controller: Some(cpu_controller),
memory_handle: Some(memory_handle),
window,
}
}
}
impl GeorgeEmuBuilder<SomeRom, NoWindow> {
/// Adds a minifb window to the emulator
pub fn window(self, mut window: Window) -> GeorgeEmuBuilder<SomeRom, SomeWindow> {
let Self {
rom,
cpu,
keyboard,
ref cpu_controller,
ref memory_handle,
..
} = self;
let renderer = Renderer::new(
self.cpu_controller.clone().unwrap(),
self.memory_handle.clone().unwrap(),
);
window.set_input_callback(Box::new(Keyboard::new(
self.memory_handle.as_ref().unwrap().clone(),
)));
GeorgeEmuBuilder {
cpu,
rom,
keyboard,
renderer: Some(renderer),
cpu_controller: cpu_controller.clone(),
memory_handle: memory_handle.clone(),
window: SomeWindow(window),
}
}
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn build(self) -> GeorgeEmu {
let keyboard = Keyboard::new(self.memory_handle.clone().unwrap());
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
Some(keyboard),
None,
))
}
}
impl GeorgeEmuBuilder<NoRom, SomeWindow> {
pub fn rom(self, rom: [u8; 0x8000]) -> GeorgeEmuBuilder<SomeRom, SomeWindow> {
let mut memory = Mem::new();
memory.load_bytes(&rom);
let memory_handle = MemHandle::new(memory);
let (cpu, cpu_controller) = Cpu::new_with_control(memory_handle.clone());
let Self {
renderer,
keyboard,
window,
..
} = self;
GeorgeEmuBuilder {
cpu: Some(cpu),
rom: SomeRom(),
keyboard,
renderer,
cpu_controller: Some(cpu_controller),
memory_handle: Some(memory_handle),
window,
}
}
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
}
impl GeorgeEmuBuilder<SomeRom, SomeWindow> {
/// Enables debug printing and breakpoint triggering
pub fn debug(mut self) -> Self {
self.cpu.as_mut().unwrap().debug = true;
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn build(self) -> GeorgeEmu {
let keyboard = Keyboard::new(self.memory_handle.clone().unwrap());
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
None,
Some(self.window.0),
))
}
#[cfg(target_arch = "wasm32")]
pub fn build(self) -> GeorgeEmu {
GeorgeEmu(imp::GeorgeEmu::new(
self.cpu.unwrap(),
self.renderer.unwrap(),
self.window.0,
))
}
}
-59
View File
@@ -1,59 +0,0 @@
// #![allow(dead_code)]
mod cpu;
mod error;
mod instructions;
mod memory;
mod types;
mod video;
use crate::cpu::Cpu;
use crate::memory::{Mem, MemMappedDevice};
use crate::video::Crtc;
use std::{
path::PathBuf,
sync::{Arc, RwLock},
thread,
};
fn main() {
let ram = MemMappedDevice::new(0x0000, 0x3FFF, 2, "ram".into());
let control = MemMappedDevice::new(0x4000, 0x5FFF, 4, "control".into());
let vram = MemMappedDevice::new(0x6000, 0xDFFF, 1, "vram".into());
let rom = MemMappedDevice::new(0xE000, 0xFFFF, 4, "rom".into());
let mut memory = Mem::new(ram);
if let Err(error) = memory.add_area(control) {
println!("Error adding vram: {:?}", error.desc);
};
if let Err(error) = memory.add_area(vram) {
println!("Error adding vram: {:?}", error.desc);
};
if let Err(error) = memory.add_area(rom) {
println!("Error adding rom: {:?}", error.desc);
};
let binary = match std::fs::File::open(PathBuf::from(
"/Users/kline/projects/winter/george-emu/src/george",
)) {
Ok(file) => file,
Err(error) => panic!("Couldn't open binary file! {:?}", error),
};
if let Err(error) = memory.read_from_bin(binary) {
println!("{:?}", error);
};
memory.write(0xFFFC, 0x00).unwrap();
memory.write(0xFFFD, 0x02).unwrap();
let shared_memory = Arc::new(RwLock::new(memory));
let cpu_memory = shared_memory.clone();
let display_memory = shared_memory.clone();
let mut screen = Crtc::new(display_memory);
thread::spawn(move || {
let mut cpu = Cpu::new(cpu_memory);
cpu.reset().unwrap();
cpu.execute();
});
screen.run();
}
+94 -84
View File
@@ -1,110 +1,120 @@
use crate::error::{GeorgeError, GeorgeErrorKind, MappingError, MemoryError}; use anyhow::{bail, Result};
use crate::types::{Byte, Word}; use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::{fs::File, io::Read}; use std::{fs::File, io::Read};
#[derive(Debug, Clone)] #[derive(Clone, Debug)]
pub struct MemMappedDevice { pub struct MemHandle(Arc<Mutex<Mem>>);
start: Word, impl MemHandle {
end: Word, pub fn new(memory: Mem) -> Self {
pages: usize, Self(Arc::new(Mutex::new(memory)))
page: usize, }
data: Vec<Byte>, pub fn read(&self, address: u16) -> u8 {
label: String, let memory = self.0.try_lock().unwrap();
memory.read(address)
}
pub fn write(&self, address: u16, data: u8) {
let mut memory = self.0.try_lock().unwrap();
memory.write(address, data);
} }
impl MemMappedDevice { pub fn dump(&self) -> [u8; 0x10000] {
pub fn new(start: Word, end: Word, pages: usize, label: String) -> Self { let memory = self.0.lock().unwrap();
Self { memory.dump()
start,
end,
pages,
page: 0,
data: vec![0x00; (end as usize + 1 - start as usize) * pages],
label,
} }
pub fn poke(&self, address: u16) {
let memory = self.0.lock().unwrap();
memory.poke(address)
} }
fn contains(&self, address: Word) -> bool {
self.start <= address && self.end >= address
}
fn swap_page(&mut self, page: usize) -> Result<(), GeorgeError> {
match page > self.pages {
true => Err(GeorgeError {
kind: GeorgeErrorKind::Memory(MemoryError::Unmapped), //TODO: should be MappingError::InvalidPage,
desc: format!(
"{:?} tried to swap to a page outside of its range",
self.label
),
}),
false => {
self.page = page;
Ok(())
}
}
}
fn size(&self) -> Word {
self.end - self.start + 1
}
fn translate_address(&self, address: Word) -> Word {
(address - self.start) + self.size() * (self.page as Word)
} // This needs to translate memory address from CPU land to local land, so
// for rom an address like 0xFFFF needs to be translated to Page X, 0xFFF
} }
#[derive(Debug, Clone)] pub trait MemoryReader {
pub struct Mem { fn read(&self, address: u16) -> u8;
areas: Vec<MemMappedDevice>, }
pub trait MemoryWriter {
fn write(&self, address: u16, data: u8);
}
#[derive(Debug, Clone, Copy)]
// This always feels wrong instead of 0xFFFF,
// but remember it's the number of elements,
// not the maximum index. 0x0000 to 0xFFFF addresses
// 0x10000 elements
pub struct Mem([u8; 0x10000]);
impl Default for Mem {
fn default() -> Self {
let bytes = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/roms/george.rom"));
let padding = [0; 0x8000];
let mem: [u8; 0x10000] = {
let mut rom: [u8; 0x10000] = [0; 0x10000];
let (one, two) = rom.split_at_mut(padding.len());
one.copy_from_slice(&padding);
two.copy_from_slice(bytes);
rom
};
Self(mem)
}
} }
impl Mem { impl Mem {
pub fn new(area: MemMappedDevice) -> Self { pub fn new() -> Self {
Self { areas: vec![area] } Self([0; 0x10000])
}
pub fn dump(&self) -> [u8; 0x10000] {
self.0
}
pub fn read(&self, address: u16) -> u8 {
self.0[address as usize]
}
pub fn write(&mut self, address: u16, data: u8) {
self.0[address as usize] = data;
}
pub fn load_rom<P>(&mut self, rom: P) -> Result<()>
where
P: AsRef<Path>,
{
let rom = match File::open(rom) {
Ok(rom) => rom,
Err(_) => bail!("rom could not be opened!"),
};
let bytes = rom.bytes();
for (address, byte) in bytes.enumerate() {
// println!("{address}");
match byte {
Ok(value) => self.write(address as u16 + 0x8000, value),
Err(_) => {
bail!("Loading rom: couldn't write byte {:#04x}", address);
} }
pub fn add_area(&mut self, area: MemMappedDevice) -> Result<(), GeorgeError> {
for existing_area in &self.areas {
if existing_area.contains(area.end) || existing_area.contains(area.start) {
return Err(GeorgeError {
desc: format!(
"Tried to assign an area already allocated to {:?}",
existing_area
),
kind: GeorgeErrorKind::Mapping(MappingError::RegionOccupied),
});
} }
} }
self.areas.push(area);
Ok(()) Ok(())
} }
pub fn read(&self, address: Word) -> Result<Byte, MemoryError> {
for area in &self.areas { pub fn poke(&self, address: u16) {
if area.contains(address) { println!("{:02x}", self.read(address));
let translated_address = area.translate_address(address);
match area.data.get(translated_address as usize) {
Some(data) => return Ok(*data),
None => return Err(MemoryError::NoDataAtAddress),
} }
pub fn load_bytes(&mut self, bytes: &[u8]) {
for (address, byte) in bytes.iter().enumerate() {
self.write(address as u16 + 0x8000, *byte)
} }
} }
Err(MemoryError::Unmapped)
}
pub fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> { pub fn read_from_bin(&mut self, f: File) -> Result<()> {
for area in self.areas.iter_mut() {
if area.contains(address) {
// println!("Writing to area {label}", label = area.label);
let translated_address = area.translate_address(address);
area.data[translated_address as usize] = data;
return Ok(());
};
}
Err(MemoryError::Unmapped)
}
pub fn read_from_bin(&mut self, f: File) -> Result<(), MemoryError> {
let bytes = f.bytes(); let bytes = f.bytes();
for (address, byte) in bytes.enumerate() { for (address, byte) in bytes.enumerate() {
match byte { match byte {
Ok(value) => self.write(address as Word, value)?, Ok(value) => self.write(address as u16 + 0x8000, value),
Err(_) => return Err(MemoryError::Unwritable), Err(_) => {
bail!("couldn't write byte {:#04x}", address);
}
} }
} }
Ok(()) Ok(())
+894
View File
@@ -0,0 +1,894 @@
vec![
Instruction::Valid(ValidInstruction {
opcode: Opcode::BRK(AddressingMode::Implied),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x02 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x03 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TSB(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ASL(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB0(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PHP(AddressingMode::Stack),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ASL(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x0b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TSB(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ASL(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR0(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BPL(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x13 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TRB(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ASL(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB1(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CLC(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INC(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x1b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TRB(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ASL(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR1(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::JSR(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x22 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x23 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BIT(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROL(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB2(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PLP(AddressingMode::Stack),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROL(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x2b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BIT(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROL(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR2(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BMI(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x33 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BIT(AddressingMode::ZeroPageIndexedWithX),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROL(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB3(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SEC(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEC(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x3b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BIT(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROL(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR3(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RTI(AddressingMode::Implied),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x42 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x43 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x44 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LSR(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB4(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PHA(AddressingMode::Stack),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LSR(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x4b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::JMP(AddressingMode::AbsoluteA),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LSR(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR4(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BVC(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x53 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x54 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LSR(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB5(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CLI(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PHY(AddressingMode::Stack),
cycles: 3,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x5b }),
Instruction::Invalid(InvalidInstruction { opcode: 0x5c }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LSR(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR5(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RTS(AddressingMode::Stack),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x62 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x63 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STZ(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROR(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB6(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PLA(AddressingMode::Stack),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROR(AddressingMode::Accumulator),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x6b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::JMP(AddressingMode::AbsoluteIndirect),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROR(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR6(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BVS(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x73 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STZ(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROR(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::RMB7(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SEI(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PLY(AddressingMode::Stack),
cycles: 4,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x7b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::JMP(AddressingMode::AbsoluteIndexedIndirect),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::ROR(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBR7(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BRA(AddressingMode::ProgramCounterRelative),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x82 }),
Instruction::Invalid(InvalidInstruction { opcode: 0x83 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STY(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STX(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB0(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEY(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BIT(AddressingMode::Immediate),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TXA(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x8b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STY(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STX(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS0(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BCC(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x93 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STY(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STX(AddressingMode::ZeroPageIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB1(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TYA(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TXS(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0x9b }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STZ(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithX),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STZ(AddressingMode::AbsoluteIndexedWithX),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS1(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDY(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDX(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xa3 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDY(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDX(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB2(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TAY(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TAX(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xab }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDY(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDX(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS2(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BCS(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::ZeroPageIndirect),
cycles: 5, // Unsure, see https://cx16.dk/65c02/reference.html#LDA
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xb3 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDY(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDX(AddressingMode::ZeroPageIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB3(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CLV(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::TSX(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xbb }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDY(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::LDX(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS3(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPY(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xc2 }),
Instruction::Invalid(InvalidInstruction { opcode: 0xc3 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPY(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEC(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB4(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INY(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEX(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::WAI(AddressingMode::Implied),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPY(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEC(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS4(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BNE(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::ZeroPageIndirect),
cycles: 5, // Unsure, look here: https://cx16.dk/65c02/reference.html#CMP
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xd3 }),
Instruction::Invalid(InvalidInstruction { opcode: 0xd4 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEC(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB5(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CLD(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PHX(AddressingMode::Stack),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::STP(AddressingMode::Implied),
cycles: 3,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xdc }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::DEC(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS5(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPX(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedIndirect),
cycles: 6,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xe2 }),
Instruction::Invalid(InvalidInstruction { opcode: 0xe3 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPX(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::ZeroPage),
cycles: 3,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INC(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB6(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INX(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::Immediate),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::NOP(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xeb }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::CPX(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::AbsoluteA),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INC(AddressingMode::AbsoluteA),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS6(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BEQ(AddressingMode::ProgramCounterRelative),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::ZeroPageIndirectIndexedWithY),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::ZeroPageIndirect),
cycles: 5,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xf3 }),
Instruction::Invalid(InvalidInstruction { opcode: 0xf4 }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INC(AddressingMode::ZeroPageIndexedWithX),
cycles: 6,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SMB7(AddressingMode::ZeroPage),
cycles: 5,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SED(AddressingMode::Implied),
cycles: 2,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithY),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::PLX(AddressingMode::Stack),
cycles: 4,
}),
Instruction::Invalid(InvalidInstruction { opcode: 0xfb }),
Instruction::Invalid(InvalidInstruction { opcode: 0xfc }),
Instruction::Valid(ValidInstruction {
opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithX),
cycles: 4,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::INC(AddressingMode::AbsoluteIndexedWithX),
cycles: 7,
}),
Instruction::Valid(ValidInstruction {
opcode: Opcode::BBS7(AddressingMode::ProgramCounterRelativeTest),
cycles: 4,
}),
]
+4
View File
@@ -0,0 +1,4 @@
#[cfg(not(target_arch = "wasm32"))]
pub mod native;
#[cfg(target_arch = "wasm32")]
pub mod wasm;
+101
View File
@@ -0,0 +1,101 @@
use minifb::Window;
use std::{
io::stdout,
process::exit,
thread::sleep,
time::{Duration, Instant},
};
use termion::{
async_stdin, clear,
cursor::{self, Goto},
event::Key,
input::TermRead,
raw::IntoRawMode,
};
use crate::{cpu::Cpu, keyboard::Keyboard, video::Renderer};
#[derive(Debug)]
pub struct GeorgeEmu {
cpu: Cpu,
renderer: Renderer,
input: Option<Keyboard>,
window: Option<Window>,
}
impl GeorgeEmu {
pub fn new(
mut cpu: Cpu,
renderer: Renderer,
input: Option<Keyboard>,
window: Option<Window>,
) -> Self {
cpu.reset();
Self {
cpu,
input,
renderer,
window,
}
}
pub fn draw(&mut self) {
self.renderer.render(self.window.as_mut())
}
pub fn cycle(&mut self) {
self.cpu.cycle()
}
pub fn run(&mut self) {
if self.window.is_none() {
let _ = stdout().into_raw_mode();
print!("{}{}", cursor::Hide, clear::All,);
}
let clock_interval = Duration::from_nanos(500);
let frame_interval = Duration::from_millis(16);
let mut next_clock = Instant::now() + clock_interval;
let mut next_frame = Instant::now() + frame_interval;
loop {
let now = Instant::now();
if now >= next_clock {
self.cpu.cycle();
next_clock += clock_interval;
}
if now >= next_frame {
if self.window.is_none() {
self.handle_input();
}
self.draw();
next_frame += frame_interval;
}
let next_run = std::cmp::min(next_clock, next_frame);
let sleep_dur = next_run - now;
sleep(sleep_dur)
}
}
fn handle_input(&mut self) {
let mut stdin = async_stdin().keys();
if let Some(Ok(key)) = stdin.next() {
match key {
Key::Char('q') => {
print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1));
exit(0);
}
Key::Char('`') => self.cpu.toggle_stopped(),
Key::Char('\n') => self.cpu.cycle(),
Key::Char('i') => self.cpu.interrupt(),
_ => {
self.input.as_mut().unwrap().read_keys(key);
}
}
} else {
self.input.as_mut().unwrap().clear_keys();
}
}
}
+29
View File
@@ -0,0 +1,29 @@
use minifb::Window;
use crate::{cpu::Cpu, video::Renderer};
#[derive(Debug)]
pub struct GeorgeEmu {
cpu: Cpu,
renderer: Renderer,
window: Window,
}
impl GeorgeEmu {
pub fn new(mut cpu: Cpu, renderer: Renderer, window: Window) -> Self {
cpu.reset();
Self {
cpu,
renderer,
window,
}
}
pub fn draw(&mut self) {
self.renderer.render(Some(&mut self.window));
}
pub fn cycle(&mut self) {
self.cpu.cycle()
}
}
-4
View File
@@ -1,4 +0,0 @@
use std::{u16, u8};
pub type Byte = u8;
pub type Word = u16;
+111 -93
View File
@@ -1,119 +1,137 @@
use crate::Mem; #[cfg(not(target_arch = "wasm32"))]
use minifb::{Window, WindowOptions}; use std::io::{self, Write};
use std::{ use std::{env, fs::File, io::Read, path::Path};
fs::File,
io::Read, #[cfg(not(target_arch = "wasm32"))]
sync::{Arc, RwLock}, use termion::{
thread::sleep, color::{self, Bg, Fg},
time::{Duration, Instant}, cursor::Goto,
}; };
use crate::{
cpu::CpuController,
memory::{MemHandle, MemoryReader},
};
use minifb::Window;
#[cfg(not(target_arch = "wasm32"))]
const FG_COLOR: u32 = 0xFFCC00; const FG_COLOR: u32 = 0xFFCC00;
#[cfg(not(target_arch = "wasm32"))]
const BG_COLOR: u32 = 0x110500; const BG_COLOR: u32 = 0x110500;
pub struct Crtc { // Wasm colors are ABGR
memory: Arc<RwLock<Mem>>, #[cfg(target_arch = "wasm32")]
buffer: Vec<u32>, const FG_COLOR: u32 = 0xFF00CCFF;
window: minifb::Window, #[cfg(target_arch = "wasm32")]
char_rom: Vec<u8>, const BG_COLOR: u32 = 0xFF000511;
const WIDTH: usize = 512;
const HEIGHT: usize = 380;
// pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
// where
// P: AsRef<Path>,
// {
// match char_rom {
// Some(path) => {
// let mut file = File::open(path).unwrap();
// let mut bin = vec![0; 0x8000];
// file.read_exact(&mut bin).unwrap();
// // println!("reading char rom");
// bin.try_into().unwrap()
// }
// None => *include_bytes!("./roms/cozette.rom"),
// }
// }
const CHAR_ROM: &[u8; 0x8000] = include_bytes!(concat!(env!("OUT_DIR"), "/cozette.rom"));
#[cfg(not(target_arch = "wasm32"))]
const ASCII_LOOKUP: [&str; 256] = [
" ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "奔", "", "", "", "", "", "", " ", "!", "\"", "#",
"$", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6",
"7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\",
"]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "ʕ", "·", "", "ʔ", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "🔮", "", "",
"", "", "🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", "", "", "", "", "", "", "",
"", "", "", "", "🎁", "", "", "", "", "", "", "", "", "",
];
#[derive(Debug)]
pub struct Renderer {
memory: MemHandle,
controller: CpuController,
} }
pub fn get_char_bin(path: &str) -> Vec<u8> { impl Renderer {
let mut file = File::open(path).unwrap(); pub fn new(controller: CpuController, memory: MemHandle) -> Self {
let mut buffer = vec![0; 0xFFFF]; Self { memory, controller }
file.read_exact(&mut buffer).unwrap();
buffer
} }
impl Crtc { pub fn render(&self, window: Option<&mut Window>) {
pub fn new(memory: Arc<RwLock<Mem>>) -> Self { match window {
let window = Window::new("screen", 512, 380, WindowOptions::default()).unwrap(); Some(window) => {
let char_rom = get_char_bin("./src/cozette.bin"); // the rest of this function is arcane wizardry
Self { // based on the specifics of george's weird
memory, // display and characters... don't fuck around w it
buffer: vec![0; 512 * 380], let mut i = 0;
window, let mut buffer = [0; WIDTH * HEIGHT];
char_rom,
}
}
fn draw(&mut self) {
match &mut self.memory.clone().try_read() {
Ok(memory) => {
let hw_ctrl = memory.read(0x4000).unwrap();
match hw_ctrl & 0b0000_1000 == 0b0000_1000 {
false => self.draw_chars(),
true => self.draw_hires(),
};
}
Err(_) => {
println!("Couldn't acquire read on shared memory in main thread");
return;
}
};
self.window
.update_with_buffer(&self.buffer, 512, 380)
.unwrap();
}
fn draw_chars(&mut self) {
let memory = match self.memory.try_read() {
Ok(read) => read,
Err(_) => {
println!("Couldn't acquire read on shared memory in main thread");
return;
}
};
for char_row in 0..29 { for char_row in 0..29 {
for char_col in 0..64 { for char_col in 0..64 {
let ascii = memory let ascii = self.read(0x6000 + i);
.read(0x6000 + (char_row as u16 * char_col as u16)) i += 1;
.unwrap();
for row in 0..13 { for row in 0..13 {
let byte = self.char_rom[ascii as usize + (row * 0x101)]; let byte = CHAR_ROM[ascii as usize + (row * 0x100)];
for i in (0..8).rev() { for bit_index in (0..8).rev() {
let buffer_index = ((char_row) * 13 + (row)) * 512 + (char_col * 8 + i); let buffer_index =
if (byte << i) & 0x80 == 0x80 { ((char_row) * 13 + (row)) * 512 + (char_col * 8 + bit_index);
self.buffer[buffer_index] = FG_COLOR; if (byte << bit_index) & 0x80 == 0x80 {
buffer[buffer_index] = FG_COLOR;
} else { } else {
self.buffer[buffer_index] = BG_COLOR; buffer[buffer_index] = BG_COLOR;
} }
} }
} }
} }
} }
window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap();
} }
fn draw_hires(&mut self) { None => {
let memory = match self.memory.try_read() { #[cfg(not(target_arch = "wasm32"))]
Ok(read) => read, {
Err(_) => { let mut stdout = io::stdout();
println!("Couldn't acquire read on shared memory in main thread"); let mut i = 0;
return; for char_row in 0..29 {
} for char_col in 0..64 {
}; let ascii = self.read(0x6000 + i);
for addr in 0x6000..0xBF00 { i += 1;
// TODO: eventually this will access memory in the weird interleaved way the hardware is let char = ASCII_LOOKUP[ascii as usize];
// designed, but this is easiest to implement for now let _ = write!(
let byte = memory.read(addr).unwrap(); // FG_COLOR = 0xFFCC00
for i in 0..8 { // BG_COLOR = 0x110500
match byte & 0x80 >> i == 0 { stdout,
true => self.buffer[(addr - 0x6000) as usize * 8 + i] = BG_COLOR, "{}{}{}{char}",
false => self.buffer[(addr - 0x6000) as usize * 8 + i] = FG_COLOR, Goto(char_col + 1, char_row + 1),
Fg(color::Rgb(0xFF, 0xCC, 0x00)),
Bg(color::Rgb(0x11, 0x05, 0x00))
);
} }
} }
} }
} }
pub fn run(&mut self) { }
let frame_duration = Duration::from_millis(16); self.controller.irq();
let mut previous_draw = Instant::now(); }
}
loop { impl MemoryReader for Renderer {
let now = Instant::now(); fn read(&self, address: u16) -> u8 {
if now - previous_draw > frame_duration { self.memory.read(address)
self.draw();
previous_draw = now;
}
sleep(Duration::from_millis(1));
}
} }
} }