Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

turtleshell

English · 简体中文 · 日本語

Two Logo systems — one from a 1981 Apple II, one from Berkeley — living in one static web page, sharing a virtual machine and nothing a user can see.

▶ Try it

https://buildx100.github.io/turtleshell/ — nothing to install; it is a static page. Type REPEAT 4 [FD 100 RT 90], then press APPLE ][ and watch the same square land on a one-bit NTSC screen.

One of the buildx100 projects.

Apple Logo II Berkeley Logo
The Apple II screen: a Sierpinski triangle in green phosphor Berkeley Logo: a Hilbert curve beside the transcript that drew it

Run it locally

npm run serve       # http://localhost:16005

Double-clicking web/index.html will not work. The page is built from native ES modules and module loading is blocked by CORS on the file:// protocol. It has to be served over HTTP. There is no build step — npm run serve is a 40-line static file server, and Node alone runs the page and the tests.

npm test            # node --test, no framework

Twenty-four of those tests drive a real browser, because four bugs walked straight past the other 272: a stylesheet rule that lost on file order, a canvas reporting its pixel buffer as its height, two elements answering to the same name, and a field you could not type into because something invisible sat in front of it. None of those are visible to Node. They skip themselves when there is no browser to drive, so a fresh clone is still green:

npm install --save-dev playwright-core
npx playwright install chromium

That is a driver for a browser, not a library the page loads. web/ still imports nothing from npm and never will.

The point

Logo was never one language, it was a family, and its dialects were shaped by the machines under them. The Apple II's colours are a side effect of NTSC decoding rather than a palette: ask for purple on an odd column and you get something else. That is not a defect to smooth over — it is what the language felt like on that machine, and the retro mode models the real one-bit framebuffer rather than faking six colours, so the artefacts come out for free.

What this is not

  • Not an Apple II emulator. No 6502, no ROM, no disk images. The interpreter is original; only the look and the dialect are borrowed, and neither of those is copyrightable. See NOTICE.
  • Not a UCBLogo compatibility project. jslogo, Calormen Logo and Turtle Academy already do browser Logo well, and this is not entering that parity race. The implemented primitive set is written down and anything outside it says so instead of pretending.
  • Not one language with a theme switch. Two dialects means two primitive registries, two error catalogues, two renderers, two terminals. Only the reader, the machine and the turtle are shared — see docs/architecture.md.

Layout

web/js/lang/      reader, virtual machine, workspace, primitive implementations
web/js/dialect/   per-dialect names, arities, precedence, error wording
web/js/turtle/    turtle state and the event log
web/js/render/    one renderer per mode
web/js/shell/     terminal
test/             node --test
docs/             why it behaves that way, and what was not verified

web/js/main.js wires these together and contains no logic of its own.

Status

Every milestone done; from here it is iteration. Measured by npm test and npm run check-manual:

tests passing 355 of 355 — 311 in Node, 44 driving Chromium
acceptance programs 22 of 22, simplest first — square, star, rosette, circle, five notes, templates, spiral, colour bars, Sierpinski, Koch snowflake, binary tree, dragon curve, Hilbert curve, an L-system, a word reversed, towers of Hanoi, merge sort, an arithmetic evaluator, a countdown, primes, an interactive dialogue, a doctor. Four of them run on one dialect only and say so
primitives checked against a manual 372 of 372 across both dialects
dialects both, switchable at runtime, each with its own renderer

They are the same programs the page offers from its examples dialog — one copy, in web/js/examples.js — so nothing on the menu is untested. That dialog shows what each one draws, and the picture is the program: run by the same interpreter the page runs, in whichever dialect is showing, rather than a screenshot in a folder that nothing checks. They are in the order a reader should meet them: six that define no procedure, then the ones that define a procedure and draw with it, then six that compute with words and lists instead of drawing, and last the two that stop and ask you something. The later ones are there because Logo is not a drawing language with a list feature: an L-system whose rule rewrites a list and whose letters are then RUN, a merge sort, an evaluator that knows times comes before plus, and thirty lines of Weizenbaum. Eighteen of the twenty-two are written in the subset both dialects share — no IFELSE, no MINUS, nothing either machine is missing — and a test runs those through both machines. The other four are the point made the other way round: each names the machine it needs, on its row in the menu, and the test asserts both halves — that it runs there and that the other machine refuses it. Two are Apple's (a tune, because that manual has a loudspeaker and the other has none; and a loop made of GO and LABEL, which no other Logo has) and two are Berkeley's (templates, and a sieve made of one FILTER). Choosing one switches the page to its machine. They are ordinary textbook constructions written for this project, not listings copied out of a book; each one is placed to fit inside 280 by 192 without running off.

Every primitive's name, aliases and input count is recorded in docs/manual-tables/ and asserted against the code in both directions, so neither a table nor a registry can be edited alone. Doing this found eight defects on the Berkeley side alone: three primitives its manual does not document, three missing parenthesised forms, half the error wording mis-capitalised, and a "division by zero" error that dialect does not have.

