Sneekie

Van GW-BASIC naar JavaScript, naast elkaar

De bron uit 1988 links, de port uit 2026 rechts — en hoe de nieuwe laag in elkaar zit.

De webversie is geen herschrijving — het is een trouwe port. De spellogica is statement voor statement vertaald uit SNEEKIE.BAS, met de originele variabelenamen en zelfs BASIC-regelnummers als commentaar. Wat echt veranderde is de dunne laag eronder: wat GW-BASIC gratis gaf — schermgeheugen, toetsenbord, geluid, GOTO — moest voor de browser opnieuw worden gebouwd. Deze pagina koppelt representatieve stukken en legt die laag uit; gebruik Uitleg voor elke BASIC-regel.

Een oud groen Sneekie-scherm dat via pixels verbonden is met een moderne browserversie.

De brug: het scherm is de datastructuur

GW-BASIC POKEte tekens rechtstreeks in het 80×25 tekstschermgeheugen van de PC en PEEKte ze terug om te zien wat waar stond. Er was geen apart "slang"- of "muur"-object — het beeld was de toestand. De port houdt dat model exact aan, als een 4000-byte Uint8Array (vram): twee bytes per cel, teken en attribuut, geadresseerd met

offset = (rij − 1) × 160 + (kolom − 1) × 2

Omdat het model identiek is, verandert de gamecode nauwelijks — alleen de woorden POKE en PEEK worden de functies poke() en peek(). De rest van deze pagina volgt uit het opnieuw bouwen van GW-BASIC's runtime rond die ene gedeelde array.

Wat opnieuw gebouwd moest worden

Het mechanisme uit 1988De vervanging uit 2026
POKE / PEEK in videogeheugen op &HB000/&HB800 poke() / peek() op een 4000-byte Uint8Array
LOCATE r,c : PRINT CHR$(n) locate(r,c); pc(n) — dezelfde cursor, dezelfde tekencodes
het scherm ververst zichzelf (het is videogeheugen) een dirty-cell set + requestAnimationFrame-redraw, met glyphs uit een ingebed IBM CP437-font
INKEY$ / INPUT$ blokkeren tot er een toets is async + een Promise-toetsenbuffer (keyOrTimeout/waitKey)
SOUND freq, ticks een Web Audio-blokgolfoscillator op dezelfde 1/18.2 s-klok
ON LEVEL GOSUB … de dispatch-arrays CFG[] en ENEMY[]
GOTO 510 / RETURN 510 (sterven) throw DEATH, gevangen rond de move-loop
variabelen (DEFINT A-Y, Nederlandse namen) de zelfde namen letterlijk behouden (T, BTEL, HART, KLAVER…)

De ene diepe wijziging: blokkeren wordt async

GW-BASIC liep van boven naar beneden en stopte gewoon bij INKEY$/INPUT$ tot je een toets indrukte. Een browsertab mag nooit zo blokkeren — daarom werden de wachtpunten awaits, waardoor playLevels() en program() async moesten worden. Een kleine Promise-toetsenbuffer vervangt de BIOS-buffer, en omdat GOTO niet bestaat, wordt de "spring eruit en sterf"-logica een geworpen DEATH-signaal dat rond de loop wordt opgevangen. Die twee trucs — await voor invoer, throw om te sterven — laten de rest een letterlijke vertaling blijven.

Een blokkerige Sneekie-slang springt door een pixelpoort van een oude CRT naar een moderne browser.