Из журнала Adventurer #6, Рыбинск, 30.03.1997  RAY +---------------------------------------+ | ---- КАК СДЕЛАТЬ ДИСКОВУЮ ВЕРСИЮ ---- | +---------------------------------------+ Вам, безусловно, знакома такая ситу- ация: при загрузке некоторой программы (неважно, игра или системка) выдается со- общение "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 код ошибки), то мы с удоволь- ствием опубликуем его достижения. Дерзай- те, виртуозы клавиатуры! На этом позволю себе закончить. Если что-то не понятно - пишите. -------------------------