The two tables are not equally strong and say so: the Berkeley manual is machine-readable text, so its arities are verified against the manual itself; the Apple manual survives only as OCR of two scans, so its arities are hand-checked and only its coverage is machine-verified.

The Apple screen is the real one: 280x192 one-bit pixels, seven to a byte, with the colour decoded out of the bit stream rather than stored. Which means a white vertical line comes out violet, a green diagonal comes out dotted, and an orange pixel re-colours the six neighbours that share its byte. Those are assertions in test/hires.test.js, not descriptions. A monochrome-monitor mode shows the same bits with no decoding at all, which is the clearest way to see that the colour was never in the data.

npm run preview -- 'REPEAT 60 [FD 60 RT 174]'   # draws it in the terminal

Its transcript is on that screen, not beside it. The machine had one glass and one raster, so the character grid is composed into the bottom of the same 280x192 picture that the turtle draws on, in the manual's proportion of twenty rows of graphics over four of text. Which means a split crops the drawing rather than shrinking it: the bottom 32 lines are still there and still being drawn into, and FULLSCREEN reveals them rather than enlarging anything.

The line you type is on that screen too, in the same glyphs, on the row the machine would have put it on — and it is still an ordinary <input>, placed over that row with only its text made transparent. The browser keeps the caret, the selection, the input method and the phone keyboard; the screen keeps the letterforms. docs/architecture.md sets out why the field itself may never be hidden, which is a lesson this project paid for.

The glass is 4:3, which is the shape the raster filled, so a turtle square comes out at 1.14 to 1. That is the machine and not a defect — it is the reason Apple Logo shipped SETSCRUNCH — and the one other shape it offers is the one that primitive could produce, for anyone who minds. The correction is applied to the display, never to the frame buffer, so no colour behaviour changes with it. docs/architecture.md sets out why that is not the same kind of thing as the artefacts.

READWORD makes a program stop mid-procedure and wait for you to type, then carry on from the same instruction. That needs no async anywhere: the machine owns its frame stack, so suspending is simply not advancing it. It was the last of the seven things that decision was made for at M0.

PAUSE is the same trick with the keyboard handed back. A procedure stops where it stands, the prompt becomes its name, and what you type runs in that procedure's scope:N at a WALK? prompt is WALK's N, because nothing had to be unwound to stop there. A mistake at the pause prompt costs the line and not the program, and CO puts it back on its feet.

Your work is kept with SAVE and LOAD, the language's own primitives, against localStorage — named workspaces you can list with CATALOG, rather than an invisible autosave a program cannot see. My Work shows that disk, hands any of it back as a .logo file, and takes one in: importing puts the file on the disk the way sliding a floppy in does, and loading it is still LOAD. A saved workspace is Logo source text, so LOAD is nothing but running what SAVE wrote: no format to version, and a file a person can read. A browser that refuses storage is reported to the program as trouble with the disk, which is what the manuals call it and, for once, exactly true.

Working: the reader; the machine with infix precedence, interruption, procedure definition, dynamic scoping, OUTPUT/STOP, tail-call elimination, suspend-for-input and the interactive pause; CATCH/THROW, the template tools (MAP, FILTER, REDUCE, FOREACH and ?), arrays, property lists, the loop words and the bit operations; contents lists, burying, tracing, stacks and queues, PARSE/RUNPARSE, and reading the keyboard a character at a time; the turtle model with wrapping and a bounded field; the event log; both dialects, both renderers and both terminals; a forty- or eighty-column character screen; the examples menu, HELP, saving, importing and exporting.

Everything either manual documents is now either implemented or written down as missing with the subsystem it waits on. The "nothing in the way, just not written yet" list is empty for both dialects. What is left needs something this does not have — file streams, macros, an addressable text screen, a reader that knows vertical bars — or is deliberately out of scope, and docs/manual-tables/ names which for every word.

Not planned: multiple turtles and sprites, which were always a stretch goal.

Not verified: only Chromium has ever loaded this page. No Firefox, no Safari, no phone. The error wording matches the manual's table but has not been compared against a running UCBLogo. docs/licensing.md lists what else is unconfirmed.

Language

The toolbar is available in English, 简体中文 and 日本語. The terminal is not translated, in either mode, and that is deliberate rather than unfinished.

It is the machine's output surface, not chrome. Its command names and error messages reproduce historical systems, so they are English the way the systems were. And in Apple mode it is a 40-column screen with a font of sixty-four shapes drawn for this project — Chinese cannot be drawn on it at all, and would arrive as a row of solid blocks. Typography the machine never had, such as an em dash, is folded to what it did have rather than blocked.

Licence

Apache-2.0 — see LICENSE. The project bundles no third-party code, fonts, ROMs or disk images at all, so there is no boundary to state: everything here is under that one licence. NOTICE records what is deliberately absent, and docs/licensing.md records the provenance chain — in particular that UCBLogo and its manual are GPL, that no UCBLogo source was read, and that the manual is not vendored here.

About

Two Logo dialects — Apple Logo II on its 1981 one-bit screen, and Berkeley Logo — living in one static page, sharing a virtual machine and nothing a user can see. No framework, no build, no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages