Inferno #07
31 мая 2005

For Coderz - Как делается крупная перемещаемая программа.

         Перемещаемость программ
 

   Поскольку крупные программы не держатся
на одних JR, да и, более того, имеют пере─ 
менные, то  расскажу, как делается крупная 
перемещаемая программа. Например, это при─ 
меняется для программирования под кэш:один 
и тот же фрагмент работает  в одном случае 
в Cache, а в другом - в обычном ОЗУ. 

1. С созданием таблицы в полуручном режиме 

   Применяется, например, в  Pro Tracker'е
при  компиляции музыки с плейером. PT хра─
нит плейер, изначально  отассемблированный
по адресу #c000. Для  того, чтобы он рабо─
тал по нужному адресу,содержимые некоторых
его  ячеек (в которых  упоминаются  адреса
процедур и переменных) пересчитываются.Ад─
реса  самих пересчитываемых ячеек хранятся
в специальной таблице. Точнее,вместо адре─
сов там содержатся  расстояния от предыду─
щей  пересчитываемой  ячейки  до следующей
(так  решил  сэкономить  первый автор  Pro
Tracker'а, потому  что  таблица  постоянно 
лежит в памяти).
 

        LD A,(SNGST+1) ;A=старший байт,
                ;адреса, по которому будем
                      ;компилировать плейер
        LD IX,TRELOC ;адрес таблицы
                   ;перемещения (релокации)
         LD HL,plaer ;где лежит плейер
        LD D,#C0 ;старший байт адреса, по
                   ;которому в прошлый раз
                      ;компилировали плейер
         LD ($-1),A
         LD E,A
        LD B,L
 REL0    LD C,(IX) 
         LD A,C
         AND A
        RET Z        ;код 0 - конец таблицы
         INC IX
         ADD HL,BC
        INC A
        JR Z,REL0    ;код 255
         LD A,(HL)
         SUB D
         ADD A,E
         LD (HL),A
        JR REL0

Код 255, как видим, особый - он пропускает
255 байт, если  до следующей ячейки больше 
255 байт (ровно 255 байт нельзя). 
   Таблица  релокации  составляется  путём
сравнения  двух  вариантов  плейера:  один
откомпилирован  под  #c000, а другой - под
#c100. Вот сравнивалка: 
 

        ORG #A000
 P1      INCBIN "PLYC0" ;плейер, 
              ;откомпилированный под #C000
 E1 
        ORG #C100
 P2      INCLUDE "playFAST",#C4 ;тот же 
                             ;плейер, будет
                 ;откомпилирован под #C100
 module ;адрес модуля=адрес конца плейера. 
      ;Тогда адрес модуля в этом экземпляре
        ;плейера будет сдвинут на #100 байт
        ;по сравнению с экземпляром "PLYC0"
         ORG #6000
         LD IX,TAB
         LD HL,P1
         LD DE,P2
        LD C,0
 CP0     LD A,(DE) 
         SUB (HL)
         JR Z,CPOK ;байты отличаются?
         DEC A     ;байты отличаются на 1?
        JR NZ,ERR  ;НЕТ???->выход по ошибке
         LD (IX),C
         INC IX
        LD C,0
 CPOK    INC HL 
         INC DE
         INC C
         JR NZ,CPNOFF
         LD (IX),255
         INC IX
        LD C,1
 CPNOFF  LD A,L 
         CP E1
         JR NZ,CP0
         LD A,H
         CP 'E1
         JR NZ,CP0
         LD (IX),0
         INC IX
        RET
;выход по ошибке:нам подсунули существенно 
;РАЗНЫЕ файлы плейеров 
 ERR     LD HL,0 
         LD DE,#5800
         LD BC,768
         LDIR
        RET
TAB=#7000 

Процедуру  следует запускать из STS. Адрес
конца таблицы увидим в регистре IX.

   2. С созданием таблицы автоматически

   В PT Util для  составления таблицы при─
