Мощь бейсика BBC Micro
Наткнулся на упоминание компьютера BBC Micro 1981 года выпуска и немного почитав википедии удивился одной его мощной черте.
Первые версии машин были реализованы на MOS 6502 работавшем на 2 МГц. ОЗУ от 16 до 32 Кб (версии A и B соответственно). ПЗУ — 32 Кб, причём они лежали в верхней части адресного пространства и нижние 16 Кб заранее делались с поддержкой маппинга, на внешние схемы расширения.
И вот верхние 16 Кб ПЗУ содержали ОС «Acorn MOS», где было много полезных базовых функций, а встроенные 16 Кб нижнего ПЗУ содержали по привычной для того времени схеме интерпретатор BASIC — собственно «BBC Micro Basic».
И вот он был весьма забавным зверем для своего времени и платформы.
Большинство других популярных 8-биток либо имели один из многочисленных, но очень похожих вариантов/производных Microsoft Basic (как, например, Apple II или Commodore 64), либо что-то кастомное, но в сильной степени заимствующее основные черты Microsoft Basic (как в ZX Spectrum).
Это были всё классические бейсики 1.x, как я еще их называю «неструктурированные бейсики».
Программирование на них во многом по духу напоминало программирование на ассемблере процессоров как раз типа MOS 6502.
По сравнению с современностью ярко выраженные черты такого программирования это:
- отсутствие локальных переменных
- подпрограммы можно вызывать оператором GOSUB и можно из них возвращаться оператором RETURN, но из-за (а) параметры передаются в глобальных переменных и ими же возвращаются (причём даже по сравнению с ассемблером был регресс, т.к. вместо внятного имени указывался лишь номер строки программы куда происходит переход)
- из инструкций передачи управления только IF/THEN (чаще всего без ELSE), FOR/NEXT и GOTO. Важно заметить, что многострочность поддерживалась только в FOR. Оператор IF же обрывался на конце строки, поэтому чтобы не плодить сверхдлинную лапшу из операторов в одной строке разделенных двоеточием GOTO множились без конца.
- определение функций оператором DEF FN по сути было только способом сокращать использование часто используемых формул в выражениях, ибо могли задавать только одно выражение с параметрами и опять таки в одну строку, без операторов.
Все эти особенности очень сильно подрывали удобство программирования и поэтому Бейсик в BBC Micro был поистине передовым для своего времени и платформы.
Во первых — в нём был ELSE в IF-ах и управляющая конструкция REPEAT/UNTIL.
Во вторых — он поддерживал создание полноценных процедур с параметрами оператором DEF PROC. Причём у процедур были и имена (всегда должны начинаться на 'PROC') и параметры и они могли быть многострочными (завершались оператором ENDPROC). То же самое касалось и функций определяемых привычными DEF FN.
Однако с этим была связана одна забавная особенность, видимо экономия на спичках — если однострочные процедуры или функции можно было размещать в любом месте программы, то многострочные страдали немного от одного упрощения — встретив DEF PROC в начале или середине программы интерпретатор бейсика автоматически отбрасывал только эту самую строку с DEF FN и ни разу не пытался пропускать следующие строки до встречи ENDPROC — то есть просто начинал выполнять следующие строки многострочных процедур или функций как основную программу. Это скорее всего означало сбой. Поэтому многострочные процедуры следовало всегда размещать в последних строках программы и не доводить до них поток выполнения никак иначе кроме как их вызовами. В процедурах можно было даже делать локальные переменные оператором LOCAL! При этом для совместимости с MS-like диалектами поддерживались конечно и GOSUB со всей их неприглядностью.
Для наглядности приведу пример как это примерно выглядело:
10 REM PROC demo
20 PROC_ScreenSetup("Hello!")
30 INPUT A$
40 PROC_ScreenSetup(A$)
50 END
60 DEF PROC_ScreenSetup(msg$)
70 CLS
80 PRINT TAB(2);msg$
90 ENDPROC
Во третьих — он поддерживал самый настоящий встроенный ассемблер! Это уже почти за гранью, но написав символ [ интерпретатор переходил в режим компиляции ассемблера 6502 (что заканчивалось при встрече символа ] ) и начинал собирать код, который потом можно было вызывать или функцией совместимой с MS-like диалектами USR или оператором CALL.
Забавно то куда происходила компиляция и как происходила интеграция с таким асмокодом.
Во первых — помимо произвольного количества вещественных или строковых переменных бейсик BBC Micro содержал жёстко прошитые целочисленные переменные с однобуквенными именами от A% до Z% (знак % в конце как раз означал целочисленность). Во первых — они были заметно быстрее по сравнению с вещественными переменными без префиксов. Лежали по фиксированным адресам и даже не стирались при запуске программы начисто оператором RUN. Во вторых — часть из них в некоторых ситуациях начинала играть особую роль.
Так при встрече [ из переменной P% брался адрес в который сейчас надо компилить последующий асмокод (аналог инструкции ORG в асмах вообще).
Кроме того при вызове асмокода через CALL в 8-битные регистры A, X и Y процессора MOS 6502 записывались нижние байты из переменных A%, X% и Y%.
Забавно как именно выбирался адрес для P% куда компилить код.
У оператора аллокации массивов DIM A(size) была особая форма — DIM A size (без круглых скобок).
Она приводила к тому, что в свободной памяти резервировалось size байт, а в переменную A записывался адрес начала этого куска байт. То есть практически malloc!
Нужно было еще вычислять минимально необходимый размер этой области для куска асмокода, увы вручную, но сама аллокация и запись нужного значения в P% перед асмокодом было уже вещью тривиальной.
Хотя можно было и просто писать по фиксированному адресу, ограничив область бейсика, как это тоже было принято в тех компьютерах.
Выглядело всё это примерно так:
10 PRINT "THIS IS BASIC"
20 DIM GAP% 20
30 P%=GAP%
40 [
50 JSR &FFE7
60 RTS
70 ]
80 CALL GAP%
Разное видел и про многое читал, но про бейсик со встроенным ассемблером, да еще и на 8 битах слышу впервые! :)
10 комментариев
Вставки на ассемблере, правда, не умеет (позволяет вставлять только чистый машкод), но зато почти все хардварные особенности компьютера были доступны из Бейсика через стандартные операторы. И поэтому нумерация строк программы на Бейсике в основном нужна только для редактирования программы (ну и оператора «DATA»). Всякие GOSUB, GOTO и т.п. оставлены только для «совместимости» и упрощения переноса Бейсик-программ с других компьютеров.
Локальные переменные, объявление новых функций и подпрограмм, обработка прерываний и нештатных ситуаций (catch/try), работа со стереозвуком — всё есть из коробки. Единственным минусом, ИМХО, является не очень высокая скорость интерпретатора.