Мини-драйвер дисковых операций © Андрей Алексеев, 1995.
Процедура, предложенная Тимоти, в определенной степени универсальна: не обязательно пользоваться для загрузки иными процедурами. Сравните Листинг_1 с тем вариантом, который предложил Илья Фомин в ZX-РЕВЮ-94, № 4, стр. 9. Не правда ли, есть сходство? Это и не удивительно - все основные моменты, как то: пересчет номера сектора, дорожки, инкремент адреса загрузки в память, стартовые и финишные процедуры выполняются аналогично. Для превращения процедуры записи (Листинг_1) в процедуру загрузки необходимо изменить всего два байта в строке, отмеченной (***):
LD IX,#2F1B
подставив вместо адреса подпрограммы записи сектора, адрес подпрограммы чтения сектора. Все будет прекрасно работать, за исключением маленькой детали: при загрузке в область используемых процедурой ячеек #5C00-#5C01 и
#5CFE-#5D01 произойдет накладка: загруженная с диска новая информация будет неверно "восстановлена" при выходе процедурой EXIT (см. Листинг_1).
Но объединение двух подпрограмм позволило бы сэкономить немало байтов. Реализация этой идеи привела к появлению простейшего драйвера дисковых операций, или мини-драйвера, который, хотя и может выполнять только две операции: запись и чтение группы секторов, зато мал по объему и полностью автономен: как и процедуры, взятые за основу, он не имеет ограничений на адрес расположения в памяти, при загрузке не использует никаких буферов и системных переменных, а при записи, аналогично процедуре, предложенной Тимоти, сам восстанавливает используемые ячейки (Листинг_2).
Листинг 2.
1 |
; (C) |
ALANSOFT'95 |
|
|
|
|
41 |
|
|
LD |
|
A, E |
2 |
; SMALL DISK-DRIVER |
|
|
|
|
42 |
|
|
LD |
|
(#5CFF) ,A |
3 |
; На |
входе должны |
|
|
|
|
43 |
|
|
LD |
|
(#5D00),HL |
4 |
; быть заданы: |
|
|
|
|
44 |
M2 |
|
PUSH |
HL |
5 |
; HL |
= адрес загрузки |
|
|
|
45 |
|
|
PUSH |
DE |
6 |
; DE |
= трек, сектор |
|
|
|
|
46 |
|
|
PUSH |
BC |
7
8 |
; B = ; C = |
число секторов 5 - чтение |
|
|
|
|
47
48 |
|
|
LD BIT |
|
IX,#2 F1B 0, C |
9 |
; C = |
6 - запись |
|
|
|
|
49 |
|
|
JR |
|
NZ, M3 |
10 |
; |
|
|
|
|
|
50 |
|
|
LD |
|
IX,#3F0A |
11 |
|
ORG |
50000 |
|
|
51 |
M3 |
|
CALL |
DOS |
12 |
|
ENT |
$ |
|
|
52 |
|
|
POP |
|
BC |
13 |
DRIVER BIT |
0,C |
|
|
53 |
|
|
POP |
|
DE |
14 |
|
JR |
NZ, M1 |
|
|
54 |
|
|
POP |
|
HL |
15 |
|
EXX |
|
|
|
|
55 |
|
|
INC |
|
H |
16 |
|
PUSH |
HL |
|
|
56 |
|
|
INC |
|
E |
17 |
|
LD |
HL, (#5C00) |
|
|
57 |
|
|
BIT |
|
4,E |
18 |
|
LD |
DE,(#5CFE) |
|
|
58 |
|
|
JR |
|
NZ, L5 |
19 |
|
LD |
BC,(#5D00) |
|
|
59 |
|
|
BIT |
|
0, C |
20 |
|
EXX |
|
|
|
|
60 |
|
|
JR |
|
NZ, M4 |
21 |
|
XOR |
A |
|
|
61 |
|
|
LD |
|
A, E |
22 |
|
LD |
|
(#5C00),A |
|
|
62 |
|
|
LD |
|
(#5CFF) ,A |
23 |
M1 |
DI |
|
|
|
|
63 |
M4 |
|
DJNZ |
|
L3 |
24 |
|
LD |
IX,#2F65 |
|
|
64 |
|
|
JR |
|
EXIT |
25 |
|
CALL |
DOS |
|
|
65 |
L5 |
|
LD |
|
E, 0 |
26 |
L1 |
PUSH |
DE |
|
|
66 |
|
|
INC |
|
D |
27 |
|
PUSH |
BC |
|
|
67 |
|
|
DJNZ |
|
L1 |
28 |
|
LD |
A, D |
|
|
68 |
|
|
EXIT |
BIT 0,C |
29 |
|
OR |
A |
|
|
69 |
|
|
JR |
|
NZ, M5 |
30 |
|
RRA |
|
|
|
|
70 |
|
|
EXX |
|
|
31 |
|
LD |
C, A |
|
|
71 |
|
|
LD |
|
(#5D00),BC |
32 |
|
LD |
A, #3C |
|
|
72 |
|
|
LD |
|
(#5CFE),DE |
33 |
|
JR |
NC, L2 |
|
|
73 |
|
|
LD |
|
(#5C00),HL |
34 |
|
LD |
A, #2C |
|
|
74 |
|
|
POP |
|
HL |
35 |
L2 |
LD |
IX,#2F4D |
|
|
75 |
|
|
EXX |
|
|
36 |
|
CALL |
DOS |
|
|
76 |
M5 |
|
EI |
|
|
37 |
|
POP |
BC |
|
|
77 |
|
|
RET |
|
|
38 |
|
POP |
DE |
|
|
78 |
DOS |
|
PUSH |
IX |
39 |
L3 |
BIT |
0,C |
|
|
79 |
|
|
JP |
|
#3D2F |
40 |
|
JR |
NZ, M2 |
|
|
80 |
|
|
END |
|
|
|
Длина блока кодов драйвера = 131 байт: |
|
|
|
|
|
|
|
|
|
|
C350 |
|
CB 41 |
20 |
12 |
D9 |
E5 |
2A |
00 |
39 |
|
|
|
C358 |
|
5C ED |
5B |
FE |
5C |
ED |
4B |
00 |
51 |
|
|
|
C360 |
|
5D D9 |
AF |
32 |
00 |
5C |
F3 |
DD |
66 |
|
|
|
C368 |
|
21 65 |
2F |
CD |
CE |
C3 |
D5 |
C5 |
D8 |
|
|
|
C370 |
|
7A B7 |
1F |
4F |
3E |
3C |
30 |
02 |
7E |
|
|
|
C378 |
|
3E 2C |
DD |
21 |
4D |
2F |
CD |
CE |
BA |
|
|
|
C380 |
|
C3 C1 |
D1 |
CB |
41 |
20 |
07 |
7B |
46 |
|
|
|
C388 |
|
32 FF |
5C |
22 |
00 |
5D |
E5 |
D5 |
11 |
|
|
|
C390 |
|
C5 DD |
21 |
1B |
2F |
CB |
41 |
20 |
8C |
|
|
|
C398 |
|
04 DD |
21 |
0A |
3F |
CD |
CE |
C3 |
04 |
|
|
|
C3A0 |
|
C1 D1 |
E1 |
24 |
1C |
CB |
63 |
20 |
64 |
|
|
|
C3A8 |
|
0C CB |
41 |
20 |
04 |
7B |
32 |
FF |
53 |
|
|
|
C3B0 |
|
5C 10 |
D0 |
18 |
05 |
1E |
00 |
14 |
FE |
|
|
|
C3B8 |
|
10 B4 |
CB |
41 |
20 |
0E |
D9 |
ED |
3F |
|
C3C0 |
43 |
00 |
5D |
ED |
53 |
FE |
5C |
22 |
DF |
C3C8 |
00 |
5C |
E1 |
D9 |
FB |
C9 |
DD |
E5 |
27 |
C3D0 |
C3 |
2F |
3D |
00 |
00 |
00 |
00 |
00 |
C2 |
К входным параметрам, задаваемым в регистрах HL, DE и B, добавился регистр С, определяющий режим работы мини-драйвера: С=5 загрузка; С=6 - запись. Значения С выбраны по аналогии со значениями при работе интерпретатора системных функций TR-DOS (#3D13 или 15635).
Что из всего этого следует?
Нет необходимости говорить о том, насколько важны программистам процедуры TR-DOS, независимые от системных переменных. Области применения их безграничны. Рассмотрим адаптацию программ под TR-DOS. Вы только посмотрите, как ловко все выходило с магнитофоном: имея процедуру LOAD_BYTES 1366 (#0556), или другую, аналогичную ей, можно было вытворять с загрузкой все, что угодно, грузить память хоть частями, хоть целиком, хоть вдоль, хоть поперек (см. статью о нестандартной загрузке в этом номере). Аналогичная процедура для записи SAVE_BYTES 1122 (#0462), дополняя первую, предоставляла программистам полную свободу в вопросах реализации начальной загрузки а также сохранения-загрузки отложенного состояния игры. И если при адаптации под TR-DOS с вопросами начальной загрузки кое-как научились бороться, то с сохранением отгрузок - дела обстоят из рук вон плохо. Во многих дисковых версиях отгрузки так и остались для ленты. Теперь, с появлением мини-драйвера, могут быть легко решены все проблемы как начальной загрузки, так и сохранения отгрузок.
Внимание! На горизонте показалась новая эра адаптации под диск! Сказанное можно прокомментировать простейшим примером.
Имеем магнитофонную версию игры, загрузчик которой выглядит примерно так: 10 CLEAR 24 063 20 LOAD "gam1"SCREEN$ 30 LOAD "gam2"CODE 24064,41472 40 RANDOMIZE USR 27392
Кодовый блок "gam1" является экраном, "gam2" - заполняет всю память от 24064 до конца (вместо всего этого может быть машиннокодовая программа с использованием процедуры LOAD_BYTES, но это не меняет сути дела).
Попробуйте адаптировать игру традиционными средствами, подставляя перед LOAD стандартный префикс:
RANDOMIZE USR 15619: REM : и уже при попытке выполнить 20 строку, Ваш компьютер ехидно заметит: "Out of memory". Сколько же всяких хитростей придется предусмотреть для решения проблемы? Это промежуточный перенос RAMTOP, например, в адрес 24999, разбивка кодового блока на два, основной из которых загружается с адреса 25000 до конца памяти, а непоместившийся начальный кусок в экранную область (бесцеремонно затирая такую красивую заставку!), а оттуда при помощи специальной машиннокодовой процедуры (загруженной сюда же, в экран), после переноса стека перебрасывается в адрес 24064 для объединения с основным блоком, и только после этого - запускается с адреса 27392.
А теперь забудьте про этот кошмар! Новая изящная методика адаптации будет выглядеть так.
1. Набираем в ассемблере (например, используя пакет "FWORD for GENS") следующий загрузчик (Листинг_3). Ассемблируем набранную процедуру:
А 16
(в GENS^ ключ 16 команды "А" служит для игнорирования обоих директив ORG и получения на выходе объектного кода в виде единого блока). Теперь сохраняем код на диске:
0,,1:loader
Листинг 3.
В HL - откуда перебрасывать (это начало подпрограммы загрузки). Сколько байтов перебрасывать (длина процедуры загрузки). Куда перебрасывать (в каждом отдельном случае может быть разным -это зависит от конкретных условий загрузки конкретной программы) - он должен совпадать со вторым ORG (строка 8).
Адрес возврата после LDIR. Там находится LDIR и RET, после которого попадаем на 23296. Метка указывает начало следующего блока обратите внимание: А1 это то же, что А2.
Конечно же, должен совпадать с
23872 HL, A1
BC,A3-A2
DE,23296
Потрековый загрузчик кодовых блоков. ORG 23296
|
|
|
; адресом, заданным в строке 4. |
9 A2 |
LD |
DE,(#5CF4) |
; Там пока еще сохранились трек и ; сектор, следующие за Бейсик; загрузчиком. |
10 |
LD |
SP,2 4 063 |
; Имитация CLEAR 24 063 в Бейсике. |
11 |
LD |
HL,16384 |
; Адрес загрузки 1-го файла. |
12 |
LD |
BC,#1B05 |
; Загрузить 27 секторов. |
13 |
CALL |
DRIVER |
; Вызов дискового драйвера. |
; После отработки его |
, в DE - останутся |
трек и сектор, следующие за 1-м файлом, |
; так что |
специально |
DE загружать не надо. |
14 |
LD |
HL,24064 |
; Адрес загрузки 2-го файла. |
15 |
LD |
BC,#A2 05 |
; Загрузить 162 сектора (41472 байта). |
16 |
CALL |
DRIVER |
; Вызов дискового драйвера. |
17 |
JP |
27392 |
; Долгожданный старт игрушки. |
18 DRIVER |
|
|
; Эта часть программы - без изменения ; (см. Листинг 2 выше). |
.. A3 |
END |
|
; Метка A3 нужна для расчета длины ; блока. |
Вот и получен кодовый блок загрузчика. Его длина составила 172 байта. Теперь, как Вы догадались по первому ORG, используем для его исходного размещения нулевую строку. Делаем её вручную, или автоматически при помощи программы "REM 2" (см. 2Х-РЕВЮ-93, №1-2, стр. 8), и загружаем в нее объектный код загрузчика: RANDOMIZE USR 15619: REM : LOAD "loader" CODE (23872+257)
Теперь набираем новую Бейсик-строку: 1 RANDOMIZE USR 23872
Вот и получен Бейсик-загрузчик будущей дисковой версии. Сохраняем его на диске. Теперь осталось только скомпоновать файлы на диске таким образом, чтобы кодовые блоки располагались сразу же за загрузчиком: сначала "gaml", следом за ним "gam2". Можно даже объединить их в единый Бейсик-моноблок, что очень удобно делать при помощи программы "ADM 7.08".
Вот и все! Запускайте игру и наслаждайтесь!
Конечно, не обязательно применять мини-драйвер для исходной загрузки. Вполне можно воспользоваться по предложенной выше схеме и процедурой Ильи Фомина, однако если в игре предполагается отгрузка состояния, то использование мини-драйвера предпочтительнее.
Самое интересное, что теперь дисковую адаптацию магнитофонных версий можно выполнять практически без разрушения исходной структуры файлов и последовательности их загрузки. Что Вы скажете насчет такого варианта: загрузка сплошного блока кодов с адреса 16384 где-нибудь до 65000 (где и размещается сам загрузчик на основе процедуры 1366). При стандартной работе TR-DOS - опять целый ворох проблем. С дисковым драйвером - ни одной. Перебрасываем его из нулевой строки в 65000, подкорректировав в Листинге_3 строки 4, и 8-17, определяющие загрузку конкретного файла и все дела!
Да, чуть не забыл. Если в магнитофонной версии активно используется Бейсик, расположенный, как известно, с адреса 23755 (а никак не с 23867), то здесь дело легко может быть поправлено при помощи совета Михаила Дмитриева, изложенного в ZX-РЕВЮ-94, №1, на стр. 58, который заключается в использовании процедуры ПЗУ RECLAM_2: LD HL,23755 LD BC,112 JP #19E8
После возврата в Бейсик от ненавистных системных переменных TR-DOS не останется и следа.
С применением автономного дискового драйвера становятся возможны и вариации на тему защиты, которые широко использовались в магнитофонных версиях, когда блок кодов загружается в область стека, подменяя адрес возврата из процедуры загрузки (об этом Вы, наверняка читали в известной книге "Тайники Вашего Спектрума", а если не читали, то теперь сможете найти её файлы на диске "АС 14", который распространяет ФОРМАК).
Используя дисковый драйвер, станут возможны принципиально новые виды защиты. Например, Вы с его помощью записываете на диск всю память: все 48К с 16384 по 65535 вместе со стеком, системными переменными и самим драйвером. После этого при помощи DISK-DOCTOR'а изменяете значение регистра С=6 на С=5, а заодно и значения HL, DE и B. Теперь при загрузке предварительно заданные в загрузчике трек, сектор и адрес загрузки будут "затерты" загружаемым блоком кодов, который подставит свои значения. Это вызовет полное изменение картины загрузки, создавая такую кашу, разобраться в которой взломщику будет очень-очень нелегко.
И еще. Работа мини-драйвера похожа на бульдога: если уж он "вцепился" в диск (начал загрузку), то что бы Вы не вытворяли, открывая карман дисковода, вынимая дискету и заново её вставляя, никакого нарушения в работе (типа "Tape loading error") это не вызовет, не помогут и манипуляции клавишей BREAK - загрузка будет закончена нормально, как только Вы дадите ему возможность продолжить работу. Мелочь, а приятно.
P.S. Все, о чем говорилось выше, оттестировано и проверено, так что смело набирайте и пользуйтесь: пашет, как зверь!
Комментарий ИНФОРКОМА.
В разделе ЭТЮДЫ в этом номере обсуждается вопрос "коллективного тестирования" процедур, которые должны послужить основой для программистов, создающих собственные программы. Предлагаем в их число включить и приведенный выше дисковый мини-драйвер.
Вопросы, требующие проверки или проработки:
1. Нет ли ограничений на использование драйвера на компьютерах разных моделей и с разными версиями ПЗУ TR-DOS (в принципе, можно предусмотреть проверку версии TR-DOS и подстройку под нее).
2. Как упростить драйвер до такой степени, чтобы не пострадала универсальность и надежность в
работе.
3. Как "усложнить" его для того, чтобы он "научился" выполнять команды "запись файла" и "загрузка файла", выполняя элементарные операции с элементами каталога диска (16-байтными группами описателей файлов).
4. Желательно, чтобы длина драйвера не превышала один сектор (256 байтов). Это необходимо, чтобы размещать его при необходимости в буфере принтера.
* * *