Рубрика "IS-DOS - программистам" No 6.
music by Ironman
(C)Леонтьев А.Г.
_______________________________
КРАТКИЙ КУРС
ПРОГРАММИРОВАНИЯ В СРЕДЕ IS-DOS.
(под общей редакцией Елисеева В. А.)
Продолжение, начало в No 5.
От редактора.
Здравствуйте, уважаемые читатели!
Сегодня в рубрике "IS-DOS - программис-
там" мы продолжаем публикацию новой кни-
ги А. Леонтьева, начатую в прошлом номе-
ре журнала. Для тех, кто еще не знает,
сообщаем, что Леонтьев Алексей Григорье-
вич является ведущим системным програм-
мистом Отдела IS-DOS, признанным автори-
тетом в области нашей системы, одним из
Отцов" IS-DOS.
Надеемся, что эта книга поможет Вам
в освоении программирования всреде
IS-DOS и пополнит ряды программистов,
способных писать под IS-DOS, новыми та-
лантами, а наш ассортимент программ - но-
выми разработками, среди которых, между
прочим, могут быть и Ваши.
- Итак:
А. Леонтьев
ЧТО ДЕЛАТЬ?
или
КАК ПЕРЕСТАТЬ БЕСПОКОИТЬСЯ И НАЧАТЬ
ПРОГРАММИРОВАТЬ В ИСДОСЕ
6. WAS IST DOS?
или САМЫЙ НИЖНИЙ УРОВЕНЬ
(0-ой уровень, он же "DOS.SYS")
Нижний уровень системы в iS-DOS
(рестарты с #00 по #1F) работает с кэшем,
блочными и символьными устройствами, ка-
налами и прерываниями. Этот уровень не
знает ни о файлах, ни о командных стро-
ках, ни об оконной технологии. Всем этим
занимаются более высокие уровни.
Итак, более подробно о рестартах:
Первые 7 (с #00 по #06) рестарты
уровня "DOS" работают с так называемой
"кэш-памятью", поэтому сперва несколько
слов о том, что это, собственно, такое.
Итак кэш блочных устройств - это
системный буфер используемый исключи-
тельно для блоков (по 256 байт) из кото-
рых состоят все блочные устройства.
Кэш создается рестартом $creat(#00).
Система автоматически создает его при
загрузке, а также при выходе в систему по
рестарту $shel0(#80) или по RET'у со зна-
чениями #F6 или #F7 в регистре А процес-
сора.
Его пересоздают также некоторые ко-
мандные файлы, например копировщики. Де-
лают они это, в основном, чтобы уменьшить
размер кэша, и, таким образом, выиграть
несколько килобайт памяти под буферы, а
также чтоб вернуть после этого кэш в
прежнее состояние.
Как уже говорилось в самом начале
книги, система iS-DOS занимает память ZX
сверху вниз, и кэш, создаваемый в послед-
нюю очередь лежит под системой. Сверху он
ограничен областью каналов. Верхний ад-
рес области кэш-памяти можно достать из
вектора конфигурации системы g_cnf, ис-
пользовав рестарт $g_cnf(#10) со смеще-
нием +32:
LD C,#10 ;$g_cnf, возвращает в
RST #10 ;HL' адрес вектора
EXX ;адрес - в HL
LD DE,32 ;прибавляем смещение
ADD HL,DE
LD E,(HL) ;получаем в DE
INC HL ;искомый верхний
LD D,(HL) ;адрес кэша
Нижний адрес кэша, естественно, за-
висит от верхнего адреса. Если Вам катас-
трофически не хватает памяти, можно уб-
рать лишние резиденты (в этом Вам помо-
жет set.com с ключом /e). Можно также
подсократить область каналов, что умеет
делать channel.com. Это вызовет перемеще-
ние всей области кэша вверх и, следова-
тельно, поднимет и нижнюю его границу,
освободив память для Ваших целей. Однако,
из Вашей программы проще всего сократить
кэш.
Размер кэша, устанавливаемый по
умолчанию, лежит в векторе g_cnf со сме-
щением +7. Адрес нижней границы кэша на-
ходится в том же векторе со смещением
+5 и +6. При необходимости его можно рас-
считать по формуле (1):
CACHE = DGCHN - CSIZE * 260 (1)
где CACHE - искомый адрес нижней границы
кэша, DGCHN - адрес начала области кана-
лов, а CSIZE - размер кэша в блоках.
По этому адресу располагается ката-
лог кэша в виде массива описателей бло-
ков (по 4 байта на каждый блок кэша),
после которого располагаются сами блоки.
Структура кэша:
┌───────┬───────┬───────────────────────┐
│Смеще- │Размер │ Описание │
│ние│ │ │
├───────┼───────┼───────────────────────┤
│ 0│EDSIZ*4│ каталог кэша, состоит │
│ │ │ из 4-байтовых описа- │
│ │ │ телей блоков: │
│ │ │ │
│ 0│4 │ описатель 1-го блока │
│ 4│4 │ описатель 2-го блока │
│ 8│4 │ описатель 3-го блока │
│ . . . │ . . │ . . . │
│EDSIZ*4│256│ 1-й блок ┐ │
│ │256│ 2-й блок ├───┐ │
│ │256│ 3-й блок ┘ │ │
│ . . . │ . . │ это, собственно, тело │
│ │ │ кэша, его адрес хра- │
│ │ │ нится в векторе g_cnf │
│ │ │ со смещением -5. │
└───────┴───────┴───────────────────────┘
где EDSIZ - размер кэша.
Описатель блока имеетследующую
структуру:
┌────┬────┬─────────────────────────────┐
│Сме-│Раз-│Описание │
│ще- │мер │ │
│ние ││ │
├────┼────┼─────────────────────────────┤
│0 │1 │регистр состояния блока, │
│││совмещенный с номером устрой-│
│││ства. Биты: │
│││ 7 - блок модифицирован (1) │
│││ 6 - блок защищен (1) │
│││ 5 - блок существует(1) │
│││ 4 - не используется │
│││ 3..0 - номер устройства, │
│││которому принадлежит блок. │
├────┼────┼─────────────────────────────┤
│1 │1 │ счетчик обращений к блоку │
├────┼────┼─────────────────────────────┤
│2 │2 │ номер блока на устройстве. │
└────┴────┴─────────────────────────────┘
ММММММММММММММММММММММММММММММММинимальный размер кэша равен 6 бло-
кам. Оптимальный в iS-DOS Classic - от 20
до 30, в CHIC - около 40.
Кэш необходим для работы файловой
службы. Через него система обращается к
устройствам, т.е. читает и пишет описате-
ли файлов каталогов, карту занятых бло-
ков устройств, заголовки устройств и т.п.
При нехватке места в кэше, в жертву при-
носится самый старый считаный блок.
Запись также осуществляется через
кэш, причем система и не имеет других бу-
феров, поэтому блок просто видоизменяет-
ся, то бишь модифицируется прямо в кэше,
а в описателе кэша устанавливается в еди-
ницу 7-ой бит.
По мере накопления модифицированных
блоков их записывают на устройство, а бит
модификации сбрасывается. Это действие
называется "автоматический флаш" или "ав-
то-флаш" (flush по-английски "слив", не
путайте с flash - флэш - "вспышка", а
также flesh - флеш - "плоть". Просьба
произносить с чисто Оксфордским пронон-
сом!)
И, наконец, рестарты:
#00 - $creat
Создает и пересоздает кэш. Требуе-
мый размер в блоках необходимо подавать в
регистре A. Содержимое остальных регис-
тров на работу рестарта не влияют.
На выходе проверьте флаг C. В слу-
чае ошибки он будет установлен в "1"
Единственная возможная ошибка - Error 130
- нехватка памяти, т.е. входное значение
регистра A так велико, что кэш не вле-
зает над адресом Utop (User Top).
Достать адрес Utop Вы можете приме-
нив рестарт $g_cnf(#10) (смещение +3 в
векторе g_cnf). Utop служит, в основном,
предохранителем от опускания кэша так
низко, что программа set.com в процессе
работы убила бы им саму себя. Поэтому
обычно Utop=26676.
Рестарт $creat(#00) обнуляет блоки
размером по 256 байт под нижней границей
области каналов ($g_cnf(#10), смещение
+32) в количестве, указанном в регистре
А, и создает каталог кэша (по 4 байта на
каждый блок).
Работать с кэшем размером менее 4
блоков небезопасно (программа на это
просто не расчитана). Рекомендуемые раз-
меры от 6..10 блоков минимум (если Вы хо-
тите выиграть побольше памяти под буферы
для программ или данных) до 30 или даже
может 70 (в iS-DOS Chic, разумеется).
Хотя наращивание кэша после 20 бло-
ков почти не дает заметного выигрыша в
скорости работы программ. Для восстанов-
ления размера кэша по-умолчанию советуем
такой вот примерчик:
LD C,#10 ;$g_cnf
RST 16
EXX ;в HL - адрес вектора
LD BC,-6 ;смещение -6, здесь
ADD HL,BC ;хранится размер кэша
;по умолчанию
LD A,(HL) ;помещаем размер в А
LD C,#10 ;$creat
RST 16 ;пересоздаем кэш
RET C
При расчетах размера создаваемого
кэша, если известны верхняя и нижняя гра-
ницы его, Вы можете пользоваться форму-
лой (2), логически вытекающей из формулы
(1):
DGCHN - CACHE
CSIZE = -------------- (2)
260
где CSIZE - искомый размер кэша, DGCHN -
нижняя граница области каналов, она же -
верхняя граница кэша, а CACHE - нижняя
граница кэша.
Пример:
LD C,#10 ;$g_cnf
RST 16
EXX ;в HL - адрес вектора
LD BC,32 ;смещение +32
ADD HL,BC
LD E,(HL) ;в DE - адрес верхней
INC HL ;границы кэша
LD D,(HL)
LD HL,CACHE;это - требуемая ниж-
;няя граница кэша,
;ниже которой вся па-
;мять может быть ис-
;пользована Вашей
;программой
EX DE,HL ;вычтем CACHE
SBC HL,DE ;из DGCHN
XOR A
LD DE,260 ;делим результат
1$ SBC HL,DE ;на 260
INC A
JR NC,1$
DEC A ;проверяем, не стал
CP 6 ;ли CSIZE меньше
JR NC,2$ ;6 блоков
LD A,130 ;Нет места! Выход с
RET ;ошибкой 130
;код ошибки в рег. А
2$ LD C,#00 ;$creat
RST 16 ;пересоздаем кэш
RET C
...
#01 - $clear
Очистка кэша от блоков текущего ус-
тройства. Эта операция необходима при
смене дискеты в дисководе или при отказе
выгрузки на диск блоков, модифицирован-
ных рестартами $modwr(#03), $modo(#2E),
$wpart(#2A), когда авто-флаш ещё не слу-
чился, а так же при записи на диск мимо
кэша блоков, считанных через кэш, дабы
диск и кэш соответствовали друг другу.
При ракботе рестарта вся информация,
имеющаяся в кэше теряется.
Если в кэше есть модифицированные
блоки, и Вы хотите их сохранить, то пе-
редочисткойвызовите рестарт
$flush(#02). Ошибок после вызова быть не
должно. Входных параметров нет.
Пример использования рестарта $clear
взят из программы format.com:
Это - начало процедуры format. Воз-
можно, перед форматированием был сменен
диск. Поэтому кэш должен быть очищен
рестартом $clear(#01). Этот пример также
будет полезен для лучшего понимания рабо-
ты рестартов $g_blk(#13), $g_drv(#14),
$binit(#0F), которые будут рассмотрены
нами далее.
Вначале с помощью $g_blk(#13) мы уз-
наем текущее блочное устройство и дос-
таем адрес тела соответствующего ему
драйвера.
Далее проверяем, является ли этот
драйвердрайверомфлоппи диска
sys_driv.blk, т.е. можно ли форматиро-
вать это устройство через TRDOS.
Затем мы пытаемся открыть это ус-
тройство как блочное устройство IS-DOS
рестартами $binit(#0F) и $open(#20) дабы
предотвратить случайное переформатирова-
ние существующего диска:
LD C,#13 ;$g_blk
RST 16 ;получаем в А номер
;текущего блочного
;устройства
INC C ;$g_drv
RST 16 ;узнаем адрес его
RET C ;драйвера
EXX ;в HL - адрес тела
;драйвера текущего
;блочного устройства
;здесь мы проверяем, является ли этот
;драйвер драйвером дисковода
LD BC,9
ADD HL,BC
LD (DRCSR+1),HL
RES 7,(HL)
;не вызывать $erdrv(1F) при ошибках на
;диске
LD C,#01 ;$clear
RST 16 ;очищаем кэш
LD C,#0F ;$binit
RST 16 ;инициализируем
;устройство
JR C,FOR_1 ;переход, на про-
;цедуру форматиро-
;вания, если устрой-
;ство не существует
LD C,#20 ;$open
RST 16 ;пытаемся открыть
;устройство
JR C,FOR_1 ;переход на формати-
;рование, если не
;открывается
...
FOR_1 ;здесь расположена
;программа формати-
;рования диска
...
#02 - $flush
Рестарт $flush(#02) необходим для
фиксации на любом блочном устройстве
(диске, винчестере или электронном диске)
изменений, произведённых в кэше после за-
писи с помощью рестартов $modwr(#03),
$modo(#2E), $wpart(#2A), $crfil(#23),
$erfil(#24) и др.
Все вышеперечисленные рестарты моди-
фицирут блоки в кэше, естественно, что
эти изменения должны в конце концов ока-
заться на диске.
Дело в том, что кэш блочных ус-
тройств был создан во-первых для ускоре-
ния работы нижних уровней системы, дабы
дважды и трижды не читать с устройства
одни и те же блоки. К тому же системе был
нужен некий свой буфер для работы с ката-
логами и файлами.
В связи с этим, запись на устрой-
ства система в большинстве случаев осу-
ществляет не напрямую и не лезет с каж-
дым новым блоком на диск, а помечает блок
в кэше как "модифицированный", то есть
устанавливает в 1 один из разрядов описа-
теля блока в кэше.
При достижении определенного коли-
чества модифицированных блоков (обычно
оно равно размеру кэша, деленному на 2
или размеру кэша минус 3) происходит ав-
томатический флаш. При этом в 1 устанав-
ливается 1-ый бит 1-го байта вектора
блочного устройства.
Входных параметров у этого рестарта
нет. Возможны различные ошибки на выходе,
например:
Ошибки кэша:
61 - указанный блок не найден
62 - реальное кол-во модифицированных
блоков не соответствует значе-
нию системной переменной ($QNMOD)
63 - сектор защищен
64 - кэш защищен
65 - нет места в кэше
66 - чтение запрещено
67 - запись запрещена
Ошибки блочного драйвера sys_driv.blk:
6 - нет диска
7 - ошибка чтения/записи
20 - обращение к диску прервано
Если модифицированных блоков в кэше
на текущем (!) устройстве нет, то рес-
тарт ничего не делает.
Советуем вызывать $flush(#02) в кон-
це вашей программы перед выходом в обо-
лочку, а так же перед запуском из Вашей
программы некоторых системныхутилит
(copy25.com, например) и перед пересосда-
нием кэша при высвобождении памяти.
Если Ваша программа писала через кэш
на несколько различных блочных устройств
(например: текущее T:, системное S:, быс-
трое Q:), то не забудьте сделать flush на
каждом из этих устройств, переключаясь на
них с помощью рестарта $swblk(#1C) или
$g_sta(#37).
от пример выхода из программы, мо-
дифицирующей блоки на нескольких устрой-
ствах сразу, скажем на Q, S и T:
LD A,"Q" ;для устройства "Q"
CALL FLUSH ;вызов подпрограммы
RET C
LD A,"S" ;для устройства "S"
CALL FLUSH ;вызов подпрограммы
RET C
LD A,"T" ;для устройства "T"
CALL FLUSH ;вызов подпрограммы
RET C
;здесь происходит восстановление среды,
;которая была на момент запуска
LD C,#41 ;$fmrst
RST #10 ;вызов рестарта
RET C
;выход в оболочку с перепечаткой экрана
;стандартным способом
XOR A
LD A,#F4
RET
;подпрограмма FLUSH
;сначала преобразуем логическое имя
;устройства ("S","Q","T") в физический
F;номер (0..7)
FLUSH LD C,#4A ;$dvtrn
RST #10
LD B,A ;номер устройства
;помещаем в рег. B
;переключаемся на соответствующее
F;устройство
LD C,#1C ;$swblk
RST #10
RET C
;вот теперь, собственно, flush
LD C,#02 ;$flush
RST #10
RET ;возврат из
;подпрограммы
#03 - $modwr
Запись одного блока (256 байт) с ад-
реса, поданного в HL на текущее блочное
устройство под номером блока, поданным в
DE. Запись происходит через кэш, т.е.
сперва в кэше освобождается место - выки-
дывается самый старый считаный блок - и
на его место переносятся заданные 256
байт.
Новый блок получает свой номер и но-
мер устройства (от 0 до 7) и тут же поме-
чается как модифицированный. Если пере-
полнения модифицированных блоков при этом
не произошло, то на этом всё и кончается,
иначе происходит автоматический флаш,
т.е. физическая запись модифицированных
блоков на текущее устройство (посред-
ством обращения к драйверу).
При работе рестарта возможны ошибки,
так что после его вызова советуем прове-
рять флаг C. Коды ошибок те же что и у
$flush(#02).
Рестарт $modwr(#03) удобен при рабо-
те непосредственно с устройством (минуя
файл). Не забудьте тольковызвать
$flush(#02) в конце программы!
Примеризжизни: программа
arzt+.com, а точнее - ее фрагмент, в ко-
тором осуществляется запись на устрой-
ство измененной таблицы занятых блоков:
$MODWR LD HL,$BUFF ;адрес рабочего
;буфера помещаем
;в рег. HL
LD DE,1 ;номер 1-го
;блока - в DE
LD A,($MLTRD+2) ;число блоков
LD B,A ;помещаем в B,
;это - счетчик
;последующего
;цикла
LD C,#03 ;$modwr
3$ RST #10
RET C
INC DE ;вызов в цикле
INC H ;для всех блоков
DJNZ 3$ ;по очереди
RET
#04 - $unird
Парный к предыдущему рестарт - чте-
ние одного блока устройства с кэша. В HL
помещаем адрес, в DE - номер блока на ус-
тройстве.
Если блока в кэше нет, процедура
считывает его через драйвер. Флаг C на
выходе сообщает о неудачно проведенной
операции. Коды ошибок, как и в предыду-
щих двух случаях, суть коды ошибок кэша и
драйвера (их описание см. $flush(#02)).
В реальной жизни этот рестарт прак-
тически не применяется, поэтому в качес-
тве примера прочитаем 0-й блок устрой-
ства в буфер BUFF, очистив перед этим на
всякий случай (если была смена диска) кэш
LD C,#01 ;$clear
RST #10 ;очистка кэша
LD DE,0;номер блока = 0
LD HL,BUFF ;адрес буфера
LD C,#04 ;$unird
RST #10
RET C
...
#05 - $mltrd
Почти тоже самое, но читается сразу
несколько блоков. Число блоков задается в
регистре B. Сперва эти блоки ищутся в кэ-
ше и копируются по означенному адресу,
затем прямо туда, куда заказывали считы-
ваются через драйвер недостающие блоки,
которые уже затем копируются в кэш.
Если число считываемых блоков пре-
восходит размер кэша пополам, то считыва-
ние происходит мимо кэша, дабы не выби-
вать из него такие часто требующиеся бло-
ки как блоки каталогов и заголовок ус-
тройства, и таким образом не замедлять
работу системы с каталогами.
Напрямую считываться блоки будут
также при наличии в кэше модифицированых
блоков. Можно заставить всегда работать
этот рестарт напрямую с драйвером, сбро-
сив 7-ой бит 0-го байта вектора блочных
устройств.
Этим рестартом пользуется рестарт
чтения части файла $rpart(#29), c по-
мощью которого в свою очередь запускают-
ся все com-файлы системы.
На всякий случай анализируйте флаг C
на выходе. Коды ошибок те же, что и у
$flush(#02).
Пример взят, опять же, из arzt+.com:
считывание карты занятых блоков устрой-
ства в буфер $BUFF. Байт $MLTRD+2 зара-
нее заполняется размером карты:
LD C,#35 ;$bkfcb
RST #10 ;считываем описатель
EXX ;диска и находим в
LD BC,-6 ;нем размер карты
ADD HL,BC
;Округлим его до целых блоков:
;и поместим по адресу $MLTRD+2
LD A,(HL)
INC HL
OR A
LD A,(HL)
JR Z,$+3
INC A
LD ($MLTRD+2),A
...
CALL $MLTRD ;вызов подпрограммы
....
;подпрограмма, использующая $mltrd(#05)
$MLTRD LD BC,#05 ;$mltrd
LD HL,$BUFF;адрес буфера - в HL
LD DE,1
RST #10
RET
$BUFF ;это - буфер
#06 - $quard
Почти тоже что и $unird(#04), но
блок считывается только в кэш (или прос-
то находится в нем). Поэтому регистр HL
на входе не используется, зато на выходе
в HL' возвращается адрес блока в кэше,
DE' - адрес заголовка блока в каталоге
кэша (необходим для дальнейшего использо-
вания $modo(#2E)).
На входе в DE подается номер блока
устройства.
Возможны различные ошибки (те же,
что и у рестарта $flush(#02)), посему
проверяйте флаг C. Удобен для работы ре-
зидентов или программ, которым жаль буфе-
ров под блок.
Пример взят из программы date.com:
;Считаем 0-ой блок устройства. Именно там
;хранится дата загрузки системы:
LD DE,0;номер блока = 0
LD C,#06 ;$quard
RST #10
RET C
EXX
;Сохраним адрес считанного блока (HL) и
,;адрес описателя этого блока в кэше (DE):
LD (TMPHL+1),HL
LD (TMPDE+1),DE
...
;...печать и изменение даты...
;(эти процедуры пропущены здесь
;за ненадобностью)
...
;выход из программы:
;в DE подаем новое значение даты.
;Сравним его с хранящимся в 0-ом блоке
;устройства и, если они разнятся,
;положим новую дату в 0-ой блок и сохра-
;ним его на устройстве рестартами
;$modo(#2E) и $flush(#02):
TMPHL LD HL,0;в этот пустой байт
;помещается вышеупо-
;мянутый адрес счи-
;танного блока
LD BC,30
ADD HL,BC
LD C,(HL)
INC HL
LD B,(HL)
;в BC теперь - старая дата, считанная
F;с устройства
EX DE,HL
XOR A
PUSH HL ; новая дата
SBC HL,BC; сравнение
POP HL
EX DE,HL
JR Z,SVSTYL ;даты равны. Обход
;Если же даты не равны - кладем новую
;поверх старой прямо в кэше:
LD (HL),D
DEC HL
LD (HL),E
;Промодифицируем блок в кэше:
TMPDE LD DE,0
;в пустой байт после DE, как Вы помните,
,;мы ранее поместили адрес описателя блока
;в кэше, полученный из рестарта $quard(6)
;несколькими строками выше.
;Этот адрес необходим для $modo.
LD C,#2E ;$modo
RST #10
RET C
LD C,#02 ;$flush
RST #10
RET C
SVSTYL
...
Следующие 3 рестарта$key(#07),
$kwait(#08) и $ktest(#09) работают с кла-
виатурой:
#07 - $key
Ввод символа с клавиатуры. Обращается
прямо в 1-ую точку входа драйвера клавиа-
туры. Адрес этой точки входа лежит в
драйвере со смещением 2). Если буфер
драйвера пуст, тогда драйвер обращается к
портам клавиатуры и ждет, пока клавиша не
будет нажата.
Выходное значение возвращается в ре-
гистре A. Оно ависит от нажатой клавиши и
от режима в котором находится устройство
ввода.
Режим определяется нулевым байтом
вектора символьного устройстваввода
g_key, который можно получить, обратясь к
рестарту $g_key(#11).
0-ой байт вектора.
Биты (0/1):
0 - строчные/ПРОПИСНЫЕ
1 - latin/русские
2 - текст/псевдографика
3 - не ждать отпускания клавиш при
$kwait(8), применяется для
макросов в редакторе,
сбрасывается сам при опустошении
буфера драйвера.
Флаг С роли не играет. В качестве
примера приводится подпрограмма из тек-
стового редактора edit.com, зажигающая и
гасящая курсор в текущих координатах и
опрашивающая клавиатуру с ожиданием или
без (управляется 5-ым битом в edcsr):
; Переключим клавиатуру в режим маленьких
,;латинских чтобы получить код управляющей
;клавиши, не зависящий от текущего состо-
;яния. 3-ий бит (макро) маскируется вось-
;мёркой дабы сохранить его, биты 0,1,2
;обнуляются:
$KEY LD C,#11 ;$g_key
RST #10
EXX
LD A,(HL)
AND 8
LD (HL),A ;lat
;Переключение на текущий драйвер вывода
;на экран (t42/t64):
CALL $TYCUR
RET C
;Позиционирование. Пересчитаем координаты
;в тексте xc в позицию на экране и поло-
F;жим YX в HL:
LD H,(IX+ys)
LD A,(IX+ys-1)
ADD A,(IX+xc)
SUB (IX+X_skip)
LD L,A
L_FLD C,#0C ;$typos
RST #10
;зажжем мигающий курсор в тек. позиции:
LD C,#76 ;$y___
RST #10
LD C,#08 ;$kwait
;Обход ожидания отпускания клавиши:
BIT 5,(IX+edcsr)
JR Z,$+3
RST #10
DEC C ;7=$key - ввод
RST #10 ;клавиши
PUSH AF ;сохраним код
LD C,#77 ;$n___
RST #10 ;погасить курсор
POP AF ;восстановим код
RET
#08 - $kwait
Ожидание отпускания клавиш. Рестарт
необходим в некоторых случаях, когда
опасна случайно дважды нажатая (удержи-
ваемая) клавиша, например в самом начале
программы, вызываемой клавишей и также
опрашивающей клавиатуру с помощью преды-
дущего рестарта.
Это - вторая точка входа в драйвер
(адрес ее находится в драйвере со смеще-
нием 4). Используется врестартах
$smbgt(#6E), $edstr(#7F), $menu(#91). На
выходе может быть всё что угодно. Пример
- см. предыдущий рестарт.
Эта процедура вызывается, скажем,
блочным оверлеем ed0+.ovr с ожиданием от-
пуска клавиши перед рестартом $analy(7E)
после печати нижней строки функций овер-
лея и при вызове функции <Cs/D>, требую-
щей подтверждения.
Такой вызов гарантирует надежность.
Однако при отметке блока та же процедура
вызывается без ожидания отпускания, что
позволяет отмечать большие блоки, удержи-
вая клавишу и используя автоповтор.
Этот рестарт при вызове опустошает
буфер драйвера клавиатуры, т.о. драйвер
"забывает" все нажатые до вызова рестар-
та клавиши. Если бы эта функция драйвера
не отключалась бы 3-им битом регистра
состояния клавиатуры, макросы работали бы
лишь до первого $kwait.
#09 - $ktest
Этот рестарт - третья точка входа в
драйвер клавиатуры. Он определяет, нажа-
та хоть одна клавиша на клавиатуре или
нет. Выход во флаге Z:
- не нажата,
NZ - нажата.
Как и $key(#07), в первую очередь
опрашивает буфер драйвера, где накапли-
ваются быстро нажатые клавиши, и который
используется для макросов или эмуляции
нажатой клавиши.
Этот рестарт удобен для опроса кла-
виатуры без ожидания, например для преры-
вания работы циклов. При нажатии клавиши
флаг на выходе - NZ, а код клавиши будет
в регистре A, а также в буфере драйвера,
откуда его необходимо извлечь рестартом
$key(#07), иначе на него наткнется еще
кто-нибудь.
Пример:
;Эта подпрограмма вернется с флагом Z,
;если клавиш нажато не было
;и с флагом NZ и кодом клавиши
;в регистре A в противном случае.
KTEST LD C,#09 ;$ktest
RST #10
RET Z
LD C,#07 ;$key
RST #10 ;извлекаем "лишний"
;код из буфера драй-
;вера клавиатуры
OR A
RET
Три следующих рестарта предназначены
для символьного вывода на экран или прин-
тер, это $type(#0A), $tycpl(#0B) и
$typos(#0C):
#0A - $type
Печать символа из регистра A на те-
кущем устройстве символьного вывода (эк-
ран или принтер).
Выбор устройства (экран или принтер)
можно осуществить при помощи рестарта
$svtyp(#1B).
Флаг на выходе при печати на экране
роли не играет. Обращаясь к принтеру мож-
но схлопотать следующие ошибки:
150 - ошибка или принтер не готов,
151 - печать прервана.
Последняя версия программы print.com
не пользуется этим рестартом.
Печать на экране происходит в теку-
щей позиции, которая хранится в байтах
1..4 вектора символьного устройства выво-
да. Вектор можно достать с помощью рес-
тарта $g_typ(#12).
Байты:
- Позиция печати в строке
2 - Номер строки на экране
Эти 2 байта подаются в HL на входе
рестарта $typos(#0C), который передает их
в регистре BC в 3-ю точку входа в драй-
вер (смещение 6).
3,4 - Адрес печати на экране
Это - левый верхний байт буквы. Нап-
ример #4000 для 0-ой позиции в 0-ой стро-
ке).
Сюда эти байты кладутрестарты
$typos(#0C) и $wtpos(#6B), Отсюда берет
их для печати следующего символа рестарт
$type(#0A). Сюда же он их и возвращает
после печати.
Рестарт обращается к 1-ой точке вхо-
да драйвера (смещение 2 от начала драйве-
ра), при этом адрес печати на экране по-
дается в регистре DE'. Этим рестартом
пользуются рестарты более высоких уров-
ней, такие как $str(#6C), $smbgt(#6E) и
др.
#0B - $tycpl
Устанавливает инверсный/нормальный
режимы печати на экране в соответствии с
содержимым регистра А:
<>0 - инверсная печать
A=0 - нормальный режим
У драйверов принтеров не использует-
ся. Флаг C на выходе значения не имеет.
Рестарт используется новой версией
текстового редактора для блочной отметки
а также программой demon.com. Обращается
во 2-ую точку входа в драйвер (адрес по
смещению 4).
#0C - $typos
Задание позиции печати на экране для
последующей печати рестартами $type(#0A),
$str(#6C), $lnstr(#6D), $smbgt(#6E),
$edstr(#7F).
Координаты печати задаются в регис-
тре HL:
- номер строки,
L - позиция в строке.
Для позиционирования относительно
окна также можно пользоваться рестартом
$wtpos(#6B). Флаг C на выходе значения не
имеет. Этот рестарт использует 3-ю точку
входа в драйвер (смещение 6).
У старых драйверов принтера эта точ-
ка не использовалась, у новых она отве-
чает за печать символа из регистра A без
перекодировки.
Вот как использует рестарты $typos и
$type программа exebat.com для печати ко-
мандной строки из системного буфера:
;Проверка наличия 8-го канала (канал
;драйвера символьного вывода), т.к. прог-
;рамма должна работать даже тогда, когда
;печатать нечем:
PRINT LD A,8 ;ty42
LD C,#16 ;$stchn
RST #10
RET C
;Позиционирование:
SBC HL,HL ;HL=0: верхняя строка
LD C,#0C ;$typos
RST #10
LD C,#45 ;$g_com
RST 16
EXX
;HL=адрес системного буфера
LD BC,#2A0A ;$type
;B=#2A=42-столько букв помещается в одну
;строку на экране, #0A - $type
;Печатаем строку до символа #D:
1$ LD A,(HL)
CP #D
JR Z,2$
RST #10
INC HL
DJNZ 1$
RET
;Допечатка пробелов:
F2$ LD A," "
RST 16 ;C=$type (см. выше)
DJNZ 2$
RET
Три рестарта, обращающиеся к драйверу
блочного устройства:
#0D - $read
Чтение блоков напрямую с драйвера
текущего блочного устройства.
HL - адрес,
DE - номер блока устройства,
B - число блоков.
Флаг C на выходе - признак ошибки.
Ошибки только те, что выдает драйвер. Так
драйвер электронного диска обычно не вы-
дает ошибок, хотя никто не мешает
встроить в него проверку, скажем, номера
блока на слишком большой номер или число
считываемых блоков на 0 или вставить про-
верку контрольной суммы блока.
У драйвера sys_driv.blk возможные
ошибки следующие:
6 - нет диска
7 - сектор не читается
20 - чтение прервано.
Обычно драйвер флоппи-диска отраба-
тывает эти ситуации обращаясь к рестарту
$erdrv(#1F) (см. ниже). Рестарт обращает-
ся к 1-ой точке входа в драйвер.
Пример взят из жизни копировщиков:
начало рабочего цикла программы abc.com.
В байты INDSK+2 и OUTDSK+2 кладутся номе-
ра входного и выходного устройств.
;B=число блоков для чтения/записи
COPY PUSH BC
INDSK LD BC,#1C ;$swblk
RST #10
POP BC
RET C
LD HL,BUFF ;буфер копирования
LD C,#0D ;$read
RST #10
RET C
PUSH BC
OUTDSK LD BC,#021C;$swblk - в C
RST #10
POP BC
RET C
LD C,#0E ;$write
RST #10
RET C
...
#0E - $write
Запись B блоков с адреса HL на теку-
щее блочное устройство (флоппи диск,
электронный диск или винчестер) начиная с
лока номер DE.
В IS-DOS любое блочное устройство
состоит из логических блоков размером 256
(#100 hex) байт, что очень удобно для
расчетов в ассемблере.
Пересчётом номера блока в трек, сто-
рону и сектор занимается драйвер. Драй-
вер флоппи диска настраивается на формат
IS-DOS дискеты сам рестартом $binit(#0F).
Это - 2-ая точка входа в драйвер.
Поскольку это прямое обращение к ус-
тройству минуя кэш, рекомендуется сразу
после вызова этого рестартавызвать
$clear(#01) или $creat(#00) во избежании
путаницы блоков в кэше и блоков на ус-
тройстве!
Ситуация с возможными ошибками точ-
но такая же как и у $read(#0D). Пример
также см. выше.
#0F - $binit
Автонастройка драйвера флоппи диска
на формат IS-DOS диска. Считывает 0-й
блок диска, проверяет наличие признака
"DSK" со смещением 10. Если признака нет,
то выходит с ошибкой 9. Иначе - настраи-
вается на параметры диска, лежащие со
смещением:
┌──┬────────────────────────────────────┐
│22│число цилиндров │
│23│тип диска (число сторон) │
,│24│размер сектора (1/2/4: 256/512/1024)│
│ │байт │
│25│число секторов на дорожке │
│64│номера секторов (до 16 штук) │
└──┴────────────────────────────────────┘
Возможна также ошибка 10 - попытка
работы с двусторонним диском на односто-
роннем дисководе или с 80-дорожечным на
40-трековом. Это 3-ий вход в драйвер.
Обычно не используется у драйверов элек-
тронного диска и винчестера (там ставят-
ся "заглушки" для совместимости).
Этот рестарт полезно вызывать при
первом обращении к новому блочному ус-
тройству для настройки драйвера на диск и
чтоб убедиться в том, что это именно
IS-DOS устройство. Кроме ошибок 9 и 10
может выдать ошибки чтения: 6, 7 и 20.
Драйвер можно настроить и на работу
с форматами TR-DOS и MS-DOS, но рестарт
здесь уже мало чем поможет.
В таких случаях лучше найти драйвер
с помощью рестарта $g_drv(#14), убе-
диться, что это именно драйвер флоп-
пи-диска, и по смещению 34(dec) сменить
размер сектора, количество секторов и
таблицу секторов.
В качестве примера рекомендуем отры-
вок из format.com приведенный после опи-
сания рестарта $clear(#01).
Cтруктура описателя
устройства (диска)
0-ой БЛОК: (0-ой сектор 0-ой дорожки)
┌────┬────┬─────────────────────────────┐
│сме-│дли-│ комментарии │
G│ще- │на │ │
│ние ││ │
├────┼────┼─────────────────────────────┤
│ 0 │ 2 │Не используется │
│ 2 │ 8 │Имя устройства │
│10 │ 3 │Признак iS-DOS: "DSK" │
│13 │ 5 │Не используется │
│18 │ 2 │Размер устройства │
│││(в блоках по 256 байт) │
│20 │ 2 │Номер 0-го блока главного│
│││каталога │
│22 │ 1 │Количество треков на устр. │
│23 │ 1 │Тип диска. Биты(0/1): │
│││ 0 - 40/80 дорожек │
│││ 1 - 1/2 стороны │
│24 │ 1 │Размер сектора: │
│││1/2/4: 256/512/1024 байт │
│25 │ 1 │Количество секторов на дор. │
│26 │ 1 │Не используется │
│27 │ 1 │Контр. сумма 32 байтового│
│││описателя ????_dos.sys │
│28 │ 2 │Не используется │
│30 │ 2 │Дата │
│32 │ 32 │Описатель ????_dos.sys │
│64 │ 16 │Таблица номеров секторов │
│││на дорожке │
└────┴────┴─────────────────────────────┘
1-ый БЛОК: Бит-карта устройства.
1 бит/блок: 0-свободен/1-занят
Описатель ????_dos.sys со смещением
32 помещается в 0-ой блокпрограммой
con.com исчитываетсязагрузчиком
boot.sys.
Ну вот и все на сегодня. В следую-
щем номере журнала мы обязательно продол-
жим публикацию книги А. Леонтьева. Далее
будут рассмотрены оставшиеся рестарты
уровня DOS.SYS с номерами от #10 до #1F.
Мы рады будем выслушать Ваши отзывы
и пожелания, а так же замеченные вами не-
точности, неясности и опечатки, неизбеж-
ные при написании и подготовке подобных
текстов.
Пишите, звоните нам или в редакцию
журнала, помните, что именно благодаря
вашему интересу к нашей системе, она до
сих пор существует и развивается.
_______________________________