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

14 Отредактировано Voldemar0 (23-02-2019 22:47)

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

Прошел всего год и я нашел ещё одну багу... Ну как багу.. Фичу. Копаться подробно лень, но ... но пришлось. Иначе моя прога вела себя довольно странно.

Все знают, что командная строка Бейсика и Сисмона довольно простая в смысле управляющих клавиш. Ну там УПР-X - сброс ввода, стрелки - перемещение курсора, РЕД - переключение режима свободного перемещения.

Режим свободного перемещения - это когда стрелки вправо-влево только двигают курсор, ничего не меняя в буфере ввода. В режиме обычного перемещения стрелка влево выкидывает последний введённый символ, стрелка вправо читает символ с экрана и заносит его в буфер.

Всё это описано в доках, в семёрке эта несложная логика работала идеально.
Есть одна особенность: когда стрелка влево нажата в начале логической строки (то есть из буфера ввода уже нечего удалять), приглашение комстроки выводится повторно, на новой строке.

А вот у девятки этот механизм (обработка стрелки влево) срабатывает не всегда. Часто курсор просто убегает на приглашение, потом на конец предыдущей строки и т.д.
Кажется, я нашел причину. Хотя и не искал специально...

Есть такой код в ИКП-Бейсике:

FD4D -   20 E1 FD   ".АЩ"   JSR   FDE1
FD50 -   49 20 ..   "i."    EOR   #20
FD52 -   F0 29 ..   "П."    BEQ   FD7D
FD54 -   49 B8 ..   "i8"    EOR   #B8
FD56 -   F0 12 ..   "П."    BEQ   FD6A
FD58 -   49 10 ..   "i."    EOR   #10
FD5A -   D0 04 ..   "P."    BNE   FD60
FD5C -   B0 1B ..   "0."    BCS   FD79       <==
FD5E -   90 1D ..   ".."    BCC   FD7D

FDE1 - это процедура COUT - вывод символа на экран. Здесь она выводит символ нажатой клавиши в процедуре ввода строки. Выводит как обычные, так и управляющие символы.
Группы команд EOR/BEQ - это, фактически, переходы на обработчики управляющих клавиш.
Последняя BNE - переход в случае если символ не имеет специальной обработки.
В случае, если была нажата стрелка влево, BNE не срабатывает, управление
передаётся на условные переходы по флагу C (BCS/BCC).

Вопрос: а откуда появится значение для этого флага и для чего нужна проверка этого условия?

Это загадка. Дело в том, что COUT сохраняет A, X, Y (это, кажется, даже в доках упоминается), но C возвращает неопределённым. Нет тут никакой договорённости. А ведь реализации COUT могут быть различными: например, если вывод идёт в файл, на принтер или на графический экран - варианты есть. Вспоминайте команды PR# и IN#.

Куда ведут эти переходы: BCS ведёт на вычитание счётчика буфера (выкидывается послений символ),
а вот BCC ... а BCC переходит снова на ожидание нажатия клавиши (т.е. символ не выкидывается).

Теперь понятно, почему использовано EOR вместо нормального CMP: это ради того, чтобы сохранить флаг C (CMP бы его изменяла). Т.е. авторы фрагмента явно расчитывали на то, что COUT в каком-то случае вернёт определённый C.

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

FD1C-  30 01 ..    ".."     BMI   FD1F
FD1E-  88 .. ..    "."      DEY   
FD1F-  88 .. ..    "."      DEY   
FD20-  60 .. ..    "ю"      RTS   
FC32-  84 24 ..    ".."     STY   24
FC34-  B1 28 ..    "1."     LDA   (28), Y
FC36-  69 DE ..    "и^"     ADC   #DE
FC38-  60 .. ..    "ю"      RTS   
FDFB-  20 5A F8    ".zЬ"    JSR   F85A
F85A-  A4 30 ..    "$."     LDY   30
F85C-  99 00 C1    "..A"    STA   C100, Y
F85F-  60 .. ..    "ю"      RTS   
FDFE-  A4 35 ..    "$."     LDY   35
FE00-  68 .. ..    "х"      PLA   
FE01-  60 .. ..    "ю"      RTS   
FD74-  49 20 ..    "i."     EOR   #20
FD76-  F0 29 ..    "П."     BEQ   FDA1
FD78-  49 B8 ..    "i8"     EOR   #B8
FD7A-  F0 12 ..    "П."     BEQ   FD8E
FD7C-  49 10 ..    "i."     EOR   #10
FD7E-  D0 04 ..    "P."     BNE   FD84
FD80-  B0 1B ..    "0."     BCS   FD9D            <==

