# 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.**
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.