1 Отредактировано Voldemar0 (10-02-2018 21:33)

Тема: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Привет!

Такая незамысловатая прога:

10  GOSUB 100
20  PRINT 3
30  STOP
100  ONERR  GOTO 120
110 E   
120  PRINT 5
130  RETURN

Как думаете, что она должна делать ? и почему ?

2

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Вроде прога должна написать 5, а потом 3. Не уверен насчёт оператора STOP, вроде END останавливает программу. В MSX бейсике именно так, а STOP команды используются для проверки нажатия клавиш CTRL+STOP. Если оператора STOP в этой версии бейсика нет, тогда после последней тройки будет опять выведена пятёрка, а затем "Return without gosub" ошибка вылезет.

3

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Ха! Прикольный цикл. RETURN работает как RESUME и перекидывает на строку с ошибкой. А может, так и должно?

4 Отредактировано Voldemar0 (08-02-2018 06:27)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

STOP - это  почти во всех бейсиках аналог END, но при его срабатывании выводится сообщение об остановке программы с указанием строки. Такой себе отладочный стоп.

-=-

Я проверил пример и в Бейсик-60 и в ИКП-Бейсике - одинакого. Нет, RETURN <> RESUME. Тут всё гораздо хуже.
При срабатывании перехода по ONERR очищается стек возвратов (кстати, а знаете, что его глубина в ИКП-бейсике - 25 уровней?) и RETURN генерирует новую ошибку - "RETURN без GOSUB". На неё вновь срабатывает обработчик ошибок.

Не знаю, какая тут логика, если это вообще не бага.

Сегодня продолжу копаться.

5

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Вообще, пишут, что это бага Applesoft бейсика и бага известная https://groups.google.com/forum/#!topic … sNawW_KQBk

Как я понял, проблема в том, что при возникновении ошибки на стек возвратов кладется специальная запись, по которой можно вернуться оператором RESUME. Если эту запись не убрать, то операторы RETURN и NEXT ломаются. Оператор RESUME эту запись удаляет, но возвращает управление на строку с ошибкой. А чтобы убрать эту запись без RESUME, надо вызывать интерпретатор напрямую (CALL -3288). Но это все про Apple II, возможно на Агате с этим что-то сделали.

6 Отредактировано Voldemar0 (08-02-2018 19:17)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Есть оператор POP - удаляет последний адрес возврата из стека, но вот будет ли он на эту специальную запись действовать?

-=-=

Выдержка из библиотки Серкова:

$DC-$DD        Содержит адрес меньший на 1 адреса
               ячейки, в которой содержится опера-
               тор, при попытке выполнения которого
               произошла ошибка. Используется опера-
               тором BASICа "RESUME".
...
$DF            Содержит указатель регистра S (для
               возвращения по "RESUME").

7

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

http://www.woz.org/letters/apple-basic

8 Отредактировано Voldemar0 (09-02-2018 08:33)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Сегодня, на третью ночь, удалось получить чистое кристализованное зло:

10  PRINT 1
20  * $32:
     !     $7F
     !     :
25  PRINT 2 
30  PRINT  KSLLSD
40  * $32:
     !     $7E
     !     :
50  PRINT 3

Зло проявляется только в ИКП-бейсике, пробовал на ИКП-7, но, полагаю, на ИКП-9 будет то же самое. На Бейсике-60 идёт без ошибок.

Гадать никому не предлагаю, так как без глубоких ковырялок тут всё равно не будет ясно что происходит. Итак:

Как известно, оператор '*' - это хитрое расширение агатовских бейсков, позволяющее исполнять команды сисмона из бейсик-программы. Но есть среди них одна специальная команда - "*X:" (X - любое число), которая активизирует встроенный в бейсик макроассемблер. Все команды макроассемблера начинаются с "!". Самая простая - "$" - это аналог команд типа ".BYTE" или "DFB" или ".DB" - заполнение памяти заданным значением.
Таким образом, строчка "*$32:!$7F!:" - аналог команды "POKE $32,$7F" (задать белый цвет, без инверсии, режим текста 32x32).

Зачем обращаться к макроассемблеру вместо POKE ? Да просто так можно компактно задать массив ячеек. Например, если нужно задать размеры текстовового окна, можно выполнить четыре POKE, а можно написать : "*32:!$00200040!:".

Специальный оператор макроассемблера "!:" - конец компиляции. Он запускает второй проход компилятора. Все бейсик-команды, встреченные между командами макроассемблера, будут выполнены повторно. Это не секрет и он описан в доках. Более того, можно, используя команды бейсика, управлять ходом компиляции. Поэтому я и называю эту технику "макроассемблером".