Это трасса выполнения команд при нажатии стрелки влево.
Первые три команды - это вычитание номера позиции (колонки) экрана, на которой находится курсор.
Двойное DEY выполняется для режима TEXT32 (когда одно знакоместо состоит из двух байт).
Затем STY 24 сохраняет значение Y в ячейке "позиция курсора".

А вот дальше выполняются загадочные команды:

FC34-  B1 28 ..    "1."     LDA   (28), Y
FC36-  69 DE ..    "и^"     ADC   #DE

Здесь берётся значение кода символа из текущих координат (т.е. после сдвига влево), после чего к нему прибавляется значение $DE + C. Именно эта команда формирует итоговое C, которое, в дальнешем, не изменяется. И никакой другой пользы в этой команде нет, так как вычисленное значение A всё равно будет утеряно при выполнении команды PLA.

В итоге C = 1 в случае если курсор перешёл на символ с кодом > $20 (или $21 - в зависимости от C до исполнения ADC). Таким образом, C = 0 (и стрелка влево не срабатывает), если мы переходим на символ с кодом $20 или меньше.

Внезапно ?

Но зачем ? Ведь этот символ мог появится на экране в результате кучи причин, ещё до вызова процедуры ввода строки ?

Я думаю, причина эта сидит в тёмных глубинах эплсофтного бейсика. Обращали когда нибудь внимание на то, как бейсик форматирует вывод программы по команде LIST ?

]30 ?:?" TESHH HFSFD P[EKR WKKPK
 SKP'"                          
                                
]LIST                           
                                
10  PRINT "TEST": * 301:        
      ! LDAX                    
      ! RTS                     
20  PRINT : PRINT : PRINT       
     "TESTT 123466789"          
30  PRINT : PRINT " TESHH       
     HFSFD P[EKR WKKPK SKP      
     '"                         

Разные загадочные отступы,  разрывы текста...
Обратите внимание как была введена строка 30 (текстовый режим 32x32) и как бейсик её отобразил в LIST.

Но представьте, что вам нужно теперь отредактировать эту строку. Вы подводите курсор к строке, пробегаете её слева направо, но ведь в листинге явно видно, что между словами TESHH и HFSFD появилась куча пробелов. Хотя они в работе и программе не нужны ? Как же от них избавится ?
Очень просто: бейсик при форматировании выводит в эти позиции как раз пробел  с кодом $20. А нормальный пробел имеет код $A0.

Если на пробеле с кодом $20 вы двигаете курсор вправо - он не вносится в буфер, происходит только сдвиг курсора. Это работает и на семёрке тоже.

А вот если вы двигаете курсор влево, семёрка безусловно выкидывает символ из буфера !!!
Т.е. если вы пробежите строку сперва вправо, а потом на несколько позиций влево, вы можете быть удивлены результату - после нажатия клавиши ВВОД строка в программу попадёт совсем не такой, как вы ожидаете.

В бейсике ИКП авторы попытались исправить это - если вы двигаете стрелку влево и она перемещает курсор на пробел с кодом $20, удаления из буфера не происходит.

А, кстати, вы знаете, что при очистке экрана он заполняется именно пробелами $20 ? ;)


Попробуйте: нажмите пробел, и потом дважды стрелку влево.
Появится второе приглашение комстроки.
А теперь очистите экран командой home, переведите курсор, например, на строку вниз и повторите опыт: пробел, две стрелки влево: никакого приглашения не появится.

Итого:
Процедура ввода строки подразумевает, что COUT (процедура вывода символа), при обработке символа $88 (стрелка влево) вернёт C = 1 в случае, если символ в новой позиции курсора имеет код выше $20.

Это не просто недокументированное поведение, а, прямо говоря - нифига не очевидное недокументированное поведение.

15

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

Скажу честно - не знаю, пригодится ли мне эта информация в будущем. Но просто читать очень интересно! Прошу прощения за оффтоп, только хотел дать знать, что данная тема пользуется вниманием :)

