БЕССИСТЕМНЫЕ ЗАМЕТКИ НА СИСТЕМНЫЕ ТЕМЫ
© Сергей Крушинский, 1994.
Программистов часто делят на "системщиков" и "проблемщиков". Первые расширяют возможности машины, вторые решают народно-хозяйственные задачи. Первые вырезают в скале дом при помощи напильника - то есть пытаются решать на Ассемблере глобальные задачи, вторые строят собачью конуру из железобетонных блоков -используют языки высокого уровня, где надо и где не надо. Еще бывают взломщики.
Одна из отличительных черт нашего "синклериста" - помимо тех, что были справедливо упомянуты в редакционном предисловии к моему очерку "SPECCY + АСТРОЛОГИЯ", № 9/10, 1993 - это то, что ему приходится сочетать в себе качества "системщика", "проблемщика" и "взломщика" одновременно - иначе со "Спектрумом" мало чего добьешься. Моей первоначальной задачей было написание астрологической программы. В ходе работы над программами "Protheus", "Sphera" и др., я столкнулся с проблемами, из-за которых пришлось лезть в дебри. А чем дальше в лес, тем больше дров!
"Горячие" клавиши.
Самый распространенный способ организации диалога программы с пользователем - это система разветвленных меню. Например, в меню - 1 предлагается: а), б), в).
Для а) предусматривается свое меню: а.а), а.б), а.в). И так далее. Такой подход универсален. Однако некоторые сервисные операции удобнее вызывать не из конкретного меню, а из любого места программы. Скажем, переключение клавиатуры из латиницы в кириллицу и обратно, вызов help^, копирование экрана на принтер, настройку каких-то параметров.
Во многих системах для этой цели предусмотрены так называемые функциональные клавиши. Расположены они в верхнем ряду и обозначены: F1, F2,...,F6. Бейсик компьютера MSX располагает командой ON KEY...GOSUB... Встретив строку ON KEY 1 GOSUB 300, интерпретатор будет знать: если пользователь нажмет клавишу F1, надо прервать программу, перейти к операторам, расположенным с указанной строки, а затем вернуться обратно.
В спектрумовском Бейсике ничего подобного нет, хотя 50 раз в секунду интерпретатор прерывает нормальный ход работы и проверяет, не нажата ли "BREAK". На Ассемблере организовать "горячие" клавиши несложно. Приведенный ниже фрагмент расположен с адреса 60199. Он состоит из двух частей: 1) инициализация прерываний IM2 и 2) обработка прерываний (с адреса 60208). Первую часть, длиной в 9 байтов, можно было бы расположить где угодно. Я предпочел вплотную присоединить ее к следующей. Вызывается она командой RANDOMIZE USR 60199. Ничего особенного не произойдет. Можно, как обычно, писать, отлаживать и запускать программы. Только теперь, если включить графический режим (Caps-Shift/9), машина как бы "зависнет" и будет "висеть" до тех пор, пока Вы не нажмете "1", "2", "3"..."5" - клавиши, к каждой из которых привязана написанная Вами процедура. Почему я сделал посредником графический режим вместо того, чтобы сразу опрашивать цифры? - В противном случае процедура годилась бы только на то, чтобы включить ее в работающие программы, да и то лишь в те, где не надо вводить числовые данные. Компьютер не стал бы спрашивать, с какой целью нажата, скажем, единица, а тупо перешел бы к соответствующей подпрограмме. А так можно и программировать. Только символы псевдографики придется вводить при помощи CHR$(), как в режиме KEYWORDS 1 Beta-Basic-a. Зато к "функциональным клавишам" можно присоединить такие полезные операции, как сохранение программы, перенумерация строк, переключение знакогенератора.
Листинг_1.
PORT1 EQU #FEFE ; Полуряд клавиатуры от CS до V
PORT2 EQU #EFFE ; Полуряд клавиатуры от 0 до 6
PORT EQU #F7FE ; Полуряд клавиатуры от 1 до 5
Обработка прерываний начинается с адр. #EB30 (60208) Это число содержится в ячейках ПЗУ 12543/12544, т.е. ячейка 12542 служит указателем. Для организации прерываний надо поместить в I число, равное (указатель-255)/256. Включаем прерывания 2-го типа.
Теперь процедура обработки прерываний:
;Временное запрещ. прерываний во избежание ;Сохранение регистров ;для корректного возврата; ;вначале, пока программа не отлажена, ;лучше перестраховаться, ;заранее сохранив все.
DI
PUSH PUSH PUSH PUSH PUSH PUSH
|
CALL |
CHECK ; |
К подпрограмме проверки клавиш. |
|
|
|
Далее можно вызывать другие - например, часы. |
|
POP |
IY ; |
Восстановление регистров. |
|
POP |
IX ; |
|
|
POP |
AF ; |
|
|
POP |
BC ; |
|
|
POP |
DE ; |
|
|
POP |
HL ; |
|
|
JP |
#0038 ; |
Возврат с использованием процедур ПЗУ. |
CHECK |
LD |
BC,PORT1 |
^ерва проверка, нажаты ли одновременно |
|
|
|
СарБ-Shift/"9". |
|
IN |
A, (C) |
Считаем байт из порта нижнего левого полуряда. |
|
AND |
%00000001 |
Выключаем все биты, кроме нулевого - он отвечает |
|
|
|
за Caps-Shift. |
|
LD |
E,A ; |
Временно сохраняем байт в регистре Е. |
|
LD |
BC,PORT2 |
Теперь то же для "9". |
|
IN |
A, (C) |
Считаем байт из порта соответствующего полуряда. |
|
AND |
%00000010 ; |
Маскируем все биты, кроме первого - "9". |
|
OR |
E ; |
Сравниваем А и Е. Если в Е сброшен нулевой бит, |
|
СР |
0 |
а в А - первый, в регистре А остается нуль; |
|
|
|
т.е. нажаты Caps Shift/"9". |
|
RET |
NZ |
иначе - возврат. |
KEYS |
LD |
BC,PORT3 |
Теперь надо ждать нажатия цифровой клавиши. |
|
IN |
A, (C) |
|
|
BIT |
0,A ; |
Клавише "1" соответствует нулевой бит байта из |
|
|
|
порта #F7FE. |
|
JP |
Z,KEY_1 |
Если он сброшен - переход |
|
|
|
к пользовательской процедуре KEY_1. |
|
BIT |
1,A ; |
Точно так же проверяем остальные 4 клавиши. |
|
JP |
Z,KEY_2 |
|
|
BIT |
2,A ; |
|
|
JP |
Z,KEY_3 |
|
|
BIT |
3,A ; |
|
|
JP |
Z,KEY_4 |
|
|
BIT |
4,A ; |
|
|
JP |
Z,KEY_5 |
|
|
JR |
KEYS ; |
В противном случае - зацикливание. |
KEY_1 |
... |
; |
Здесь может быть инструкция к Вашей программе |
|
RET |
; |
возврат в MAIN. |
KEY_2 |
... |
; |
А здесь - переключение знакогенератора. |
|
RET |
; |
|
KEY_3 |
... |
; |
Можно также предусмотреть принудительное |
|
|
; |
выполнение команды STOP. |
KEY_4 |
... |
; |
Или переход к заданной Бейсик-строке. |
KEY_5 ... ;Копия экрана.
RET ;
. Мне кажется, процедуру KEYS хорошо было бы начать с печати меню в верхней или нижней строке
экрана:
<1> HELP <2> RUS/LAT <3> STOP и т.д. Только при печати лучше не связываться с ПЗУ. Как известно, при включенном режиме IM2 нельзя обращаться к TR-BOS. Если Вы планируете это сделать, временно перейдите на стандартные прерывания (для этого надо написать коротенькую процедуру, повторяющую 1 часть, только вместо команды IM 2 в соответствующем месте должна быть команда IM 1.
Русификация под EPSON. Когда я стал писать программы, выдающие текст по-русски и рассчитанные на широкий круг пользователей, оказалось, что одна из главных трудностей - это их адаптация к принтеру. Самый распространенный способ
русификации "Спектрума" - через настройку системной переменной CHARS на адрес набора русских символов, расположенных в порядке, который предписан стандартом KOH-7 - имеет ряд недостатков. Скажем, для сортировки символов нужна специальная подпрограмма-перекодировщик. Чтобы текст распечатывался на принтере, надо дать команду самому принтеру: "перейти на русский символьный набор". Если идет смешанный русско-латинский текст, то принтер приходится переключать постоянно. Ничего архисложного тут нет, беда лишь в том, что у разных принтеров разные команды. Да еще и с их интерфейсами царит полная неразбериха. К примеру, у меня к компьютеру присоединена микросхема интерфейса ZX-LPRINT III, а принтер MC-6312, EPSON - совместимый. Для того чтобы перейти в КОИ-7, надо дать команды: LPRINT CHR$ 1;"1"; CHR$ 14. Первые два кода уведомляют интерфейс, что последний код (14) - команда принтеру. Между прочим, из машинного кода это сделать не так просто, как кажется на первый взгляд. Спрашивается, откуда мне знать нюансы других интерфейсов и принтеров (тем более что о собственном-то я из-за недостатка справочной литературы знаю далеко не все?. Один из выходов - написать дополнительную программу для настройки принтера - как поступили авторы "ARTSTUDIO". К сожалению, и здесь всего не предусмотришь - как с тем же "ARTSTUDIO" не предусмотрели термоструйную печатающую головку.
Другой недостаток: пользователю не так-то просто во всем разобраться, если только он не программист-системщик. Собственно говоря, рядовой пользователь и не обязан знать подробности, которые потребует от него программа-настройщик. Скорее всего, он будет действовать методом тыка, раз за разом машина будет зависать; наконец, человек плюнет и, если рядом нет женщин и детей выскажет ряд "комплиментов" в адрес автора.
Согласитесь, не так уж много у нас программистов, которые могут решить эту задачу грамотно. Я не отношу себя к их числу и потому предлагаю поступать по-другому: использовать кодировку, получившую название "ГОСТ-альтернативная". В этом стандарте способен работать любой русифицированный EPSON-или IBM-совместимый принтер, начиная с упомянутого MC-6312. Коды от 64-го до 127-го - это обычная ASCII-таблица. Дальше идут русские буквы в алфавитном порядке и пустые ячейки, где можно разместить любые символы (аналог UDG). Последний код 239 - "я". (Напомню, что в ПЗУ "Спектрума" коды, начиная со 128го соответствуют символам псевдографики и ключевым словам Бейсика). Таким образом, вся таблица занимает 1664 байт (по 8 байтов на каждый символ).
В чем удобства? Во-первых, в последние годы EPSON приобрел репутацию стандарта. Принтеры этого типа распространились повсеместно. Нет ничего зазорного, если программист заявит, что его программа рассчитана на EPSON (попробуй он сказать, что на "Роботрон" - на смех поднимут!). Во-вторых, если это необходимо, принтер достаточно один раз в начале программы настроить на кодировку "ГОСТ-альтернативная" и забыть о нем. Можно не обременять этим программу - на принтере есть кнопка, которая служит для того же. В-третьих: никаких хлопот с сортировкой русских слов.
. Вначале надо создать, используя графический редактор, новую таблицу символов - латинских и русских, не помешают и символы графики - курсор, какие-нибудь галочки, крестики и т.п. (их коды: 176-223).
Затем - это, пожалуй, самое трудоемкое дело - придется написать собственную, привязанную к ПЗУ, процедуру экранной печати. Впрочем, примеры таких процедур не раз и не два приводились в публикациях "Инфоркома". Проще всего взять и переписать одну из них. Наконец, не помешает и собственная процедура ввода, учитывающая возможность работы с верхней частью символьной таблицы.
Так или иначе, большинство серьезных прикладных и игровых программ используют собственные процедуры ввода-вывода. Встречали ли Вы хоть раз "фирменную" игру, где ввод данных (например, Вашего имени) выглядел так же, как в стандартной команде IMPUT, привязанной к нижней строке экрана? Достаточно один раз отмучиться, написать процедуру ввода-вывода любой сложности, а потом использовать ее во всех программах. (Фирма Hisoft использует один и тот же редактор в Ассемблере, Паскале и Си). Можно обращаться к ним при обработке прерываний второго типа, не рискуя нарваться на неприятности, как бывает в случае с RST #10. Можно печатать не 32, а 64 символа в строке. Короче говоря, одним выстрелом убиваешь сразу нескольких зайцев.
Долой ЯВЕРТЫ - да здравствует ЙЦУКЕН!
Разумеется, для ввода символов "А"..."Я" ГОСТ-альтернативных, надо написать драйвер клавиатуры, иначе говоря, объяснить машине: "если включен русский режим и нажата клавиша "А", то печатай не символ с кодом 65, а символ с кодом 128". То же и для других клавиш. Спрашивается, как привязать к английским клавишам русские буквы? Многие синклеристы, среди них - авторы русифицированных версий текстовых редакторов "THE LAST WORD" и "TASWORD", предпочитают, чтобы наша "Р" соответствовала латинской "R', наша "Б" - латинской "B" и т.д. Таким образом, вместо английского "QWERTY" - "ЯВЕРТЫ". По-моему, это чистое недоразумение! Это совершенно не соответствует печатной машинке, а потому профессиональный секретарь, переводчик, журналист скорее вернется к ней, чем станет переучиваться. Нет, конечно, и я могу, скрепя сердце, набрать в редакторе "RUSTAS" имена и телефоны друзей. Но как бы я работал с текстами, если бы не обладал "THE LAST WORD''-ом, русифицированным под пишущую машинку фирмой "Кординал" - ума не приложу. Наверное, пришлось бы писать собственный редактор, а не заметки. Во всяком случае, если мы хотим, чтобы "Спектрум" использовался не только как детская игрушка, путь может быть только один - русификация под "ИЦУКЕН" (или "ЦУКЕНГ"). Как же добиться столь благородной цели?
. Предположим, у нас уже написана процедура, выполняющая сканирование клавиатуры и, в случае, если поступает печатный символ, возвращающая его код в переменной LETTER. Перед тем, как выдавать его на экран и помещать в INPUT-буффер, надо вызвать подпрограмму DECODE (перекодировка). При этом используется ячейка REGIME, которая содержит 0, если режим латинский и 1 - если русский.
Листинг_2.
если в LETTER - 0, немедленный возврат.
В родном алфавите больше букв, чем в английском, и некоторые символы не уместятся на нижних трех рядах - их придется привязать к значкам вроде доллара и "тюрьмы".
CP JP CP JP CP JP CP JP CP JP CP JP CP JP CP RET
Z,JAIL
Z,DOLLAR " % "
Z,PROCNT "&"
Z,AND_
Z,APOSTR
",ZKOB_1
","KOB_2
64
C
в остальных случаях если код < 64 - возврат. Цифры и знаки препинания незачем перекодировать.
SUB 63
LD B, A
LD HL,CODTAB-1
LOOK INC HL
DJNZ LOOK
LD A, (HL)
FOUND LD (LETTER),A RET
В таблице, расположенной с адреса CODTAB, даны коды, соответствующие кодам из нижней части ASCII. Первый - 158 "Ю", соответствует коду 64 "@", второй - 148 - коду латинской "А" и т.д.
Пропускаем нужное число байтов. Теперь HL указывает на нужный код,
который помещается в переменную LETTER вместо исходного. Можно выйти из "DECODE" и перейти к печати.
JAIL |
LD |
A,238 |
|
" ю" |
вместо |
"#" |
|
JR |
FOUND |
|
|
|
|
DOLLAR |
LD |
A,149 |
; |
|
вместо |
|
|
JR |
FOUND |
|
|
|
|
PROCNT |
LD |
A,229 |
; |
"x" |
вместо |
н о, i
0 |
|
JR |
FOUND |
|
|
|
|
AND_ |
LD |
A, 157 |
; |
"Э" |
вместо |
|
|
JR |
FOUND |
|
|
|
|
APOSTR |
LD |
A,237 |
; |
"э" |
вместо |
", > |
|
JR |
FOUND |
|
|
|
|
SKOB_1 |
LD |
A, 134 |
; |
"6" |
вместо |
|
|
JR |
FOUND |
|
|
|
|
SKOB_2 |
LD |
A,166 |
; |
" ж" |
вместо |
|
|
JR |
FOUND |
|
|
|
|
DEFB 0
CODTAB - с этого адреса начинается таблица:
158,148,136,145,130,147,128,143,144,152,142,139,132,156,146,153, 135,137,138,155,133,131,140,150,151,141,159,229,237,166,161,195, 129,228,168,225,162,227,160,175,224,2 32,174,171,164,236,226,233, 167,169,170,235,165,163,172,230,231,173,239,149,157,134,129,255
Переключаться из русского режима в латинский и наоборот можно либо из программы сканирования клавиатуры - так же, как проверяется CAPS-LOCK - либо при обработке прерываний. Допустим, поступил сигнал: изменить переменную REGIME. Следующая программка осуществляет это: RUSLAT LD HL,REGIME LD A, (HL) XOR 1 LD (HL) , A
RET
Простая картотека.
TR-DOS - не идеальная операционная система, однако если уж она получила такое распространение у нас в стране, стоит использовать как можно больше ее возможностей. Например, файлы прямого и последовательного доступа.
Файл последовательного доступа - это почти то же самое, что список DATA, только хранится он не в программе, а на диске. Понятно, замена второго первым сокращает объем программы - от DATA вообще можно отказаться. Допустим, программа вычислила порядковый номер дня недели, N: 0 - Воскресенье, 1 - Понедельник и т.д. (о том, как это сделать, читайте в "SPECCY+АСТРОЛОГИЯ"). Дальше нужно найти в списке дней соответствующее название. Обычный способ: 10 RESTORE 50 20 FOR k=0 TO N 30 READ X$ 40 NEXT k
50 DATA "Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница", "Суббота" X$ - искомый день.
То же самое с использованием дисковой памяти:
10 RANDOMIZE USR 15619: REM: OPEN #5,"DAYS", R 20 FOR k=0 TO N 30 INPUT #5; X$ 40 NEXT k 50 CLOSE #5
Удобно хранить на диске значения констант или какие-нибудь параметры программы - особенно те, которые пользователь может менять и сохранять по своему усмотрению. Файлы последовательного доступа подобны одномерным массивам. Можно быстро считать или изменить любую запись, зная ее порядковый номер.
В астрологической программе "SPHERA" я организовал простенькую картотеку с данными на клиентов. Каждая запись содержит: имя, день, месяц, год рождения; географическую долготу и широту. Самая первая запись -общее число записей в файле. Их можно удалять, находить по заданному ключу (первая буква имени или первые несколько букв имени или имя целиком), добавлять. Понимаю, что это самая примитивная из возможных катотек, но в моем частном случае ее вполне хватило на то, чтобы обеспечить обмен данными между основной программой и диском. Вот вкратце механизм работы.
1. Поиск Записей по Заданному ключу. Открыть файл F$. ОТ - номер канала, LENGTH - длина записи: RANDOMIZE USR 15619: REM :OPEN #CH,F$ RND ,LENGTH
Положить найденный стринг X$="",FOUND (НАИДЕН) + 0 (ЛОЖЬ) Запросить и ввести ключ, Y$.
Считать первую запись - указатель общего числа записей в файле, MAX: INPUT #CH;(0),A$: LET MAX=VAL A$
Просмотреть число записей, равное МАХ, в поисках записей, чье первое поле совпадает с ключом Y$. Если такие записи найдены, выдать их на экран вместе с номерами; присвоить флагу FOUND значение "ИСТИНА" (1):
FOR L=1 TO MAX INPUT #CH;(L),A$
IF A$(TO LEN Y$)=Y$ THEN PRINT A$: LET FOUND=1 NEXT L
Если не найдена ни одна запись, сообщить эту печальную новость, а затем предложить либо ввести новый ключ и начать поиск заново, либо выйти из программы в этом случае переход к последнему шагу.
Если хоть одна запись найдена (FOUND=1), предложить следующее: удалить запись; изменить ее; передать основной программе. Запросить номер записи с которой производится та или иная операция. Выполнить соответствующую подпрограмму.
Пример: в качестве ключа задана буква "Л". На экране появятся: 1. Лена
12. Лева
43. Леший
Хочется избавиться от Лешего. Даем команду "удалить" и на запрос "номер?" набираем: "43" (или подводим курсор к третьей строке и нажимаем ENTER, или, ловко орудуя "мышкой". впрочем, не стоит вдаваться в подробности!).
Закрыть файл:
RANDOMISE USR 15619: REM: CLOSE #CH Выйти.
2. Удаление Записи.
Допустим номер записи N=12. Надо уменьшить указатель MAX на единицу и переписать его на диск:
LET MAX=MAX-1
LET Z$=STRING(LENGTH,"0")
прошу прощения, но для краткости привожу функцию Beta-Basic-a. Поскольку каждая запись - строка, длина которой = LENGTH, a MAX мы получаем, преобразуя строку в число функцией VAL, в незанятых ячейках должен быть "0".
LET A$=Z$(TO LENGTH - LEN STR$ MAX) + STR$ MAX PRINT #CH;(0),A$
таким образом, если LENGTH=10, a MAX=12, нулевая запись будет выглядеть так: "0000000012" Затем организуется цикл; сдвигающий записи влево:
FOR I=N+1 TO MAX INPUT #CH;(I),A$ PRINT #CH;(I-1),A$ NEXT I RETURN
3. Пополнение списка строкой Y$.
Программа просматривает все записи. Если в списке нет ни одной, чье имя (первое поле) совпадает с именем, которое надо добавить, MAX увеличивается на 1 и строка записывается в конец:
PRINT #CH;(MAX),Y$
В противном случае - запрос: "переписать/выйти". При ответе "переписать", новая запись заменяет старую. ."Играя" с переменными MAX, LENGTH и прочими, легко создать и другие полезные для картотеки функции. Разумеется, приличная база должна уметь сортировать данные, причем не только по первому, а по любому полю - во-первых, для наглядности, во-вторых, для ускорения поиска. Начинать лучше не с поиска, а с просмотра, желательно использовать многооконный интерфейс и бегающий курсор. Кстати, все это есть в картотеке, которая прилагается к операционной системе IS-DOS. Но моей задачей было не соревноваться с такими "монстрами" как MASTERFILE или IS-DOS, а обеспечить свою программу минимальным сервисом.
УНИВЕРСАЛЬНЫЙ ЗАГРУЗЧИК
Мы уже касались в "РЕВЮ" направления, связанного с разработкой загрузчиков программ, которые не требуют никакой адаптации при переносе с магнитофона на диск. Такие программы одинаково хорошо загружаются как с магнитофона, так и с диска. Универсальные загрузчики на уровне Бейсика были предложены Андреем Алексеевым в статьях "WHAM THE MUSIC BOX" (РЕВЮ-93 № 11-12 и РЕВЮ-94 № 1). Сегодня, возвращаясь к этой теме, предлагаем вниманию читателей другой универсальный загрузчик. Свой вариант предлагает Романенко С.Н. из г. Днепропетровск. Загрузчик содержит фрагмент в машинном коде:
3A535C |
LD |
A, (23635) |
FE3B |
CP |
59 |
CA033D |
JP |
Z,15619 |
3A5D5C |
LD |
A, (23645) |
C602 |
ADD |
A,2 |
325D5C |
LD |
(23645),A |
3E6F |
LD |
A,111 |
32BB5C |
LD |
(23739),A |
C9 |
RET |
|
Если расположить коды в начальной строке программы, сразу за оператором REM, то запускается этот блок из Бейсика например так:
1 REM к о д ы (22 байта)
10 RANDOMIZE USR (PEEK 2 3 635+2 5 6*PEEK 23636+5): REM : LOAD "program"CODE
Работает блок кодов не просто, а очень просто. Сначала проверяется системная переменная PROG. Если младший байт равен 59, то есть (PROG)=23867=59+256*93), то значит, подключен БЕТА-диск. В этом случае происходит переход на адрес 15619 - происходит загрузка с диска. Иначе, если PROG не изменено, то в аккумулятор загружается число из ячейки 23645. После увеличения его на два байта (двоеточие и оператор REM), управление будет передано оператору LOAD "program"CODE. Затем в ячейку 23739 заносится число 111 для того, чтобы при загрузке с ленты на экран не выводились названия файлов. После этого - возврат в Бейсик.
Нам очень понравилась идея, заложенная в этом методе. Однако при тестировании загрузчика были выявлены и его "узкие" места. Поэтому добавим несколько слов от себя.
Во-первых, о ячейке 23645. По этому адресу в таблице системных переменных находится переменная CH_ADD - адрес следующего интерпретируемого символа. Величина эта - двухбайтная. Правда, увеличить ее требуется при загрузке с магнитофона всего на два байта, так что скорее всего, увеличив младший байт на два, мы получим верный результат. Но не исключен случай, когда младший байт равен, например, FEH (254) или FFH (255). В этом случае при увеличении на два содержимого аккумулятора мы получим неверный результат, так как не учитывается старший байт CH_ADD. Поэтому правильнее будет величину CH_ADD загружать в двухбайтный регистр, скажем, HL, и его увеличивать на два.
Другой момент. Системная переменная PROG в случае дискового варианта не всегда равна 23867. Обладатели прошивки "Турбо-90" могут проделать простой эксперимент. Если после инициализации интерфейса БЕТА-диска Вы сделаете PRINT PEEK 23635+256*PEEK 23636, то естественно, получите 23867. Теперь войдите в монитор командой "*", затем вернитесь в Бейсик (S.SH+8). Теперь выполните RANDOMIZE USR 15616. Обратите внимание, что при выходе в TR-DOS появилось фирменное сообщение, что происходит при работе только при инициализации TR-DOS (то есть, когда область Бейсика отодвигается на 112 байт и формируются системные переменные TR-DOS. Теперь опять выполните PRINT PEEK 23635+256*PEEK 23636. Теперь значение PROG изменилось, оно равно 23979 -увеличилось на 112 байт за счет смещения области Бейсика.
В такой ситуации кодовый блок универсального загрузчика передаст управление на загрузку с магнитофона. Чтобы загрузчик работал правильно, можно несколько изменить логику перехода. Переходить на загрузку с диска не в том случае, если (PROG)=23867, а в том случае, если (PROG) не равно 23755 (когда БЕТА-диск не инициализирован).
Усовершенствованный вариант кодового блока загрузчика выглядит так (кстати, длина его не изменилась):
5D4 0 |
3A535C |
LD |
A,(#5C53) |
5D43 |
FECB |
CP |
#CB |
5D45 |
C2033D |
JP |
nz ,#3d03 |
5D4 8 |
2A5D5C |
LD |
HL,(#5C5D) |
5D4B |
23 |
INC |
HL |
5D4C |
23 |
INC |
HL |
5D4D |
225D5C |
LD |
(#5C5D),HL |
5D50 |
3E6F |
LD |
A, # 6F |
5D52 |
32BB5C |
LD |
(#5CBB),A |
5D55 |
C9 |
RET |
|
|
Десятичный |
дамп блока кодов: |
58,83,92,254,203,194,3,61,42,93,92,35,35,34,93,92,62,111,50,187,92,201 И еще об одной тонкости в работе этого оригинального загрузчика.
Аварийная ситуация может возникнуть, если Вам понадобится загрузить кодовый блок под принудительный адрес (например, блок кодов-экран после редактирования в "ARTSTUDIO" будет иметь адрес 24320, и загружать его нужно командой LOAD "name"CODE 16384). Ошибочной в этом случае будет такая команда загрузки: 20 RANDOMIZE USR (PEEK 2 3 635+2 5 6*PEEK 23636+5): REM: LOAD "name" CODE 16384
Дело в том, что при загрузке с диска все будет в порядке, но при загрузке с магнитофона интерпретатор Бейсика следом за числом 16384 будет искать код 14 (число) и следом за ним само число в пятибайтной форме. В Бейсик-строке, которая приведена выше нет ни кода 14 ни самого числа, так как все, что набрано в строке 20 после оператора REM - вводится как чистый текст.
Выход из этого затруднительного положения не простой, а очень простой. Вместо 16384 пишите: VAL "16384". И конечно же, Вы понимаете, что из-за TR-DOS недопустимо выражение: LOAD "name" SCREENS. Кроме того в одной Бейсик-строке может находиться только команда загрузки одного файла.
В заключение говорим большое спасибо, Романенко С.Н. за свежую идею.
Приятно, что в нашей почте все чаще начинают появляться письма по поводу дисковой операционной системы TR-DOS. Мы можем порадовать наших читателей письмом Цыгулина Алексея из г. Новосибирска, в котором он приводит более полный список системных переменных, чем был в РЕВЮ-93 № 1-2.
Таблица системных переменных TR-DOS.
#адрес |
адрес |
длина |
комментарий |
5CB6 |
23734 |
1x |
Используется, если есть INTERFACE 1 (если значение равно #F4), то область |
|
|
|
переменных не переносится; если равно #00, то проверяется 23832. |
5CC2 |
23746 |
1x |
Содержит #С9; используется системой TR-DOS для вызова подпрограмм из SOS. |
5CC8 |
23752 |
1 |
Код, определяющий режим работы дисковода А: |
|
|
|
бит 7 = 0 - дисковод 40-дорожечный |
|
|
|
бит 7 = 1 - дисковод 80-дорожечный |
|
|
|
бит 1 = 1 - дисковод двухсторонний |
|
|
|
бит 0 = 0 - использовать 80-дорожечный дисковод, как 40-дорожечный. |
5CC9 |
23753 |
1 |
То же для дисковода B: |
5CCA |
23754 |
1 |
То же для дисковода C: |
5CCB |
23755 |
1 |
То же для дисковода D: |
5CCC |
23756 |
1x |
Текущий сектор при чтении каталога. |
5CCD |
23757 |
1x |
#80 - готовность дисковода. |
5CCE |
23758 |
1x |
#00 - чтение сектора; #FF - запись. |
5CD6 |
23766 |
1x |
При #FF - команда не выполнена. |
5CD7 |
23767 |
2x |
Промежуточный старт (тип В и С) после проверки типа дисковода содержит число |
|
|
|
дорожек. |
5CD9 |
23769 |
2x |
Внутренний аналог CH ADD, промежуточная длина (тип В и С). |
5CDB |
23771 |
2x |
Промежуточная длина программы. |
5CDD |
23773 |
8 |
Имя файла в ASCII. |
5CE5 |
23781 |
1 |
Тип файла. |
5CE6 |
23782 |
2 |
При типе С - стартовый адрес; при типе В - длина Бейсик-программы. |
5CE8 |
23784 |
2 |
Длина файла. |
5CEA |
23786 |
1 |
Объем файла в секторах. |
5CEB |
23787 |
1 |
Номер первого сектора файла (0-15). |
5CEC |
23788 |
1 |
Номер первого трека файла. |
5CEF |
23791 |
1x |
1 - если есть INTERFACE 1. |
5CF4 |
23796 |
1x |
Текущий номер сектора. |
5CF5 |
23797 |
1x |
Текущий номер трека. |
5CF6 |
23798 |
1 |
Дисковод для временной операции (0-3). |
5CF7 |
23799 |
2 |
При возврате из 15616 - обнуляется. |
5CF8 |
23800 |
1 |
Дисковод при операции с двумя файлами #FF, если канал открыт. |
5CF9 |
23801 |
1 |
Дисковод при операции с двумя файлами признак операции READ/VERIFY номер |
|
|
|
дисковода (при команде #07). |
5CFA |
23802 |
1 |
Время перемещения головки дисковода А: |
5CFB |
23803 |
1 |
Время перемещения головки дисковода В: |
5CFC |
23804 |
1 |
Время перемещения головки дисковода С: |
5CFD |
23805 |
1 |
Время перемещения головки дисковода D: |
5CFE |
23806 |
1x |
Код команды для 1818ВГ93. |
5CFF |
23807 |
1x |
Номер сектора для подпрогр. чтение/запись сектора. |
5D00 |
23808 |
2x |
Текущий адрес буфера (#05/#06). |
5D02 |
23810 |
2x |
Сохраняет HL для внутренних нужд. |
5D04 |
23812 |
2x |
Сохраняет DE для внутренних нужд. |
5D06 |
23814 |
1 |
Количество знаков при поиске имени файла (для команды #01). Начальное значение #09 |
5D07 |
23815 |
1x |
#09.
Счетчик удаленных файлов (для команды #12). |
5D08 |
23816 |
1x |
Первый символ имени файла (для команды #12). |
5D0C |
23820 |
1x |
Флаг состояния рабочего буфера TR-DOS (257 байт с адреса 23846) #FF - буфер |
|
|
|
отсутствует; #00 - буфер существует. |
5D0E |
23822 |
1x |
Флаг принадлежности команды #FF - работает Бейсик; иначе - TR-DOS. |
5D0F |
23823 |
1x |
Код ошибки TR-DOS; внутри TR-DOS, при неравенстве нулю вводит команду |
|
|
|
RETURN, иначе пустую строку (подпрограмма #20EF). |
5D10 |
23824 |
1x |
Старший байт ошибки, при вызове 15616 обнуляется; при вызове 15635 необходимо |
|
|
|
обнулять принудительно во избежание ошибочных ситуаций. |
5D11 |
23825 |
2x |
Адрес строки команды для TR-DOS при вызове 15616 повторяет E LINE (23641) при |
|
|
|
вызове 15619 повторяет CH ADD (23645). |
5D13 |
23827 |
2x |
Копия ERR SP при равенстве старшего байта #АА автоматически выполняется |
|
|
|
команда RUN "boot", а в 23833 код #FE. |
5D15 |
23829 |
1x |
При равенстве #00 печатает сообщение TR-DOS, иначе - не печатает. |
5D16 |
23830 |
1 |
Копия системного регистра (555ТМ9). |
5D17 |
23831 |
1 |
При неравенстве #АА, при вызове 15616 рисуется заставка, при равенстве #FF не |
|
|
|
попадает на ошибку при чтении неверного адресного маркера. |
5D18 |
23832 |
1x |
Используется при подключении INTERFACE 1 (если значение равно #FF, то |
|
|
|
меняются местами блоки в памяти по адресам 23747-23859 объемом 45, при вызове |
|
|
|
TR-DOS заносится #FF). |
5D19 |
23833 |
1 |
Дисковод по умолчанию. |
5D1A |
23834 |
2x |
Внутренний адрес процедуры завершения (201). |
5D1C |
23836 |
2x |
Сохраняет SP. |
5D1E |
23838 |
1 |
Номер файла, если он найден (команда #0А). |
5D20 |
23840 |
3 |
Первые три символа введенной строки. |
Символом "x" отмечены переменные TR-DOS, которые не рекомендуется изменять, т.к. это может привести к нарушению работы системы.
Алексей уточняет, что команда #17 (для регистра С) означает "выбрать нижнюю сторону", а #18 "настройка на диск".
Наш корреспондент также сообщает о некоторых особенностях работы TR-DOS.
"Вот что я заметил. Если при работе команды MOVE нажать BREAK, или произойдет какой-либо сбой), то на диске образуются потерянные сектора (что-то вроде потерянных кластеров в IBM-PC) и TR-DOS их не видит. Можете подсчитать количество свободных секторов плюс сумму длин файлов - она будет меньше положенного! Найти "дырку" можно при помощи простой программы вроде DIR (см. РЕВЮ-93, № 1-2, стр. 26). Если удалить все файлы после "дырки", то она исчезает. Именно из-за этого может не сработать команда "OPEN" (тот же РЕВЮ, стр.27), так как при существовании "дырок" (а их можно сделать специально), расстояние между файлами будет равно длине файла плюс длина "дырки", а это совсем не то, что надо! Так что лучше изменить программу "OPEN": удалить строки 1050-1070, а 1080 оставить, но без IF, то есть: 1080 LET lens=PEEK (a+12)+PEEK (a+11)<>0)
При этом "дырки" останутся, но не будут включены в файл.
Дырки, по-видимому, образуются из-за того, что вначале переносится исходный заголовок, потом файл, а затем заголовок изменяется на новый и переписывается.
ИФК: Добавим несколько слов от себя. При создании программы "OPEN", о которой упоминает Алексей, в первую очередь встал вопрос о том, на основании чего рассчитывать длину файла в секторах? Варианта два. Первый -длину файла рассчитывать исходя из его длины в байтах (целое число секторов плюс один, как это делается в строке 1080). Второй вариант разность между началом выбранного файла и началом следующего. Однако первый вариант может тоже дать ошибочные данные. Речь идет вот о чем. Вы, наверное, не раз обращали внимание, что встречаются программы, состоящие из одного Бейсик-файла (в последнее время все чаще). Если Вы обратите внимание на длину Бейсик-файла в байтах, то увидите что-то порядка 100-200 байт, значит должен быть занят 1 сектор. В заголовке же Вы видите, к примеру, 120 секторов. В программе применен специальный способ загрузки (о котором речь еще пойдет ниже). Коды грузятся в память начиная с того трека и сектора, где было закончено чтение Бейсик-загрузчика. Грузится заданное число секторов в заданную область, параметры определяются загрузчиком в машинных кодах, спрятанным в Бейсик-программе. Сам кодовый блок программы расположен сразу же за Бейсик-загрузчиком как раз в "дырке", искусственно созданной. Если это не последний файл на дискете, то обработка дискеты программой "OPEN" с изменениями, предложенными Алексеем, приведут к тому, что обрабатываемый файл станет длиной 1 сектор. Остальное место придется на "дырку", в которой находятся коды. Теперь Вы не сможете переписать такую программу на другую дискету, так как перепишется только 1 сектор.
Поэтому, длина файла в секторах в программе "OPEN" рассчитывается вторым способом для всех файлов, кроме последнего, для которого неопределенно понятие: "начало следующего файла". Естественно, он же последний. Так что его длина в секторах рассчитывается первым способом, однако есть риск, что последний файл как раз
окажется с измененной длиной (в секторах). Истиную длину в этом случае можно определить только "раскрутив" Бейсик-загрузчик и просмотрев параметры машиннокодовых команд загрузки.
А теперь перейдем к следующему письму. Его прислал Фомин Илья из г. С-Петербурга.
Скорость загрузки программ с диска можно заметно увеличить за счет исключения периодического обращения к нулевой дорожке, которое происходит при чтении очередного файла программы. Выигрыш во времени будет тем больше, чем больше файлов необходимо программе и чем дальше она находится от начала диска. Все это достигается за счет замены Бейсик-загрузчика - загрузчиком в кодах, который использует точку входа в TR-DOS с адресом #3D13 (15635). При этом необходимо подготовить исходные данные в регистрах: LD B,число загруж. секторов. LD C,#05
LD DE,(23796) -текущие дорожка/сектор (см. Таблицу системных переменных TR-DOS). LD HL,адрес загрузки. CALL #3D13
Количество секторов и адрес загрузки каждого кодового файла Вам даст команда LIST. Разумеется, если в Бейсик-загрузчике задан принудительный адрес загрузки кодового файла (например, загрузка файла-картинки в адрес 16384), то необходимо задавать именно его. При создании кодового загрузчика надо повторить приведенную выше процедуру для каждого загружаемого файла программы, добавить в конце в зависимости от конкретных обстоятельств либо RET, либо JP START ADR. Поместите полученный машинный код любым привычным для Вас способом в Бейсик (например, в начальную строку после REM) и сохраните полученный загрузчик на ленте. Выглядеть он будет примерно так:
0 REM ... здесь находятся коды
1 CLEAR 24 999: INK NOT PI: PAPER NOT PI: BORDER NOT PI: CLS: RANDOMIZE USR 23872
Затем, сразу же после загрузчика запишите на диск файлы, необходимые программе, в том же порядке, как это указано в загрузчике. Теперь можете удалить кодовые файлы, запомнив их суммарную длину (при удалении файла удаляется только заголовок файла, а сами коды на диске - не затрагиваются). После этого любым DISK-DOCTOR-ом исправьте длину загрузчика в секторах на величину, равную суммарной длине всех файлов (включая сам загрузчик). Вот и все! Перепишите получившийся Бейсик-файл на другую дискету, и Вы увидите, что он, несмотря на видимое отсутствие кодовых файлов, прекрасно работает, и при этом не обращается к нулевой дорожке.
ИФК: в этом способе как раз применяется способ хранения кодовых блоков в "дырке", о чем говорилось в предыдущем материале. Чтобы избежать конфликтов с файловой структурой, Вы можете не удалять кодовые файлы и не изменять длину в секторах Бейсик-загрузчика DISK-DOCTOR-ом. Можете оставить все файлы, как они есть. Хотя можете как угодно изменить их имена и адреса загрузки (например, обнулить адреса при помощи того же DISK-DOCTOR-а). Загрузка все равно будет выполняться при помощи потрекового чтения, без обращения к нулевой дорожке. Зато полностью сохраняется файловая структура TR-DOS. В любом случае, как поступить - решит для себя каждый сам, в зависимости от того, что нужно: сделать так, чтобы все было максимально "ясно и прозрачно" или сделать так, чтобы нежелательные "хаккеры" подольше поломали себе головы.
Илья приводит еще несколько соображений по защите программ от копирования с помощью кнопки "MAGIC": "При своей работе она изменяет системную ячейку, используемую для сканирования клавиатуры (#5С10), занося туда код 201 (RET). Организовав циклический опрос этой ячейки (например, в подпрограмме опроса клавиатуры), Вы сможете сразу же установить факт нажатия "MAGIC" и принять соответствующие меры. Этот способ, кстати, использован в программе "DCU 2".
Другим способом защиты может быть использование того факта, что "MAGIC" в момент нажатия заносит большое количество данных на стек. Здесь способов защиты может быть много, например, разместить стек в верхней части экрана. Тогда при работе "MAGIC" непомерно "разбухший" стек частично "залезет" в ПЗУ и по окончании работы "MAGIC" в него будут сняты совсем не те данные, какие были в него занесены.
Существует еще один способ непосредственное программирование ВГ-93. Этот способ наиболее эффективен, так как "MAGIC", после некоторого "раздумья" просто испортит Ваш диск. К сожалению, текст этой процедуры я привести не могу, так как загрузчик, где она находится, очень мощно закрыт от просмотра с помощью циклической защиты типа "ксорка", которая содержит множество вложенных недокументированных команд, второго режима прерывания и т.д. Однако я не прекращаю своих попыток, есть уже некоторые успехи, и я думаю, что скоро я смогу порадовать читателей РЕВЮ".
* * *