К сожалению, авторы не заморочились на проверку некорректных обращений к макроассемблеру: например, можно дважды подряд вызвать '*X:' и это не приведёт к ошибке. Просто второй проход будет выполнен только от последнего встреченного '*X:' (не помню, в каком из бейсиков проверял).
Для возврата не используется стек, только отдельные ячейки для хранения точки начала компиляции. Так что выполнение "!:" похоже на GOTO по условию "номер прохода = 1".

А найденная бага относится как раз к механизму возврата. Для того, чтобы макроассемблер помнил, какой проход выполняется, есть специальная ячейка $FF.

REM $FF - номер прохода встроенного ассемблера (ИКП):
REM   FF - нет ассемблирования (после RUN и второго ':'),
REM   00 - первый проход (после первого '!')
REM   88 - второй проход (после первого ':')

REM $FF - номер прохода встроенного ассемблера (Basic-60):
REM   xx - нет ассемблирования (НЕ ИНИЦИАЛИЗИРУЕТСЯ?!)
REM   FF - ждём первый проход (после '*XXXX:')
REM   00 - первый проход (после первого '!')
REM   88 - второй проход (после ':')
REM   68 - ??

И это всё почти понятно и логично.
Но есть один необычный участок кода в интерпретаторе:

E0AF -   68 .. ..   "х"     PLA   
E0B0 -   48 .. ..   "h"     PHA   
E0B1 -   C9 F3 ..   "IС"    CMP   #F3    
E0B3 -   D0 11 ..   "P."    BNE   E0C6
E0B5 -   BA .. ..   ":"     TSX   
E0B6 -   BD 02 01   "=.."   LDA   0102, X
E0B9 -   C9 DE ..   "I^"    CMP   #DE
E0BB -   D0 09 ..   "P."    BNE   E0C6
E0BD -   E6 FF ..   "ФЪ"    INC   FF             <============ ?????
E0BF -   A9 C4 ..   ")D"    LDA   #C4
E0C1 -   A0 E0 ..   " Ю"    LDY   #E0
E0C3 -   60 .. ..   "ю"     RTS   

E0C4 -   00 .. ..   "."     BRK   

E0C5 -   00 .. ..   "."     BRK   

Одна из бессонных ночей ушла на то, чтобы понять, в каких случаях будет выполнена команда по адресу Е0BD. Она выполняется при обращении к неинициализированной длинной переменной. Какое отношение эта операция имеет к макроассемблеру я даже вообразить не могу.

Мне удалось найти фрагмент исходника какого-то бейсика (явно дизасм эплософта, но, похоже, это именно то, с чего собирался Бейсик-60):

         ORG $E0AF      <== это реально фрагмент исходника - так, по простому, тут делили текст на отдельные файлы
ISLETC1  PLA
         PHA 
         CMP #>SRXT1+2
         BNE ISLETC2
         TSX
         LDA $102,X
         CMP #<SRXT1
         BNE ISLETC2
         INC FLGJOB
         LDA #>ZERTBL
         LDY #<ZERTBL
         RTS
ZERTBL   DFB $00,$00

Но - повторюсь - в Бейск-60 ошибка не проявляется.

А в ИКП, как отчётливо видно, происходит вот что:
когда в строке 30 происходит обращение к необъявленной переменной, срабатывает INC $FF, макроассемблер теперь уверен, что он идёт на первый проход, и встреченная в строке 40 команда "!:" отбрасывает его на второй проход на строку, которая была запомнена ещё ранее - т.е. номер 20. И мы имеем крайне замысловатый неожиданный переход.

9

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Во как. Искали одну багу, нашли другую.
Ведь в примере из первого поста, когда ИКП-шный бейсик встретит необъявленную переменную, он же не пойдет на второй проход ассемблера без команды "!:"?

Кроме того, в эмуляторе Apple II пример из первого поста зацикливается точно также, как в ИКП-шном бейсике, хотя макроассемблера в нем нет. В Applesoft есть кусок кода по адресу $E087, почти совпадающий с приведенным исходником, но там нет команды INC FLGJOB.

10 Отредактировано Voldemar0 (09-02-2018 19:43)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Бага была одна: прога внезапно оказывалась совсем не там, где ожидалось. По TRACE сложно было что-то увидеть: управление экраном в проге забивает весь вывод TRACE. Я предположил только, что дело в ONERR (он срабатывает при попытке прочитать отсутствующий необязательный файл) и стал разбирать, как вообще происходят переходы в бейсике.

Выяснил полезное (ИКП-Бейсик):

