1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-11 10:10:49 +00:00
nescript/examples/sha256/entering_state.ne

127 lines
4.1 KiB
Text
Raw Normal View History

examples/sha256: interactive SHA-256 hasher with on-screen keyboard An end-to-end FIPS 180-4 SHA-256 hasher running entirely on the NES. The player types up to 16 ASCII characters on a 5x8 on-screen keyboard, presses Enter, and the program computes and displays the 64-character hex digest. Layout (`examples/sha256/*.ne`): constants.ne layout + K[64] / H_INIT[8] tables (declared as `var` with init_array because the v0.1 compiler treats `const u8[N] = [...]` as a no-op — noted in the file) assets.ne 44-tile Tileset (A..Z, 0..9, punctuation, special keys, cursor) shared between BG and sprite layers background.ne static nametable (title, labels, keyboard grid) painted at reset state.ne globals sha_core.ne 32-bit byte primitives (copy, xor, and, add, not, rotr, shr) in inline asm + sigma/Sigma mixers + schedule/round steps + fold render.ne OAM helpers for cursor, input buffer, and 64-nibble digest keyboard.ne key dispatch table entering_state.ne cursor navigation + typing + auto-demo computing_state.ne phased driver (48 schedule steps + 64 rounds + fold across ~30 frames at 4 iterations each) showing_state.ne renders the 256-bit digest as 8 rows of 8 sprite glyphs Implementation notes: - All 32-bit words live as 4 little-endian bytes in `wk[64]`, `w[256]`, `h_state[32]` so every primitive walks four bytes with `LDA {arr},X`/`STA {arr},X` chains and, for adds, a carry chain. - Every primitive reads its parameters straight out of the transport slots `$04`/`$05` rather than `{dst}`/`{src}` substitutions: the inline-asm resolver looks parameters up in the analyzer's allocation table but the codegen spills them to a different per-function RAM slot, so `{dst}` would resolve to a ZP slot nothing ever writes to. Bypassing the substitution entirely sidesteps the issue without a compiler change. - Rotate-right by any amount is a byte-rotate loop plus a bit- rotate loop so the 10 SHA amounts (2, 6, 7, 11, 13, 17, 18, 19, 22, 25) all compile to a handful of chained `ROR`s. - The headless jsnes golden auto-types "NES" after 1 s of idle and captures its SHA-256 digest AE9145DB5CABC41FE34B54E34AF8881F462362EA20FD8F861B26532FFBB84E0D — byte-identical to `shasum` / `hashlib.sha256(b"NES")`. Build: `cargo run --release -- build examples/sha256.ne` https://claude.ai/code/session_01FRmSBruVWCufm3LsUVMs8v
2026-04-16 14:02:58 +00:00
// sha256/entering_state.ne — the interactive typing state.
//
// Reads controller 1 each frame, moves the keyboard cursor on
// D-pad presses, types a character on A, deletes a character on
// B, and starts compression on START (or when the cursor is
// over the ↵ key and A is pressed). SELECT clears the buffer at
// any time so the user can start over.
//
// If the user doesn't press anything in the first AUTO_DELAY
// frames, the handler auto-types DEMO_TEXT ("NES") and
// transitions to Computing so the jsnes golden always captures
// a fully-rendered hash.
state Entering {
on enter {
kb_row = 0
kb_col = 0
idle_timer = 0
debounce = 0
blink_timer = 0
}
on frame {
// ── Debounce + blink tick ───────────────────────
if debounce > 0 {
debounce -= 1
}
blink_timer += 1
if blink_timer >= 40 {
blink_timer = 0
}
// ── D-pad navigation ────────────────────────────
var pressed: u8 = 0
if debounce == 0 {
if button.left {
if kb_col > 0 {
kb_col -= 1
} else {
kb_col = KB_COLS - 1
}
debounce = 8
pressed = 1
} else if button.right {
kb_col += 1
if kb_col >= KB_COLS {
kb_col = 0
}
debounce = 8
pressed = 1
} else if button.up {
if kb_row > 0 {
kb_row -= 1
} else {
kb_row = KB_ROWS - 1
}
debounce = 8
pressed = 1
} else if button.down {
kb_row += 1
if kb_row >= KB_ROWS {
kb_row = 0
}
debounce = 8
pressed = 1
} else if button.a {
// Press the key under the cursor. Backspace
// and enter dispatch to their actions; any
// other character is appended to `msg`.
var ch: u8 = current_key()
if ch == KEY_BKSP {
input_backspace()
} else if ch == KEY_ENTER {
if msg_len > 0 {
transition Computing
}
} else {
input_append(ch)
}
debounce = 10
pressed = 1
} else if button.b {
input_backspace()
debounce = 10
pressed = 1
} else if button.start {
if msg_len > 0 {
transition Computing
}
debounce = 10
pressed = 1
} else if button.select {
input_clear()
debounce = 10
pressed = 1
}
}
// ── Idle / auto-demo ────────────────────────────
if pressed == 1 {
idle_timer = 0
} else {
if idle_timer < 255 {
idle_timer += 1
}
if idle_timer == AUTO_DELAY {
if msg_len == 0 {
// Auto-type "NES" → SHA-256 digest visible
// in the headless jsnes golden.
input_append(0x4E) // N
input_append(0x45) // E
input_append(0x53) // S
transition Computing
}
}
}
// ── Render ──────────────────────────────────────
// Input buffer takes 16 sprites, cursor 1 (when on).
// Well under the 64-sprite OAM budget.
draw_input()
if blink_timer < 25 {
draw_cursor(1)
}
}
}