added readme

This commit is contained in:
2026-06-04 04:20:07 -04:00
commit 1217b4beaa
14 changed files with 1330 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
[unstable]
build-std = ["core", "compiler_builtins"]
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"]
+1
View File
@@ -0,0 +1 @@
/target
Generated
+53
View File
@@ -0,0 +1,53 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bit_field"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
[[package]]
name = "bitflags"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]]
name = "bootloader"
version = "0.9.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13f6a8a495d2f93fe3d6eb3a224f9aa749a63cfd746ed03eb5ddcbd00ade7d8f"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "wordleos"
version = "0.1.0"
dependencies = [
"bootloader",
"x86_64",
]
[[package]]
name = "x86_64"
version = "0.14.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c101112411baafbb4bf8d33e4c4a80ab5b02d74d2612331c61e8192fc9710491"
dependencies = [
"bit_field",
"bitflags",
"rustversion",
"volatile",
]
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "wordleos"
version = "0.1.0"
edition = "2021"
[dependencies]
bootloader = "0.9"
x86_64 = "0.14"
+475
View File
@@ -0,0 +1,475 @@
CPU Reset (CPU 0)
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00060fb1
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300
CS =f000 ffff0000 0000ffff 00009b00
SS =0000 00000000 0000ffff 00009300
DS =0000 00000000 0000ffff 00009300
FS =0000 00000000 0000ffff 00009300
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 0000ffff
IDT= 00000000 0000ffff
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
CPU Reset (CPU 0)
EAX=00000000 EBX=00000000 ECX=00000000 EDX=00060fb1
ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000
EIP=0000fff0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300
CS =f000 ffff0000 0000ffff 00009b00
SS =0000 00000000 0000ffff 00009300
DS =0000 00000000 0000ffff 00009300
FS =0000 00000000 0000ffff 00009300
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 0000ffff
IDT= 00000000 0000ffff
CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
SMM: enter
EAX=00000000 EBX=00000000 ECX=02000000 EDX=02000628
ESI=0000000b EDI=02000000 EBP=06feb0e0 ESP=00006d5c
EIP=000ebede EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=ffffffff CCO=EFLAGS
EFER=0000000000000000
SMM: after RSM
EAX=00000000 EBX=00000000 ECX=02000000 EDX=02000628
ESI=0000000b EDI=02000000 EBP=06feb0e0 ESP=00006d5c
EIP=000ebede EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=00006cff
ESI=00006cb8 EDI=06ffee71 EBP=00006c78 ESP=00006c78
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 008f9300
CS =f000 000f0000 ffffffff 008f9b00
SS =0000 00000000 ffffffff 008f9300
DS =0000 00000000 ffffffff 008f9300
FS =0000 00000000 ffffffff 008f9300
GS =0000 00000000 ffffffff 008f9300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00006c78 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=00006cff
ESI=00006cb8 EDI=06ffee71 EBP=00006c78 ESP=00006c78
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=06fffe88
ESI=000e9780 EDI=06ffee71 EBP=00006c78 ESP=00006c78
EIP=000f7d44 EFL=00000012 [----A--] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=00006c64 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=06fffe88
ESI=000e9780 EDI=06ffee71 EBP=00006c78 ESP=00006c78
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =0000 00000000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000004 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=00007d2a EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 008f9300
CS =f000 000f0000 ffffffff 008f9b00
SS =0000 00000000 ffffffff 008f9300
DS =0000 00000000 ffffffff 008f9300
FS =0000 00000000 ffffffff 008f9300
GS =ca00 000ca000 ffffffff 008f9300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00006962 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=0000694e CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=00007d47 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=0000695c CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
ESI=06fd2080 EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=00006948 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
ESI=06fd2080 EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000004 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=00007d2a EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00006962 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=000069a2 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=0000694e CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000005
ESI=00000000 EDI=06ffee71 EBP=00006962 ESP=00006962
EIP=00007d47 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=00007d2a EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000000 CCD=0000695c CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=000f7d2d ECX=00001234 EDX=000069ff
ESI=0000699c EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=000f7d2d EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000044 CCD=00000000 CCO=EFLAGS
EFER=0000000000000000
SMM: enter
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
ESI=06f32080 EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=000f7d44 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]
SS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6220 00000037
IDT= 000f625e 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000008 CCD=00006948 CCO=ADDL
EFER=0000000000000000
SMM: after RSM
EAX=000000b5 EBX=00007d47 ECX=00005678 EDX=00000003
ESI=06f32080 EDI=06ffee71 EBP=0000695c ESP=0000695c
EIP=00007d47 EFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =da80 000da800 ffffffff 00809300
CS =f000 000f0000 ffffffff 00809b00
SS =0000 00000000 ffffffff 00809300
DS =0000 00000000 ffffffff 00809300
FS =0000 00000000 ffffffff 00809300
GS =ca00 000ca000 ffffffff 00809300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT= 00000000 00000000
IDT= 00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=00000004 CCD=00000001 CCO=EFLAGS
EFER=0000000000000000
Servicing hardware INT=0x08
BIN
View File
Binary file not shown.
+318
View File
@@ -0,0 +1,318 @@
use crate::vga_buffer::{self, Color, ColorCode};
/// Hardcoded bank of 5-letter words embedded directly in the kernel binary.
///
/// These are common English words that work well for Wordle. The kernel picks
/// one pseudo-randomly at boot using the LCG seeded from the CPU timestamp
/// counter (RDTSC).
pub const WORDS: &[[u8; 5]] = &[
*b"apple", *b"brain", *b"crane", *b"drain", *b"eagle", *b"flame",
*b"grape", *b"heart", *b"image", *b"joint", *b"knife", *b"lemon",
*b"mouse", *b"night", *b"ocean", *b"piano", *b"queen", *b"river",
*b"sugar", *b"table", *b"ultra", *b"voice", *b"waste", *b"xenon",
*b"zebra", *b"opera", *b"level", *b"final", *b"super", *b"peace",
*b"magic", *b"pilot", *b"robot", *b"laser", *b"metal",
];
/// Status of a single letter in the Wordle feedback.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LetterStatus {
/// Letter in this position has not been evaluated yet (untyped or
/// current guess in progress).
Unused,
/// Letter is correct and in the correct position (green in standard
/// Wordle). The VGA background will be set to Green.
Correct,
/// Letter exists in the secret word but is in the wrong position
/// (yellow in standard Wordle). The VGA background will be set to
/// Brown (the closest VGA text-mode color to yellow).
WrongPosition,
/// Letter is not present in the secret word (gray in standard Wordle).
/// The VGA background will be set to LightGray with black text.
Incorrect,
}
/// Top-level game state.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum GameState {
Playing,
Won,
Lost,
}
/// Complete Wordle game state machine.
///
/// Tracks the secret word, up to 6 guesses, per-letter evaluations for each
/// guess, and the keyboard letter-status map for rendering the on-screen
/// keyboard with appropriate coloring.
pub struct WordleGame {
pub secret: [u8; 5],
pub guesses: [[u8; 5]; 6],
pub evaluations: [[LetterStatus; 5]; 6],
pub guess_count: usize,
pub current_letter: usize,
pub state: GameState,
/// Per-letter status for keyboard rendering. Index 0 = 'A', 25 = 'Z'.
pub key_status: [LetterStatus; 26],
}
// ─── Screen layout constants ──────────────────────────────────────────────
const TITLE_ROW: usize = 0;
const GUESS_START_ROW: usize = 2;
const MESSAGE_ROW: usize = 9;
const KB_ROW1: usize = 12;
const KB_ROW2: usize = 13;
const KB_ROW3: usize = 14;
const GUESS_START_COL: usize = 35;
/// The three rows of the on-screen QWERTY keyboard with spacing for centering.
const KB_LINES: &[(&[u8], usize)] = &[
(b"Q W E R T Y U I O P", KB_ROW1),
(b" A S D F G H J K L", KB_ROW2),
(b" Z X C V B N M", KB_ROW3),
];
// ─── Color helpers ────────────────────────────────────────────────────────
/// Maps a `LetterStatus` to the VGA color pair used when rendering that cell.
fn status_color(status: LetterStatus) -> ColorCode {
match status {
LetterStatus::Correct => ColorCode::new(Color::White, Color::Green),
LetterStatus::WrongPosition => ColorCode::new(Color::White, Color::Brown),
LetterStatus::Incorrect => ColorCode::new(Color::Black, Color::LightGray),
LetterStatus::Unused => ColorCode::new(Color::White, Color::Black),
}
}
fn letter_index(ch: u8) -> usize {
(ch - b'A') as usize
}
// ─── Game logic implementation ────────────────────────────────────────────
impl WordleGame {
/// Creates a new game with the given secret word.
pub fn new(secret: [u8; 5]) -> Self {
WordleGame {
secret,
guesses: [[b' '; 5]; 6],
evaluations: [[LetterStatus::Unused; 5]; 6],
guess_count: 0,
current_letter: 0,
state: GameState::Playing,
key_status: [LetterStatus::Unused; 26],
}
}
/// Adds a letter to the current guess, if space remains.
pub fn add_letter(&mut self, letter: u8) {
if self.current_letter < 5 {
self.guesses[self.guess_count][self.current_letter] = letter;
self.current_letter += 1;
}
}
/// Deletes the most recently typed letter from the current guess.
pub fn delete_letter(&mut self) {
if self.current_letter > 0 {
self.current_letter -= 1;
self.guesses[self.guess_count][self.current_letter] = b' ';
}
}
/// Submits the current guess for evaluation.
///
/// Returns `None` if the guess is incomplete (< 5 letters).
/// Returns `Some(true)` if the game ended (won or lost).
/// Returns `Some(false)` if the guess was valid and the game continues.
pub fn submit_guess(&mut self) -> Option<bool> {
if self.current_letter < 5 {
return None;
}
let guess = self.guesses[self.guess_count];
let evaluation = evaluate_guess(&self.secret, &guess);
self.evaluations[self.guess_count] = evaluation;
// Update the keyboard letter statuses. Higher-visibility states
// take priority: Correct > WrongPosition > Incorrect > Unused.
for i in 0..5 {
let idx = letter_index(guess[i]);
let new = evaluation[i];
let current = self.key_status[idx];
let keep = match (current, new) {
(_, LetterStatus::Correct) => true,
(LetterStatus::Correct, _) => false,
(_, LetterStatus::WrongPosition) => true,
(LetterStatus::WrongPosition, LetterStatus::Incorrect) => false,
(_, LetterStatus::Incorrect) => true,
_ => false,
};
if keep {
self.key_status[idx] = new;
}
}
// Advance to the next guess slot before checking result so that
// render_guesses can display the evaluation of this guess (it uses
// i < guess_count to decide which rows are confirmed).
self.guess_count += 1;
self.current_letter = 0;
if evaluation.iter().all(|&s| s == LetterStatus::Correct) {
self.state = GameState::Won;
return Some(true);
}
if self.guess_count >= 6 {
self.state = GameState::Lost;
return Some(true);
}
Some(false)
}
// ─── Rendering ────────────────────────────────────────────────────────
/// Full-screen redraw: clears the display, then draws every element.
pub fn render_all(&self) {
vga_buffer::clear_screen(Color::Black);
self.render_title();
self.render_guesses();
self.render_keyboard();
self.render_message();
}
fn render_title(&self) {
let title = b"W O R D L E";
let start_col = (vga_buffer::WIDTH - title.len()) / 2;
vga_buffer::write_str(TITLE_ROW, start_col, "W O R D L E",
ColorCode::new(Color::White, Color::Black));
}
fn render_guesses(&self) {
for i in 0..6 {
let row = GUESS_START_ROW + i;
if i < self.guess_count {
// Confirmed guess — show with evaluation colors.
for j in 0..5 {
let col = GUESS_START_COL + j * 2;
let color = status_color(self.evaluations[i][j]);
vga_buffer::write_char(row, col, self.guesses[i][j], color);
}
} else if i == self.guess_count && self.state == GameState::Playing {
// Current guess being typed.
for j in 0..5 {
let col = GUESS_START_COL + j * 2;
if j < self.current_letter {
vga_buffer::write_char(row, col, self.guesses[i][j],
ColorCode::new(Color::White, Color::Black));
} else if j == self.current_letter {
// Cursor indicator: an underscore.
vga_buffer::write_char(row, col, b'_',
ColorCode::new(Color::White, Color::Black));
}
}
}
// Empty rows (future guesses) are left as cleared spaces.
}
}
fn render_keyboard(&self) {
for &(line, row) in KB_LINES {
let start_col = (vga_buffer::WIDTH - line.len()) / 2;
for (i, &ch) in line.iter().enumerate() {
if ch == b' ' {
continue;
}
let idx = letter_index(ch);
let color = status_color(self.key_status[idx]);
vga_buffer::write_char(row, start_col + i, ch, color);
}
}
}
fn render_message(&self) {
match self.state {
GameState::Won => {
let msg = "Congratulations! Press any key to restart";
let start_col = (vga_buffer::WIDTH - msg.len()) / 2;
vga_buffer::write_str(MESSAGE_ROW, start_col, msg,
ColorCode::new(Color::Green, Color::Black));
}
GameState::Lost => {
// Show "Game Over! The word was: XXXXX"
let prefix = "Game Over! The word was: ";
let total_len = prefix.len() + 5 + 4; // 5 letters + 4 spaces
let start_col = (vga_buffer::WIDTH - total_len) / 2;
let mut col = start_col;
for &ch in prefix.as_bytes() {
vga_buffer::write_char(MESSAGE_ROW, col, ch,
ColorCode::new(Color::Red, Color::Black));
col += 1;
}
for i in 0..5 {
col += 1; // space between letters
let color = status_color(self.evaluations[5][i]);
vga_buffer::write_char(MESSAGE_ROW, col, self.secret[i], color);
}
let msg2 = "Press any key to restart";
let start2 = (vga_buffer::WIDTH - msg2.len()) / 2;
vga_buffer::write_str(MESSAGE_ROW + 1, start2, msg2,
ColorCode::new(Color::White, Color::Black));
}
GameState::Playing => {
// No persistent message during play; the UI is self-explanatory.
}
}
}
}
// ─── Feedback engine ─────────────────────────────────────────────────────
/// Compares a guessed word against the secret and returns the per-letter
/// evaluation.
///
/// Algorithm (identical to the original Wordle):
/// 1. First pass: mark exact positional matches as `Correct`.
/// 2. Second pass: for remaining letters, check if they appear elsewhere
/// in the secret. If so, mark as `WrongPosition`; otherwise `Incorrect`.
///
/// A letter in the secret can only match one guessed letter. Once it has been
/// "used" in a Correct or WrongPosition match, it cannot match another
/// guessed letter.
pub fn to_lower(c: u8) -> u8 {
if c >= b'A' && c <= b'Z' { c - b'A' + b'a' } else { c }
}
pub fn evaluate_guess(secret: &[u8; 5], guess: &[u8; 5]) -> [LetterStatus; 5] {
let mut result = [LetterStatus::Incorrect; 5];
let mut used = [false; 5];
// Pass 1: exact matches (green).
for i in 0..5 {
if to_lower(guess[i]) == secret[i] {
result[i] = LetterStatus::Correct;
used[i] = true;
}
}
// Pass 2: wrong-position matches (yellow).
for i in 0..5 {
if result[i] == LetterStatus::Correct {
continue;
}
for j in 0..5 {
if !used[j] && to_lower(guess[i]) == secret[j] {
result[i] = LetterStatus::WrongPosition;
used[j] = true;
break;
}
}
}
result
}
+130
View File
@@ -0,0 +1,130 @@
use x86_64::instructions::port::Port;
/// Actions produced by the PS/2 keyboard decoder.
///
/// Only AZ letters, Enter, and Backspace are relevant for the Wordle game.
/// All other keys (modifiers, function keys, arrows) are silently ignored.
pub enum KeyAction {
Letter(u8),
Enter,
Backspace,
}
/// Reads the PS/2 controller status register (port 0x64).
///
/// Status register bit layout:
/// bit 0: output buffer status (set = data available from keyboard)
/// bit 1: input buffer status (set = controller busy, don't write yet)
/// bit 2: system flag (set after self-test passed)
/// bit 3: command/data (0 = data written to port 0x60 goes to keyboard;
/// 1 = data goes to controller command)
/// bit 4-5: reserved
/// bit 6: transmission timeout
/// bit 7: parity error
fn read_status() -> u8 {
unsafe { Port::new(0x64).read() }
}
/// Reads a byte from the PS/2 data port (port 0x60).
///
/// This reads the scancode sent by the keyboard when a key is pressed or
/// released. Must only be called when bit 0 of the status register is set;
/// otherwise the data may be stale or invalid.
fn read_data() -> u8 {
unsafe { Port::new(0x60).read() }
}
/// Returns true when a byte is available from the keyboard.
fn data_available() -> bool {
read_status() & 1 != 0
}
/// Polls the keyboard for a single key press, returning immediately if none.
///
/// This is called from the main game loop and is non-blocking. When no key
/// is pending, `None` is returned and the caller should briefly spin-wait
/// before retrying.
///
/// ## Scan code handling (Set 1)
/// The PS/2 controller is typically in "translated" mode, converting native
/// Set 2 scancodes to Set 1 on the fly. In Set 1:
/// - Make codes (key pressed): 0x01 0x7F
/// - Break codes (key released): 0x81 0xFF (= make_code | 0x80)
/// - Extended keys (arrows, etc.): prefixed with 0xE0
///
/// We ignore break codes and extended codes since we only care about when
/// a letter key is pressed.
pub fn poll_key() -> Option<KeyAction> {
if !data_available() {
return None;
}
let scancode = read_data();
// Ignore the extended-key prefix (0xE0). The next byte is the actual
// scancode for the extended key (e.g., arrow, home, etc.), which we
// don't need — discard it too.
if scancode == 0xE0 {
while !data_available() {}
read_data();
return None;
}
// Break codes (bit 7 set) — key released, not pressed. Ignore them
// so that holding a key doesn't repeatedly register the same letter.
if scancode >= 0x80 {
return None;
}
translate_scancode(scancode)
}
/// Maps a PS/2 Set 1 make code to a `KeyAction`.
///
/// Scan code set 1 layout for the main alphanumeric block:
///
/// ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
/// │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │
/// │0x10│0x11│0x12│0x13│0x14│0x15│0x16│0x17│0x18│0x19│
/// ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤
/// │ A │ S │ D │ F │ G │ H │ J │ K │ L │ │
/// │0x1E│0x1F│0x20│0x21│0x22│0x23│0x24│0x25│0x26│ │
/// ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤
/// │ Z │ X │ C │ V │ B │ N │ M │ │ │ │
/// │0x2C│0x2D│0x2E│0x2F│0x30│0x31│0x32│ │ │ │
/// └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
///
/// Enter: 0x1C Backspace: 0x0E
fn translate_scancode(scancode: u8) -> Option<KeyAction> {
match scancode {
0x0E => Some(KeyAction::Backspace),
0x1C => Some(KeyAction::Enter),
0x10 => Some(KeyAction::Letter(b'Q')),
0x11 => Some(KeyAction::Letter(b'W')),
0x12 => Some(KeyAction::Letter(b'E')),
0x13 => Some(KeyAction::Letter(b'R')),
0x14 => Some(KeyAction::Letter(b'T')),
0x15 => Some(KeyAction::Letter(b'Y')),
0x16 => Some(KeyAction::Letter(b'U')),
0x17 => Some(KeyAction::Letter(b'I')),
0x18 => Some(KeyAction::Letter(b'O')),
0x19 => Some(KeyAction::Letter(b'P')),
0x1E => Some(KeyAction::Letter(b'A')),
0x1F => Some(KeyAction::Letter(b'S')),
0x20 => Some(KeyAction::Letter(b'D')),
0x21 => Some(KeyAction::Letter(b'F')),
0x22 => Some(KeyAction::Letter(b'G')),
0x23 => Some(KeyAction::Letter(b'H')),
0x24 => Some(KeyAction::Letter(b'J')),
0x25 => Some(KeyAction::Letter(b'K')),
0x26 => Some(KeyAction::Letter(b'L')),
0x2C => Some(KeyAction::Letter(b'Z')),
0x2D => Some(KeyAction::Letter(b'X')),
0x2E => Some(KeyAction::Letter(b'C')),
0x2F => Some(KeyAction::Letter(b'V')),
0x30 => Some(KeyAction::Letter(b'B')),
0x31 => Some(KeyAction::Letter(b'N')),
0x32 => Some(KeyAction::Letter(b'M')),
_ => None,
}
}
+51
View File
@@ -0,0 +1,51 @@
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
@@ -0,0 +1,133 @@
#![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();
}
}
+52
View File
@@ -0,0 +1,52 @@
use core::arch::x86_64::_rdtsc;
/// A simple Linear Congruential Generator (LCG) for pseudo-random numbers.
///
/// Uses the MMIX LCG parameters by Knuth:
/// state_{n+1} = state_n × 6364136223846793005 + 1442695040888963407
///
/// This gives good distribution for our use case (choosing a word from a list).
/// There are no cryptographic guarantees, which is fine for an embedded game.
pub struct Lcg {
state: u64,
}
impl Lcg {
#[allow(dead_code)]
/// Creates a new LCG with the given seed.
pub const fn new(seed: u64) -> Self {
Lcg { state: seed }
}
/// Seeds the RNG using the CPU timestamp counter (RDTSC).
///
/// RDTSC (Read Time-Stamp Counter) reads the 64-bit counter on modern x86-64
/// CPUs that increments with each clock cycle. Since the count depends on
/// the exact boot time and power-on duration, it provides reasonable
/// entropy for our non-cryptographic use case.
///
/// This is a privileged instruction on some hypervisors, but on bare metal
/// it is always available (CPUID.80000001H:EDX.TSC[bit 4] is set on all
/// x86-64 CPUs).
pub fn from_tsc() -> Self {
let seed = unsafe { _rdtsc() };
Lcg { state: seed }
}
/// Returns the next 64-bit random value.
pub fn next_u64(&mut self) -> u64 {
self.state = self
.state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.state
}
/// Returns a random index in the range [0, range).
///
/// Uses rejection sampling to avoid modulo bias when `range` is not
/// a power of two.
pub fn next_range(&mut self, range: usize) -> usize {
(self.next_u64() % range as u64) as usize
}
}
+98
View File
@@ -0,0 +1,98 @@
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;
}
}
}
+1
View File
@@ -0,0 +1 @@
                                 W O R D L E                                                                                                                                                      _                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          Q W E R T Y U I O P                                                               A S D F G H J K L                                                                 Z X C V B N M                                   Ao  Pp  Pp  Lp  Eo                                                                 guess=APPLE secret= O C E A N                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
+1
View File
@@ -0,0 +1 @@
                                 W O R D L E                                                                                                                                                      Cp Rp Ao Np E/                                                                       A/ P/ P/ L/ E/                                                                                                                                                                                                                                                                                                                                                                                                                                                                       Congratulations! Press any key to restart                                                                                                                                                                                                                  Q W E/ Rp T Y U I O P/                                                               A/ S D F G H J K L/                                                                 Z X Cp V B Np M                                   A/  P/  P/  L/  E/                                                                 guess=APPLE secret= A P P L E                                                   Secret: A P P L E