меняется  более удобный метод, при котором
ассемблировать  нужно не 2-3 раза, а всего
1. Происходит это так. 
   Плейер  для  PT Util  сидит в отдельном
исходнике, который  при INCLUDE'инге его в
основную программу ассемблируется как обы─
чный  плейер. Но если его попытаться отас─
семблировать  как самостоятельную програм─
 му, то:
  1. Автоопределяется:адрес компиляции ра─
вен #8000, следовательно, нужно произвести 
компиляцию для создания перемещальной таб─ 
 лицы. 
   2. Плейер ассемблируется как макрос
                 MACRO pp
(весь целиком  загоняется в макрос), в па─ 
мять при этом не кладётся. (То же происхо─ 
 дит и при упомянутом INCLUDE'инге .) 
  3. Макрос плейера ассемблируется по-нас─
тоящему, с локальными  метками, под  адрес 
#8000.  (А вот этого  при  INCLUDE'инге не 
 происходит.) 
  4. Макрос плейера ассемблируется по-нас─
тоящему ещё раз,уже не с локальными метка─ 
ми, под адрес #C000. (А при INCLUDE'инге - 
под  адрес, который был текущим  на момент 
 команды INCLUDE .) 
  5. Ассемблируется сравнивалка. Адрес за─
пуска  программы - переход на сравнивалку. 
То  есть: запускаем  по RUN - составляется 
таблица. Можно сохранить её вручную. Позже 
я освоил процедурку SAVEOBJ, её самое мес─ 
то применить  здесь - тогда по RUN таблица 
не  только  составится, но и сохранится. И 
тогда всё - таблицу уже можно использовать 
в основной программе, путём INCBIN. В про─ 
цедуру SAVEOBJ из инициализационного куска 
программы  (точнее, из  подразумевающегося 
таковым - например, из нашей  сравнивалки) 
можно выйти через JP nenado. Я имею в виду 
мою  версию SAVEOBJ, а не ту, которую рас- 
пространял Capry. 

         3. Через макросы j, c, l

   В ZXRar я реализовал  ещё более удобный
способ: таблица перемещения строится прямо
на этапе ассемблирования основной програм─
мы, самим аласмом. При этом никаких INCBIN
вообще не нужно.
   Для такого способа требуется определить
следующие макросы:
 

       MACRO rg
 CurCa=$ 
         ORG RelCa
        DW CurCa+
 RelCa=$ 
         ORG CurCa
       ENDM
 

        MACRO j
         rg 2
        IFN ?j1-2 ;есть ли 2-й параметр
                                  ;макроса?
         JP ,1   ;если есть, то такой JP
         ELSE
         JP       ;если нет, то эдакий
         ENDIF
       ENDM
 

        MACRO c
         rg 2
         IFN ?j1-2
         CALL ,1
         ELSE
         CALL 
         ENDIF
       ENDM
 

        MACRO l
         LD ,1
         rg 0-1 ;просто -1 в старых аласмах
               ;было нельзя, т.к. в макросе
                ;rg получилось бы CurCa+-1.
                      ;В новых можно.
       ENDM

И они используются в перемещаемом фрагмен─
те вместо команд JP [cc,]nn, CALL [cc,]nn,
LD (...),rp, LD rp,(...). 
   Но сначала надо задать начальный адрес,
от которого будет строиться таблица:

RelCa=#C000 

   Как видим, здесь на каждый релоцируемый