16 Отредактировано Voldemar0 (24-02-2019 18:41)

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

Спасибо :)

Инфа пригодится в том случае, если захочешь перенаправить ввод/вывод программ на какое нибудь вновь разработанное устройство. Например, дисплей высокого разрешения. Или устройство распознавания голоса :)

17 Отредактировано avivanov76 (24-02-2019 20:06)

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

Voldemar0 пишет:

Я думаю, причина эта сидит в тёмных глубинах эплсофтного бейсика.

Мне казалось, что режим свободного перемещения был разработчиками Агата переделан, а уж листинг с разными пробелами - так вообще чисто наше изобретение.

В Apple-вском мониторе режим свободного перемещения позволяет двигать курсор клавишами I, J, K, M. Учитывая, что стрелок вверх и вниз на клавиатуре Apple не было, думаю понятно зачем этот режим придумали :)

Кроме того, там была поддержка нескольких ANSI ESC кодов как у терминалов VT-100. То есть, там можно двигать курсор нажимая ESC A, ESC B, ESC C, ESC D, после чего режим ввода автоматом меняется на обычный.

А эплсофтный бейсик выводить "разные пробелы" не умеет, и если редактировать строку, выведенную командой LIST, то в нее попадут все "лишние" пробелы слева. Был хак: POKE 33,33. При этом убирались отступы слева и строчки можно было отредактировать без добавления лишних пробелов (пробелы в конце строки, похоже, игнорируются).

Apple-вская процедура COUT, кстати, флаг C ставит. Там есть инверсный режим вывода текста (переключается командами монитора I и N), а в процедуре делается сравнение кода символа с $A0. Если код больше, то он будет выводиться с учетом этого режима.

18 Отредактировано Voldemar0 (25-02-2019 07:04)

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

> Мне казалось, что режим свободного перемещения был разработчиками Агата переделан, а уж листинг с разными пробелами - так вообще чисто наше изобретение.

Вполне возможно, что это именно наше.

Замечу, что там даже не пробелами форматирование выполняется, а - технически точнее - позиционированием курсора. Т.е. что-то вроде "if ((current_char == ' ') && (HTAB > 24)) { VTAB++; BASCALC(); HTAB = 5; }"
То есть форматирование выполняется без участия COUT.
Если дублировать вывод на другое устройство (принтер, файл..) - в них никакого форматирования уже нет.
А пробелы $20 возникают потому, что при скроллинге очистка последней строки выполняется всё той же процедурой, которая чистит экран.

19

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

Раз уж пошло такое обсуждение, задам свой вопрос.
Почему в ИКП бейсике, при включении 64 колоночного текстового режима командой "TEXT=32(33)", перестает нормально работать все кроме LIST. Должно ли вообще это работать?
Также странно себя ведет команда GET, на первую нажатую клавишу она возвращает непонятное значение, а только на вторую выдает код символа.

20 Отредактировано Voldemar0 (26-02-2019 20:39)

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

> Почему в ИКП бейсике, при включении 64 колоночного текстового режима командой "TEXT=32(33)", перестает нормально работать все кроме LIST. Должно ли вообще это работать?

Не должно.

На семёрке всего 16 мелких (TEXT=, GR=) и 4 крупных (MGR=, HGR=) страниц.
Нулевые страницы лежат на служебных/системных областях ОЗУ.
Когда включается графический режим, бейсик сразу чистит страницу и, через это, серъёзно рушит свою же работу.
Переключение в текстовый режим не очищает страницу, но любой скролинг или вывод не туда, куда надо, тоже убъёт многое.
Если указать номер страницы больше чем число страниц, то старшие биты номера будут отброшены (для текста бит A5 только влияет на выбор текстового разрешения). Соответственно text=16 == text=32 == text 0 и т.д.

На девятке формально страниц гораздо больше (32 и 8), но, в силу специфики адресации (и/или пофигизма), никто не заморачивался расширением драйверов IOSub (т.е. они остались семёрочными, во многом), поэтому получилось ещё смешнее: страницы выше 16й включаются, но вывод происходит в нижние 16 страниц.

Например: text=2 - нормальный режим, text=18 - аппаратно включается 18я страница, но драйвер будет выводить данные во 2ю страницу и т.д. После 32й страницы всё повторяется.

