| dist | ||
| examples | ||
| lessons | ||
| resources | ||
| runtime-server | ||
| scenarios | ||
| scripts | ||
| src | ||
| test | ||
| .dockerignore | ||
| .gitignore | ||
| Dockerfile | ||
| favicon.ico | ||
| index.html | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| VERSION | ||
IA64.CC
IA64.CC is a web x86 assembly learning environment with an MS-DOS style interface.
It includes an assembler, a debugger, scenario-based lessons, register and flag views, memory explorers, breakpoints, stack display, and a VGA text screen. The emulator uses Unicorn, Keystone, and Capstone in the browser.
Requirements
- Node.js with npm
- Python 3 for the local static server
- Chrome or Chromium for smoke tests
Install
make install
Build And Check
make check
make test
make smoke
make check builds the generated browser files and runs TypeScript and syntax checks.
Run Locally
make serve
Open:
http://localhost:8000/index.html
make serve stops any existing process on the selected port before starting the server. Use PORT=8080 make serve to choose another port. The development server disables HTTP caching so rebuilt files are visible after a browser reload.
Scenarios
Scenario files live in scenarios/ as .scenario.json files. A scenario contains:
- codemap data
- title, description, icon, difficulty, and victory message
- initial register state
- tutorial steps and goals
The build creates a generated dist/scenarios.js manifest from the files in scenarios/.
CodeMap Runtime Contract
The browser keeps an authoring CodeMap for editing and debugging. In that file:
srcat the top level is optional source metadata used to reopen the full assembler document.zones[n].srcis optional per-zone source metadata used by the debugger, labels and symbol views.zones[n].kindis authoring/UI metadata:codezones appear in the debugger Code menu, whiledataandnoinitzones appear in the Data menu for Memory1 and Memory2.zones[n].asm,zones[n].address,zones[n].codeandentryare the executable runtime data.
For a remote runtime, source fields are not authoritative. Before hashing or sending a program to a VM server, use the runtime projection from CodemapCore.toRuntimeCodemap() or CodemapCore.runtimeCodemapJson(). That projection strips src, zones[n].src, names and UI-only fields, then keeps only the stable executable payload:
- format/version
- target architecture
- entrypoint
- zones with address, code mode and base64 machine bytes
IA64.CC directives split that intent in source too: .code 16/32/64 marks an executable zone, .data 16/32/64 marks a loaded data zone, .noinit 16/32/64 marks a work/result zone, and offset @title resolves any loaded zone by title.
Hybrid Runtime Server
The project now includes a first Rust backend scaffold in runtime-server/.
The browser still defaults to full-client execution. Hybrid mode is opt-in and
uses IA64RuntimeClient to talk to http://localhost:8080.
The mode, endpoint, log level, and text-screen transport can be selected from
the debugger menu with Runtime > Configuration; the choice is stored in
browser local storage.
Start the backend:
make runtime-up
Select the runtime implementation:
make runtime-up
make runtime-up RUNTIME_ENGINE=unicorn
make runtime-up RUNTIME_ENGINE=mock
unicorn is the default server runtime. It creates one Unicorn x86 VM instance
per session, so multiple sessions can execute independently with isolated CPU
and memory state. mock only advances the current address and exists to test the
HTTP/WebSocket protocol. If a runtime queue itself becomes unavailable, the
server returns 503 RUNTIME_UNAVAILABLE.
Run the backend unit tests without installing Rust locally:
make runtime-test
Useful client-side calls from the browser console:
IA64RuntimeClient.setRuntimeConfig({ mode: 'hybrid' })
IA64RuntimeClient.health()
The server accepts a standard ia64.codemap, validates it, strips authoring
metadata into ia64.runtime-codemap, computes a stable hash, stores the runtime
payload by hash, and creates lightweight sessions. The current execution engine
is Unicorn by default; the mock runtime remains available for protocol tests.
The backend runtime is now hidden behind a small VmRuntime interface. The
server can select UnicornRuntime or MockRuntime with IA64_RUNTIME_ENGINE,
while the HTTP routes already depend on the interface rather than on one
specific implementation.
In hybrid mode the debugger can store CodeMaps on the Rust runtime, create
sessions, and exchange bounded step/run commands with the server runtime.
Status logs show the remote hash, session id, snapshot current address, snapshot
version, and execution delta so the protocol can be verified.
After session creation, the debugger prefers the runtime WebSocket for
step/run mirroring and falls back to HTTP if the socket is unavailable.
Remote snapshots are mirrored in the debugger Status window and the remote
instruction pointer is highlighted in the Code window when it is visible.
Snapshots include mode-specific general registers, the instruction pointer,
stack pointer, flags, segment registers, VGA text rows, small memory windows,
and register deltas.
The debugger Runtime menu can stay in Mirror mode or switch to
Follow, which makes the local instruction pointer follow the remote snapshot
current address as an explicit preview mode.
Hybrid execution is intentionally sliced. The browser sends bounded step or
run commands instead of asking the backend to run forever. This keeps the UI
responsive, gives the runtime a chance to accept keyboard/timer events between
slices, and avoids long blocking Unicorn executions. The debugger sends the
current breakpoint list with every remote step, run, and key command; the
Unicorn runtime stops before executing a breakpoint address, while ignoring the
breakpoint already under the instruction pointer at the start of the command so
single-step remains usable.
The remote screen snapshot supports text rows plus attributes, 320x200 indexed graphics bytes, and compact WebP images. The debugger can render remote text mode either as DOM cells or as a WebP image. Graphics snapshots are sent as WebP when available. Keyboard IRQ1 is only injected when an explicit keyboard event is sent to the runtime; in the browser UI, physical keyboard events are forwarded only while the virtual keyboard overlay is open, which avoids accidental IRQs while using menus or shortcuts.
The server also exposes GET /api/sessions/{id}/vga.html?refresh=1000, which
can be embedded in an iframe to display a VGA snapshot with periodic refresh.
For streaming control, the server exposes GET /api/sessions/{id}/ws. The
socket sends an initial hello snapshot, then accepts JSON commands such as
{"command":"snapshot"}, {"command":"step","count":1},
{"command":"run","count":1000}, {"command":"key","key":"a"}, and
{"command":"ping"}. Responses use the same snapshot/delta payload as HTTP.
Repository Layout
src/: TypeScript sourcetest/: unit and browser smoke testsscenarios/: scenario JSON filesresources/: app imagesdist/: vendored browser libraries and generated build outputsscripts/build.mjs: build script
Generated files in dist/ are ignored where they are produced by this project. Vendored runtime libraries and assets remain versioned because the app is served as a static web application.