адрес  приходится  2 байта в таблице (а не
1, как было в Pro Tracker'е ).Тут экономия 
памяти  ни к чему - ведь таблица нужна то─
лько на этапе запуска ZXRar'а.
   Вот  место  процедуры  инициализации  в
ZXRar, которое  занимается  перекидыванием 
перемещаемого фрагмента ZXRar в кэш (пред─
варительно  выяснив, есть ли на компьютере
 кэш):
         DI
         IN A,(#FB)
         LD H,0
         LD A,(HL)
         INC (HL)
         CP (HL)
         JR Z,ncach
         LD HL,End ;здесь лежит табл.
                                ;релокации.
                   ;Почему здесь - см.ниже!
         LD B,RelSz ;длина табл. релокации.
              ;Как она считается - см.ниже
 Rel0    LD E,(HL) 
         INC HL
         LD D,(HL)
         INC HL
         LD A,(DE)
         SUB 'cachecode
         LD (DE),A
         DJNZ Rel0
         LD HL,cachecode
         LD D,B,E,L ;младший байт адреса
              ;не меняем, а старший будет 0
         LD B,'cachelen+1 ;вместо
                          ;LD BC,cachelen
                          ;(выигрыш=1 байт)
        LDIR
ncach 

   Перед автосборкой ZXRar'а таблицу рело─
кации надо,естественно,перебросить в хвост
программы, чтобы  получился  один  кодовый
блок. Вот как для этого оформляются после─
дние строки исходника ZXRar'а:

DISPLAY "reloc tab end=",RelCa 
End=end 
RelSz=RelCa-#C000/2 
 end=RelSz*2+end 
        INCLUDE "mrip*",#C0 ;автосборщик
                                ;(см. IG#5)
         ORG $
         LD HL,#C000
         LD DE,End
         LD BC,RelSz*2
         LDIR
        JP nenado

   С nenado тот же фокус, что был в случае
SAVEOBJ. Это  переход  на сохранялку (если 
удерживается Caps Shift ) или запуск прог─
раммы по адресу  GO (если Caps Shift отпу─
щен).

A. Coder 



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

Классика - Альманашник. А. С. Пушкин.

For Coderz - Распознавание и вычисление арифметических выражений по их символьной записи.

Inferno - Авторы журнала.

For Coderz - О дисциплине при создании больших проектов.

Интервью - Вопросы Константину Свиридову (Conan) о сайте zxnext.narod.ru.

Ликбез - Принципы конвертирования графики PC-ZX.

For Coderz - Программирование смены диска/дисковода на Скорпионе.

Sofтинка - DNA_OS v0.431 - пакет утилит для работы с винчестерами, RAM-дисками и дискетами.

For Coderz - Программирование под DNA_OS ZET-9, пакет утилит для работы с устройствами хранения данных.

Sofтинка - Проблемы и недоработки пакета утилит для работы с устройствами хранения данных DNA_OS.

Ликбез - Подробно о дисковых форматах, имеющих FAT.

Inferno - Вступление от редактора.

Inferno - Ошибки в предыдущих номерах.

For Coderz - Маленькие программерские хитрости.

Gameland - О новых играх: Oneyroid, Dizzy forever, Dridlock.

For Coderz - Пишем архиватор. Практические принципы LZ упаковки.

Gameland - Прохождение новых отгрузок для игры "Чёрный Ворон".

For Coderz - Программирование для видеорежима 384x304.

Inferno - Письма в редакцию.

Звук - Идеи Megus'а по поводу трекера для AY/YM.

Inferno - Об оболочке.

For Coderz - Основы оптимизации под процессор Z80.

Ликбез - Расположение разделов на винчестере.

Gamedev - 3D проецирование пола/трассы в играх.

Звук - Дикие идеи для AY трекеров.

Реклама - Реклама от Романа Чунина.

Реклама - Реклама от В. Богдановича

For Coderz - Как делается крупная перемещаемая программа.

Ремонт - Неисправности Pentagon 128+ и их ремонт.

Inferno - Содержание номера.

Разное - Мысли о конкурсе на лучший софт.

Others - Перенос программного обеспечения на ZX Spectrum с PC.

Видео - Об упаковке видео для ZX Spectrum.


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

Похожие статьи:
Artique - Schafft: о том почему недостаток, невнимание, и иногда даже отрицательное отношение к ascii-art на Spectrum'е.
Interface - история создания виртуального пати Antique Toy.
Сандулов - Французкий журналист Морис Мошино посетил церковь сатаны в Сан-Франциско, где встретилься с ее верховным жрецом Антоне Лавеем. Вот что он рассказал об этом человеке...

В этот день...   18 июля