Таким образом, убить ИКП-бейсик можно точно также как семёрочный, но кроме того можно сделать себе сюрприз в регионе 16-31 страниц текста и ещё можно убить бейсик включением 1-й страницы текста - у ИКП там тоже служебная область.

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

Если планируешь писать большую бейсиковскую программу, нужно помнить, что в ИКП сперва идёт text=2, затем ещё 256 байт служебки (так что text=3 тоже не стоит использовать) и потом свободное ОЗУ, в которое падает бейсик-программа. Поэтому если хочешь использовать высшую графику - выбирай старшие номера страниц - MGR=3, HGR=3. Хотя и в этом случае есть риск, что при интенсивных строковых операциях у тебя блок данных бейсика залезет на видеоозу. Чтобы этого избежать, нужно ловко жонглировать параметрами LOMEM: и HIMEM:.

Если большую программу планируешь на Бейсике-60, то лучше подумай ещё раз об ИКП-бейсике.
Потом глубоко вдохни и вникни: бейсик-прога идёт от адреса $800 (TEXT=1) вверх, до .. до куда не жалко.
Переменные идут от $9600 вниз. Пока не врежутся в программу. Текстовая страница по умолчанию : TEXT=15.
Это $7800.$7FFF. Дорастёт ли у тебя до видеоозу программа раньше чем данные или данные забьют текст раньше, чем программа - это ты, как архитектор, должен угадать сам. LOMEM: и HIMEM: действуют.
Куда пихать MGR или HGR - ... думай, тыжпрограммист. :)

В аттаче полезный текст на эту тему.
Он, правда, достался нам не целиком, как бы хвоста нет. Но и то, что есть, очень познавательно.


> Также странно себя ведет команда GET, на первую нажатую клавишу она возвращает непонятное значение, а только на вторую выдает код символа.

http://forum.agatcomp.ru//viewtopic.php?id=136

Post's attachments

Attachment icon basic.txt 50.5 kb, 294 downloads since 2019-02-26 

21 Отредактировано vvhitevvizard (26-08-2021 04:06)

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

Voldemar0 пишет:

Процедура ввода строки подразумевает, что COUT (процедура вывода символа), при обработке символа $88 (стрелка влево) вернёт C = 1 в случае, если символ в новой позиции курсора имеет код выше $20.

Это не просто недокументированное поведение, а, прямо говоря - нифига не очевидное недокументированное поведение.

Потрясающее расследование. Доказывает еще раз, что заигрывание с передачей флага C сквозь множество команд и возвратов - мина замедленного действия. Те, кто будут править код позже (или сам автор какое-то время спустя) - скорее всего забудут и/или не учтут такую особенность.

22

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

Флаг C - тот же аргумент, который может быть для функцций входным или выходным или и тем и другим сразу.
Когда пишешь хоть чуть-чуть неочевидный код, обычно в комментариях сразу указываешь, что у тебя на входе, что на выходе.

;
; Читает байт из канала чтения (abr_Data), возвращает C = 0, если r_IncTimer >= 220
; Таймаут - время одного оборота (~200 мс) + одного сектора (чтобы гарантированно
; прочитать все сектора за один оборот) (~9.5 мс)
;
fd_ReadByte:
 mov    r16, r_IncTimer
 cpi    r16, 220
 brsh   fd_RB_ret               ; TimeOut, C = 0
 
 ldi    abr_Addr, f8r_ctlCtrl
 rcall  ab_Read
; andi  abr_Data, f8m_RdReady
 andi   abr_Data, f8m_BufReady
 breq   fd_ReadByte

 ldi    abr_Addr, f8r_Read
 rcall  ab_Read
 sec
fd_RB_ret:
 ret

(кусок кода моста2)

А тут лазили и правили прямо по скомпилированному, не сильно документированному коду и, наверняка, никто не заморачивался сильно его комментировать даже в процессе правки.
В девятке много чего переделали, но и ошибок новых накидали кучу.
Начиная от сисмона ПЗУшного.

23 Отредактировано vvhitevvizard (26-08-2021 21:09)

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

Я в таких случаях (если код не критический, требующий экономии каждого такта) пришел к использованию специальных "волатильных" ячеек в нулевой странице, соглашение об их использовании было глобальным на все модули программы и каждая подпрограмма могла менять их не восстанавливая. Они же использовались для возврата нескольких значений.