D788      sw_Normal  Обычный переход на следующую строку
D89A        sw_PUSH  Закидывает параметры возврата на стек (GOSUB)
D8F9         sw_POP  Вытаскивает значения из стека (RETURN)
F27C       sw_ONERR  Запоминает номер строки, в которой сидит ONERR GOTO
F290       sw_ERROR  Сработал обработчик ошибок, запоминаем строку с ошибкой (218-9)
F2A0      sw_GETPTR  Перещёлкиваемся на строку с ONERR (и выполняем там GOTO)
D4FD     sw_ASM_POP  Возврат на начало ассемблерного куска (второй проход)
D4CF    sw_ASM_push  Сохранение указателя на начало ассемблерного куска

тут не процедуры, а только точки, где начинаются манёвры с номером текущей строки (и, заодно, с указателем на исполняемую инструкцию).

$75..$76 - номер исполняющейся строки (TRACE)
$B8..$B9 - указатель на обрабатываемый интерпретатором байт программы

11

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

> Ведь в примере из первого поста, когда ИКП-шный бейсик встретит необъявленную переменную, он же не пойдет на второй проход ассемблера без команды "!:"?

Тут не совсем понял: в первом примере (где про ONERR) в строке 110 имеется SYNTAX ERROR. Необъявленность тут ни причем. А вот что там ассемблер будет делать - не знаю, пробуй :)

12 Отредактировано Voldemar0 (09-02-2018 20:31)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

А вот ещё сюрпризы :

Простая команда почти всегда и везде выполняется (из бейсика):

*$30A#$4B8.$4E8M

- копирование области памяти.

Сюрпризы начинаются отсюда:

X=$30A
*X#$4B8.$4E8M

Работает в Бейсик-60 и ИКП-Бейсик-7. В ИКП-Бейсик-9 копирует не по адресу X, а по 0.

Теперь так:

R%=$30A
*R%#$4B8.$4E8M

В Бейсик-60 виснет, в ИКБ-Бейсик-7 даёт синтаксическую ошибку на знак "%", в ИКП-Бейсик-9 зависает.

PS Срочно мне значёк "Отличник тестирования" !
А ведь всего -то хотел свою старую прогу с бейсик-60 в ИКП перетащить :((
Там строк -то всего 200.

PPS  Кому помешала досовская команда VERIFY, что её выпилили под корень в ИКП ?

13 Отредактировано Voldemar0 (10-02-2018 22:50)

Re: Немного про одну заморочку бейсика (и последующий хитпарад глюков)

Эххххх :(

Продолжаем хитпарад.

Проще некуда прога:

10  FOR X = 242 TO 254
20     S$ = S$ +  CHR$ (X)
40  NEXT 

Она не виснет и вполне корректно исполняется.
Одна проблемка: в некоторых версиях ИКП Бейсика-7 чуть большим числом операций, особенно строковых, вполне можно убить работу ДОС.

Как известно, в ИКП память делится чуть умнее, чем в Бейсик-60. Если последний почти никогда не переключает режим ОЗУ (только младший/старший разделы D000..DFFF), то в ИКП широко используется переключение ОЗУ региона 8000..BFFF. В семёрке в нулевом банке хранит свои данные ДОС, в первом банке - бейсик. В девятке бейсик в этом регионе пользует сегменты ОЗУ  4 5, а ДОС - сегменты C D.

Всё бы хорошо, но вот попалась версия ИКП-Бейсика-7, которая иногда забывает переключить память, в результате бейсик валит свои переменные в первый банк. Как только куча чуть -чуть забивается - она залетает на данные ДОС. Варианты проявлений глюков бесконечны.

Программа, приведённая в начале сообщения, заполняет ОЗУ примерно такой, отчётливо видимой в памяти строкой:

BFA0-  7B 7C 7D 7E 7E 72 73 74  75 76 77 78 79 7A 7B 7C   шэщччрстужвьызшэ
BFB0-  7D 7D 72 73 74 75 76 77  78 79 7A 7B 7C 7C 72 73   щщрстужвьызшээрс
BFC0-  74 75 76 77 78 79 7A 7B  7B 72 73 74 75 76 77 78   тужвьызшшрстужвь
BFD0-  79 7A 7A 72 73 74 75 76  77 78 79 79 72 73 74 75   ыззрстужвьыырсту
BFE0-  76 77 78 78 72 73 74 75  76 77 77 72 73 74 75 76   жвььрстужвврстуж
BFF0-  76 72 73 74 75 75 72 73  74 74 72 73 73 72 72 00   жрстуурсттрссрр.

Но интересно здесь то, в какой половине ДопОЗУ эта строка хранится. В правильном бейсике - нулевой банк (C408), в неправильном - первый (C409).

И - да - глючная версия не выдаёт ошибок самоконтроля по команде FP.
Версия была относительно распространённой, встречалась в нескольких разобранных коллекциях. Говоря точнее - это целая группа версий. Баг был явно от авторов ИКП-адаптации бейсика. Но, в дальнейшем, был ими исправлен.