Маленькие хитрости для драйверов.
Решил рассмотреть некоторые моменты для ясности. В качестве первого примера - разборки с командами SET которые прерывают обработку команды (например выполняют .CHAIN или .EXIT). На форуме уже есть несколько таких драйверов.
К примеру, драйвер поддерживает несколько команд SET (скажем [NO]OPT и VALUE) которые просто меняют какие-либо параметры драйвера и ON которая завершается по .EXIT (например для запуска системных команд). В этом случае, понятно, что команда вроде "SET XX ON,OPT" не дойдет до конца и звершится на разборе ON. Чтобы не вводить в заблуждение пользователя, будет полезно если драйвер предупредит об этом...
Код:
.DRSET ON,1,O.ON
;+
;ЗДЕСЬ R5 УКАЗЫВАЕТ НА СИМВОЛ КОМАНДНОЙ СТРОКИ, СЛЕДУЮЩИЙ ЗА ТЕКУЩЕЙ ОПЦИЕЙ.
;НАПРИМЕР ЕСЛИ ДЛЯ "SET XX ON,OPT" R5 УКАЗЫВАЕТ НА СИМВОЛ ",".
;СТРОКА ЗАПИСАНА В ОБРАТНОМ ПОРЯДКЕ.
;+
O.ON: TSTB -(R5) ;ПРОВЕРЯЕМ СЛЕДУЮЩИЙ СИМВОЛ
BEQ O.EXEC ;НУЛЕВОЙ БАЙТ - КОНЕЦ СТРОКИ
JSR R0,10$ ;ПЕЧАТАЕМ СООБЩЕНИЕ
.ASCII /?XX-W-ON must be last option in set command/<15><12>
.ASCIZ /?XX-W-Options ignored - /
.EVEN
;+
;В СТЕКЕ ЛЕЖИТ СОХРАНЕННОЕ СОДЕРЖИМОЕ R0. ДЛЯ ПРИЛИЧИЯ ЕГО МОЖНО
;ДОСТАТЬ, НО МОЖНО И ЗАБИТЬ - ВЕДЬ МЫ БУДЕМ ДЕЛАТЬ .EXIT
;
;ВО ВРЕМЯ ВЫПОЛНЕНИЯ КОМАНЛЫ SET В ПАМЯТЬ СЧИТЫВАЮТСЯ ДВА ПЕРВЫХ
;БЛОКА ДРАЙВЕРА. ПРИ УСПЕШНОМ ЗАВЕРШЕНИИ, МОНИТОР ЗАПИСЫВАЕТ ЭТИ ДВА
;БЛОКА ОБРАТНО В ФАЙЛ ДРАЙВЕРА. В НАШЕМ СЛУЧАЕ МЫ ЗАВЕРШАЕМ ОБРАБОТКУ
;МАКРОВЫЗОВОМ .EXIT, СООТВЕТСТВЕННО ДРАЙВЕР НЕ БУДЕТ ЗАПИСАН ОБРАТНО
;НА ДИСК И НИКТО НАМ НЕ МЕШАЕТ ИСПОЛЬЗОВАТЬ ОБЛАСТЬ ВТОРОГО БЛОКА
;В КАЧЕСТВЕ БУФЕРА.
;-
10$: .ADDR #1000,R1 ;ПОЛУЧАЕМ АДРЕС БУФЕРА
MOV R1,R3 ;СОХРАНЯЕМ ЕГО - ПРИГОДИТСЯ
20$: MOVB (R0)+,(R1)+ ;КОПИРУЕМ СООБЩЕНИЕ
BNE 20$ ;
DEC R1 ;УБИРАЕМ НУЛЕВОЙ БАЙТ
30$: MOVB -(R5),(R1)+ ;КОПИРУЕМ ОСТАТОК КОМАНДЫ SET
BNE 30$ ;
.PRINT R3 ;ПЕЧАТАЕМ СООБЩЕНИЕ
O.EXEC: ;ДЕЛАЕМ ЧТО НУЖНО
.EXIT ;ВЫХОД
Данный пример при выполнении команды SET XX ON,NOOPT,VALUE=1 будет выводить предупреждение
Код:
?XX-W-ON must be last option in set command
?XX-I-Options ignored - NOOPT,VALUE=1
---------- Post added at 22:05 ---------- Previous post was at 20:42 ----------
При написании драйвера следует помнить, что типы процессоров не ограничиваются одними ВМ1 и ВМ2. Особенно это актуально для ДВК.
Данный драйвер перехватывает прерывание 10, отлавливает команды с кодом 77 и печатает хашик.
Код:
.DRDEF XX,333,0,0,0,0
.DRPTR FETCH=*NO*,LOAD=XXLOA,UNLOAD=XXUNL
.DRBEG XX
XXINT:: MOV @SP,-(SP) ;ПОЛУЧАЕМ ПРЕДПОЛОЖИТЕЛЬНЫЙ АДРЕС
SUB #2,@SP ;КОМАНДЫ
CMP #77,@(SP)+ ;ЕСЛИ КОД КОМАНДЫ НЕ 77
BNE 10$ ;ОТДАЕМ ПРЕРЫВАНИЕ
TSTB @#177564 ;ПЕЧАТАЕМ СИМВОЛ #
BPL .-4 ;
MOVB #'#,@#177566 ;
10$: SEC ;ВОССТАНАВЛИВАЕМ БИТ C
JMP @(PC)+ ;ОТДАЕМ В СИСТЕМУ
XXISR:: .BLKW
.DREND XX
.PSECT SETOVR
XXLOA:: MOV @R5,R5 ;ПОЛУЧАЕМ АДРЕС XXLQE
MOV @#10,XXISR-XXLQE(R5) ;СОХРАНЯЕМ СИСТЕМНЫЙ ОБРАБОТЧИК
ADD #XXINT-XXLQE,R5 ;УСТАНАВЛИВАЕМ НАШ
MOV R5,@#10 ;
RETURN ;ВОЗВРАТ
XXUNL:: MOV @R5,R5 ;ПОЛУЧАЕМ АДРЕС XXLQE
MOV XXISR-XXLQE(R5),@#10 ;ВОССТАНАВЛИВАЕМ ОБРАБОТЧИК
RETURN ;ВОЗВРАТ
.END
Здесь сразу три ошибки. Для XM/ZM мониторов драйвер получит неизвестно что вместо кода команды для виртуальной программы (бит 10 в JSW установлен) или для программы, запущенной через VBGEXE. Код должен быть поправлен:
Код:
XXINT:: MOV @SP,-(SP)
SUB #2,@SP
.IF EQ MMG$T
CMP #77,@(SP)+
.IFF
MFPI @(SP)+
CMP #77,(SP)+
.ENDC
Вторая ошибка - тупое восстановление вектора при выгрузке драйвера. Если после нашего драйвера кто-то еще перехватил вектр 10, выгрузка может дать непредсказуемый эффект. Поэтому стоит проверить сначала можно ли делать выгрузку:
Код:
XXUNL:: CLR R0 ;ПРИ ОШИБКЕ В R0 МОЖНО
;ПОМЕСТИТЬ АДРЕС СООБЩЕНИЯ
;0-НЕТ СПЕЦИАЛЬНОГО СООБЩЕНИЯ
MOV @R5,R5 ;ПОЛУЧАЕМ АДРЕС XXLQE
MOV R5,R4 ;ПОЛУЧАЕМ АДРЕС XXINT
ADD #XXINT-XXLQE,R4 ;
CMP R4,@#10 ;ПРОВЕРЯЕМ ISR
BNE 10$ ;
MOV XXISR-XXLQE(R5),@#10 ;ВОССТАНАВЛИВАЕМ
TST (PC)+ ;КУ
10$: SEC ;КЮ
RETURN ;
Третья ошибка относится к маловероятному (но вполне возможному) случаю когда между сохранением старого значения вектора и установкой нового кто-то успеет перехватить вектор. В FB/XM/ZM мониторах сиуация вполне возможная. Так что такие вещи стоит выполнять на приоритете 7 процессора.
---------- Post added at 22:15 ---------- Previous post was at 22:05 ----------
Еще пара слов про XB/XM/ZB/ZM мониторы (а заодно и про TSX). В случае с драйвером, мы заранее знаем будет он работать в системе с MMU или без. Соответственно многое можно упростить. Например наличие MMU автоматически означает наличие EIS (можно не писать универсальные заменители MUL/DIV/SOB итд). Также наличие MMU гарантирует наличие PSW по адресу 1(77)77776. Команд же MFPS/MTPS вполне может не быть (11/34, Электроника 100/25)...
---------- Post added at 22:43 ---------- Previous post was at 22:15 ----------
Пара слов про драйверы, перехватывающие вектора 4 или 10. Если в момент входа в прерывание значение INTLVL >=0 в RMON, нет смысла что-то делать дальше - при отдаче такого прерывания в систему последовал бы ?MON-F-System halt (однако если драйвер используется чтобы сэмулировать команды процессора которые использует другой драйвер из под .DRAST...). Можно воспользоваться недокументированной фичей для такого случая (на примере прошлого драйвера):
Код:
...
XXINT:: TST @(PC)+
INTLVL: .BLKW
BPL 10$
...
XXLOA:: MOV @R5,R5 ;ПОЛУЧАЕМ АДРЕС XXLQE
MOV $INPTR-XXLQE(R5),R0 ;ПОЛУЧАЕМ АДРЕС $INTEN
CMP (R0)+,(R0)+ ;ПОЛУЧАЕМ АДРЕС INTLVL
MOV R0,INTLVL-XXLQE(R5) ;СОХРАНЯЕМ В ДРАЙВЕРЕ
...
Примечание: в RT-11SJ без поддержки таймера нет INTLVL.
По мотивам недавней темы...
Пример выполнения определенного кода при любом выходе из программы (.EXIT, CTRL/C, ?MON-F-Ошибка):
Код:
$E16LS ==: 316 ;СМЕЩЕНИЕ ДО ТАБЛИЦЫ EMT 340-357
$JOBNU ==: 322 ;ТЕКУЩИЙ НОМЕР ЗАДАНИЯ
E6.EXI =: 20 ;ХУК ДЛЯ .EXIT
.MCALL .PRINT ;СИСТЕМНЫЕ МАКРОСЫ
START:: MOV @#$SYPTR,R5 ;ПОЛУЧАЕМ АДРЕС RMON
ADD R5,JOBNU ;СОХРАНЯЕМ АДРЕС JOBNUM
ADD $E16LS(R5),R5 ;ПОЛУЧАЕМ АДРЕС ТАБЛИЦЫ EMT
MOV E6.EXI(R5),R0 ;ПОЛУЧАЕМ АДРЕС ПОДПРОГРАММЫ
ADD R5,R0 ;...ОБРАБОТКИ .EXIT
MOV R0,E6EXI ;СОХРАНЯЕМ
MOV #EHOOK,R0 ;УСТАНАВЛИВАЕМ ХУК
SUB R5,R0 ;...
MOV R0,E6.EXI(R5) ;...ДЛЯ .EXIT
JMP R0 ;КОМАНДА, ВЫЗЫВАЮЩАЯ ПАДЕНИЕ
EHOOK:: TST @(PC)+ ;ФОНОВОЕ ЗАДАНИЕ АКТИВНО?
JOBNU:: .WORD $JOBNU ;
BNE 10$ ;НЕТ
;+
;ЗДЕСЬ ПОМЕЩАЕМ ЛЮБОЙ КОД КОТОРЫЙ ДОЛЖЕН ВЫПОЛНЯТЬСЯ ПРИ ЛЮБОМ ВЫХОДЕ
;ИЗ ФОНОВОГО ЗАДАНИЯ. ДЛЯ .EXIT ИМЕЕТ ЗНАЧЕНИЕ РАВЕН R0 НУЛЮ ИЛИ НЕТ.
;ПОСКОЛЬКУ МЫ ЛОВИМ ТОЛЬКО СВОЙ ОСТАНОВ, ПРОСТО СЧИТАЕМ, ЧТО ЕМУ НЕ НУЖНО
;БЫТЬ РАВНЫМ НУЛЮ (НЕ ВЫПОЛНЯТЬ .HRESET ПО ВЫХОДУ).
;-
.PRINT #EXMSG ;ПЕЧАТАЕМ СООБЩЕНИЕ
MOV @#$SYPTR,R0 ;ПОЛУЧАЕМ АДРЕС RMON
ADD $E16LS(R0),R0 ;ПОЛУЧАЕМ АДРЕС ТАБЛИЦЫ EMT
MOV E6EXI,-(SP) ;УБИРАЕМ
SUB R0,@SP ;...ХУК
MOV (SP)+,E6.EXI(R0) ;...ДЛЯ .EXIT
10$: JMP @(PC)+ ;ВЫПОЛНЯЕМ .EXIT
E6EXI:: .BLKW ;
EXMSG: .ASCIZ <15><12>/*** BYE BYE ***/<15><12>
.END START
Код:
.EX ATEXIT
?MON-F-Trap to 10 001042
*** BYE BYE ***
.