minimum viable product

This commit is contained in:
2026-06-04 09:33:24 -04:00
parent 1217b4beaa
commit 4c2236bf5d
15 changed files with 1274 additions and 309 deletions
+3 -9
View File
@@ -1,9 +1,3 @@
[unstable] [alias]
build-std = ["core", "compiler_builtins"] build-kernel = "build -p kernel --target x86_64-unknown-none -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem"
build-std-features = ["compiler-builtins-mem"] run-kernel = "run -p kernel --target x86_64-unknown-none -Z build-std=core,compiler_builtins -Z build-std-features=compiler-builtins-mem"
[build]
target = "x86_64-unknown-none"
[target.x86_64-unknown-none]
rustflags = ["-Clink-arg=-Tsrc/linker.ld", "-Crelocation-model=static"]
Generated
+729 -8
View File
@@ -2,12 +2,33 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.10.3" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.12.1" version = "2.12.1"
@@ -15,10 +36,360 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]] [[package]]
name = "bootloader" name = "bitvec"
version = "0.9.34" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f6a8a495d2f93fe3d6eb3a224f9aa749a63cfd746ed03eb5ddcbd00ade7d8f" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bootloader"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5fb69232f250c0d5b74eeb285702b4b0479f574980f45dc9bef97b2c65e2021"
dependencies = [
"anyhow",
"bootloader-boot-config",
"fatfs",
"gpt",
"llvm-tools",
"mbrman",
"serde_json",
"tempfile",
]
[[package]]
name = "bootloader-boot-config"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a5e475c13d3d988e105b76b92aeea94614c12590c33495165c02b16e895375"
dependencies = [
"serde",
]
[[package]]
name = "bootloader_api"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c074a3d1075b26ed77e502d6af8ab1263abbdc3d026c166e46480f973287293"
[[package]]
name = "builder"
version = "0.1.0"
dependencies = [
"bootloader",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "crc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "fatfs"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"log",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
"wasip3",
]
[[package]]
name = "gpt"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69"
dependencies = [
"bitflags 2.12.1",
"crc",
"log",
"uuid",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
"serde",
"serde_core",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "kernel"
version = "0.1.0"
dependencies = [
"bootloader_api",
"x86_64",
]
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "llvm-tools"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf"
[[package]]
name = "log"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "mbrman"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1fc3bff63c208d4a14301c6cb807af2d1a0760052584ce3f9a737b55fb85498"
dependencies = [
"bincode",
"bitvec",
"serde",
"serde-big-array",
"thiserror",
]
[[package]]
name = "memchr"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.12.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
@@ -26,6 +397,143 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde-big-array"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f"
dependencies = [
"serde",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
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.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "uuid"
version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "volatile" name = "volatile"
version = "0.4.6" version = "0.4.6"
@@ -33,11 +541,218 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]] [[package]]
name = "wordleos" name = "wasip2"
version = "0.1.0" version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [ dependencies = [
"bootloader", "wit-bindgen 0.57.1",
"x86_64", ]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.12.1",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.12.1",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
] ]
[[package]] [[package]]
@@ -47,7 +762,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491" checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"bitflags", "bitflags 2.12.1",
"rustversion", "rustversion",
"volatile", "volatile",
] ]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+8 -7
View File
@@ -1,8 +1,9 @@
[package] [workspace]
name = "wordleos" members = ["kernel", "builder"]
version = "0.1.0" resolver = "2"
edition = "2021"
[dependencies] [profile.dev]
bootloader = "0.9" panic = "abort"
x86_64 = "0.14"
[profile.release]
panic = "abort"
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "builder"
version = "0.1.0"
edition = "2021"
[dependencies]
bootloader = "0.11"
+51
View File
@@ -0,0 +1,51 @@
use std::path::Path;
fn main() {
let args: Vec<String> = std::env::args().collect();
let kernel_path = if args.len() > 1 {
args[1].clone()
} else {
let target = std::env::var("TARGET").unwrap_or_else(|_| "x86_64-unknown-none".to_string());
let profile = std::env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
.unwrap_or_else(|_| ".".to_string());
// Builder is at workspace/builder, kernel is at workspace/kernel
let workspace = Path::new(&manifest_dir).parent().unwrap();
format!("{}/target/{}/{}/kernel", workspace.display(), target, profile)
};
let kernel_path = Path::new(&kernel_path);
if !kernel_path.exists() {
eprintln!("Kernel not found at: {}", kernel_path.display());
eprintln!("Usage: builder <path-to-kernel-elf>");
std::process::exit(1);
}
let out_dir = std::env::var("OUT_DIR").unwrap_or_else(|_| {
std::env::var("CARGO_MANIFEST_DIR")
.map(|d| format!("{}/../target", d))
.unwrap_or_else(|_| "target".to_string())
});
// Disable bootloader's verbose framebuffer logging (default: trace-level yellow text)
let mut boot_config = bootloader::BootConfig::default();
boot_config.frame_buffer_logging = false;
// Create UEFI bootable image
println!("Creating UEFI boot image from {}", kernel_path.display());
let mut uefi_img = bootloader::UefiBoot::new(&kernel_path);
uefi_img.set_boot_config(&boot_config);
let uefi_path = Path::new(&out_dir).join("wordleos-uefi.img");
uefi_img.create_disk_image(&uefi_path)
.expect("failed to write UEFI image");
println!("UEFI image written to: {}", uefi_path.display());
// Also create BIOS bootable image for compatibility
println!("Creating BIOS boot image from {}", kernel_path.display());
let mut bios_img = bootloader::BiosBoot::new(&kernel_path);
bios_img.set_boot_config(&boot_config);
let bios_path = Path::new(&out_dir).join("wordleos-bios.img");
bios_img.create_disk_image(&bios_path)
.expect("failed to write BIOS image");
println!("BIOS image written to: {}", bios_path.display());
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "kernel"
version = "0.1.0"
edition = "2021"
[dependencies]
bootloader_api = "0.11"
x86_64 = "0.14"
+59
View File
@@ -0,0 +1,59 @@
pub const FONT_5X7_W: usize = 5;
pub const FONT_5X7_H: usize = 7;
pub static FONT5X7: [[u8; 7]; 26] = [
[0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001], // A
[0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110], // B
[0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110], // C
[0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110], // D
[0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111], // E
[0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000], // F
[0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01110], // G
[0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001], // H
[0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], // I
[0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100], // J
[0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001], // K
[0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111], // L
[0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001], // M
[0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001, 0b10001], // N
[0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], // O
[0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000], // P
[0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101], // Q
[0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001], // R
[0b01110, 0b10001, 0b10000, 0b01110, 0b00001, 0b10001, 0b01110], // S
[0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100], // T
[0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], // U
[0b10001, 0b10001, 0b10001, 0b01010, 0b01010, 0b00100, 0b00100], // V
[0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001], // W
[0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001], // X
[0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100], // Y
[0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111], // Z
];
pub fn get_5x7(ch: u8) -> Option<&'static [u8; 7]> {
if ch.is_ascii_uppercase() {
Some(&FONT5X7[(ch - b'A') as usize])
} else {
None
}
}
pub static PUNCT5X7: &[(u8, [u8; 7])] = &[
(b'!', [0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100]),
(b'\'', [0b00100, 0b00100, 0b00100, 0b00000, 0b00000, 0b00000, 0b00000]),
(b',', [0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000]),
(b'-', [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000]),
(b'.', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100]),
(b':', [0b00000, 0b00100, 0b00100, 0b00000, 0b00100, 0b00100, 0b00000]),
(b'_', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111]),
(b' ', [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000]),
];
pub fn get_punct(ch: u8) -> Option<&'static [u8; 7]> {
for &(c, ref glyph) in PUNCT5X7 {
if c == ch {
return Some(glyph);
}
}
None
}
View File
+85 -3
View File
@@ -1,5 +1,78 @@
use core::sync::atomic::{AtomicBool, Ordering};
use x86_64::instructions::port::Port; use x86_64::instructions::port::Port;
const TIMEOUT: u32 = 1_000_000;
static KEYBOARD_READY: AtomicBool = AtomicBool::new(false);
/// Initialises the PS/2 controller and keyboard.
///
/// UEFI firmware often leaves the PS/2 controller uninitialised, so we must
/// perform the full startup sequence ourselves before the keyboard will work.
/// If no PS/2 controller is present (common on modern UEFI systems), this
/// returns gracefully without hanging.
pub fn init() {
// Disable both PS/2 ports
if !try_write_command(0xAD) { return; }
if !try_write_command(0xA7) { return; }
// Flush output buffer
let _ = try_read_data();
// Read and update configuration byte
if !try_write_command(0x20) { return; }
let config = match try_read_data() {
Some(val) => val & !0x07,
None => return,
};
if !try_write_command(0x60) { return; }
if !try_write_data(config) { return; }
// Controller self-test
if !try_write_command(0xAA) { return; }
if try_read_data() != Some(0x55) { return; }
// Enable keyboard port
if !try_write_command(0xAE) { return; }
// Reset keyboard
if !try_write_data(0xFF) { return; }
if try_read_data() != Some(0xFA) { return; } // ACK
if try_read_data() != Some(0xAA) { return; } // self-test passed
KEYBOARD_READY.store(true, Ordering::SeqCst);
}
fn try_write_command(cmd: u8) -> bool {
if !poll_write_ready() { return false; }
unsafe { Port::new(0x64).write(cmd) }
true
}
fn try_write_data(data: u8) -> bool {
if !poll_write_ready() { return false; }
unsafe { Port::new(0x60).write(data) }
true
}
fn poll_write_ready() -> bool {
for _ in 0..TIMEOUT {
if read_status() & 2 == 0 {
return true;
}
}
false
}
fn try_read_data() -> Option<u8> {
for _ in 0..TIMEOUT {
if read_status() & 1 != 0 {
return Some(unsafe { Port::new(0x60).read() });
}
}
None
}
/// Actions produced by the PS/2 keyboard decoder. /// Actions produced by the PS/2 keyboard decoder.
/// ///
/// Only AZ letters, Enter, and Backspace are relevant for the Wordle game. /// Only AZ letters, Enter, and Backspace are relevant for the Wordle game.
@@ -45,16 +118,25 @@ fn data_available() -> bool {
/// is pending, `None` is returned and the caller should briefly spin-wait /// is pending, `None` is returned and the caller should briefly spin-wait
/// before retrying. /// before retrying.
/// ///
/// ## Scan code handling (Set 1) /// ## Scan code handling
/// The PS/2 controller is typically in "translated" mode, converting native /// The PS/2 keyboard defaults to Scan Code Set 2. Most PS/2 controllers
/// Set 2 scancodes to Set 1 on the fly. In Set 1: /// run in "translated" mode, converting Set 2 scancodes to Set 1 on the
/// fly. In Set 1:
/// - Make codes (key pressed): 0x01 0x7F /// - Make codes (key pressed): 0x01 0x7F
/// - Break codes (key released): 0x81 0xFF (= make_code | 0x80) /// - Break codes (key released): 0x81 0xFF (= make_code | 0x80)
/// - Extended keys (arrows, etc.): prefixed with 0xE0 /// - Extended keys (arrows, etc.): prefixed with 0xE0
/// ///
/// We do NOT explicitly change the keyboard's scan code set (command 0xF0)
/// because doing so would corrupt the controller's translation — the
/// keyboard stays in its default Set 2, and the controller handles the
/// conversion to Set 1 transparently.
///
/// We ignore break codes and extended codes since we only care about when /// We ignore break codes and extended codes since we only care about when
/// a letter key is pressed. /// a letter key is pressed.
pub fn poll_key() -> Option<KeyAction> { pub fn poll_key() -> Option<KeyAction> {
if !KEYBOARD_READY.load(Ordering::SeqCst) {
return None;
}
if !data_available() { if !data_available() {
return None; return None;
} }
+94
View File
@@ -0,0 +1,94 @@
#![no_std]
#![no_main]
mod font;
mod vga_buffer;
mod keyboard;
mod rng;
mod game;
use game::{GameState, WordleGame, WORDS};
use keyboard::KeyAction;
use rng::Lcg;
use vga_buffer::{Color, ColorCode};
use bootloader_api::{entry_point, BootInfo};
entry_point!(kernel_main);
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
let fb = boot_info.framebuffer.take()
.expect("no framebuffer provided by bootloader");
vga_buffer::init(fb);
vga_buffer::clear_screen(Color::Black);
keyboard::init();
loop {
let mut rng = Lcg::from_tsc();
let secret = WORDS[rng.next_range(WORDS.len())];
let mut game = WordleGame::new(secret);
game.render_all();
loop {
if let Some(action) = keyboard::poll_key() {
match action {
KeyAction::Letter(ch) => game.add_letter(ch),
KeyAction::Backspace => game.delete_letter(),
KeyAction::Enter => {
if game.submit_guess().is_none() {}
}
}
game.render_all();
if game.state == GameState::Won || game.state == GameState::Lost {
wait_for_keypress();
break;
}
}
spin_delay();
}
}
}
fn wait_for_keypress() {
loop {
if keyboard::poll_key().is_some() {
return;
}
spin_delay();
}
}
fn spin_delay() {
for _ in 0..10000 {
core::hint::spin_loop();
}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
vga_buffer::clear_screen(Color::Red);
let lines = [
" KERNEL PANIC ",
"",
"The system has encountered",
"a fatal error and must halt.",
];
for (i, &line) in lines.iter().enumerate() {
let col = (vga_buffer::WIDTH - line.len()) / 2;
vga_buffer::write_str(10 + i, col, line,
ColorCode::new(Color::White, Color::Red));
}
if let Some(loc) = info.location() {
let _ = (loc.file(), loc.line());
}
loop {
x86_64::instructions::hlt();
}
}
View File
+228
View File
@@ -0,0 +1,228 @@
use crate::font;
use bootloader_api::info::{FrameBuffer, PixelFormat};
pub const WIDTH: usize = 80;
pub const HEIGHT: usize = 25;
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
fn color_to_rgb(color: Color) -> (u8, u8, u8) {
match color {
Color::Black => (0, 0, 0),
Color::Blue => (0, 0, 170),
Color::Green => (0, 170, 0),
Color::Cyan => (0, 170, 170),
Color::Red => (170, 0, 0),
Color::Magenta => (170, 0, 170),
Color::Brown => (170, 85, 0),
Color::LightGray => (170, 170, 170),
Color::DarkGray => (85, 85, 85),
Color::LightBlue => (85, 85, 255),
Color::LightGreen => (85, 255, 85),
Color::LightCyan => (85, 255, 255),
Color::LightRed => (255, 85, 85),
Color::Pink => (255, 85, 255),
Color::Yellow => (255, 255, 85),
Color::White => (255, 255, 255),
}
}
#[derive(Clone, Copy)]
pub struct ColorCode {
fg: Color,
bg: Color,
}
impl ColorCode {
pub const fn new(fg: Color, bg: Color) -> ColorCode {
ColorCode { fg, bg }
}
}
struct FbState {
ptr: *mut u8,
width: usize,
height: usize,
stride: usize,
bpp: usize,
is_bgr: bool,
x_off: usize,
y_off: usize,
cell_w: usize,
cell_h: usize,
}
unsafe impl Send for FbState {}
static mut FB: Option<FbState> = None;
pub fn init(fb: FrameBuffer) {
let info = fb.info();
let buf = fb.into_buffer();
let ptr = buf.as_mut_ptr();
let is_bgr = matches!(info.pixel_format, PixelFormat::Bgr);
let cell_w = (info.width / WIDTH).max(1);
let cell_h = (info.height / HEIGHT).max(1);
let x_off = (info.width - cell_w * WIDTH) / 2;
let y_off = (info.height - cell_h * HEIGHT) / 2;
unsafe {
FB = Some(FbState {
ptr,
width: info.width,
height: info.height,
stride: info.stride,
bpp: info.bytes_per_pixel,
is_bgr,
x_off,
y_off,
cell_w,
cell_h,
});
}
}
fn with_fb<F, R>(f: F) -> R
where
F: FnOnce(&mut FbState) -> R,
{
unsafe {
let fb = &mut *core::ptr::addr_of_mut!(FB);
f(fb.as_mut().expect("Framebuffer not initialized"))
}
}
fn write_pixel(state: &mut FbState, x: usize, y: usize, r: u8, g: u8, b: u8) {
if x >= state.width || y >= state.height {
return;
}
let offset = (y * state.stride + x) * state.bpp;
unsafe {
if state.is_bgr {
*state.ptr.add(offset) = b;
*state.ptr.add(offset + 1) = g;
*state.ptr.add(offset + 2) = r;
} else {
*state.ptr.add(offset) = r;
*state.ptr.add(offset + 1) = g;
*state.ptr.add(offset + 2) = b;
}
}
}
fn fill_rect(state: &mut FbState, x: usize, y: usize, w: usize, h: usize, r: u8, g: u8, b: u8) {
let x_end = (x + w).min(state.width);
let y_end = (y + h).min(state.height);
for py in y..y_end {
for px in x..x_end {
write_pixel(state, px, py, r, g, b);
}
}
}
fn get_glyph(ch: u8) -> Option<&'static [u8; 7]> {
if let Some(g) = font::get_5x7(ch) {
return Some(g);
}
font::get_punct(ch)
}
fn draw_glyph_at(
state: &mut FbState,
cell_x: usize, cell_y: usize, cell_w: usize, cell_h: usize,
ch: u8,
fg_r: u8, fg_g: u8, fg_b: u8,
bg_r: u8, bg_g: u8, bg_b: u8,
) {
fill_rect(state, cell_x, cell_y, cell_w, cell_h, bg_r, bg_g, bg_b);
let glyph = match get_glyph(ch) {
Some(g) => g,
None => return,
};
if cell_w < 3 || cell_h < 3 {
return;
}
let scale_h = (cell_w.saturating_sub(2)) / font::FONT_5X7_W;
let scale_v = (cell_h.saturating_sub(2)) / font::FONT_5X7_H;
let scale = scale_h.min(scale_v).min(8);
if scale == 0 {
return;
}
let glw = font::FONT_5X7_W * scale;
let glh = font::FONT_5X7_H * scale;
let off_x = (cell_w - glw) / 2;
let off_y = (cell_h - glh) / 2;
for row in 0..font::FONT_5X7_H {
for col in 0..font::FONT_5X7_W {
if glyph[row] & (1 << (4 - col)) != 0 {
fill_rect(
state,
cell_x + off_x + col * scale,
cell_y + off_y + row * scale,
scale, scale,
fg_r, fg_g, fg_b,
);
}
}
}
}
pub fn clear_screen(bg: Color) {
let (r, g, b) = color_to_rgb(bg);
with_fb(|state| {
fill_rect(state, 0, 0, state.width, state.height, r, g, b);
});
}
pub fn write_char(row: usize, col: usize, ch: u8, color: ColorCode) {
if row >= HEIGHT || col >= WIDTH {
return;
}
let (fg_r, fg_g, fg_b) = color_to_rgb(color.fg);
let (bg_r, bg_g, bg_b) = color_to_rgb(color.bg);
with_fb(|state| {
let cell_x = state.x_off + col * state.cell_w;
let cell_y = state.y_off + row * state.cell_h;
draw_glyph_at(
state,
cell_x, cell_y, state.cell_w, state.cell_h,
ch,
fg_r, fg_g, fg_b,
bg_r, bg_g, bg_b,
);
});
}
pub fn write_str(row: usize, col: usize, s: &str, color: ColorCode) {
for (i, &ch) in s.as_bytes().iter().enumerate() {
if col + i < WIDTH {
write_char(row, col + i, ch, color);
} else {
break;
}
}
}
-51
View File
@@ -1,51 +0,0 @@
ENTRY(_start);
SECTIONS
{
. = 0x200000;
.rodata : ALIGN(4K)
{
*(.rodata .rodata.*)
}
.text : ALIGN(4K)
{
*(.text .text.*)
}
.data.rel.ro : ALIGN(4K)
{
*(.data.rel.ro .data.rel.ro.*)
}
.data : ALIGN(4K)
{
*(.data .data.*)
}
.got : ALIGN(4K)
{
*(.got)
}
.bss : ALIGN(4K)
{
*(.bss .bss.*)
}
/DISCARD/ :
{
*(.comment)
*(.debug_abbrev)
*(.debug_info)
*(.debug_aranges)
*(.debug_ranges)
*(.debug_str)
*(.debug_frame)
*(.debug_line)
*(.debug_loc)
*(.symtab)
*(.strtab)
}
}
-133
View File
@@ -1,133 +0,0 @@
#![no_std]
#![no_main]
mod vga_buffer;
mod keyboard;
mod rng;
mod game;
use game::{GameState, WordleGame, WORDS};
use keyboard::KeyAction;
use rng::Lcg;
use vga_buffer::{Color, ColorCode};
use bootloader::{entry_point, BootInfo};
entry_point!(kernel_main);
// Called by the bootloader after it has set up 64-bit long mode, page tables,
// and a stack. The `boot_info` parameter provides information about the
// memory map, framebuffer, etc. — but for our VGA-text-mode display we
// don't need it.
//
// How the bootloader works:
// 1. BIOS/UEFI loads the bootloader (from `bootloader` crate).
// 2. Bootloader transitions CPU from real mode → protected mode → long mode,
// sets up page tables with VGA framebuffer at 0xB8000 identity-mapped,
// loads our ELF, and jumps to kernel_main.
// 3. We arrive with interrupts disabled (RFLAGS.IF = 0), at CPL 0 (ring 0).
fn kernel_main(_boot_info: &'static BootInfo) -> ! {
// ── Initialisation ─────────────────────────────────────────────────
vga_buffer::clear_screen(Color::Black);
// ── Main game loop (runs forever, restarting after each game) ──────
loop {
// Seed the RNG from the CPU timestamp counter so every boot picks
// a different secret word (assuming the machine has been on for a
// different amount of time).
let mut rng = Lcg::from_tsc();
let secret = WORDS[rng.next_range(WORDS.len())];
let mut game = WordleGame::new(secret);
game.render_all();
// ── Per-game input loop ────────────────────────────────────────
loop {
if let Some(action) = keyboard::poll_key() {
match action {
KeyAction::Letter(ch) => game.add_letter(ch),
KeyAction::Backspace => game.delete_letter(),
KeyAction::Enter => {
if game.submit_guess().is_none() {
// Incomplete guess — the game shows nothing
// for this; we just re-render as-is.
}
}
}
game.render_all();
if game.state == GameState::Won || game.state == GameState::Lost {
// Wait for any key press, then restart the game.
wait_for_keypress();
break;
}
}
// Brief spin delay to keep the CPU from saturating at 100 %
// while polling. 10k iterations of `pause` is roughly 1050 µs
// on modern CPUs — invisible to human input but vastly reduces
// power draw.
spin_delay();
}
}
}
/// Blocks until any key is pressed (used between games).
fn wait_for_keypress() {
loop {
if keyboard::poll_key().is_some() {
return;
}
spin_delay();
}
}
/// Short delay using the x86 `pause` instruction (rep; nop).
///
/// The `pause` instruction hints to the CPU that this is a spin-wait loop,
/// allowing it to reduce power consumption and improve hyper-thread
/// performance. It behaves as a no-op architecturally.
fn spin_delay() {
for _ in 0..10000 {
core::hint::spin_loop();
}
}
/// Panic handler — invoked on unrecoverable errors.
///
/// Displays a red crash screen with a message, then halts forever.
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
vga_buffer::clear_screen(Color::Red);
let lines = [
" KERNEL PANIC ",
"",
"The system has encountered",
"a fatal error and must halt.",
];
for (i, &line) in lines.iter().enumerate() {
let col = (vga_buffer::WIDTH - line.len()) / 2;
vga_buffer::write_str(10 + i, col, line,
ColorCode::new(Color::White, Color::Red));
}
// The panic message payload is the `Arguments` type. We could format
// it with a custom `core::fmt::Write` implementation, but since we're
// already in a crash state, we keep it simple: just display the line
// number if available.
if let Some(loc) = info.location() {
let file = loc.file();
let line_num = loc.line();
// Truncated display for the panic location.
let file_start = if file.len() > 30 { &file[file.len() - 30..] } else { file };
// Print at a fixed offset; we can't do dynamic formatting in no_std
// without a formatter.
let _ = (file_start, line_num);
}
loop {
x86_64::instructions::hlt();
}
}
-98
View File
@@ -1,98 +0,0 @@
use core::ptr::write_volatile;
/// VGA text mode display dimensions.
///
/// The standard VGA text mode provides an 80×25 grid of character cells.
/// Each cell is 2 bytes: [ASCII byte] [attribute byte].
/// The buffer starts at physical address 0xB8000 and is memory-mapped into
/// the CPU's address space. The x86_64 bootloader identity-maps this region,
/// so we can write to it with regular memory stores — but we must use volatile
/// writes to prevent the compiler from optimizing away MMIO operations.
pub const VGA_ADDR: *mut u8 = 0xB8000 as *mut u8;
pub const WIDTH: usize = 80;
pub const HEIGHT: usize = 25;
/// Standard VGA text-mode colors.
///
/// The attribute byte layout:
/// bits 0-3: foreground color (0 = black, 15 = white)
/// bits 4-6: background color (0-7, hardware limit; bit 7 = blink)
#[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
/// A packed attribute byte encoding foreground + background color.
#[derive(Clone, Copy)]
pub struct ColorCode(u8);
impl ColorCode {
/// Constructs a color code from foreground and background colors.
///
/// ## Hardware note
/// The VGA attribute byte stores the background color in bits 4-6 (3 bits,
/// limiting backgrounds to 0-7). Bit 7 is the blink enable bit, which we
/// keep cleared. The foreground color uses bits 0-3 (including intensity
/// on bit 3), giving 16 possible foreground colors.
pub const fn new(fg: Color, bg: Color) -> ColorCode {
ColorCode(((bg as u8) << 4) | (fg as u8))
}
}
/// Clears the entire VGA screen to the given background color.
///
/// Writes a space character (0x20) with the appropriate attribute byte
/// to every cell on the screen.
pub fn clear_screen(bg: Color) {
let color = ColorCode::new(Color::White, bg);
for offset in (0..(WIDTH * HEIGHT * 2)).step_by(2) {
unsafe {
write_volatile(VGA_ADDR.add(offset), b' ');
write_volatile(VGA_ADDR.add(offset + 1), color.0);
}
}
}
/// Writes a single character at position (row, col) with the given color.
///
/// The position is bounds-checked against the 80×25 screen. Writes outside
/// the visible area are silently ignored.
pub fn write_char(row: usize, col: usize, ch: u8, color: ColorCode) {
if row < HEIGHT && col < WIDTH {
let offset = (row * WIDTH + col) * 2;
unsafe {
write_volatile(VGA_ADDR.add(offset), ch);
write_volatile(VGA_ADDR.add(offset + 1), color.0);
}
}
}
/// Writes a string starting at position (row, col) with the given color.
///
/// Stops at the right edge of the screen to avoid wrapping.
pub fn write_str(row: usize, col: usize, s: &str, color: ColorCode) {
for (i, &ch) in s.as_bytes().iter().enumerate() {
if col + i < WIDTH {
write_char(row, col + i, ch, color);
} else {
break;
}
}
}