Перемещаемость программ
Поскольку крупные программы не держатся
на одних 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