КРАТКИЙ
КУРС ПРОГРАММИРОВАНИЯ В СРЕДЕ IS-DOS.
(под общей редакцией Елисеева В. А.)
1. РАСПРЕДЕЛЕНИЕ ПАМЯТИ В iS-DOS:
Операционная система iS-DOS изначально писалась с
расчетом ее работоспособности в старом стандарте ZX с 48 KB памяти и хотя бы
одним дисководом. Посему в iS-DOS Classic младшие 16 KB
памяти компьютера практически не используются (там находится ПЗУ BASIC).
Выше, с адреса #4000, находится экран.
Поскольку связь с флоппи-диском осуществляется через
TR-DOS, то сохранены некоторые переменные BASIC и TR-DOS.
Т.о. нам остается память от 23900(#5D5C) по 65535(#FFFF).
Буфер принтера (#5B00.. ..#5BFF) используется для
командного файла exebat.com, необходимого для работы bat-файлов. Область с
23900(#5D5C) по 23999(#5DBF) резервируется под программы mon.com и menu.com,
которые также как и exebat.com вызывают другие com-файлы.
Область выше 24000 обычно используется для загрузки
системных или пользовательских com-файлов. "Командники" грузятся по
адресу загрузки (12-ый, 13-ый байты описателя файла). В ту же точку передается
управление. 95% всех com-файлов имеет адрес загрузки 24000.
Сверху область com-файлов ограничена кэшем блочных
устройств.
Кэш блочных устройств содержит прочитанные с помощью
некоторых рестартов блоки. Он необходим для работы почти всех рестартов уровня
файловой службы (с #20 по #38 и с #3B по #3F), многих рестартов уровня командной
строки, работающих с файлами и каталогами, и некоторых рестартов верхнего
уровня: $shsubr(#8E), $shpanl(#90).
При запуске com-файлов с помощью рестартов $run(#48)
или $exebat(#44) система сама следит за неприкосновенностью кэша. Если com-файл
не влезает под кэш, генерируется ошибка 130. Адрес кэша и его размер можно
узнать у системы с помощью системного рестарта $g_cnfg(#10):
LD C,#10 ;$g_cnfg
RST #10
EXX ;в HL - адрес вектора
конфигурации системы
LD BC,5 ;смещение +5
ADD HL,BC
LD E,(HL) ;адрес кэша
INC HL ;помещаем в DE
LD D,(HL)
INC HL ;смещение +7
LD A,(HL) ;размер кэша в
блоках
Теперь будет не сложно проверить, хватает ли памяти,
и, если нет, пересоздать кэш (если он нужен для работы программы) размером
поменьше с помощью рестарта $creat(#00).
Если кэш временно не нужен, этой памятью можно
воспользоваться, но затем обязательно пересоздать кэш рестартом $creat(#00).
Выше кэша находится область каналов. Адрес, размер и
указатель области каналов достаются через уже знакомый рестарт $g_cnfg(#10) со
смещениями +32, +34 и +36 соответственно. В каналах хранится самая необходимая
системная информация, такая как каналы драйверов и устройств, каналы панелей и
резидентов, а посему начиная от адреса области каналов и до #FFFF лучше ничего
не трогать!
Над областью каналов находятся вперемешку (в порядке
загрузки) драйверы и резиденты. Нижним будет резидент (драйвер) загруженный
последним. Адрес нижнего резидента равен адресу конца области каналов
(см. выше).
Резиденты и драйверы доступны по именам с помощью
рестартов $fndev(#51) и $run(#48) или по номерам каналов драйверов или
устройств.
Так любой драйвер или резидент Вы можете достать,
зная номер его канала:
; в A
помещаем номер канала
LD C,#16 ;$stchn
RST #10
RET C ;выход по ошибке - нет канала и т. п.
EXX ;в HL - адрес тела канала
INC HL
INC HL
INC HL
INC HL
LD E,(HL)
INC HL
LD D,(HL) ;в DE - адрес тела драйвера
.. и т.д.
(описание $stchn и каналов см. ниже)
Резидентную задачу можно запустить, зная номер ее
канала, рестартом $exeres(#53).
Еще выше расположены 5 уровней системы. Сидят они
сверху вниз, т. е. самый нижний уровень "DOS" (работа с устройствами,
кэшем, каналами и прерываниями) располагается на самом верху. Далее идут
"DUD" (файловая служба), "COM" (интерпретатор командной
строки), "WIN" (рестарты работы с окнами, печати строк и т. п.) и в
самом низу "SHELL" (файловая оболочка, работа с панелями, меню).
Два верхних уровня ("оконная технология" и
"работа с панелями") могут быть временно сняты программой set.com с
ключом /L, но используется это крайне редко.
Увидеть, чего где сколько, можно с помощью программы
Q:UTILshow.com. Она позволяет увидеть все основные адреса системы, адреса
загрузки уровней, резидентов и драйверов, адрес и размер кэша и многое другое.
ПРИМЕР РАСПРЕДЕЛЕНИЯ ПАМЯТИ
iS-DOS Classic
0
|
ПЗУ BASIC-48
|
16384
|
экран
|
24000
|
область com-файлов
|
35903
|
кэш
|
43443
|
каналы
|
44143
|
резиденты и драйверы
|
50304
|
уровень 4 SHELL
|
54800
|
уровень 3 WIN
|
58472
|
уровень 2 COM
|
60726
|
уровень 1 DUD
|
63296
|
уровень 0 DOS
|
Вся дополнительная память ZX (128, 256
и т.д.) используется или для виртуального электронного диска, к которому
обращаются через специальный драйвер как к обычному устройству (лучше всего его
назначить устройством Q:, тогда работа в системе резко ускоряется), или под
буферы (например, копировщиками).
В январе 1995 года создан еще один вариант iS-DOSa:
"iS-DOS Chic" (исдос шик),
использующий нижние 16 KB для размещения неизменяемой части ядра системы. Это
оказалось возможным на компьютерах KAY-256, Scorpion ZS-256, Profi и др. им подобным.
Данные модификации Спектрума позволяют в нижней
странице памяти разместить 0-ой банк памяти. Вместо него в верхней странице
открывается 8-ой банк.
В нижнюю страницу помещаются как правило неизменяемые
части системных программ (для возможности прошить эту страницу в ПЗУ),
знакогенераторы t42 и t64 (2KB+1KB), благодаря чему транзитная область расширяется
почти вдвое (до 30 KB вместо 15 при минимальном количестве резидентов и
29-блочном кэше).
ПРИМЕР РАСПРЕДЕЛЕНИЯ ПАМЯТИ
iS-DOS Chic
#0010
|
RST #10
|
#003B
|
подпрограмма драйвера электронного диска
|
#0047
|
признак типа системы/компьютера
|
#0067
|
таблица для драйвера электронного диска
|
#0101
|
буфер для драйвера электронного диска
|
#0201
|
знакогенератор t64 (1K)
|
#0601
|
не используется
|
#093A
|
ядро системы (неизменяемая часть)
|
#3800
|
знакогенератор t42 (2K)
|
#4000
|
экран
|
#5DC0
|
область com-файлов
|
#A5A2
|
кэш (49 блоков)
|
#D6F0
|
каналы
|
#DAD8
|
резиденты и драйверы
|
#F365
|
уровень 4: SHELL
|
изменяемая часть
|
#F78C
|
уровень 3: WIN
|
#FA88
|
уровень 3: COM
|
#FC0C
|
уровень 1: DUD
|
#FDE6
|
уровень 0: DOS
|
2. НЕСКОЛЬКО ОБЩИХ СЛОВ:
В системе iS-DOS вызов системных подпрограмм
осуществляется с помощью команды RST 16 (или RST #10, если кто любит
шестнадцатеричные). При этом в iS-DOS Classic должен быть открыт 4-ый канал
бейсика (после загрузки так оно и есть, если Вы только не вызывали сами
процедуру #1601 или не запускали в исдосе скажем MONS-4).
Код функции подается в регистре C. Его старшие 3
разряда определяют уровень системы. Обычно их (уровней) 5 (с 0 по 4).
Дополнительный временный уровень с номером 7 ставит текстовый редактор.
Отладчик устанавливает уровень номер 5. Дополнительные уровни устанавливают
также базы данных.
Такой способ связи с системой или с собственным ядром
пакета из оверлеев чрезвычайно удобен, так как ядро и оверлеи полностью
развязываются адресно и после перетрансляции ядра не нужно перелинковывать все
оверлеи. В противном случае после изменения системы пришлось бы
перетранслировать все командные и резидентные файлы, многие драйверы и т. д. и
т. п.
При работе большинства рестартов система сохраняет
регистры BC, DE, HL, IX и IY. У многих рестартов выходное значение регистровой
пары AF сигнализирует об успехе операции. При этом поднятый в "1"
флаг C означает ошибку и регистр A в этом случае содержит её код. В большинстве
случаев при ошибках рекомендуем просто отваливать по флагу C:
Например:
LD C,#02 ;flush
RST #10 ;вызов рестарта
RET C ;выход при ошибке
...
Некоторые рестарты, как, например рестарты оконной
технологии $wt, $adrwt, $box и др. могут вернуться с любым флагом, т.е. ни флаг
C, ни какой другой не являются здесь признаком ошибки, и обрабатывать их после
вызова рестарта не только не полезно, но и ошибочно. В дальнейшем, при более
подробном рассмотрении рестартов для каждого из них будут указаны возможные
ошибки. Для некоторых рестартов важен не только флаг C на выходе, но и флаг Z,
а также содержимое регистров. Такие случаи оговариваются особо.
Большинство рестартов, как уже было сказано,
сохраняют регистровые пары BC, DE, HL, IX. Исключение: рестарты $exebat и $run. Ими передается
управление командным файлам или резидентным программам. При выходе по RET'у из
вызывавшихся программ мы попадаем прямо в основную программу минуя процедуру восстановления
регистров.
ВНИМАНИЕ! АЛЬТЕРНАТИВНЫЕ
РЕГИСТРЫ
ПРИ RST 16 НЕ СОХРАНЯЮТСЯ!
Более того, именно ими многие рестарты возвращают в
программу такую полезную информацию, как адреса системных векторов (массивов),
адреса или номера блоков устройств, адреса каналов, драйверов и т. д. и т. п.
Входные же данные можно передать лишь через основные регистры.
ВАША ПЕРВАЯ ПРОГРАММА
Программы в iS-DOS'ее могут
быть двух основных типов: командные файлы и резиденты.
Командные файлы (о резидентах мы поговорим позднее)
имеют расширение *.com и загружаются по адресу, указанному в 12-м и 13-м байтах
описателя файла, иначе говоря, со смещением 12 (#0C) в описателе файла и
запускаются с адреса загрузки. Вызывать их можно из других программ по
командной строке рестартом $run. Например:
LD HL,LINE ;адрес командной строки
помещаем в HL
LD C,#48 ;$run
RST #10
RET C
;описание командной строки, завершается
кодом ENTER (#0D)
...
LINE DEFM "Q:RESset user.res"
DEFB #0D
Из оболочки iS-DOS'а
командный файл можно запустить подведя к нему файловый курсор и нажав Enter,
либо описав его в текстовом файле Q:SHELLextkey.txt, или из монитора командных строк mon.res
(mon+.res, mon.com), а так же из bat-файла
(подробнее см. книгу В. Елисеева "IS-DOS
- Первое знакомство").
Создать свой командный файл не просто, а очень
просто. Сперва при помощи текстового редактора создайте исходный текстовый файл
в стандарте любимого Вами ассемблера. Рекомендуем наш as2.com. Файл должен
иметь расширение *.as (некоторые любят *.asm). В начале программы установите
адрес загрузки директивой ORG (желательно не ниже 24000):
ORG 24000
Затем разместите тело вашей программы.
Выходить в iS-DOS советуем
по команде:
RET
При нормальном выходе флаг C должен быть сброшен,
флаг Z установлен, в регистре A помещаем код выходной операции:
#00 - ничего не делать
#F0 - перепечатать одну текущую панель, при этом на
ней сохраняется открытым текущий каталог, который был на панели до этого,
сохраняется позиция курсора, отметка файлов сбрасывается.
#F1 - то же самое, но курсор устанавливается в начало
панели.
#F2 - перепечатать обе панели, сохранив в текущей
панели ранее открытый каталог и позицию курсора. Перепечатывается также и
верхняя строка подсказки, однако, окно монитора командной строки не очищается.
#F3 - то же, но курсор устанавливается в начало
панели.
#F4 - полностью очищает экран и перепечатывает обе
панели и строку подсказки. На текущей панели сохраняется ранее открытый каталог
и позиция курсора.
#F5 - то же, но курсор устанавливается в начало
панели.
#F6 - полное обновление экрана, перепечатка обеих
панелей и подсказки (как и при F4), кроме того заново пересоздается кэш. Размер
кэша берется из вектроа g_cnfg со смещением -6. Позиция курсора и каталог в текущей
панели сохраняются.
#F7 - то же, но курсор устанавливается в начало
панели.
#F8 - перепечатка текущей панели с сохранением
открытого каталога, позиции курсора и отметки файлов.
#17 - (в старых версиях - #FB) -
перепечатка текущей панели, при этом текущее устройство на ней открывается
заново (перечитывается его корневой каталог).
При ошибке флаг C должен быть установлен, в регистре
A помещается код ошибки.
Оттранслируйте программу ассемблером, отлинкуйте
полученный объектный файл программой link.com. Для их вызова можно написать
bat-файл, а можно просто нажимать Enter, наведя курсор сперва на asm-файл, затем
на obj (В файле Q:extent.txt должны быть такие строки:
asm:Q:ASas /auto
obj:Q:ASlink /old /sym
S:ASrst
Пользуйтесь при написании стандартными названиями
рестартов, тогда Вам пригодится файл глобальных меток rst.obj, а программа
будет более читабельной. Обязательно сосчитайте контрольную сумму com-файла программой
ch.com. Для этого установите курсор на Ваш com-файл и вызовите из командной
строки программу ch.com. В случае bat-файла опишите вызов ch.com прямо в нем.
4. РЕЗИДЕНТНЫЕ ПРОГРАММЫ
Резидентной программой в системе IS-DOS
называется программа, постоянно находящаяся в специальной области памяти и,
поэтому, доступная для работы в любое время без подгрузки ее с диска.
Работа с резидентной программой подразделяется на три
этапа:
1. Установка (загрузка) в память с диска, настройка
на адрес и инициализация.
2. Основная работа
3. Удаление программы из памяти
1-й и 3-й этапы осуществляются командой
set.com. Она же создает 18-байтовый канал, описывающий резидентную программу.
Установленные (загруженные) резидентные программы
можно увидеть при помощи программ show.com и eliminat.com. Обращаться к
резидентным программам можно по именам из командной строки, используя встроенную
команду DOS "@", например:
@date+3 или @scan ch+ *.*
Последнее выражение содержит сразу 2 имени
резидентов. Это вызвано тем, что некоторые резидентные программы предназначены
для работы в паре со специальными служебными резидентами - scan.res и univ.res.
Эти служебные резиденты осуществляют подбор файлов (по маске, отметке и т. п.)
и передачу их в качестве параметра резиденту, запускаемому в паре с ними.
Т. о. Вы запускаете служебную резидентную программу,
указав в качестве первого параметра имя рабочего резидента, а в качестве
второго - ключ или маску для подбора файлов. Служебный резидент по имени
определяет номер канала рабочего резидента, запоминает его и многократно
запускает по номеру, предварительно открывая следующий файл, подходящий под
указанную маску или ключ.
Резиденты, работающие в паре с резидентами scan.res и
univ.res обычно имеют специальную защиту от прямого обращения к ним через
команду "@".
Структура резидентной
программы:
Смещение
|
Длина
|
Комментарий
|
0
|
2
|
Адрес процедуры инициализации. Она вызывается при
перемещении (при этом в регистре A подается FF) или удалении (в рег. A
подается FE) программой SET.com. Если этот адрес равен 0, то процедура не
вызывается. Эта программа должна подключать, если это необходимо, резидента к
цепочке прерываний или к другой программе, перехватывать рестарты и восстанавливать
их.
|
2
|
2
|
Адрес главного входа, т.е. процедуры запуска по
имени рестартами $exebat(#44) и $run(#48) или по номеру канала рестартом
$exeres(#53) Если адрес равен 0, то запускается с 4-го байта.
|
4
|
R
|
Тело программы - собственно машинный код
|
R+4
|
2
|
#FFFF - отделяет тело программы от последующей
служебной информации
|
R+6
|
2n
|
таблица настраиваемых адресов - 1. Смещения от
начала вычисляются по LSA. Единица вычитается для пущей простоты настройки
таких команд как: CALL, JP, LD HL, LD
A,(nn), наиболее часто встречающихся в программе.
|
С появлением программ ассемблера и
сборщика (as.com и link.com) все заботы о разделителе и таблице настраиваемых
адресов у программиста отпали. Чтобы собрать резидентную задачу надо лишь
зарезервировать в начале файла 4 байта (2 слова) для адресов точек входа (см.
только что приведенную структуру) и отлинковать объектный файл(ы) с ключом
/res. Не забудьте лишь установить ORG отличный от нуля, чтобы адрес #FFFF не
спутался с разделителем!
5. ДРАЙВЕРЫ УСТРОЙСТВ
Драйвер - это резидентная программа специального
назначения. Он обслуживает физическое или логическое устройство одного из трех
типов:
1. Блочные устройства: файлы типа *.blk, номера
каналов драйверов F8..FF, номера каналов устройств - #00..#07
2. Символьные устройства вывода: файлы типа *.typ и
*.lpr, номера каналов драйверов F0..F7, номера каналов устройств - #08..#0F
3. Cимвольные устройства ввода: файлы типа *.key
номера каналов драйверов E8..EF, номера каналов устройств - #10..#1F
Установленный в систему драйвер имеет только
8-буквенное имя (тип в канале не хранится). typ от lpr можно отличить по 0-му
биту в 13-ом байте драйвера (5-ый байт в векторе g_typ(#12)). В начале каждого
драйвера располагается вектор стандартной структуры:
Смещение
|
Длина
|
Имя
|
Комментарий
|
0
|
2
|
INST
|
Программа, инициализации вызываемая как и для всех резидентов программой
SET.com, а также при каждом переключении устройства, обслуживаемого драйвером.
При этом в регистре A подается номер устройства
|
2
|
2
|
ENTRY1
|
1-ая точка входа
|
4
|
2
|
ENTRY2
|
2-ая точка входа
|
6
|
2
|
ENTRY3
|
3-я точка входа
|
8
|
8
|
|
служебная информация, хранящаяся также в описании канала устройства и
в векторе устройства - регистры состояния, адреса буферов и т. п.
|
16
|
...
|
|
тело драйвера
|
Точка входа INST может использоваться
для переключения драйвера на соответствующее устройство (задействовано в
sys_driv.blk и в драйверах винчестеров), а также информирует
драйвер (и резидент) о том, что его передвинули или отключают. Вся информация
подается в регистре A:
A=0..7 - номер устройства, на которое переключается
драйвер
A=FE - драйвер (резидент) отключается, т.е. будет
снят или просто текущее устройство скоро будет сменено. Делается это на тот
случай если драйвер в рабочем состоянии перехватывает обращения к какому-либо
рестарту или другому драйверу.
A=FF - драйвер (резидент) только что передвинут.
Входы ENTRY1, 2, 3
соответствуют группам системных рестартов IS-DOS:
Драйвер
|
ENTRY1
|
ENTRY2
|
ENTRY3
|
blk
|
$read
|
$write
|
$binit
|
typ
|
$type
|
$tycpl
|
$typos
|
key
|
$key
|
$kwait
|
$ktest
|