Из журнала Info Guide #7, Рязань, 06.2005 Перемещаемость программ Поскольку крупные программы не держатся на одних 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+\0 RelCa=$ ORG CurCa ENDM MACRO j rg 2 IFN ?j\1-2 ;есть ли 2-й параметр ;макроса? JP \0,\1 ;если есть, то такой JP ELSE JP \0 ;если нет, то эдакий ENDIF ENDM MACRO c rg 2 IFN ?j\1-2 CALL \0,\1 ELSE CALL \0 ENDIF ENDM MACRO l LD \0,\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