ОШИБКИ ПЗУ
Обзор по материалам зарубежной печати
Сегодня, уважаемые читатели, мы продолжаем разговор о вскрытых ошибках и неточностях стандартного ПЗУ "Спектрума". Начало статьи см. в предыдущем выпуске на стр. 209 - 210.
8. Ошибка CLOSE#.
Профессионалы считают эту ошибку наиболее серьезной из всех. С точки зрения рядового пользователя она, может быть, таковой и не является, поскольку ему редко приходится иметь дело с нестандартными каналами и потоками.
Ошибка проявляется в тех случаях, когда внешняя периферия, имеющая собственное ПЗУ для обслуживания каналов и потоков не подключена. В этой случае, если вы дадите команду на закрывание потока CLOSE #n, а сам поток #n никогда перед этим и не открывался, то Ваш "Спектрум" вместо того, чтобы предупредить Вас о том, что синтаксис неверен, зависает, а иногда (реже) сбрасывается.
Ошибка вызвана тем, что таблица данных, находящаяся в ПЗУ по адресу 1716Н (5910 DEC) не заканчивается, как ей положено, нулевым байтом 00.
Интересно отметить, что и фирма "AMSTRAD", перекупив у К.Синклера права на производство "Спектрум"-совместимых машин, не исправила эту ошибку в ПЗУ для "Spectrum+2", хотя другие изменения в ПЗУ сделала. Казалось бы, уж если все равно меняешь ПЗУ (чего не делал сам К. Синклер дабы не снизить совместимость программного обеспечения и не огорчать простых пользователей), то можно было бы и исправить этот дефект.
9. Ошибка CHR$ 9.
Управляющий код CHR$ 9 должен был действовать противоположно коду CHR$ 8. Код CHR$ 8 называется BACKSPACE и вызывет перемещение курсора (текущей позиции печати) влево. Код же CHR$ 9 должен был бы по аналогии называться FORWARDSPACE и вызывать перемещение курсора или позиции печати на одно знакоместо вправо без изменения содержимого текущего знакоместа.
На практике он не делает ни того, ни другого. Более того, в результате его применения, содержимое текущей позиции окрашивается в текущие цвета INK и PAPER, т.е. в нем заложена двойная ошибка.
Листинг 1
HL хранит адрес вершины стека калькулятора.
Запомнили вершину стека в системной переменной X PTR. Вызвали процедуру ПЗУ PRINT FP. при этом пятибайт-ное число снялось с вершины стека калькулятора. восстановили старый адрес вершины стека к-ра, т.е. теперь HL указывает на новый адрес вершины стека + 5. Число FFFBH равно -5 DEC (по правилам двоичной дополнительной арифметики. Теперь HL указывает на новую вершину стека калькулятора. Запомнили ее в соответствующей системной переменной.
2А655С PR_FP_OK LD HL,(STKEND)
FD362600 LD (X_PTR),00 ; Погасили старший байт указателя Х PTR.
C9 RET ; Возврат.
Первый недосмотр состоит в том, что процедура ПЗУ, выполняющая перемещение курсора вправо (0A3DH = 2621 DEC) должна бы заканчиватъся не командой возврата RET, а командой безусловного перехода JP 0ADC. Вторая же ошибка, связанная с цветом состоит в том, что когда эта процедура работает, надо запоминать состояние системной переменной MASK_T (5C8FH =23695 DEC), затем выставлять в ней число 0FFH, а по окончании работы процедуры восстанавливать запомненное значение.
10. Ошибка CHR$ 8.
Есть ошибки и в процедурах, выполняющих перемещение курсора влево. В большинстве случаев с кодом CHR$ 8 все в порядке, но, к сожалению, не всегда.
Так, если у вас текущей позицией печати является знакоместо с координатами AT 1,0; то BACKSPACE не работает.
Более того, есть возможность смещения влево из координаты 0,0; а это уже совершенная чепуха с неожиданными результатами.
Ошибка находится в процедуре, обслуживающей перемещение курсора влево (0А23Н = 2595 DEC). Она проверяет номер экранной строки, на которой установлен курсор, но вместо того, чтобы "отловить" нулевую строку и заблокировать в ней перемещение курсора, делает это для первой строки. Очевидно, в команде программистов у К. Синклера была некоторая несогласованность. Конкретная причина - в том, что по адресу 0А33Н = 2611 DEC должно быть число 019Н вместо 018Н.
11. Ошибка STR$.
Эта ошибка проявляет себя как в БЕЙСИКе, так и при программировании в машинном коде. Вызвать ее очень просто:
PRINT "KU-KU" + STR$ 0.5
По такой команде компьютер напечатает только 0.5.
Программисты, работающие в машинном коде, могут столкнуться с этой ошибкой при вызове часто встречавшейся процедуры PRINT_FP. Эта процедура находится по адресу 2DE3H = 11747 DEC и служит для того, чтобы выдать на печать по текущему подключенному каналу то действительное число, которое в данный момент находится на вершине стека калькулятора.
Процедура, обрабатывающая оператор STR$ в своей работе тоже обращается к процедуре FRINT_FP и таким образом эта ошибка проникает и в БЕЙСИК.
Эта ошибка происходит в тех случаях, когда число на вершине стека калькулятора находится в интервале от -1 до +1, исключая границы и число 0. Дело в том, что на вершине стека оставляется ошибочный ноль, который и вызывает все проблемы.
Для тех, кто работает на БЕЙСИКе, эту проблему обойти несложно. Достаточно ввести временную переменную, например так: LET a$=STR$ 0.5 PRINT "KU-KU" + a$
Для тех, кто работает в машинном коде, в этом случае лучше не пользоваться процедурой PRINT_FP, а заменить ее какой-либо своей, например приведенной в Листинге 1.
12. Ошибки кодов управления цветом.
Если в качестве текущего канала для выдачи информации Вами выбран какой-либо нестандартный канал (иначе говоря, если вы работаете с пользовательским каналом), то операторы управления цветом, например такие, как PAPER 4 дадут сообщение об ошибке
С; Nonsense in BASIC.
Если подпрограмма, обслуживающая вывод информации в канал, возвращает после своей работы выключенный флаг C (флаг CARRY флагового регистра F).
Чтобы избавиться от ошибки, необходимо предусмотреть, чтобы все процедуры, обслуживающие вывод информации в каналы, возвращались с выключенный флагом CARRY по крайней мере для управляющих кодов от 10 до 15-го, а также для тех параметров,
которые следуют за кодами управления цветом.
С этой ошибкой связана и еще одна, касающаяся кодов управления цветом, но здесь она относится только к операторам временного изменения цвета, т.е. к тем случаям, когда оператор управления цветом является квалификатором оператора PRINT.
Итак, если в качестве текущего установлен канал, отличный от "S" или "К", т.е. последний оператор PRINT печатал не на экран, то команды установки временных цветовых атрибутов в операторе PRINT по ошибке выдадут цветовой код не в текущий, а в предыдущий канал.
Ошибка связана с неспособностью обслуживающей процедуры избрать канал "S" для этой цели по адресу 21Е1Н = 8673 DEC.
Обойти ошибку можно, если перед каждой командой временной установки цвета вставить пустой оператор PRINT:
PRINT;:INK 4
И еще одна смежная ошибка, которая относятся только к 128-килобайтным машинам, а точнее говоря - к тому порту RS232, который в них встроен.
На этих моделях команда:
LPRINT INK 4
вызовет сообщение об ошибке:
С; Nonsense in BASIC
Этому есть две причины. Во-первых, программа, которая обслуживает вывод на новый канал "p" (встроенный порт RS232) почему-то ошибочно полагает, что за кодом управления цветом должны идти два параметра, а не один. А во-вторых, соответствующая процедура ПЗУ включает флаг CARRY, а к чему это приводит Вы уже знаете.
В идеале на этих моделях по адресу 0086D (для Sp+128) и 0088С (для Sp+2) должен стоять байт 02 вместо 01, и, кроме того, по адресу 0087С (для SP+128) и 0089В (для Sp+2) вместо инструкции CCF
должна стоять пара:
SCF CCF
13. Ошибка SCREEN$.
Эта ошибка похожа на ошибку STR$. При расчете функции SCREEN$ из БЕЙСИКа на вершине стека калькулятора полученный результат ошибочно дублируется.
В результате такое выражение, как: IF "x" = SCREEN$ (0,0) THEN PRINT "KU-KU"
всегда будет печатать "KU-KU", независимо от того, что имеется на экране в позиции с координатами (0,0). К счастью, эта ошибка характерна только для БЕЙСИКа. Соответствующая процедура ПЗУ при вызове ее из машинного кода работает нормально (о том, как ее использовать, мы писали в книге "Элементарная графика", т.1).
В БЕЙСИКе путь обхода этой ошибки прост и выполняется переприсвоением переменной:
LET a$ = SCREEN$ (0,0): IF "x" = a$ THEN PRINT "KU-KU"
Ошибки в редакторе
Есть несколько оплошностей, связанных со встроенным редактором "Спектрума".
14. Ошибка Scroll?.
Когда в нижней части экрана при листинге программы появляется сообщение Scroll? или когда там появляется какое-либо сообщение, связанное с обслуживанием магнитофона, то компьютер ждет от вас нажатия какой-либо клавиши.
Проблема состоит в том, что есть несколько комбинации клавиш, которые нажимать при этом нельзя.
Это TRUE VIDEO, INV VIDEO, CAPS LOCK, GRAPHIC и EXTEND MODE. Если Вы их нажмете, то вместо продолжения работы получите в нижней части экрана последнюю редактированную строку с включенным курсором. Самое интересное, что на 128-килобайтных машинах, работающих в режиме 128К этот курсор будет соответствовать режиму 48К.
Ошибка находится в процедуре, обслуживающей ввод с клавиатуры KEY_INPUT, которая находится в ПЗУ по адресу 10A8H = 4264 DEC. Эта процедура обрабатывает такие комбинации, как CAPS LOCK и пр., но этого не надо делать в данном случае, когда компьютер просто ждет нажатия какой-либо клавиши в ответ на свое сообщение.
15. Ошибка курсора текущей строки.
Эта ошибка проявляется только на 48-килобайтных моделях, поскольку редактор 128-килобайтных машин сильно отличается. Наберите:
9000 PRINT9001EDIT
(здесь символ "" обозначает нажатие клавиши "ENTER").
Если при этом у Вас в программе нет строк с номером, большим, чем 9000, то Вы увидите эту ошибку в нижней части экрана, и строке редактирования появится курсор текущей строки (символ ">").
Этой ошибки не было бы, если бы программа OUT_LINE, расположенная в ПЗУ по адресу 1855Н = 6229 DEC, проверяла бы четвертый бит системной переменной FLAGS2 и, когда он включен, не печатала бы курсор ">".
16 Ошибка ведущего пробела.
Вы знаете, что операторы и функции БЕЙСИКа в "Спектруме" набираются не по буквам, а словами (токенами). Для того, чтобы отдельные ключевые слова на экране не сливались друг с другом, встроенный редактор автоматически вставляет между ними пробелы, но делает это не очень стабильно, попробуйте, например: CLS: FOR i=1 ТО 5: PRINT CHR$ 244: NEXT i
В принципе, для того, чтобы компьютер мог решить, когда надо давать ведущий пробел, а когда нет, существует системная переменная FLAGS, когда ведущий пробел не нужен, нулевой бит этой системной переменной должен быть включен.
Проблема была бы решена, если бы этот флаговый бит автоматически включался всякий раз после исполнения команды CLS или при печати управляющих кодов от 00 до 1FH.
17. Ошибка K-режима.
Если при работе с компьютером у Вас включен курсор "К", это означает, что машина находится в командном режиме и следующее нажатие клавиши должно будет восприниматься, как ввод ключевого слова оператора или функции. Например, нажатие на клавишу "p" в этом режиме даст ввод ключевого слова PRINT.
А что будет, если Вы задержите палец на клавише дольше, чем это необходимо? В этой случае, как положено начнется автоповтор, но K-режим останется принудительно включенным.
Одним словом, если Вам надо сделать например: PRINT P или NEXT N
то Вы получите вместо этого
PRINT PRINT или
NEXT NEXT
Ошибка находится в процедуре K_REPEAT, расположенной в ПЗУ по адресу 0310H = 784 DEC.
Чтобы ошибки не было, процедура должна вычитать число А5Н из кода нажатой клавиши в тех случаях, когда этот код больше, чем Е5Н. Тогда повторное ключевое слово будет заменено на прописную литеру.
18 Ошибка проверки синтаксиса.
Эта ошибка проявляется только на машинах 48К, а на машинах 128К она мудро игнорируется.
Дело в том, что у Вас есть возможность ввести в программную строку такие ключевые слова, как ERASE, MOVE, FORMAT, CAT, например
ERASE симв. строка
MOVE строка, строка
FORMAT строка
CAT
Очевидно, что эти команды не могут быть выполнены, если у Вас не подключена соответствующая периферия, например INTERFACE ONE с микродрайвом.
И, конечно, при запуске программы на исполнение, она будет прервана с сообщением об ошибке. Спрашивается, почему же нельзя было отловить эту ошибку при проверке синтаксиса перед вводом строки в память?
На 128-килобайтных машинах тоже можно ввести такие ключевые слова, но при запуске программы они будут игнорироваться и восприниматься так, как воспринимается оператор RЕМ.
Ошибки калькулятора
Теперь рассмотрим несколько ошибок, связанных со встроенным в ПЗУ калькулятором. О некоторых из них мы так или иначе уже упоминали в своих прочих работах.
19. Ошибка MOD_DIV.
Эта ошибка связана с работой кода калькулятора 32h. По этой команде со стека калькулятора должны сниматься два верхних пятибайтных числа, например x и y и вместо них на стек должны отправляться
x MOD y и x DIV y
(именно в этом порядке).
Напомним, что x MOD y - это остаток от целочисленного деления x на y, а x DIV y - это целая часть частного от деления x на y.
Таким образом,
x MOD y = x - y*INT(x/y) x DIV y = INT (x/y)
В своих расчетах процедура, обслуживающая эту функцию калькулятора, использует нулевую ячейку памяти калькулятора M0, а с этой ячейкой есть одна особенность. Дело в том, что при вычислении функции INT эта ячейка коррумпируется, если аргумент при INT меньше нуля. Таким образом, функция MOD_DIV калькулятора дает неверный результат, когда x/y число отрицательное.
Ошибки могло бы и не быть, если бы процедура, занимающаяся расчетом этой функции (а она расположена в ПЗУ по адресу 36A0H = 13964 DEC) использовала бы в своих расчетах не нулевую ячейку памяти калькулятора, а первую (M1).
20. Ошибка E_TO_FP.
В системе команд калькулятора есть команда с кодом 3C. Ее назначение - умножение числа, находящегося на вершине стека калькулятора на множитель, равный 10 в степени A, где A - содержимое аккумулятора микропроцессора.
Вся неприятность в том, что калькулятор после своего включения командой RST 28 не резервирует содержимое аккумулятора, в отличие от содержимого регистра B. Поэтому, к тому времени, как вы воспользуетесь командой калькулятора 3C, есть большая вероятность того, что в аккумуляторе будет не подготовленное вами число, а что-то совсем другое.
Единственный выход - выйти из калькулятора, прогрузить аккумулятор нужным Вам числом, выполнить нужное умножение вызовом процедуры ПЗУ E_TO_FP и снова вернуться в калькулятор:
Endcalc
LD A, xx CALL 2D4FH RST 28
Процедура E_TO_FP находится в ПЗУ по адресу 2D4FH = 11599 DEC.
21. Ошибка INKEY$#0.
Обычно нулевой поток представляет собой клавиатуру, поэтому естественно предположить, что INKEY$#0 - то же самое, что и просто INKEY$ без номера потока.
Тем не менее это не так, и почти необратимо INKEY$#0 выдает пустую символьную строку, что делает эту функцию полностью бесполезной.
Надо также заметить, что в системе команд калькулятора есть команда с кодом 1А, которая служит для расчета функции INKEY$#X, где X - число, содержащееся на вершине стека калькулятора. И эта команда калькулятора будет бесполезной, если поток X представляет клавиатуру.
Ошибка находится в подпрограмме ПЗУ по адресу 1634Н=5684 DEC, которая устанавливает канал "X" - текущим каналом. В этой подпрограмме по адресу 1638К стоит ошибочная команда RES 5,(FLAGS), выключающая пятый бит системной переменной FLAGS. в результате этого ошибочно отбивается любое нажатие клавиши вместо того, чтобы быть принятый к рассмотрению.
Ошибку можно было бы исправить, если в подпрограмме READ_IN (3645Н = 13893 DEC) сохранить значение системной переменной FLAGS на время вызова подпрограммы CHAN_OPEN (1601Н = 5633 DEC).
На этом мы заканчиваем обзор ошибок и неточностей в ПЗУ стандартного компьютера "ZX-Spectrum". Конечно же это не все из того, что оттуда можно выудить, но очень экзотические ошибки, которые проявляются например только на машинах типа "ZX-Spectrum+2" и только при подключенном Интерфейсе-1 мы не рассматриваем, поскольку вероятность встретить среди миллионов наших пользователей подобную конфигурацию конечно есть, но она не более сотой доли процента.
Обзор подготовлен по материалам зарубежной печати; основные первоисточники:
1. Dr. Yan Logan, Dr. Frank O'Hara. "The Complete Spectrum ROM Disassembly".
2. Dr. FranK O'Hara "Understanding Your Spectrum".
3. Dr. Yan Logan "Understanding Your Spectrum".
4. Andrew Pennell "Master Your ZX Microdrive".
5. Tony Stratton "Understanding Your Spectrum".
6. Paul Harrison "Understanding Your Spectrum".
7. Stephen Kelly & others "Understanding Your Spectrum".
8. Chris Thornton "Understanding Your Spectrum".
Алексеев А.Г.