(C) RAY
(C) Музыка PHANTOM LORD/ACCEPT CORP.
╔───────────────────────────────────────╗
│ ──── КАК СДЕЛАТЬ ДИСКОВУЮ ВЕРСИЮ ──── │
╚───────────────────────────────────────╝
Вам, безусловно, знакома такая ситу-
ация: при загрузке некоторой программы
(неважно, игра или системка) выдается со-
общение "Disked by ...", a затем, при по-
пытке, скажем, загрузить что-то, вы ви-
дите помаргивающий бордер. Какие слова
будут сказаны в адрес того самого "диске-
тизатора", надеюсь, вы сами догадаетесь.
В общем, я считаю так : можно писать в
загрузчике, что программа дискетирована
только тогда, когда сделана ПОЛНАЯ ДИСКО-
ВАЯ версия. Если же просто приляпан дис-
ковый loader, то уж лучше ничего не пи-
сать или отметить, что-де не полностью
прога дискетирована. Правда последний ва-
риант мало кого может устроить, поэтому я
поделюсь с вами некоторой информацией о
том, как сделать полную дисковую версию
программы.
Итак, у вас есть некий кодовый блок,
в котором надо изменить загрузку/выгрузку
каких-либо данных с ленты на диск. Снача-
ла смотрим адрес загрузки блока. Обычно
на единицу меньше этого адреса устанавли-
вается стек. (ВНИМАНИЕ : убедитесь, что
после запуска программа не изменяет свое-
го положения в памяти, в частности, не
перебрасывает ли себя ниже этого адреса!)
Обычно минимальный адрес бывает 24500.
Если у блока такой адрес или больший -
все О.к., 90%, что проблем с переделкой
под диск не возникнет. Если же нет - си-
туация чуть более сложная и ее рассмотрим
позже (в этой статье рассматривается дис-
кетизация стандартными методами TR-DOS).
Перед тем, как искать точки входа в
ленточные процедуры загрузки/записи, по-
лезно просмотреть всю память на предмет
нахождения незанятого программой места.
Как показала практика, в любой проге
всегда найдется байт 150..200 свободного
места (можно кусками, байт по 45). А это-
го с лихвой хватит для наших целей. Если
же нет - можно использовать место, кото-
рое занимает BASIC-загрузчик.
Теперь начинаем искать точки входа.
Тут тоже может быть два варианта:
1) программа использует собственные про-
цедуры загрузки/записи;
2) программа пользуется процедурами из
ПЗУ.
В первом случае искать точки входа
труднее, но зато не нужно будет потом на-
ходить свободное место в программе, т.к.
новые подпрограммы как раз уместятся на
месте старых.
Во втором случае можно попытаться
найти вызовы ПЗУ.
Рекомендую сначала найти точку входа
в SAVE, т.к. в ней легче определить пара-
метры отгружаемого блока (начальный адрес
/обычно в регистре IX/ и длину /рег. DE/).
Приведу некоторые точки входа для
ленточных подпрограмм в ПЗУ, вытащенные
из разных программ:
мнемоника байты для поиска
─────────────────────────────────────────
SAVE: CALL 1218 CD C2 04
CALL 1222 CD C6 04
LOAD: CALL 1366 CD 56 05
CALL 2050 CD 02 08
CALL 1378 CD 62 05
CALL 1889 CD 61 07
─────────────────────────────────────────
Кроме этого встречаются и экзотические
способы:
CALL #0605
где SAVE или LOAD определяется
по ячейке 23688 = 0 - SAVE
1 - LOAD
Еще более интересный способ:
SAVE: CALL 2416,
где
IX - первые 17 байт - заголовок,
затем данные;
длина задается так:
LD DE, длина
LD (IX+11),D
LD (IX+12),E .
Если же у вас подозрение, что в
программе используются собственные проце-
дуры работы с лентой, то можно сделать
следующее. Т.к. обычно такие процедуры
делаются на основе ПЗУ'шных, то ищите
такую последовательность:
код мнемоника
──────────────────
08 EX AF,AF'
13 INC DE
DD 2B DEC IX
F3 DI
Это кусочек из процедуры, расположенной в
ПЗУ по адресу 1222 (#04C6). Если вы нашли
нечто подобное, значит это начало (почти)
подпрограммы записи на ленту. Осталось
только найти, где осуществляется ее вызов
и определить параметры отгружаемого бло-
ка.
Если ничего из вышеприведенного не
помогло - попробуйте поискать сообщения,
которые программа выдает при работе с
лентой. Обычно эти сообщения находятся
рядом с соответствующими процедурами.
Итак, все мы нашли. Замените адреса
вызова найденных подпрограмм на приведен-
ные ниже. Правда, есть еще один момент:
может быть три варианта отгрузки (для
загрузки - аналогично):
1) в один и тот же файл;
2) в одно и то же место на диске или
"в себя";
3) в разные файлы (наиболее предпочти-
тельно).
В первом случае имя файла фиксирова-
но. Во втором оно вообще не нужно. А в
третьем нужно его как-то вводить (такое
обычно встречается в адвентюрных играх).
Рассмотрим эти случаи...
1. В один Файл:
───────────────
SAVE
CALL SET_NAME ; готовим сист.
; переменные
LD C,10 ; ищем файл
CALL 15635
LD A,C
BIT 7,C
JR NZ,SAV_BL ; его нет
LD DE,(23878) ; нашли, rewrite
LD BC,#nn06
LD HL,адрес
CALL 15635
RET
SAV_BL ; создаем новый
LD HL,адрес
LD DE,размер
LD C,11
CALL 15635
RET
где адрес - адрес начала отгружаемого
блока;
размер - его размер в байтах;
nn = его размер в секторах.
LOAD
CALL SET_NAME ; готовим сист.
; переменные
LD C,10 ; ищем файл
CALL 15635
LD A,C
BIT 7,C
JR Z,LD_1
; Сообщение "файл не найден" или...
RET
LD_1 ; грузим...
LD A,C
LD C,8
CALL 15635
XOR A
LD (23801),A
LD (23824),A
LD C,14
CALL 15635
RET
; Подпрограмма установки сист.переменных
SET_NAME
LD HL,NAME ; перекидываем
LD DE,23773 ; имя файла
LD BC,8
LDIR
LD A,"C" ; тип файла м.б.
LD (23781),A ; любой, кроме
; "#","D","B"
LD A,9 ; поиск файла
LD (23814),A ; по 9 символам
RET
NAME DEFM "filename"
2. В одно и то же место ("в себя"):
───────────────────────────────────
SAVE
LD HL,адрес
LD DE,(T_S)
LD BC,#nn06
CALL 15635
RET
LOAD
LD HL,адрес
LD DE,(T_S)
LD BC,#nn05
CALL 15635
RET
T_S DEFW #SSTT ; дорожка и сектор,
; куда писать/читать
3. В разные файлы:
──────────────────
Случай аналогичный 1, только процедуру
SET_NAME надо заменить на INP_NAME.
* Если вы дискетируете адвентюру, то
лучше найти в программе процедуру ввода
строки и использовать ее для ввода имени
файла. Как это можно использовать - пока-
зано ниже. В противном случае вам придет-
ся написать ее самостоятельно.
NAME EQU ; адрес, куда считывается
; строка
INPUT EQU ; адрес процедуры считывания
; строки
FROM EQU ; параметр "адрес"
LEN EQU ; параметр "длина" (BYTES)
INP_NAME
LD HL,NAME ; очищаем имя
LD DE,NAME+1 ; файла
LD BC,7
LD (HL),#20
LDIR
CALL INPUT ; см. *
LD A,"C" ; тип файла м.б.
LD (23781),A ; любой, кроме
; "#","D","B"
LD A,9 ; поиск файла
LD (23814),A ; по 9 символам
LD HL,NAME
LD B,8
CHECK LD A,(HL)
CP NN ; признак конца
JR NZ,NEXTS ; строки код NN
LD A,#20 ; заменяем на
LD (HL),A ; пробел
NEXTS INC HL
DJNZ CHECK
LD HL,NAME
LD DE,23773
LD BC,8
LDIR
RET
И в заключение хочу поделиться одной
подпрограммкой, которая позволяет сменить
дисковод:
вызов: LD D,n ; n-номер диска
CALL CNG_D ; 0-A, 1-B, 2-C
....
CNG_D DI
LD A,#C9 ; отключаем об-
LD (#5CC2),A ; работку ошибок
; (см. ADV #5)
LD (IY+0),#FF ; сбрасываем
XOR A ; все коды
LD (23824),A ; ошибок
LD A,D ; устанавливаем
LD (23833),A ; системные пе-
LD (23798),A ; ременные TRDOS
LD C,1 ; выбор
CALL #3D13 ; дисковода
LD C,#18 ; настройка на
CALL #3D13 ; диск
EI
RET
P.S. Если не делать настройку на диск, то
при смене дисковода может получиться так,
что он будет читать только нулевой трек.
P.P.S. Приведенные в ADV #5 и этом номере
процедуры не являются идеальными, хотя и
вполне работоспособными. Если кто-то зна-
ет как можно сделать лучше (или еще луч-
ше - драйвер с прямым программирование ВГ
и обработкой ошибок, например, так: стан-
дартный вызов, а по завершению работы в
регистре A код ошибки), то мы с удоволь-
ствием опубликуем его достижения. Дерзай-
те, виртуозы клавиатуры!
На этом позволю себе закончить. Если
что-то не понятно - пишите.
─────────────────────────
Мы продолжаем цикл статей, посвящен-
ных дисковым загрузчикам. Слово нашему
корреспонденту.
(C) Nicolas Viper
"Чем ленивее человек, тем
больше любой его поступок
похож на подвиг."
Народная мудрость
Задержка великовата, ну а кому нынче
легко ? Доставайте свои мозги из баночек
с растворами, протрите, у кого запыли-
лись, и вникайте.
Прежде всего необходимо заметить,
что утверждение, будто в проверке пра-
вильности чтения (далее: ППЧ) ДОС нам по-
мочь не может оказалось НЕВЕРНЫМ. Только,
в свою очередь, мы должны помочь ему.
Взгляните на листинг:
ORG 40000
DI ;Инсталляция:
LD HL,#FD00 ;-Пре
LD A,H ; ры
LD DE,#FD01 ; ва
LD BC,256 ; ний
LD (HL),#FE
LDIR
LD I,A
IM 2
CALL MUSRUN ;-Музыки
EI
LD DE,#0100 ;-Откуда,
LD HL,16384 ; куда и
LD B,27 ; сколько будем
CALL LOADER ; грузить
JP MUSRUN ; Music off + RET
*************** ГРУЗИМ *****************
LOADER
LD IX,#2F65 ;Раскрутка
CALL DOS
L1
PUSH DE
PUSH BC
LD A,D
OR A
RRA
LD C,A
LD A,#3C
JR NC,L2
LD A,#2C
L2
LD IX,#2F4D ;Ставим череп на
CALL DOS ;дорогу
POP BC
POP DE
PUSH HL
PUSH DE
PUSH BC
CALL LOA_COM
POP BC
POP DE
POP HL
;Пересчет:
INC H ;- адреса
LD A,E ;- сек
INC A ; то
AND 15 ; ра
LD E,A
JR NZ,L5 ;- дороги
INC D
L5
DJNZ L1 ;Новый сектор
DI ;Выход
IM 1
EI
RET
Эта часть не поменялась со времен первого
пришествия. А вот самое важное:
LOA_COM
LD C,#5F ;Пересчет номера
LD A,E ;сектора, засыл-
INC A ;ка в регистр
LD IX,#2A53 ;сектора ВГ-шки
CALL GODOS
TRY1
PUSH HL ;На стек
PUSH DE
TRYMET
XOR A ;Ошибок нет
LD (ERRFLG+1),A ;(пока :)
LD B,A ;B=0
LD C,#1F ;Команда
LD A,#80 ;"чтение
LD IX,#2A53 ; сектора"
CALL DOS
LD C,#7F ;Чтение сектора
LD IX,#3FE5
CALL GODOS
POP DE ;Взяли со стека
POP HL
ERRFLG
LD A,0 ;Проверка на
OR A ;ошибки во время
JR NZ,TRY1 ;чтения. (под-
LD D,1 ;робнее описано
LD IX,#2F2F ;ниже)
JP GODOS
DOS
HALT
GODOS
PUSH IX
JP #3D2F
IMCOM ;Словили преры-
LD (STACK),SP ;вание
PUSH IX
PUSH HL ;Тут тоже в
PUSH DE ;двух словах
PUSH AF ;не опишешь :)
PUSH BC ;Ищите подроб-
CALL MUSIC ;ности ниже.
LD HL,(STACK)
LD E,(HL)
INC HL
LD D,(HL)
LD A,D
CP #40
JR NC,RETOS
CP #3F
JR NZ,PA_CNT
POP BC
PUSH BC
LD A,B
LD (ERRFLG+1),A
PA_CNT
LD HL,0-#2F3A
OR A
ADC HL,DE
JR Z,RTBR
LD A,#C3
POPALL
LD (CHANGE),A
POP BC
POP AF
POP DE
POP HL
POP IX
EI
CHANGE
JP #3D2F
RETOS
LD A,#C9
JR POPALL
RTBR
LD SP,(STACK)
POP AF
EI
JP TRYMET
STACK DW 0
ORG #C000
INSERT "MUSIC"
ORG #FEFE
JP IMCOM
MUSRUN EQU #C000
MUSIC EQU #C006
По адресу #2F2F в TR-DOS:
#2F2F IN A,(#1F)
AND #7F
RET Z
DEC D
PUSH HL
PUSH DE
JR NZ,#2EXX ;Неважно куда
HALT ;) Ловим!
Кратенько:
По поводу IMCOM : задача этой процедуры -
проверить, когда пришло прерывание.
Возможны варианты:
- в ОЗУ. Тогда нам все по барабану - иг-
раем музыку и вертаемся.
- в ПЗУ (считаем, что это ДОС!!! По-идее,
мы в СОС не попадаем). Тогда тоже ва-
рианты:
- Схватили с адреса #2F3A - это зна-
чит, что мы словили HALT (см. выше),
что, в свою очередь, говорит о том, что
при чтении произошла одна из "стан-
дартных" ошибок - потеря данных, не-
совпадение контрольных сумм и т.п.
- ...с адреса #3FXX (XX - любое значе-
ние) - опять-таки, по идее, мы там мо-
жем перехватить только процедуру чте-
ния сектора. Это значит, что, увы, у
нас неопределенное положение - надо
закончить процедуру, но из-за задержки
на проигрывание мелодии и т.п. мы
упустили время и чтение придется пов-
торить.
При возникновении ошибки, мы кладем
по адресу (ERRFLG + 1) НЕ ноль - это го-
ворит, что необходимо перечитать сектор
(см. с метки ERRFLG).
Все остальное не так сложно и было
расписано раньше. Пишите, если что не
так, в редакцию или мыльте мне:
nicky@univ.uniyar.ac.ru
Хотя, судя по предыдущим публикаци-
ям, вы, дорогие читатели, все равно не
обратили на данную процедуру никакого
внимания - глюков в ней было полно, а
никто так и не написал: "...из-за этого
Вашего #@$&$-го прога я себе дискету ис-
портил, любимую!"
(C) Котов А.В. (CAV Inc.)