Replace mermaid diagrams with PNG renders
Browse files- .gitattributes +2 -0
- README.md +227 -276
- diagram_body.png +3 -0
- diagram_structure.png +0 -0
- diagram_wrapper.png +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
diagram_body.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
diagram_wrapper.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,276 +1,227 @@
|
|
| 1 |
-
---
|
| 2 |
-
license: mit
|
| 3 |
-
tags:
|
| 4 |
-
- onnx
|
| 5 |
-
- emulator
|
| 6 |
-
- chip-8
|
| 7 |
-
- retro-computing
|
| 8 |
-
- computation
|
| 9 |
-
- not-machine-learning
|
| 10 |
-
pipeline_tag: other
|
| 11 |
-
library_name: onnxruntime
|
| 12 |
-
---
|
| 13 |
-
|
| 14 |
-
# CHIP-8 in ONNX
|
| 15 |
-
|
| 16 |
-
A complete CHIP-8 emulator implemented as a pure ONNX computation graph.
|
| 17 |
-
No custom operators, no execution-provider extensions, no Python in the
|
| 18 |
-
hot loop β the entire CPU lives inside the model. Standard
|
| 19 |
-
[ONNX Runtime](https://onnxruntime.ai/) 1.26 CPU EP runs it unmodified.
|
| 20 |
-
|
| 21 |
-
This is not a machine-learning model. There are no weights, no training,
|
| 22 |
-
no inference in the statistical sense. It is a CPU expressed as a
|
| 23 |
-
computation graph, because it turns out ONNX has all the primitives a
|
| 24 |
-
CPU needs: bitwise ops, indexed memory access, conditional dispatch, and
|
| 25 |
-
a `Loop` operator that's Turing-complete with the rest of the op set.
|
| 26 |
-
|
| 27 |
-

|
| 28 |
-
|
| 29 |
-
The image above is **the literal output of `Run()`** on `chip8_snake_demo.onnx`
|
| 30 |
-
β a `uint8[90, 32, 64]` tensor returned in one call, with no inputs.
|
| 31 |
-
|
| 32 |
-
## Two models, one CPU
|
| 33 |
-
|
| 34 |
-
| File | Inputs | Outputs | Notes |
|
| 35 |
-
|---|---|---|---|
|
| 36 |
-
| `chip8_cpu.onnx` | RAM + register state + key state + trip count | Updated RAM + register state | Load any CHIP-8 ROM into RAM, call once per game tick |
|
| 37 |
-
| `chip8_snake_demo.onnx` | *(none β fully baked)* | `uint8[90, 32, 64]` frame stack | Single `Run()` returns a 90-frame movie of the Snake title screen |
|
| 38 |
-
|
| 39 |
-
The two models share the same inner CPU. The demo wraps that CPU in an
|
| 40 |
-
outer `Loop` whose body executes 30 instructions per frame and whose
|
| 41 |
-
**scan output** is the framebuffer β that's how ONNX naturally accumulates
|
| 42 |
-
"one frame per outer iteration" into a single tensor.
|
| 43 |
-
|
| 44 |
-
## How it works
|
| 45 |
-
|
| 46 |
-
### State
|
| 47 |
-
|
| 48 |
-
CHIP-8 has 4 KB of RAM, sixteen 8-bit registers, a 12-bit program counter,
|
| 49 |
-
a 12-bit index register, a tiny stack, two 8-bit timers, and a 64Γ32
|
| 50 |
-
monochrome display. All of it lives in three tensors that flow through the
|
| 51 |
-
`Loop` as carried dependencies:
|
| 52 |
-
|
| 53 |
-
| Tensor | Shape | Dtype | Holds |
|
| 54 |
-
|---|---|---|---|
|
| 55 |
-
| `regs` | `[40]` | `int32` | V0..VF, I, PC, SP, DT, ST, RNG seed, tick counter, stack[16] |
|
| 56 |
-
| `ram` | `[4096]` | `uint8` | Program code, font, sprite data, working memory |
|
| 57 |
-
| `display` | `[2048]` | `uint8` | 64Γ32 framebuffer, one byte per pixel |
|
| 58 |
-
|
| 59 |
-
### The Loop body β one CHIP-8 instruction per iteration
|
| 60 |
-
|
| 61 |
-
Each iteration of the inner `Loop` fetches, decodes, and executes one
|
| 62 |
-
CHIP-8 instruction.
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
#
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
*
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
*"run a program for N steps, return one tensor per step"* in a single
|
| 229 |
-
`Run()` call.
|
| 230 |
-
* Provide a tiny, complete, self-contained reference for anyone who wants
|
| 231 |
-
to do non-ML things with ONNX.
|
| 232 |
-
|
| 233 |
-
If you want to play CHIP-8 games, there are a hundred better emulators.
|
| 234 |
-
If you want to see what happens when you treat ONNX as a programming
|
| 235 |
-
language, you're in the right place.
|
| 236 |
-
|
| 237 |
-
## Performance
|
| 238 |
-
|
| 239 |
-
Measured on a Windows ARM64 laptop with ONNX Runtime 1.26 CPU EP, opset 21:
|
| 240 |
-
|
| 241 |
-
| Workload | Throughput |
|
| 242 |
-
|---|---|
|
| 243 |
-
| CHIP-8 instructions per second | ~2,500 |
|
| 244 |
-
| Full snake-title demo (90 frames Γ 30 ipf = 2,700 instructions) | ~1.1 s |
|
| 245 |
-
| Inner Loop body | ~600 ONNX nodes |
|
| 246 |
-
| Generic CPU model file size | ~40 KB |
|
| 247 |
-
| Snake demo model file size | ~48 KB (includes ROM) |
|
| 248 |
-
|
| 249 |
-
This is plenty fast for CHIP-8 β most CHIP-8 games target 500β1000 Hz CPU
|
| 250 |
-
and the model handily exceeds that. ONNX-as-a-CPU is not, however, going
|
| 251 |
-
to be competitive with anything that wants to run a real-time emulator
|
| 252 |
-
properly; per-node overhead in `Loop` bodies dominates everything.
|
| 253 |
-
|
| 254 |
-
## What's inside the box
|
| 255 |
-
|
| 256 |
-
```
|
| 257 |
-
.
|
| 258 |
-
βββ chip8_cpu.onnx # Generic CHIP-8 CPU (40 KB)
|
| 259 |
-
βββ chip8_snake_demo.onnx # Self-contained Snake-title movie (48 KB)
|
| 260 |
-
βββ snake.ch8 # Public-domain Snake ROM (1.4 KB)
|
| 261 |
-
βββ example_output.gif # What you get when you Run() the demo
|
| 262 |
-
βββ README.md # This file
|
| 263 |
-
```
|
| 264 |
-
|
| 265 |
-
## License
|
| 266 |
-
|
| 267 |
-
* Code & model files: **MIT**.
|
| 268 |
-
* Bundled `snake.ch8` ROM: **CC0** (from
|
| 269 |
-
[JohnEarnest/chip8Archive](https://github.com/JohnEarnest/chip8Archive)).
|
| 270 |
-
|
| 271 |
-
## Credits
|
| 272 |
-
|
| 273 |
-
* CHIP-8 was created by Joseph Weisbecker in 1977 for the COSMAC VIP.
|
| 274 |
-
* `snake.ch8` is by John Earnest, CC0.
|
| 275 |
-
* Built with the standard ONNX op set (opset 21) and tested with
|
| 276 |
-
ONNX Runtime 1.26.
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- onnx
|
| 5 |
+
- emulator
|
| 6 |
+
- chip-8
|
| 7 |
+
- retro-computing
|
| 8 |
+
- computation
|
| 9 |
+
- not-machine-learning
|
| 10 |
+
pipeline_tag: other
|
| 11 |
+
library_name: onnxruntime
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# CHIP-8 in ONNX
|
| 15 |
+
|
| 16 |
+
A complete CHIP-8 emulator implemented as a pure ONNX computation graph.
|
| 17 |
+
No custom operators, no execution-provider extensions, no Python in the
|
| 18 |
+
hot loop β the entire CPU lives inside the model. Standard
|
| 19 |
+
[ONNX Runtime](https://onnxruntime.ai/) 1.26 CPU EP runs it unmodified.
|
| 20 |
+
|
| 21 |
+
This is not a machine-learning model. There are no weights, no training,
|
| 22 |
+
no inference in the statistical sense. It is a CPU expressed as a
|
| 23 |
+
computation graph, because it turns out ONNX has all the primitives a
|
| 24 |
+
CPU needs: bitwise ops, indexed memory access, conditional dispatch, and
|
| 25 |
+
a `Loop` operator that's Turing-complete with the rest of the op set.
|
| 26 |
+
|
| 27 |
+

|
| 28 |
+
|
| 29 |
+
The image above is **the literal output of `Run()`** on `chip8_snake_demo.onnx`
|
| 30 |
+
β a `uint8[90, 32, 64]` tensor returned in one call, with no inputs.
|
| 31 |
+
|
| 32 |
+
## Two models, one CPU
|
| 33 |
+
|
| 34 |
+
| File | Inputs | Outputs | Notes |
|
| 35 |
+
|---|---|---|---|
|
| 36 |
+
| `chip8_cpu.onnx` | RAM + register state + key state + trip count | Updated RAM + register state | Load any CHIP-8 ROM into RAM, call once per game tick |
|
| 37 |
+
| `chip8_snake_demo.onnx` | *(none β fully baked)* | `uint8[90, 32, 64]` frame stack | Single `Run()` returns a 90-frame movie of the Snake title screen |
|
| 38 |
+
|
| 39 |
+
The two models share the same inner CPU. The demo wraps that CPU in an
|
| 40 |
+
outer `Loop` whose body executes 30 instructions per frame and whose
|
| 41 |
+
**scan output** is the framebuffer β that's how ONNX naturally accumulates
|
| 42 |
+
"one frame per outer iteration" into a single tensor.
|
| 43 |
+
|
| 44 |
+
## How it works
|
| 45 |
+
|
| 46 |
+
### State
|
| 47 |
+
|
| 48 |
+
CHIP-8 has 4 KB of RAM, sixteen 8-bit registers, a 12-bit program counter,
|
| 49 |
+
a 12-bit index register, a tiny stack, two 8-bit timers, and a 64Γ32
|
| 50 |
+
monochrome display. All of it lives in three tensors that flow through the
|
| 51 |
+
`Loop` as carried dependencies:
|
| 52 |
+
|
| 53 |
+
| Tensor | Shape | Dtype | Holds |
|
| 54 |
+
|---|---|---|---|
|
| 55 |
+
| `regs` | `[40]` | `int32` | V0..VF, I, PC, SP, DT, ST, RNG seed, tick counter, stack[16] |
|
| 56 |
+
| `ram` | `[4096]` | `uint8` | Program code, font, sprite data, working memory |
|
| 57 |
+
| `display` | `[2048]` | `uint8` | 64Γ32 framebuffer, one byte per pixel |
|
| 58 |
+
|
| 59 |
+
### The Loop body β one CHIP-8 instruction per iteration
|
| 60 |
+
|
| 61 |
+
Each iteration of the inner `Loop` fetches, decodes, and executes one
|
| 62 |
+
CHIP-8 instruction.
|
| 63 |
+
|
| 64 |
+

|
| 65 |
+
|
| 66 |
+
The dispatch is **branchless**: every opcode subgraph runs every iteration,
|
| 67 |
+
and a chain of `Where` ops at the end picks the one whose pattern matches.
|
| 68 |
+
This trades wasted work for a flat, regular graph that's much easier to
|
| 69 |
+
read than a 35-deep nested `If` ladder β and it doesn't actually cost more
|
| 70 |
+
in practice, because the per-node overhead of ONNX Runtime's `Loop` is the
|
| 71 |
+
dominant cost anyway.
|
| 72 |
+
|
| 73 |
+
### The outer structure β wrapping the CPU into a movie
|
| 74 |
+
|
| 75 |
+

|
| 76 |
+
|
| 77 |
+
`Loop` in ONNX has two output kinds:
|
| 78 |
+
|
| 79 |
+
* **Carried outputs** β values threaded between iterations (here: `regs`,
|
| 80 |
+
`ram`, `display`).
|
| 81 |
+
* **Scan outputs** β values *emitted per iteration* and concatenated along
|
| 82 |
+
a new leading axis (here: the framebuffer).
|
| 83 |
+
|
| 84 |
+
The movie model exploits scan outputs: one outer iteration = one frame
|
| 85 |
+
emitted = one row of the final `frames` tensor. There is no Python loop
|
| 86 |
+
anywhere in this pipeline; the entire 90-frame animation is produced
|
| 87 |
+
inside a single `sess.run()` call.
|
| 88 |
+
|
| 89 |
+
### What's in the model file
|
| 90 |
+
|
| 91 |
+
A `Loop` operator wrapping a single `GraphProto` body. The body has
|
| 92 |
+
~600 nodes β mostly `Gather`, `ScatterND`, `BitShift`, `BitwiseAnd`,
|
| 93 |
+
`Equal`, and `Where`. No node is a custom op. The whole `chip8_cpu.onnx`
|
| 94 |
+
file is ~40 KB.
|
| 95 |
+
|
| 96 |
+

|
| 97 |
+
|
| 98 |
+
## Usage
|
| 99 |
+
|
| 100 |
+
### Run the bundled demo
|
| 101 |
+
|
| 102 |
+
```python
|
| 103 |
+
import onnxruntime as ort
|
| 104 |
+
import numpy as np
|
| 105 |
+
from PIL import Image
|
| 106 |
+
|
| 107 |
+
sess = ort.InferenceSession("chip8_snake_demo.onnx",
|
| 108 |
+
providers=["CPUExecutionProvider"])
|
| 109 |
+
frames, = sess.run(None, {}) # no inputs!
|
| 110 |
+
|
| 111 |
+
print(frames.shape, frames.dtype)
|
| 112 |
+
# (90, 32, 64) uint8
|
| 113 |
+
|
| 114 |
+
# Save the final frame
|
| 115 |
+
final = (frames[-1] > 0).astype(np.uint8) * 255
|
| 116 |
+
Image.fromarray(final, mode="L").resize((512, 256)).save("snake_frame.png")
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
That's the entire usage. No tokenizer, no preprocessing, no postprocessing
|
| 120 |
+
β `Run()` returns pixels.
|
| 121 |
+
|
| 122 |
+
### Load any CHIP-8 ROM into the generic CPU
|
| 123 |
+
|
| 124 |
+
```python
|
| 125 |
+
import onnxruntime as ort
|
| 126 |
+
import numpy as np
|
| 127 |
+
|
| 128 |
+
sess = ort.InferenceSession("chip8_cpu.onnx",
|
| 129 |
+
providers=["CPUExecutionProvider"])
|
| 130 |
+
|
| 131 |
+
# Initial state
|
| 132 |
+
def initial_ram(rom: bytes) -> np.ndarray:
|
| 133 |
+
FONT = bytes.fromhex("F0909090F02060202070F010F080F0F010F010F0"
|
| 134 |
+
"9090F01010F080F010F0F080F090F0F010204040"
|
| 135 |
+
"F090F090F0F090F010F0F090F09090E090E090E0"
|
| 136 |
+
"F0808080F0E0909090E0F080F080F0F080F08080")
|
| 137 |
+
ram = np.zeros(4096, dtype=np.uint8)
|
| 138 |
+
ram[0x50:0x50+80] = np.frombuffer(FONT, dtype=np.uint8)
|
| 139 |
+
ram[0x200:0x200+len(rom)] = np.frombuffer(rom, dtype=np.uint8)
|
| 140 |
+
return ram
|
| 141 |
+
|
| 142 |
+
regs = np.zeros(40, dtype=np.int32)
|
| 143 |
+
regs[17] = 0x200 # PC
|
| 144 |
+
regs[21] = 0xAB # RNG seed
|
| 145 |
+
ram = initial_ram(open("snake.ch8", "rb").read())
|
| 146 |
+
display = np.zeros(2048, dtype=np.uint8)
|
| 147 |
+
keys = np.zeros(16, dtype=np.uint8)
|
| 148 |
+
|
| 149 |
+
# Run 30 CHIP-8 instructions per tick
|
| 150 |
+
for tick in range(60):
|
| 151 |
+
regs, ram, display = sess.run(None, {
|
| 152 |
+
"regs_in": regs,
|
| 153 |
+
"ram_in": ram,
|
| 154 |
+
"display_in": display,
|
| 155 |
+
"keys": keys,
|
| 156 |
+
"trip_count": np.array(30, dtype=np.int64),
|
| 157 |
+
})
|
| 158 |
+
|
| 159 |
+
# `display` is now a uint8[2048] framebuffer β reshape to (32, 64) to view.
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
A bundled ROM (`snake.ch8`, public domain) is included so you can try this
|
| 163 |
+
straight away.
|
| 164 |
+
|
| 165 |
+
## Why this exists
|
| 166 |
+
|
| 167 |
+
It's a question about what ONNX *is*. The ONNX operator set, once it grew
|
| 168 |
+
`Loop`, `If`, the `Bitwise*` family (opset 18) and `ScatterND` with
|
| 169 |
+
reduction modes, became Turing-complete in any reasonable sense of the
|
| 170 |
+
phrase. This model demonstrates the consequence: ONNX Runtime, designed
|
| 171 |
+
for evaluating neural networks, can also evaluate **arbitrary
|
| 172 |
+
computations** β including a working game console β without modification.
|
| 173 |
+
|
| 174 |
+
Concretely the project exists to:
|
| 175 |
+
|
| 176 |
+
* Probe how far the standard ONNX op set actually goes as a general
|
| 177 |
+
computation target.
|
| 178 |
+
* Demonstrate that `Loop` + `Scan output` give you a clean way to express
|
| 179 |
+
*"run a program for N steps, return one tensor per step"* in a single
|
| 180 |
+
`Run()` call.
|
| 181 |
+
* Provide a tiny, complete, self-contained reference for anyone who wants
|
| 182 |
+
to do non-ML things with ONNX.
|
| 183 |
+
|
| 184 |
+
If you want to play CHIP-8 games, there are a hundred better emulators.
|
| 185 |
+
If you want to see what happens when you treat ONNX as a programming
|
| 186 |
+
language, you're in the right place.
|
| 187 |
+
|
| 188 |
+
## Performance
|
| 189 |
+
|
| 190 |
+
Measured on a Windows ARM64 laptop with ONNX Runtime 1.26 CPU EP, opset 21:
|
| 191 |
+
|
| 192 |
+
| Workload | Throughput |
|
| 193 |
+
|---|---|
|
| 194 |
+
| CHIP-8 instructions per second | ~2,500 |
|
| 195 |
+
| Full snake-title demo (90 frames Γ 30 ipf = 2,700 instructions) | ~1.1 s |
|
| 196 |
+
| Inner Loop body | ~600 ONNX nodes |
|
| 197 |
+
| Generic CPU model file size | ~40 KB |
|
| 198 |
+
| Snake demo model file size | ~48 KB (includes ROM) |
|
| 199 |
+
|
| 200 |
+
This is plenty fast for CHIP-8 β most CHIP-8 games target 500β1000 Hz CPU
|
| 201 |
+
and the model handily exceeds that. ONNX-as-a-CPU is not, however, going
|
| 202 |
+
to be competitive with anything that wants to run a real-time emulator
|
| 203 |
+
properly; per-node overhead in `Loop` bodies dominates everything.
|
| 204 |
+
|
| 205 |
+
## What's inside the box
|
| 206 |
+
|
| 207 |
+
```
|
| 208 |
+
.
|
| 209 |
+
βββ chip8_cpu.onnx # Generic CHIP-8 CPU (40 KB)
|
| 210 |
+
βββ chip8_snake_demo.onnx # Self-contained Snake-title movie (48 KB)
|
| 211 |
+
βββ snake.ch8 # Public-domain Snake ROM (1.4 KB)
|
| 212 |
+
βββ example_output.gif # What you get when you Run() the demo
|
| 213 |
+
βββ README.md # This file
|
| 214 |
+
```
|
| 215 |
+
|
| 216 |
+
## License
|
| 217 |
+
|
| 218 |
+
* Code & model files: **MIT**.
|
| 219 |
+
* Bundled `snake.ch8` ROM: **CC0** (from
|
| 220 |
+
[JohnEarnest/chip8Archive](https://github.com/JohnEarnest/chip8Archive)).
|
| 221 |
+
|
| 222 |
+
## Credits
|
| 223 |
+
|
| 224 |
+
* CHIP-8 was created by Joseph Weisbecker in 1977 for the COSMAC VIP.
|
| 225 |
+
* `snake.ch8` is by John Earnest, CC0.
|
| 226 |
+
* Built with the standard ONNX op set (opset 21) and tested with
|
| 227 |
+
ONNX Runtime 1.26.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
diagram_body.png
ADDED
|
Git LFS Details
|
diagram_structure.png
ADDED
|
diagram_wrapper.png
ADDED
|
Git LFS Details
|