#10 (16) $g_cnfg
Этот рестарт выдает на выходе в 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'а. Не забудьте также восстановить все как было при снятии резидента.
#11 (17) $g_key
Рестарт выдает в 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$
#12 (18) $g_typ
Рестарт возвращает в 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 - горизонтальный размер символа в точках
...
#13 (19) $g_blk
Рестарт возвращает в HL' адрес символьного устройства вывода и номер текущего устройства в регистре A (от 0 до 7). Входные регистры не влияют. Флаг C на выходе роли не играет. Что касается примера, смотрите отрывок из программы format.com приведенный при описании рестарта $clear(#01).
#14 (20) $g_drv
Этот рестарт по номеру устройства в регистре 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).
#16 (22) $stchn
Сей рестарт по номеру в регистре 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 указывает на путь альтернативной панели.
#17 (23) $dlchn
Этот рестарт находит канал по номеру в регистре 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$
#18 (24) $crchn
Этот рестарт создает канал. Номер канала задается на входе в регистре A. Размер канала в регистре DE. Возможные ошибки:
124 - ошибка в структуре области каналов
126 - канал с таким номером уже есть
127 - неверная длина канала (DE=0)
128 - нет места для канала
При нормальном завершении работы рестарта адрес канала выдается на выходе в HL'. Рестарт необходим для таких системных трюков, как создание устройств и установка драйверов и резидентов. Им пользуются программы set.com и dev.com, т.к. только этим рестартом можно создавать каналы с номерами от #00 до #17 и от #D8 до #FF. Для создания же каналов пользователя предназначен следующий рестарт:
#19 (25) $newch
Этот рестарт также как и предыдущий создает канал, но номер ему он дает сам, выбирая наименьший свободный в диапазоне от #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 - Ошибка в структуре области каналов
#1A (26) $swkey
Переключение устройств символьного ввода (клавиатурных). В теперешнем исдосе не применяется. В старых версиях на драйвере ttyin.blk "висели" 2 устройства. Одно было для работы в редакторе, т.е. на него переключались рестарты $smbgt(#6E) и $edstr(#7F). Другим устройством пользовались все остальные, и работало оно обычно лишь в режиме маленьких латинских букв. Т.о. развязывались режимы работы в строковом редакторе и в оболочке и утилитах, пользовавшихся рестартом $key(#07) напрямую или через рестарт $menu(#91). Однако, в момент переключения устройств останавливались макросы. Развязку пришлось осуществить другим способом, а от 2 устройств отказаться. В принципе никто не мешает написать программу, переключающуюся на другой драйвер клавиатуры и работающий с ним или даже с двумя поочередно.
#1B (27) $swtyp
Переключение устройств вывода на экран или принтер. Используется программами tv.com, edit.com и некоторыми программами печатающими на принтере, например: PrintLux, Picasso. Вход и выход описаны выше. Пример см. к рестарту $g_typ(#12).
#1C (28) $swblk
Переключение блочных устройств. Интерфейс стандартный для всех трех $sw... рестартов, и добавить тут решительно нечего. Применяется программами: arzt+, abc, format, doctor, практически всеми копировщиками. См. пример к рестарту $read(#0D).
Перемещение информации о текущем состоянии при работе с драйверами и устройствами можно представить в виде следующей схемы:
set.com: драйвер -> устройство
$ldnew(#1D): устройство -> вектор драйвер -> вектор
sw...(#1A-#1C): вектор -> старое устройство новое устройство -> вектор
Итак, set.com, загрузив драйвер, создает устройства (т.е. каналы), и копирует в них 8 байт из драйверов со смещения 8. Рестарты смены устройств сохраняют эти же 8 байт системного вектора в канале отключаемого устройства и грузят на их место аналогичные 8 байт из подключаемого устройства.
#1D (29) $ldnew
Этот рестарт используется при подключении новых устройств. И используется он, похоже, только программой 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
#1E (30) $l_im2
Рестарт работает с цепочкой функций, вызываемых по прерываниям от таймера. Как уже рассказывалось при описании работы рестарта $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.
#1F (31) $erdrv
Этот рестарт создан исключительно для драйвера 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$ …