# newtris-newton-recomp **A static recompilation of *Newtris* (Apple Newton, 1993) — the first title brought up on [`newtonrecomp`](https://github.com/sp00nznet/newtonrecomp), and (as far as we can find) the first Newton application ever turned into a native executable.**

Newtris running as recompiled native C: a T-piece falling above a stack of blocks, drawn with the package's own 1-bit sprite sheet in the Newton LCD palette

Not an emulator and not a screenshot of one — this is the 1993 NewtonScript bytecode recompiled to C, running natively. The blocks are the package's own 16×16 sprite tiles; the falling T-piece is drawn by DrawPiece, the stack by viewDrawScript.

*Newtris* is Tetris for the MessagePad: blocks fall, you tap left or right of the field to move them, two circular-arrow buttons rotate, tapping the bottom drops fast, completed rows go poof. Shareware, $15, uploaded to AOL in November 1993 by BugByte. It is an ideal first recompilation target for a reason that has nothing to do with Tetris. A Newton application is not machine code — it is a graph of NewtonScript objects whose functions are bytecode for a 30-opcode stack VM. *Newtris* is **32 bytecode functions and 3.5 KB of bytecode, with zero native ARM**, yet it still touches every part of the runtime that matters: the view tree, the drawing model, an idle timer, pen input, and sound. This repository is the **reference title**: it pulls in the `newtonrecomp` toolkit as a submodule, adds the per-title host that runs the recompiled C, and builds a native executable. The reusable recompiler lives in [`newtonrecomp`](https://github.com/sp00nznet/newtonrecomp) — fork that to recompile a different Newton app. > **No package here.** `Newtris 1.4.pkg` is © 1993 BugByte Inc. and is **not** > committed, and neither is the C the recompiler derives from it. Supply your own > copy — sha1 `a12be07f177463fbddee04972f5be4eeacacec50`, as archived by > [UNNA](http://www.unna.org/). Everything in this repo is original work. ## Status — it plays **Newtris runs, in a window, with keyboard input.** All 32 functions are recovered with their original names, recompiled to C, and linked against the `newtonrecomp` runtime. **No interpreter is linked into the binary.** The falling piece, the rotation, the collision, the row clearing and the scoring are the 1993 bytecode, translated, running natively. Nothing about the game logic is stubbed. The host supplies the 15 ROM globals the game calls (`Array`, `Ticks`, `Random`, `RelBounds`, `DrawXBitmap`, `mod`, …) and answers the view messages it sends. Everything else is Newtris. ``` $ newtris "Newtris 1.4.pkg" --headless viewSetupDoneScript ok: GameBoard 10 columns, NextPiece 3 Start pressed: GameStatus 1, level 1, score 0 falling piece py: 0 -> 1 after one idle tick after 4 idle ticks, score 0 (board drawn at 40,24): |..........| |..........| |..........| |..........| |..........| |....####..| <- the falling piece, drawn by DrawPiece |..........| ... after 40 idle ticks, score 160: |..........| ... |.....##...| <- what PutPiece settled onto GameBoard, |.....##...| drawn by viewDrawScript |....####..| ``` The game's own view frame comes out of the package — `theForm`, 42 slots — deep cloned into the runtime heap so the game can mutate it. Its own `viewSetupDoneScript` builds the board. Starting the game is a **tap on the Start button**: its recompiled `buttonClickScript` is what sets the level, clears the board and spawns the first piece. Keys send the same messages the pen did. The blocks are the package's own bitmaps: `Shapes` is a **128×16 sprite sheet of eight 16×16 tiles**, and `DrawXBitmap(bounds, icon, i, j)` draws tile *(i, j)* — the tile size *is* the destination size, which is why `previews` (280×20) yields seven 40×20 piece previews from the same call. The sounds are the package's three `simpleSound` frames, 8-bit signed samples at the rate stored as a big-endian double, wrapped in a WAV. Still to do: a soup for the high score, and a proper view system instead of the host's stubs. *Newtris* uses no exception handlers, so all 32 functions emit with nothing skipped. ```c /* viewDrawScript @0x29d0: 0 arg(s), 124 bytes of bytecode */ newt_ref newt_fn_viewDrawScript_29d0(newt_ctx *cx, newt_ref self, newt_ref *args) { newt_ref s[7]; /* operand stack, depth resolved statically -- no runtime sp */ newt_ref v[11]; /* _nextArgFrame, _Parent, _implementor, iy, py, ix, px, ... */ newt_ref lit[8]; ... L_46: /* 46: find-var 0 ; 'GameBoard */ s[0] = newt_find_var(cx, lit[0]); /* 47: get-var 5 ; ix */ s[1] = v[5]; /* 48: freq.aref 2 */ s[0] = newt_aref(cx, s[0], s[1]); /* 49: get-var 3 ; iy */ s[1] = v[3]; /* 50: freq.aref 2 */ s[0] = newt_aref(cx, s[0], s[1]); /* 52: freq.not-equals 6 */ s[0] = newt_not_equals(cx, s[0], s[1]); /* 53: branch-if-false 84 */ if (!newt_truthy(s[0])) goto L_84; ``` That is `if GameBoard[ix][iy] <> 0 then …`, as C, from a 1993 shareware package. A stripped 1993 shareware package with no debug information hands back its own function list, because in NewtonScript a function's name is the slot of whichever frame holds it: ``` $ python newtonrecomp/tools/newtpkg.py dis "Newtris 1.4.pkg" 32 functions InstallScript@0x198 args=1 79B 8 literals 7 locals viewSetupDoneScript@0x488 args=0 114B 16 literals 6 locals ReadTimes@0x4a8 args=0 97B 14 literals 8 locals NextLevel@0x5b0 args=0 72B 7 literals 3 locals UndrawPiece@0x618 args=0 123B 8 literals 11 locals SaveTimes@0x650 args=0 134B 17 literals 8 locals NewPiece@0x6e0 args=0 84B 13 literals 7 locals DrawPiece@0x720 args=0 115B 9 literals 11 locals viewClickScript@0x28d0 args=1 98B 11 literals 6 locals viewIdleScript@0x28f0 args=0 255B 20 literals 12 locals GameStart@0x2910 args=0 19B 4 literals 3 locals MoveLeft@0x2930 args=0 234B 14 literals 12 locals MoveRight@0x2950 args=0 236B 14 literals 12 locals Rotate@0x2970 args=0 255B 15 literals 8 locals CCWRotate@0x29f0 args=0 255B 15 literals 8 locals PutPiece@0x2990 args=0 268B 21 literals 9 locals RemoveRows@0x29b0 args=0 574B 21 literals 21 locals viewDrawScript@0x29d0 args=0 124B 8 literals 11 locals ... ``` That is the entire game: `NewPiece`, `MoveLeft`, `MoveRight`, `Rotate`, `CCWRotate`, `PutPiece`, `RemoveRows`, `NextLevel`, and `SaveTimes` writing the high score to a soup. Nothing left to reverse-engineer — only to translate. Better still, the bodies read as source. `viewDrawScript` in full: ``` 0 push-const 0 1 set-var 3 ; iy nested loops over the board 19 get-var 3 ; iy 20 push-const 64 ; 16 23 freq.multiply py := iy * 16 26 set-var 4 ; py 46 find-var 0 ; 'GameBoard 47 get-var 5 ; ix 48 freq.aref 49 get-var 3 ; iy 50 freq.aref if GameBoard[ix][iy] <> 0: 52 freq.not-equals 53 branch-if-false 84 64 get-var 6 ; px 65 get-var 4 ; py 66 push-const 64 ; 16 69 push-const 64 ; 16 72 push 1 ; 'RelBounds 73 call 4 RelBounds(px, py, 16, 16) 74 find-var 2 ; 'Shapes -- its result stays on the stack and 75 push 3 ; 'icon becomes DrawXBitmap's first argument 76 get-path 1 (no `pop` between the two calls) 77 push-const 28 ; 7 80 push-const 0 ; 0 81 push 4 ; 'DrawXBitmap 82 call 4 DrawXBitmap(, Shapes.icon, 7, 0) 108 find-var 5 ; 'GameStatus 110 freq.equals 111 branch-if-false 122 if GameStatus = 1: 114 find-var 6 ; 'board 115 push 7 ; 'GameStart 118 send 0 board:GameStart() 123 return ``` ``` $ python newtonrecomp/tools/newtpkg.py info "Newtris 1.4.pkg" Newtris 1.4.pkg: package0 flags=00000000 dirsize=252 parts=1 part 0: type='form' kind=NOS size=28584 objects: 624 bytecode: 32 functions, 3507 bytes, 2093 instructions array x186 4564 bytes symbol x177 2257 bytes frame x168 3048 bytes String x48 2360 bytes instructions x32 3507 bytes bits x8 1676 bytes samples x3 2036 bytes real x1 8 bytes mask x1 100 bytes ``` Every one of those 32 functions decodes cleanly and ends in `return`. The largest is 574 bytes of bytecode; the smallest is 4. The instruction mix is `push-const` 354, `get-var` 281, `push` 256, `freq-func` 210, `find-var` 177, `set-var` 161 — a plain stack machine, no surprises. ### What the game is made of The 177 symbols name the game's own structure, which is the whole appeal of recompiling bytecode rather than machine code — the program tells you what it is: | symbol | what it will become | |--------|---------------------| | `viewDrawScript` | the board renderer — 8 `bits` bitmaps, one `mask` | | `viewIdleScript` | the fall timer; the game's clock | | `viewClickScript` | pen input: tap left / right / bottom of the field | | `buttonClickScript` | the two rotate buttons | | `viewSetupDoneScript` | view-tree construction | | `InstallScript` / `RemoveScript` | package install hooks (no-ops for us) | | `HighScore`, `Score` | persistence — the one place a soup is needed | | `samples` | 3 sound samples, 2 KB | That table *is* the runtime surface this title needs. Nothing more. ### Progress - [x] Package parsed, object graph walked (624 objects), all 32 functions decoded. - [x] Title picked and characterised; runtime surface known and bounded. - [x] **All 32 functions recovered as named CodeBlocks** — arguments, locals, and literals resolved. None share bytecode; none are anonymous closures. - [x] **All 32 recompiled to C** — `newtcc emit`, each line annotated with its pc and disassembly. Compiles clean under `cl /W4 /WX`. Nothing skipped. - [x] **The board renderer runs** — `viewDrawScript` executes as native C against the runtime's object model and blits exactly the occupied cells. - [x] **The ROM globals it calls** — `Array`, `Ticks`, `Random`, `SetRandomSeed`, `RelBounds`, `DrawXBitmap`, `MakeRect`, `mod`, `negate`, `NumberStr`, … in C. - [x] **First pixels** — a framebuffer behind `DrawXBitmap`, a Win32 window, and an idle timer at the game's own `gameSpeed`. - [x] **Playable** — blocks fall, rotate, land, rows clear, score counts up. Arrow keys move and rotate; space drops a step. - [x] **Real bitmaps** — the package's own `bits` sprite sheets, blitted 1-bit. - [x] **Real geometry** — views placed by their `viewBounds`; the child views are bound into the form's slots by the name NTK left in their `debug` slot. - [x] **Sound** — the three `simpleSound` frames play. - [x] **Level pacing** — the drop timer re-arms when `NextLevel` lowers `gameSpeed`. - [ ] **The high score** — `SaveTimes`/`ReadTimes` want a soup. - [ ] **A real view system**, so the host stops standing in for `protoView`. - [ ] **Pen input** — `viewClickScript` exists; the keyboard shortcuts it today. - [ ] **High score persists** (a soup, or the smallest thing that stands in). - [ ] Screenshots, and a differential check against the interpreter oracle. ## Build & run Bring your own `Newtris 1.4.pkg`. Needs CMake and a C compiler. ```powershell git clone --recurse-submodules https://github.com/sp00nznet/newtris-newton-recomp cd newtris-newton-recomp # recompile all 32 functions to annotated C python newtonrecomp/tools/newtcc.py emit "Newtris 1.4.pkg" games/newtris/generated # build the host against the toolkit runtime, and run it cmake -S . -B build cmake --build build --config Release .\build\Release\newtris.exe "Newtris 1.4.pkg" # a window; arrows + space .\build\Release\newtris.exe "Newtris 1.4.pkg" --headless # drive it, print the board .\build\Release\newtris.exe "Newtris 1.4.pkg" --shot out.bmp # play a while, save a frame ``` Arrow keys move and rotate the piece, down counter-rotates, space drops it a step, escape quits. Each key press sends the game the same message its button views sent when tapped with the pen. `generated/` is `.gitignore`d: it is derived from the package, so it is not committed. The host (`games/newtris/src/main.c`) is original work. To read what was recompiled: ```powershell python newtonrecomp/tools/newtpkg.py info "Newtris 1.4.pkg" python newtonrecomp/tools/newtpkg.py dis "Newtris 1.4.pkg" RemoveRows ``` ## Layout ``` newtonrecomp/ the toolkit (git submodule) docs/NOTES.md what's inside this package, and the bring-up plan games/newtris/src/main.c host: load, clone the view, supply the ROM globals games/newtris/src/video.c framebuffer, Win32 window, keys -> message sends games/newtris/generated/ recompiler output (derived from the package; ignored) ``` ## Credits The recompiler, runtime, and host are original — see [`newtonrecomp`'s credits](https://github.com/sp00nznet/newtonrecomp#credits--references) for the reverse-engineering and preservation work this stands on, chiefly the [UNNA](http://www.unna.org/) archive and Paul Guyot's [Einstein](https://github.com/pguyot/Einstein) emulator. ## License MIT — see [`LICENSE`](LICENSE), which covers the code in this repository. *Newtris* is © 1993 BugByte Inc. and was distributed as shareware; this project ships **no game data** and no recompiler output derived from it. Independent, non-commercial preservation/recompilation work, not affiliated with or endorsed by Apple or BugByte.