ZX Format #06
29 июля 1997

IS-DOS - программистам: краткий курс - программирование в среде IS-DOS.

<b>IS-DOS</b> - программистам: краткий курс - программирование в среде IS-DOS.
 Рубрика "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.
     Мы рады будем выслушать Ваши  отзывы
и пожелания, а так же замеченные вами не-
точности, неясности и опечатки,  неизбеж-
ные при написании и  подготовке  подобных
текстов.
     Пишите,  звоните нам или в  редакцию
журнала,  помните,  что именно  благодаря
вашему интересу к нашей системе,  она  до
сих пор существует и развивается.

_______________________________



Другие статьи номера:

Сегодня в номере - содержание журнала.

Авторы - авторы журнала ZX-Format No.6

От авторов - свершилось давно ожидаемое событие...

Игрушки - Последний утюг (новелла по игре 48 утюгов).

Игрушки - Приключения Винни Пуха. Часть вторая.

Игрушки - описание игры The Crypt (Castle Master 2).

Игрушки - описание редактора Адевентюр - PAW (часть 1).

Игрушки - описание редактора Адевентюр - PAW (часть 2).

Игрушки - описание редактора Адевентюр - PAW (часть 3).

Игрушки - описание редактора Адевентюр - PAW (часть 4).

Игрушки - описание редактора Адевентюр - PAW (часть 5).

Программистам - Beta Basic: продолжение разговора о бейсике (часть 2).

Программистам - General Sound: Руководство по программированию.

Программистам - MMD - драйвер. Описание структуры драйвера модема для терминальной программы MMD.

Программистам - AI от В.Медноногова. Подробное описание "волнового алгоритма" трассировки (автоматического рассчета оптимального) пути, с примером реализации на Basic.

Программистам - Искуственный интеллект. Продолжение цикла статей об "AI". Общие основы нахождения пути к цели.

Программистам - Тr-Dos для программистов. Макс Петров завершает свой рассказ о нетрадиционых методах работы с диском.

Программистам - обмен опытом: "3-colour". Описание эффекта "8-цветов на точку", хелп к вьюверу и сколько слов о конвертации картинок в формат "3-colour".

Программистам - обмен опытом: "3-colour". Несколько слов о конвертации картинок в формат RGB.

Программистам - обмен опытом: программирование мультиколорных эффектов.

IS-DOS - пользователям: как выполнить индивидуальную настройку системы IS-DOS на конкретную модель ZX Spectrum-совместимого компьютера и на выполнение Ваших задач.

IS-DOS - пользователям: как скопировать системный диск IS-DOS и остаться при этом в живых.

IS-DOS - программистам: краткий курс - программирование в среде IS-DOS.

IS-DOS - news: новые программы IS-DOS.

Железо - Краткий рассказ о возможностях процессора Z-180.

Железо - Multiviewer. Описание доработочки, позволяющей мерять скорость программ по бордюру без влезания в коды - легким нажатием кнопки.

Железо - о новом проекте фирмы Peters - "Sprinter". Новый Spectrum-совместимый компьютер нового поколения Speccy.

Железо - Мнение пользователя о скорпионовском контроллере IDE HDD - SMUC.

Железо - SuperSpectrum: об одном проекте Spectrum-совместимой машины. Её особенностью является совместимость с PC.

Железо - X-Trade FAQ. Ответы на наиболее часто задаваемые вопросы по GS и XTR-модему.

Премьера - Flash tracker. Описание 4-х канального редактора цифровой музыки, работающего с SoundDrive, от самого автора SoundDrive - Flash Inc.

Премьера - Описание последней версии универсальной терминальной программы, используемой в SpbZxNet.

Премьера - Mortal Kombat: что ждёт Вас в полной версии игры и некоторые коментарии к demo версии.

Премьера - XReversy: презентация новой игрушки, из популярного семейства "реши задачку - посмотри картинку".

Интервью - Интервью с одним из известнейших спектрумистов - Андреем Ларченко.

Здесь был ты - Рассказ "Абсолютная власть".

Здесь был ты - Рассказ "Дорога".

Здесь был ты - Повелитель зубов: пародия на одну популярную трилогию...

Почта - Обратная связь: ответное письмо Alex'а из Нижнего Тагила, выставленного в прошлом номере в "Уголок ламера".

Почта - Письма читателей: Андрей Яковлев, Денис Токарчук, Алексей Гаркулим, Александр Гордеев, Евгений Шумилов, Ниточкин Вадим, Михаил Ларкин.

Почта - бесплатная реклама и обьявления.

Разное - Страшилка.: Nemo рассуждает о месте PC и Spectrum'а в современной России.

Разное - Рецензия Nemo на книжку по цифровой схемотехнике. Для всех, кто хоть когда-нибудь испытывал желание включить паяльник и...

Разное - анкета: Результаты нашего социологического опроса спектрумистов.

Разное - Конкурс. Краткий отчет о наших конкурсах.

Разное - Проблемы рынка ПО: когда загнется Спектрум. Во всем ли виноваты Хакеры?

Разное - Перспективы ПО. Краткий обзор готовящегося к выходу ПО: Fast Tracker, Pro Sound Creator, Чёрный Ворон.

Разное - Перспективы ПО. Адвентюра From Beyond или "Извне".

Разное - мемуары о Питерской модемной сети для ZX Spectrum - SPbZXNet.

Amiga Club - Между нами, пользователями: сравнение характеристик Amiga 1200 с IBM PC.

Amiga Club - сравниваем производительность Амиг и PC. Насколько Амига актуальна в современных играх?


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Реклама - реклама и обьявления.
Реклама - Реклама и объявления ...
Разное - фрагменты эхоконференции FIDO о фестивале Enlight'97.

В этот день...   25 сентября