Или прямую правку операндов в коде выполняемой процедуры (что является дурным тоном программирования, но оправданно учитывая острую нехватку регистров общего назначения у 6502). В примере ниже, для процедуры побитового скролла строки текста в двухбанковом графическом режиме, если динамическую модификацию кода заменить на сохранение в стек/в ячейки памяти, код перестанет помещаться на экране, визуально усложняясь и фактически замедляясь в производительности. Локальные метки начинающиеся с M - признак динамически модифицируемых строк. Я даже не использую Y (только A и Х) - Y я освободил планируя добавить дополнительные эффекты при скроле, но так и не реализовал. ;)

Spoiler
;_________гор бегущ строка, сдвг по тчк влево
RUNNING_TXTLINE
;  вх: Y-мл,A-ст куда; адр сдвг сект
;  X-откуда: ст адр сдвг симв
;  яч C - длн сдвг сект
    STY _M1+1         ;мл куда
    STX _M2+2         ;ст адреса сдвигаемого симв
    JSR _1

    INC _M2+2
    LDA _M3+2
    EOR SCR

_1
    STA _M3+2         ;ст куда
_M1
    LDA #0
_3
    TAX             ;нач внешн цикла
_M2  ;H dyn.mod.
    ASL SEG1,X     ;сдв 1 байт симв (из 8)
    STX _M3+1         ;мл адр
    LDX C             ;мини-цикл: сдв 1 стрк
_4
_M3 ;LH dyn.mod.
    ROL SEG1,X
    DEX
    BPL _4

    CLC
    ADC #40h
    BCC _3
    RTS             ;вых: X=FF

От такой практики на x86 я конечно же отказался - спасибо локальным переменным в стеке и удобной адресации.

Спасибо за ссылку. Как всегда от Возняка, интересное чтение от уникального человека создавшего компьютер и базовый софт к нему единолично. :)
В соседней теме мы обсуждали теоретическую возможность эмуляции CHAIN и EXEC операторов для склеивания двух A-файлов во время исполнения. Здесь Воз упоминает немного о раздельном хранении операндов и операторов в его целочисленном Бейсике:
I pushed the operand references onto one stack and operator codes onto another.
На диске в A-файле это тоже хранится раздельно?

24

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

Я думаю, что в эпловском бейсике всё шло подряд, а длиных переменных не было ни в applesoft ни в integer basic.
Их ввели в агатовском бейсике.
Но можешь проверить.
В агатовском таблица длиных переменных храниться в файле после тела программы.

А вот что в памяти происходит - тема отдельной диссертации, ей Волков занимался, и где-то на форуме она даже приаттачена. Может быть как раз в этой ветке.

25 Отредактировано vvhitevvizard (27-08-2021 02:21)

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

Почитав немного "Бейсик. Тонкости при программировании" выложенный тобой в топике (за что отдельное спасибо!),
призадумался над работой со строками (в Бейсик-60 и Бейсик-ИКП?):
Поиск строки осуществляется так, интерпретатор переходит к самой первой строке программы и проверяет 'это строка 15000?', если нет, то он переходит к следующей строке и производит аналогичную проверку, и т.д. до тех пор, пока он не найдет строку 15000.
Это точно? Используется линейный неэфективный поиск в массиве двухбайтовых значений которые, как мне казалось, могли бы храниться и отсортированными чтобы использовать двоичный поиск итд.

Есть и еще одна деталь оператора GOTO (GOSUB). Код оператора занимает в памяти 1 байт, а следующий за ним номер столько байт сколько цифр в этом номере. Например, оператор GOSUB1 займет в памяти 2 байта, а оператор GOSUB15000 - 6 байт. Допустим, что у нас в программе есть сто операторов GOSUB15000, теперь если мы их заменим на оператор gosub1, а подпрограмму со строки 15000 перенесем в 1-ую строку, то мы высвободим (только этим переносом) 400 байт памяти.
Тут вообще я в шоке. Номер строки в поле операнда хранится в символьном виде даже во время исполнения??? Документ правда писался под Агат-7 и с тех пор могло что-то измениться в том же BasicMaster95.

Но если это все верно, то управляющий код в моем случае надо помещать в шапку программы. :) Чисто академическая оптимизация, понятно, что самое адекватное вообще отказаться от Бейсика.