Этот рестарт выдает на выходе в HL'
адрес вектора конфигурации ядра (см. ПРИЛОЖЕНИЕ 3) и системное блочное
устройство S: в регистре A. Входные
регистры не влияют. Флаг C на выходе роли не играет. Для примера
рассмотрим перехват рестарта опроса клавиатуры, что может быть полезно,
например, чтобы подвесить попискивание на клавиши.
LD C,$g_cnfg
RST 16
EXX
LD BC,8
ADD HL,BC ;адрес
0-го уровня DOS
LD E,(HL)
INC HL
LD D,(HL)
LD HL,14 ;$key*2
ADD HL,DE ;KEY
LD (FN_A+1),HL
LD E,(HL)
INC HL
LD D,(HL)
LD (FN_RST+1),DE
PUTADR LD DE,KEY
FN_A LD HL,0
DI
LD (HL),E
INC HL
LD (HL),D
EI
RET
KEY
…
FN_RST CALL 0
…
Теперь при каждом вызове рестарта $key
управление будет передаваться Вашей программе KEY, где
Вы можете делать всё что угодно до и после обращения к драйверу. Если вздумаете
оформить программу в виде резидента, а это самое разумное, не забудьте, что
резидента могут сдвинуть, при этом надо заново положить новый адрес в таблицу
рестартов DOS'а. Не забудьте также восстановить все как было при
снятии резидента.
Рестарт выдает в HL'
адрес вектора символьного устройства ввода, а в A - канал
текущего устройства ввода (обычно 8). Входные регистры не влияют. Флаг C
на выходе роли не играет.
Пример: используем счетчик прерываний для получения
псевдослучайного числа в интервале от 1 до 16:
LD C,$g_key
RST 16
EXX
LD BC,-20
ADD HL,BC
LD A,(HL)
AND #F
INC A
Счетчик уменьшается на 1 каждое
прерывание. Вы можете использовать его для запуска некоей процедуры через
определенное время. Для этого в счетчик положите интервал времени в 1/50 долях
секунды, а со смещением -14 адрес процедуры. Через этот же вектор Вы можете
получить доступ к цепочке процедур вызываемых по прерываниям. Цепочка устроена
очень просто: перед каждой процедурой цепочки резервируются 2 пустых байта,
являющихся признаком конца цепочки. При установке в цепочку следующего
"звена" сюда кладется его адрес. Занимается этим рестарт $l_im2(#1E).
LD C,$g_key
RST 16
EXX
LD BC,-16
ADD HL,BC
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL ;HL=адрес
1-го звена цепи
;достанем
следующий адрес:
1$ DEC HL
LD D,(HL)
DEC HL
LD E,(HL)
;если
0, то это конец цепи:
LD A,D
OR E
RET Z
;иначе работаем с ним, к примеру печатаем этот адрес
на экране, или проверяем, не лежит ли этот адрес
;в интересующем нас драйвере или уровне...
CALL IMTST ;DE сохраняется
EX DE ,HL
JR 1$
Рестарт возвращает в HL'
адрес символьного устройства вывода и номер текущего устройства в регистре A
(от #10 до #17). Входные регистры не влияют. Флаг C на выходе не
играет роли. Как пример: переключение на другой драйвер печати на
экране, используемый в программе tv.com:
$swap LD C,$g_typ
RST
16
;Сохраним
номер текущего устройства:
LD
(DEV0+1),A
;Следующее
устройство:
NEXT INC A
;должно
быть в пределах от #08 до #0F:
CP #10
JR NZ,$+3
RRA ; A=8
DEV0 CP 0
RET Z ;Других устройств нет
LD B,A ;сохраним номер
;Достанем
драйвер:
LD C,$g_drv
RST 16
LD A,B
JR C,NEXT ;устройства видимо нет
;Проверим
13-й байт драйвера: у .typ=0, у .lpr=1
EXX
LD BC,13
ADD HL,BC
LD A,(HL)
EXX
OR A
LD A,B ;номер устройства
JR NZ,NEXT
;Всё OK.
Можно переключаться:
LD C,$swtyp ;#1B
RST 16
RET C
LD C,$g_typ
RST 16
EXX
LD A,(HL)
;A=4/6
- горизонтальный размер символа в точках
...
Рестарт возвращает в HL' адрес
символьного устройства вывода и номер текущего устройства в регистре A
(от 0 до 7). Входные регистры не влияют. Флаг C на выходе
роли не играет. Что касается примера, смотрите отрывок из программы format.com приведенный при описании рестарта $clear(#01).
Этот рестарт по номеру устройства в
регистре A ищет драйвер. Если всё OK, то на
выходе флаг C сброшен, A=номер канала драйвера, HL'=адрес
тела драйвера. Возможные ошибки:
124 -
Ошибка в структуре области каналов
125 - нет канала
Устроен
этот рестарт очень просто и в принципе делает вот что:
LD C,$stchn ;найти канал номер A
RST 16
RET C
EXX
LD A,(HL)
;номер
канала драйвера (см. ПРИЛОЖЕНИЕ 7)
EXX
RST
16 ;канал драйвера
RET C
EXX
INC HL
INC HL
INC HL
INC HL
;адрес
тела драйвера (см. ПРИЛОЖЕНИЕ 8)
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL ;HL=адрес тела
драйвера
Примеры
применения самого рестарта $g_drv см. описания рестартов $g_typ(#12)
и $clear(#01).
Сей рестарт по номеру в регистре A
ищет соответствующий канал. Если находит, то:
флаг C сброшен
A сохранится
HL'-адрес тела канала
BC'-длина канала
DE'-указатель за последний канал (он же $g_cnf+36).
Возможные ошибки те же, что и у рестарта $g_drv(#14).
Пример из предыдущего рестарта также будет полезен. Еще пример -
добывание канала пути альтернативной (или текущей) панели:
LD C,$gmpan
RST 16
EXX
DEC BC
LD A,(BC) ;текущая панель 0/1
INC BC
OR A
JR NZ,$+3 ;Z для текущей
INC BC
LD A,(BC)
LD C,$stchn
RST 16
RET C
EXX
INC HL
INC HL
INC HL
LD A,(HL) ;канал пути панели
EXX
RST 16
RET C
EXX
;HL
указывает на путь альтернативной панели.
Этот рестарт находит канал по номеру в
регистре A и удаляет его. Возможные ошибки см. $g_drv(#14). В жизни этот
рестарт, как и создание канала применяется не часто. Любят этим заниматься
программы set.com, exebat.com, print.com. Каналы необходимы при установке
резидентов и тем более драйверов (ведь устройства суть каналы). Каналы удобны
там, где надо передать не очень большой объём информации от одной программы
другой (print) или сохранить кое-что для себя, а памяти для буферов нет
(exebat). Для примера напишем программку, удаляющую все лишние каналы
пользователя. Они могут оставаться в системе при аварийном завершении работы
программ их создававших. Каналы пользователя имеют номера с #18 по #D7, из них
первые 5 обычно заняты под системные надобности. Например:
#18 - путь левой панели
#19 - "тропа" поиска командников (path)
#1A - левая панель
#1B - путь правой панели
#1C - правая панель
Поэтому
начнём с канала #1D:
LD BC,#1D00+$dlchn
1$ LD A,B
CP #D8
RET Z
RST 16
JR NC,2$
;Флаг
C: ошибка
CP 125 ;нет
канала
SCF
RET NZ
2$ INC B
JR 1$
Этот рестарт создает канал. Номер
канала задается на входе в регистре A. Размер канала в регистре DE. Возможные ошибки:
124 -
ошибка в структуре области каналов
126 -
канал с таким номером уже есть
127 -
неверная длина канала (DE=0)
128 - нет
места для канала
При нормальном завершении работы рестарта адрес
канала выдается на выходе в HL'. Рестарт необходим для таких системных трюков,
как создание устройств и установка драйверов и резидентов. Им пользуются
программы set.com и dev.com, т.к. только этим рестартом можно создавать каналы
с номерами от #00 до #17 и от #D8 до #FF. Для создания же каналов пользователя
предназначен следующий рестарт:
Этот рестарт также как и предыдущий
создает канал, но номер ему он дает сам, выбирая наименьший свободный в
диапазоне от #18 до #D7. Длину канала, как и для $crchn подавайте в DE.
Возможны те же ошибки, что и у предыдущего рестарта, за исключением,
пожалуй, ошибки 126. Зато можно схлопотать ошибку: 129 - нет свободного номера,
но для этого придется постараться. Если все OK, то флаг C сброшен, а в регистре
A будет номер созданного канала. Регистр HL' как и в предыдущем случае укажет
на адрес тела канала. Пример:
ORG 24000
LD DE,CHSIZ
LD C,$newch
RST 16
RET C
PUSH DE
EXX
POP BC
LD DE,CHBUF
EX DE,HL
LDIR
XOR A
LD A,#F8
RET
CHBUF DEFM
|Канал пользователя|
CHSIZ EQU
$-CHBUF
Следующие 3 рестарта переключают
устройства всех трех исдосовских типов: символьного ввода и вывода и блочные.
Номер входного устройства всем трём рестартам подается в регистре B, причем
имеют значение лишь 3 младших разряда, т.к. все 3 рестарта смены устройств
маскируют входной параметр. Возможны ошибки:
121 - нет устройства, т.е. нет канала с номером B.
122 - нет драйвера, обслуживающего данное устройство.
124 -
Ошибка в структуре области каналов
Переключение устройств символьного ввода
(клавиатурных). В теперешнем исдосе не применяется. В старых версиях на
драйвере ttyin.blk "висели" 2 устройства. Одно было для работы в
редакторе, т.е. на него переключались рестарты $smbgt(#6E) и $edstr(#7F).
Другим устройством пользовались все остальные, и работало оно обычно лишь в
режиме маленьких латинских букв. Т.о. развязывались режимы работы в строковом
редакторе и в оболочке и утилитах, пользовавшихся рестартом $key(#07) напрямую
или через рестарт $menu(#91). Однако, в момент переключения устройств
останавливались макросы. Развязку пришлось осуществить другим способом, а от 2
устройств отказаться. В принципе никто не мешает написать программу,
переключающуюся на другой драйвер клавиатуры и работающий с ним или даже с
двумя поочередно.
Переключение устройств вывода на экран или принтер.
Используется программами tv.com, edit.com и некоторыми программами печатающими
на принтере, например: PrintLux, Picasso. Вход и выход описаны выше. Пример см.
к рестарту $g_typ(#12).
Переключение блочных устройств.
Интерфейс стандартный для всех трех $sw... рестартов, и добавить тут решительно
нечего. Применяется программами: arzt+, abc, format, doctor, практически всеми
копировщиками. См. пример к рестарту $read(#0D).
Перемещение информации о текущем состоянии при работе
с драйверами и устройствами можно представить в виде следующей схемы:
set.com: драйвер -> устройство
$ldnew(#1D):
устройство -> вектор драйвер -> вектор
sw...(#1A-#1C): вектор -> старое устройство новое
устройство -> вектор
Итак, set.com, загрузив
драйвер, создает устройства (т.е. каналы), и копирует в них 8 байт из драйверов
со смещения 8. Рестарты смены устройств сохраняют эти же 8 байт системного
вектора в канале отключаемого устройства и грузят на их место аналогичные 8
байт из подключаемого устройства.
Этот рестарт используется при
подключении новых устройств. И используется он, похоже, только программой
set.com. На входе ему подают номер устройства в регистре A и адрес системного
вектора минус 9 в регистре HL. Каналы устройства и драйвера должны быть уже
созданы и заполнены. Из канала устройства рестарт достает 8 байт данных (со
смещением 2, см. ПРИЛОЖЕНИЕ 7, перед описанием $g_drv(#14)) и копирует их в вектор
соответствующего устройства (ПРИЛОЖЕНИЯ 2,5,6, см. рестарты
$g_***(#11…#13)). Со смещением 0 в канале устройства рестарт добывает номер
канала драйвера, по нему находит заполненный канал, в котором со смещением 4
(см. ПРИЛОЖЕНИЕ 8, перед рестартом $g_drv(#14)) находится адрес
драйвера. После чего три точки входа в драйвер (смещения 2,4,6) копируются в
системный вектор в позиции -8,-5,-2 соответственно. Нечто подобное делается и
при переключении на другое устройство рестартами $sw***(#1A…#1C). Вот что
делает set.com после загрузки или удаления любого резидента или уровня:
LD C,$g_key
LD B,3
1$ PUSH BC
RST 16
EXX
LD BC,-9
ADD HL,BC
LD C,$ldnew
RST 16
POP BC
INC C
DJNZ 1$
RET
Рестарт работает с цепочкой функций,
вызываемых по прерываниям от таймера. Как уже рассказывалось при описании
работы рестарта $g_key(#11), в исдосе по прерываниям вызывается сперва драйвер
клавиатуры, а затем программы пользователя, образующие цепь. Перед точкой входа
в каждую программу цепи резервируется 2 байта, для ссылки на следующую
программу цепи. 0 в этих байтах является признаком конца цепочки. Рестарту на
вход подается адрес устанавливаемой/снимаемой процедуры в регистре HL. Регистр
A сообщает рестарту, устанавливать (A<>0) или удалять из цепи данную
процедуру. На время вызова рестарта обязательно запретите прерывания! Возможная
ошибка только одна и проявляется она лишь при снятии фоновой задачи: 39
- нет такой фоновой задачи.
Начало цепочки фоновых задач можно достать из вектора
рестарта $g_key(#11)(см. выше)
Пример:
ORG 24000
DEFS
4 ;2 точки входа резидента
LD A,-1
LD HL,IM_2
LD C,$l_im2
RST 16
RET C
XOR A
LD A,#F8
RET
DEFW 0
IM_2 LD A,25
DEC A
JR NZ,$+4
LD A,25
LD (IM_2+1),A
RET NZ
1$ LD A,0
CPL
LD (1$+1),A
OUT (-2),A
RET
Отлинкуйте эту программку
с ключом /res, загрузите полученный резидент, установив на него
файловый курсор и нажав <Enter>. Вызовите резидентную задачу командой @имя. В
принципе этот же пример можно отлинковать и запустить как com-файл, но после
подобного примерчика рекомендуем перезагрузиться, т.к. удаление из цепочки
здесь не предусмотрено, а set.com при снятии резидента сам отключит его от
цепочки IM 2.
Этот рестарт создан исключительно для драйвера sys_driv.blk,
который вызывает его в случае ошибки, а рестарт всего лишь печатает ваши
любимые окна и ждёт ответа.
BREAK
Retry <R>
Abort <A>
|
|
Disk Error
Track 0
Sector 0
|
|
No Disk!
Retry <R>
Abort <A>
|
|
Retry Abort
Ignore?
|
|
Read Only!
Retry <R>
Abort <A>
|
Интерфейс следующий:
B=0: "Disk Error", D=трек,
E=сектор
B=1: "Read Only"
B=2: "No Disk"
B=3:
"Break"
На выходе в регистре A код нажатой
клавиши в ВЕРХНЕМ регистре.
Хотя этот рестарт формально является рестартом
нижнего уровня, в 0-ом уровне находится лишь маленькая процедурочка,
опрашивающая 7-ой бит 1-го байта вектора блочных устройств ($g_blk(#13),
см. выше) и, если бит сброшен, обращающейся по адресу ERDEV (вектор
ядра системы, смещение +38, доступен через рестарт $g_cnf(#10),
см. выше). Вы можете перехватить на себя данное обращение драйвера или
запретить драйверу обращаться к этому рестарту, установив 7-ой бит 1-го байта
вектора в 1. После этого по любой ошибке драйвера управление будет возвращаться
сразу вашей программе с установленным флагом C и уже
знакомыми кодами ошибок 6,7 и 20. Так поступают программы format, doctor и т.п. (см. пример к рестарту $clear(#01)).
Пример "тормозов" из новой версии драйвера ed128-b.blk:
TRANS
;1.8.96 BREAK:
LD C,$ktest ;нажата клавиша ?
RST
16
JR Z,1$ ;если нет, то иди на 1$
LD C,$key ;какая клавиша ?
RST 16
CP #16 ;<Cs/Space> ?
JR NZ,1$
PUSH BC
LD BC,#300+$erdrv
RST 16
POP BC
CP "A" ;"A":
"ABORT"
LD A,20 ;отваливаем с ошибкой 20
SCF
RET Z
1$ …