Адаптация игровых программ системы TR-DOS к профессиональной системе iS-DOS
© Михаил Головин тел. (095) 148-50-04, ноябрь 1993 года.
Летом 1993 года на рынке программных средств машин класса ZX появилась качественно новая профессиональная дисковая операционная система iS-DOS. Удобный интерфейс, многокаталоговая файловая система, наличие контекстно-зависимой подсказки и многооконный интерфейс делают эту систему приятной и удобной в повседневной работе.
Несмотря на наличие программ, разработанных и поставляемых с iS-DOS, многие интересующие нас программные средства пока работают только под управлением TR-DOS. Примером таких программ могут служить наборы игрушек, которые есть почти у каждого пользователя. В связи с тем, что диски, работающие в TR-DOS, имеют другую логическую и физическую структуру, чем в iS-DOS, может возникнуть ряд проблем при переносе игр из TR-DOS в iS-DOS. Решить их Вам поможет программа-загрузчик игровых программ TR-DOS, описанная в данной статье (Листинг приведен ниже).
Сразу можно оговориться, что данный загрузчик не является полностью универсальным. С его помощью можно адаптировать игры для режима 48К, хотя понятие принципов его работы может позволить Вам написать свою программу, преодолевающую это ограничение.
Зададимся вопросом: "Как можно адаптировать игры или другие полезные программы к iS-DOS?" Существуют два решения этой проблемы. Первое: Адаптация с помощью средств iS-DOS. Второе: Написать свой собственный загрузчик. В данной статье будут рассмотрены оба варианта.
Многие могут спросить: "А зачем нужна вся эта кутерьма?! Мы и в TR-DOS неплохо играем и работаем." На этот вопрос можно ответить так. iS-DOS многокаталоговая файловая система и каждая игрушка может иметь свой каталог и хранить там свои служебные файлы. Они никогда не перепутаются, как это частенько бывает в TR-DOS. И надо выбрать для себя что лучше: куча дисков одни в TR-DOS, другие в iS-DOS или все диски под одной системой. Надо наводить порядок.
Итак, первый способ адаптации - это адаптация с помощью средств iS-DOS, называемых CDPACKER.COM, NMIPACK.COM, SPACKER.COM. Первый позволяет паковать и делать запускаемыми файлы типа CODE, второй нужен для упаковки MAGIC файлов, а третий для упаковки картинок. Созданные с помощью упаковщиков файлы можно запустить из iS-DOS применив программу EXEPACK.COM и EXESCR.COM соответственно. Как пользоваться всеми перечисленными файлами можно прочесть в системном руководстве к ним (в help файлах).
У этого способа на первый взгляд нет никаких недостатков. Но как только вы начнете его применять на практике к хорошим игрушкам, которые почти не пакуются, вы получите сообщение системы о том, что ей не хватает памяти для работы (распаковки), и Вы ничего с этим сделать не сможете. И напоследок, когда я пробовал использовать CDPACKER.COM, мной была замечена некорректная работа со стеком, в итоге игрушки просто отказывались работать, повисали или просто сбрасывались. Эти и другие причины привели к тому, что в итоге пришлось написать свой собственный загрузчик лишенный многих этих недостатков.
Итак, способ второй.
Используем специально написанный загрузчик (смотри листинг). Чтобы понять работу Game Loader^) нужно разобраться с физической и логической структурой диска, работающего под iS-DOS. С физической точки зрения диски под iS-DOS бывают двух видов: отформатированные под TR-DOS (не загружаемые) и отформатированные под iS-DOS (загружаемые). Первые имеют на каждой дорожке 16 секторов по 256 байт на сектор, а вторые 5 секторов по 1024 байта соответственно (это позволяет сделать их автозагружаемыми). С логической точки зрения все диски это устройства с одинаковой структурой: блоками данных по 256 байт, количество которых записано в заголовке устройства (на диске). Система сама производит при своей работе необходимые пересчеты и поэтому для пользователя этот процесс является прозрачным (не видимым). Многие игрушки для запуска требуют порой почти всю доступную основную память, поэтому все необходимые расчеты должен производить сам загрузчик, так как во
время его работы система iS-DOS из памяти полностью выгружается.
Теперь рассмотрим листинг программы. В качестве средства программирования избран Zeus Assembler (версия входящая в PHT 3.6), позволяющий переустанавливать несколько раз адрес начала ассемблирования директивой ORG и компилирующий прямо на диск с помощью директивы OPEN. Это дало возможность совместить две части загрузчика: Интерфейсную (0110 - 1520) и Основную (1530 - 2670) в одной программе, не требующей настройки адресов последней. Первая часть программы производит настройку второй (системные параметры диска), вторая часть и является собственно загрузчиком.
Строка 0140 содержит имя файла данных (картинка + блок кодов), который будет загружен (о создании этого файла смотри далее).
Строки 0160-0240 и 0260-0310 содержат текст, который появляется при загрузке или ошибке.
Строки 0360-0550 выводят окно с поясняющей надписью на экран, перемещают Основную Часть в буфер принтера (#5B00), вызывают подпрограмму инициализации системных переменных, устанавливает Первый Режим Прерываний и стандартный обработчик сообщений об ошибках (для корректной работы TR-DOS), устанавливает верхнюю границу стека (из расчета Адрес Загрузки Кодов минус 256*3) и передает управление Основной Части (перемещенной уже в буфер принтера: #5B00).
Строки 0590-1120: подпрограмма инициализации системных переменных. Читается заголовок текущего устройства, по соответствующим смещениям определяются и инициализируются Размер Сектора, Количество Секторов на треке, Данные о Секторах на треке Основной Части. Затем производится поиск файла, имя которого записано по адресу FNAME, определяется Номер Блока Описателя Сегмента, Атрибут Состояния Файла и если файл не сегментирован, то Номер Блока Описателя Сегмента будет номером первого блока данных файла (он записывается в переменную FDISP). Если файл сегментирован то читается Блок Описателя Сегмента и проверяется количество сегментов у файла. Если их больше одного, то совершается переход на подпрограмму обработки ошибок (ERROR), иначе номер первого сегмента (блока данных файла) заносится в переменную FDISP. Сбрасывается флаг ошибок (C=0) в регистре флагов, и происходит возврат в основную программу.
Строки 1160-1350: подпрограмма обработки ошибок. Выводит окно с сообщением об ошибке, "бибикает" и выходит в систему.
Строки 1390-1520: подпрограмма звукового сигнала. Формирует звуковой сигнал, с миганием рамки. Затем происходит возврат в вызывающую программу.
Строки 1509-1680 загружают картинку, вызывают распаковщик, загружают блок кодов и передают ему управление.
Строки 1740-2330: подпрограмма чтения секторов. Вызывается подпрограмма преобразования Номера Первого Сегмента в Трек, Сектор, Смещение в секторе (нужно для секторов размером 1024 байта). Изменяется переменная FDISP, чтобы она указывала на следующий блок данных (для этого файл данных должен быть непрерывным). Если Смещение не нулевое, то адрес загрузки файла корректируется соответствующим образом. Это накладывает небольшое ограничение: перед файлом и после него в памяти должно быть пустое (не используемое) место, в котором НЕЛЬЗЯ ни чего располагать, так как оно может использоваться при загрузке. Затем производятся необходимые расчеты для переменных Трек, Сектор. Происходит вызов TR-DOS и загрузка кода данных. Далее берется следующий сектор, если он выходит за пределы трека, то берется следующий трек. И так до тех пор, пока не загрузится нужное количество секторов.
Строки 2350-2650: подпрограмма дешифрации номера блока в номера Трека, Сектора, Смещения.
Строка 2670 содержит Данные о Секторах на Треке (номера секторов).
Что мы должны сделать, чтобы наша программа заработала?
Пусть у нас есть заархивированый файл-картинка SCREEN.CODE 32768,4800 разархивирующийся по команде RANDOMIZE USR 32768 и файл игрушка GAME.CODE 26000,30000 запускаемый командой RANDOMIZE USR 26000. Теперь из этих двух файлов нужно создать один файл данных, назовем его gamefile.dat. Создается он посредством склейки SCREEN и GAME, длина каждого из них должна быть кратной 256 байт. Файлы склеиваются в памяти или прямо на TR-DOS диске. Они записываются друг за другом и у первого файла в директории изменяется количество занимаемое им секторов на суммарное для обоих файлов, также изменяется слово длины файла (оно должно быть кратно 256 байт) , затем первый файл копируется на другой диск или сразу же в iS-DOS.
ВНИМАНИЕ!!!
На том диске, где вы изменяли параметры файла, вы должны восстановить исходное значение количества секторов и слово длины первого файла. Для этих целей можно использовать функции PHT 3.6: записи и восстановления каталога на 80 треке.
Некоторые дисковые мониторы позволяют проделывать операцию склейки автоматически.
Итак файл данных создан. Далее вносим изменения в листинг (строки 0140: вместо <ИМЯ ФАЙЛА ДАННЫ1Х> пишем gamefiledat (без точки !), 0540: <ВЕРШИНА СТЕКА>=26000-256*3 (рассчитать самим), 1610: < ДЛИНА КАРТИНКИ>=19 (блоков по 256 байт), 1620: <АДРЕС ЗАГРУЗКИ>=32768, 1640: <АДРЕС РАЗ АРХИВ АТОР А>=32768, 1650: < ДЛИНА ИГРУШКИ>=118 (блоков по 256 байт), 1660:<АДРЕС ЗАГРУЗКИ>=26000, 1680: <СТАРТОВЫЙ АДРЕС>=26000.
Ассемблируем получившийся листинг в .com файл, переносим в iS-DOS не забыв исправить с помощью команды rename (iS-DOS) или аналогичной команды PHT 3.6 стартовый адрес на тот, который указан в строке 0080, перенося в этот же каталог файл данных, получим экземпляр игрушки загружающейся в iS-DOS.
Примечания.
1. Обращение к системным функциям iS-DOS происходит через RST #10. Код вызываемой функции помещается в регистр C, остальные регистры инициализируются по мере необходимости. При возврате обычно используется альтернативный набор регистров. Если возникает ошибка, то флаг C в регистре флагов устанавливается, иначе сбрасывается.
2. Длинные файлы (более 127 блоков) при копировании получаются всегда сегментированными. Если они копируются из TR-DOS посредством from_trd.com или при помощи модифицированной мной программы filecopy.com с ключом /l, то этого можно избежать.
3. Следует помнить, что при копировании файлов в iS-DOS учитывается не сколько он занимает блоков, а его реальная длина. Так если файл занимает 10 блоков и имеет длину 1 байт, то в iS-DOS скопируется только этот байт.
Листинг программы-загрузчика игровых программ TR-DOS. iS-DOS Utility: Game Loader v1.00 25-Nov-1993 (C) by M.Helloween
0010 0020 0030 0040 0050 0060 0070 0080 0090 0100 0110 0120 0130 0140 0150 0160 0170 0180 0190 0200 0210 0220 0230 0240 0250 0260 0270 0280 0290 0300 0310 0320 0330 0340 0350 0360 0370 0380 0390 0400 0410 0420 0430 0440 0450 0460 0470 0480
0490 0500 0510 0520 0530
"iS-DOS "Loader #0D,
#0D,
" iS-DOS #0D,
" by
#03
DM DM DB DM DB DM DB DM DB
DM DB DM DB DM DB
DB DB
LD
LD
LD
RST
LD
LD
RST
LD
LD
LD
LDIR
CALL
RET
DI
IM
LD
LD
Zeus Assembler ( PHT v3.6 )
Адрес загрузки .com файла: #5DC0 (24000)
#5DC0 START
Данные о .dat файле и тело "Интерфейсной Части"
"Имя .dat Файла"
"Текст Заставки"
&
Game Adaptation"
M.Helloween"
"Текст Ошибки"
# 0D, # 0D,
"Файл Данных должен быть'
# 0D,
" НЕПРЕРЫВНЫМ"
#03
4, 9, 6, 24, 58,5 9, 24
IX,V_WND A,2 C,#61 #10
HL,TEXT1 C,#67 #10
HL,GAMLO DE,_GAM_
bc,#0100
;Перекинуть INITS
C 1
A, #C9 (#5CC2),A
"Вектор Окна"
; Адрес " Вектора Окна "
;Код: "Вывод Окна"
;Адрес "Текста Заставки" ;Код: "Печать Текста (HL)
Окне"
'Основную Часть"
буфер принтера Инициализировать системные переменные
Режим Прерываний стандартный обра
ботчик сообщений об ошибках
LD SP,<ВЕРШИНА CTEKA>
JP _GAM_
;
;Инициализация системных переменных ;
INITS LD C,#06
LD
RST
RET
EXX
LD
ADD
LD
LD
INC
LD
LD
LD ADD LD LD
LDIR
LD
LD
RST
RET
EXX
PUSH
LD
ADD
LD
INC
LD
POP
LD
ADD
LD
AND
JR
LD
DE,#0000 #10
C
BC,00024 HL,BC A,(HL) (SSIZE),A HL
A,(HL) (QSECT),A
BC,00039 HL, BC DE,STBUF
bc,#0010
HL,FNAME
C, #34 #10
C
HL
BC,#0011 HL, BC E,(HL) HL
D,(HL) HL
BC,00011 HL, BC A,(HL) #40
NZ, NEXT9 C, #06
0970 0980 0990
1000 RST #10
1010 EXX
1020 LD A, (HL)
1030 DEC A
104 0 JP NZ,ERROR
1050 INC HL
1060 LD E, (HL)
1070 INC HL
1080 LD D, (HL)
1090
1100 NEXT 9 LD (FDISP) ,DE
1110 XOR A
1120 RET
1130 ;
114 0 ;Ошибка: Файл сегментирован
1150 ;
1160 ERROR LD IX,V_WND
0540 0550 0560 0570 0580 0590 0600 0610 0620 0630 0640 0650 0660 0670 0680 0690 0700 0710 0720 0730 0740 0750 0760 0770 0780 0790 0800 0810 0820 0830 0840 0850 0860 0870 0880 0890 0900 0910 0920 0930 0940 0950 0960
;Переход на "Основную Часть"
;Код: "Загружает Блок (DE) ;текущего устройства и возвращает ;его адрес в регистровой паре HL"
;Инициализация "Размера Сектора'
;Инициализация "Колличества ;Секторов на Треке"
;Инициализация "Данных ;о Секторах на Треке" ;Адрес "Имени .dat Файла" ;Код:"Поиск и Открытие Файла"
;DE= Номер Блока Описателя Сегмента
; Атрибут Состояния Файла
; Переход, если Файл не ; Сегментирован ; Код: "Загружает Блок (DE) ; текущего устройства и возвращает ;его адрес в регистровой паре HL"
; Переход, если Файл Сегментирован
;DE = Номер Блока первого сегмента, ; если Файл Данных ОдноСегментен
; Сбросить Флаг Ошибок
; Адрес " Вектора Окна "
1170 |
|
|
LD |
(IX+4),23 |
|
1180 |
|
|
LD |
(IX+6),10 |
|
1190 |
|
|
LD |
A, 2 |
|
1200 |
|
|
LD |
C,#61 |
;Код: "Вывод Окна" |
1210 |
|
|
RST |
#10 |
|
1220 |
|
|
LD |
HL,TEXT2 |
;Адрес "Сообщения об Ошибке" |
1230 |
|
|
LD |
C,#67 |
;Код: "Печать Текста (HL) в Окне" |
1240 |
|
|
RST |
#10 |
|
1250 |
|
|
CALL |
BEEP |
;"Посигналить" об Ошибке |
1260 |
|
|
POP |
HL |
|
1270 |
|
|
LD |
C, #07 |
; Код: "Ввод Символа (A)" |
1280 |
|
|
RST |
#10 |
|
1290 |
|
|
XOR |
A |
|
1300 |
|
|
LD |
A, #F4 |
;Код: "Выход с Очисткой Экра- |
1310 |
|
|
|
|
;на, инициализацией системных |
1320 |
|
|
|
|
;переменных, переключение на |
1330 |
|
|
|
|
; основное устройство печати в |
1340 |
|
|
|
|
;прямом режиме" |
1350 |
|
|
RET |
|
|
1360 |
; |
|
|
|
|
1370 |
; |
Звуковой сигнал |
|
1380 |
; |
|
|
|
|
1390 |
BEEP |
XOR |
A |
|
1400 |
|
|
LD |
B, A |
|
1410 |
BEEP 1 |
DEC |
H |
|
1420 |
|
|
JR |
NZ,BEEP2 |
|
1430 |
|
|
XOR |
#11 |
|
1440 |
|
|
OUT |
(#FE),A |
|
1450 |
|
|
LD |
H, #EE |
|
1460 |
BEEP2 |
DEC |
L |
|
1470 |
|
|
JR |
NZ,BEEP1 |
|
1480 |
|
|
XOR |
#11 |
|
1490 |
|
|
OUT |
( #FE) ,A |
|
1500 |
|
|
LD |
L, #FE |
|
1510 |
|
|
DJNZ |
BEEP1 |
|
1520 |
|
|
RET |
|
|
1530 |
|
|
|
|
|
1540 |
|
iS-DOS GAME |
LOADER V1.00 |
|
1550 |
|
|
("Основная часть") |
|
1560 |
|
|
|
|
|
1570 |
|
Адре |
с загрузки: #5B00 (23296) |
|
1580 |
|
|
|
|
|
1590 |
GAMLO |
EQU |
$ |
|
1600 |
|
|
ORG |
#5B00 |
|
1610 |
|
_GAM_ |
LD |
C,<ДЛИНА КАРТИНКИ> |
; Длина Картинки в блоках (256 байт) |
1620 |
|
|
LD |
HL,<АДРЕС ЗАГРУЗКИ> |
;Адрес Буфера загрузки Картинки |
1630 |
|
|
CALL |
READS |
|
1640 |
|
|
CALL |
<АДРЕС РАЗАРХИВАТОРА> |
; Вывод Картинки на Экран |
1650 |
|
|
LD |
C,<ДЛИНА ИГРУШКИ> |
; Длина Игрушки в блоках (256 байт) |
1660 |
|
|
LD |
HL,<ДДРЕС ЗАГРУЗКИ> |
; Адрес Буфера загрузки Игрушки |
1670 |
|
|
CALL |
READS |
|
1680 |
|
|
JP |
<СТАРТОВЫЙ АДРЕС> |
; Запуск игрушки |
1690 |
; |
|
|
|
|
1700 |
; |
Подпрограмма чтения сектора |
|
1710 |
; |
HL = |
Адрес |
буфера |
|
1720 |
; |
C = |
Количество секторов |
|
1730 |
; |
|
|
|
|
1740 |
READS |
CALL |
G_TSD |
; Инициализация переменных |
1750 |
|
|
|
|
;"Трека, Сектора, Смещения" |
1760 |
|
|
PUSH |
HL |
|
1770 |
|
|
LD |
B, #00 |
|
1780 |
|
|
LD |
HL, (FDISP) |
|
1790 |
|
|
ADD |
HL, BC |
;HL = Номер Блока следующего |
;сегмента
1800 |
|
LD |
(FDISP),HL |
1810 |
|
POP |
HL |
1820 |
DISPL |
EQU |
$ + 1 |
1830 |
|
LD |
A, #00 |
1840 |
|
LD |
B, A |
1850 |
|
INC |
B |
1860 |
|
LD |
DE,#0100 |
1870 |
_LOOP |
OR |
A |
1880 |
|
SBC |
HL, DE |
1890 |
|
INC |
C |
1900 |
|
DJNZ |
_LOOP |
1910 |
|
ADD |
HL, DE |
1920 |
|
DEC |
C |
1930 |
|
LD |
A, C |
1940 |
LOOP 1 |
PUSH |
AF |
1950 |
TRACK |
EQU |
$+1 |
1960 |
|
LD |
D,0 |
1970 |
|
PUSH |
HL |
1980 |
SECTR |
EQU |
$+1 |
1990 |
|
LD |
C,0 |
2000 |
|
LD |
B, #00 |
2010 |
|
LD |
HL,STBUF |
2020 |
|
|
|
2030 |
|
ADD |
HL, BC |
2040 |
|
LD |
E,(HL) |
2050 |
|
POP |
HL |
2060 |
|
PUSH |
HL |
2070 |
|
LD |
BC,#0105 |
2080 |
|
CALL |
#3D13 |
2090 |
|
POP |
HL |
2100 |
|
LD |
A,(SSIZE) |
2110 |
|
LD |
B, A |
2120 |
|
LD |
DE,#0100 |
2130 |
???00 |
ADD |
HL, DE |
2140 |
|
DJNZ |
???00 |
2150 |
|
LD |
A, (SECTR) |
2160 |
|
INC |
A |
2170 |
QSECT |
EQU |
$+1 |
2180 |
|
|
|
2190 |
|
CP |
0 |
2200 |
|
JR |
NZ,NEXT1 |
2210 |
|
LD |
A,(TRACK) |
2220 |
|
INC |
A |
2230 |
|
LD |
(TRACK),A |
2240 |
|
XOR |
A |
2250 |
NEXT1 |
LD |
(SECTR),A |
2260 |
SSIZE |
EQU |
$+1 |
2270 |
|
LD |
C, #01 |
2280 |
|
POP |
AF |
2290 |
|
OR |
A |
2300 |
|
SBC |
A, C |
2310 |
|
JR |
Z,NEXT2 |
2320 |
|
JR |
NC,LOOP1 |
2330 |
NEXT2 |
RET |
|
2340 |
; |
|
|
2350 |
G_TSD |
PUSH |
HL |
2360 |
FDISP |
EQU |
$+1 |
2370 |
|
LD |
HL,#0000 |
2380 |
|
LD |
A,(SSIZE) |
2390 |
|
LD |
E,A |
2400 |
|
XOR |
A |
2410 |
|
LD |
(SECTR),A |
2420 |
|
LD |
(TRACK),A |
;Адрес Переменой "Смещение"
;Адрес Переменной "Трека"
;Адрес Переменной "Сектора'
;HL = Адрес "Данных ;о Секторах на Треке"
Код: "Считать Один Сектор
в TR-DOS" Системный вызов TR-DOS
;A = Размер Сектора
;A = Номер Сектора
;Адрес "Переменной Количество ; Секторов"
; Адрес " Переменной Размер Сектора "
;A = Номер Трека
;A = Размер Сектора
;Начальная установка переменных: "Сектора и Трека"
2430 |
|
LD |
D,A |
2440 |
LOOP3 |
OR |
A |
2450 |
|
SBC |
HL, DE |
2460 |
|
JR |
C,NEXT4 |
2470 |
|
LD |
A,(SECTR) |
2480 |
|
INC |
A |
2490 |
|
PUSH |
HL |
2500 |
|
LD |
HL,QSECT |
2510 |
|
|
|
2520 |
|
CP |
(HL) |
2530 |
|
POP |
HL |
2540 |
|
JR |
NZ,NEXT3 |
2550 |
|
LD |
A,(TRACK) |
2560 |
|
INC |
A |
2570 |
|
LD |
(TRACK),A |
2580 |
|
XOR |
A |
2590 |
NEXT3 |
LD |
(SECTR),A |
2600 |
|
JR |
LOOP3 |
2610 |
NEXT4 |
ADD |
HL, DE |
2620 |
|
LD |
A, L |
2630 |
|
LD |
(DISPL),A |
2640 |
|
POP |
HL |
2650 |
|
RET |
|
2660 |
; |
|
|
2670 |
STBUF |
DS |
#10 |
;A = Номер Сектора
;"Данные о Секторах на Треке'
;HL = Адрес Переменной ;"Количество Секторов"
;A = Номер Трека
;A = Смещение в секторе