Від GW-BASIC до JavaScript, поруч
Код 1988 року ліворуч, порт 2026 року праворуч — і як зібраний новий шар під ним.
Вебверсія не є переписуванням — це вірний порт. Ігрова логіка перекладена з SNEEKIE.BAS оператор за оператором, з оригінальними назвами змінних і навіть номерами рядків BASIC у коментарях. Насправді змінився тонкий шар під грою: те, що GW-BASIC давав безкоштовно — памʼять екрана, клавіатуру, звук, GOTO — довелося відбудувати для браузера. Ця сторінка ставить поруч показові фрагменти і пояснює цей шар; для кожного рядка BASIC є сторінка Пояснення.
Міст: екран і є структура даних
GW-BASIC записував символи прямо у текстову памʼять екрана PC 80×25 через POKE і читав їх назад через PEEK, щоб дізнатися, що де стоїть. Окремого обʼєкта "змія" чи "стіна" не було — зображення було станом. Порт зберігає цю модель точно: 4000-байтовий Uint8Array (vram), два байти на клітинку, символ і атрибут, адресуються так:
offset = (рядок − 1) × 160 + (стовпець − 1) × 2
Оскільки модель однакова, ігровий код майже не змінюється — слова POKE і PEEK лише стають функціями poke() і peek(). Усе інше на цій сторінці випливає з відбудови решти runtime GW-BASIC навколо одного спільного масиву.
Що довелося відбудувати
| Механізм 1988 року | Заміна 2026 року |
|---|---|
POKE / PEEK у відеопамʼять на &HB000/&HB800 |
poke() / peek() у 4000-байтовому Uint8Array |
LOCATE r,c : PRINT CHR$(n) |
locate(r,c); pc(n) — той самий курсор, ті самі коди символів |
| екран оновлює себе сам (це і є відеопамʼять) | набір брудних клітинок + requestAnimationFrame, glyphs з вбудованого шрифту IBM CP437 |
INKEY$ / INPUT$ блокують до клавіші |
async + Promise-буфер клавіатури (keyOrTimeout/waitKey) |
SOUND freq, ticks |
Web Audio square-wave oscillator на тому самому годиннику 1/18.2 с |
ON LEVEL GOSUB … |
dispatch-масиви CFG[] і ENEMY[] |
GOTO 510 / RETURN 510 (смерть) |
throw DEATH, перехоплений навколо циклу руху |
змінні (DEFINT A-Y, нідерландські назви) |
ті самі назви збережені буквально (T, BTEL, HART, KLAVER…) |
Одна глибока зміна: блокування стає async
GW-BASIC ішов згори вниз і просто зупинявся на INKEY$/INPUT$, доки ви не натиснете клавішу. Вкладка браузера не може так блокуватися — тому точки очікування стали await, а playLevels() і program() стали async. Малий Promise-буфер клавіатури замінює BIOS key buffer, а оскільки GOTO у JavaScript немає, рядки "вистрибнути і померти" стають кинутим сигналом DEATH, перехопленим навколо циклу. Ці два прийоми — await для вводу, throw для смерті — дозволяють решті коду лишатися буквальним перекладом.