ZX-Ревю 1995 №1 1994 г.

Профессиональный подход - Автоматизация создания релоцируемых программ.


Автоматизация создания релоцируемых программ

(С) Дмитрий Козлов, Николай Шустов, Н.Новгород, 1994.

В первом номере "ZX-РЕВЮ" за 1994 год ИНФОРКОМ бросил клич: "Все на создание релоцируемой версии MEMORY EDITOR-a!". Идея тоже могут воспользоваться этими процедурами, так как прошивки ПЗУ Бейсика для 48К и 128К по адресам

#1FC3-#1FC8 совпадают.

* * *

автоматического создания релоцируемых программ уже давно бродила в наших головах. И вот, наконец, ей надоело там бродить: на свет появился RPMU (Relocatable Program Make Utility). Но на самом деле это громкое название не совсем точно отражает ее возможности. Программу, которую Вы хотите сделать перемещаемой. Вам все же придется писать самим. Но Вы можете не ломать голову, руки, ноги и другие части тела в поисках ответа на вопрос: "Куда бы засунуть этот CALL ?". Ведь ужасно обидно, что есть команда относительного перехода JR, но нет команды относительного вызова подпрограммы, что-нибудь вроде CALLR—

"Да, а собственно к чему весь этот сыр-бор вокруг релоцируемых (слово-то какое выдумали!) программ?" - скажет какой-нибудь пользователь SPECCY, с трудом отрываясь от экрана, на котором его COBRA МК III, после тяжелых и продолжительных боев, добила на-конец-то несчастный PHYT0N. Но те люди, которые используют ZX-SPECTRUM не только как игровую приставку к телевизору, не раз кусали себе локти, пытаясь использовать две утилиты одновременно, которые используют для своей работы одни и те же адреса. Однако, если бы эти программы были релоцируемыми (слово-то какое замечательное!), таких проблем не возникало бы.

Как Вы знаете, есть два пути создания релоцируемых программ. Первый - это писать ее перемещаемой с самого начала, то есть отказаться от команд, использующих абсолютную адресацию: CALL addr, ЭР addr, LD reg_pair,(addr) и т.п. (где addr - адрес чего-ни-будь в Вашей программе). Если относительного смещения aR не хватает, то приходится организовывать цепочки aR-ов, где первый aR указывает на второй, тот на следующий и т.д. И это еще не все проблемы, готовые свалиться на Вашу голову.

Очевидно, что больших и серьезных программ таким путем создать нельзя. Поэтому в большинстве случаев используется другой подход, примерами реализации которого могут служить такие известные программы, как GENS и MOWS. В них к исполняемому коду добавлены таблица настройки и специальная процедура, которая по этой таблице осуществляет привязку программы к конкретным адресам. Метод, конечно, хороший, но создавать таблицу настройки вручную довольно утомительно. Может быть из-da всех этих сложностей и написано так мало релоцируемых программ.

Вот мы и подумали: "А почему бы не свалить все заботы по созданию настроечной таблицы на SINCLAIR?". Это была наша первая гениальная идея. Она нам понравилась и мы подумали: "А как же выделить те байты, которые необходимо изменить, чтобы программа смогла работать в других адресах ОЗУ?". И тут нас осенила вторая гениальная идея: "А ведь это очень просто узнать, если сравнить между собой два экземпляра одной и той же программы, ассемблированных по разным адресам (с разными значениями директивы ORG). И по этим данным таблица строится элементарно!".

Это послужило отправной точкой для написания нашей программы. То что получилось Вы можете увидеть ниже:

ПРОФЕССИОНАЛЬНЫЙ ПОДХОД

Relocatable Program Make Utility Copyright 1994 by LEGION SOFT vl.00 APR-01-94

ORG aR

30000 START

PART3

значение ORG 1-го экз. адрес 1-го экземпляра адрес 2-го экземпляра длина экземпляра

0RG1 ADR1 ADR2 LEN

DEFW DEFW DEFW DEFW

ПРОФЕССИОНАЛЬНЫЙ ПОДХОД

START LD HL, (0RG1) ;

LD ВС,OFFSET ; подготовительные

ADD HL,BC

LD B,H ; операции

LD C,L ;

EXX ; сохраняем HL* для кор-

PUSH HL ; рентного возврата в

; BASIC

LD ВС,(LEN) ; по адресу CD_LEN :

LD (CD_LEN),BC ; длина экземпляра

LD HL,Pl_LEN-OFFSET; по адресу SC_LEN :

ADD HL,BC ; длина экземпляра +

LD (SC_LEN),HL ; скорректированная

длина PARTI

LD HL,(ADR2)

LD DE,(ADR1)

PUSH HL ; в IX - адрес начала

POP IX ; настроечной

ADD IX,ВС ; таблицы

LOOP3 LD А,(DE)^ ; основной цикл поиска

CP (HL) ; различающихся

DR Z,NXT_B ; байтов

PUSH HL EXX

POP HL

LD E,(HL) ;

INC HL ; в DE - корректируемое

LD D,(HL) ; значение

EX DE,HL ;

OR A ;

SBC HL,BC ; ну вот,

EX DE,HL ; скорректировали !

LD (HL),D

DEC HL ; и запихали его

LD (HL),E ; обратно

LD DE,(ADR2) ; в HL - значение

OR A ; смещения до

SBC HL,DE ; скорректированного

LD DE ,P1__LEN-OFFSET

ADD HL,DE ; значения

LD (IX+O),L

LD (IX+1),H ;

INC IX ; в таблицу его !

INC IX ;

ПРОФЕСС1

10НАЛЬНЫЙ

ПОДХОД I

EXX

пропускаем

LDI

; 1 (один) байт

fXT_B

LDI

; следующий байт

JP

PE,LOOP3

( пока есть такой )

PUSH

IX

POP

DE

XOR

A

; нулевое слово -

LD

(DE),A

; в таблицу

INC

DE

(это маркер конца)

LD

(DE),A

р

INC

DE

; DE - за таблицей

LD

HL,PART2

; добавили PART2

LD

BC,P2_LEN

; в конец файла

LDIR

*

PUSH

DE

р

LD

DE,(ADR2)

*

DEC

DE

; добавили PART1

LD

HL,P1 END-1

в начало файла

LD

ВС,PI LEN

LDDR

DE HL A

HL,DE

в HL - суммарная длина программы

ЕХ

(SP),HL

; ее - в стек,

; HL * - восстановили

ЕХХ

; в ВС-суммарная длина

POP

ВС

; программы

RET

все !

INC POP OR SBC

Вызов программы: LET A=USR ЗЕ4, суммарная длина программы. Адрес

#007С

_RET PARTI

LD

PUSH DI

EQU

АЛ AF

в А после возврата -начала: ADR2-P1_LEN

; адрес RET в ПЗУ

сохраняем состояние триггера прерываний (*)

CALL EQU

_RET

LABEL-PARTI

LABEL OFFSET

DEC

SP

; вместе с (**)

DEC

SP

; определили адрес LABEL

POP

ВС

в ВС его !

ПРОФЕССИОНАЛЬНЫЙ

ПОДХОД

EQU

$+1

; SC_LEN=aflpec данных

LD

HL, 0

; этой ( <— ) команды

ADD

HL,BC

; т.о. HL=адрес таблицы

LD

E,(HL)

INC

HL

LD

D,(HL)

; DE = смещение из

INC

HL

; таблицы

LD

A,D

OR

E

; конец таблицы ?

PUSH

HL

; <-- (***)

RET

Z

; если да,

то в результате (***)

переход на PART2, иначе просто сохранили HL

SC LEN.

LOOP1

EX ADD

DE,HL HL,BC

в DE - корректируемое значение

LD

INC

LD

E,(HL) HL

D,(HL)

ну вот, скорректировали !

EX

ADD

EX

DE,HL HL,BC DE,HL

и запихали его обратно

(HL),D HL

(HL),E

LD

DEC

LD

POP OR

HL

LOOP1

закрутили цикл

PI END-PARTI

P1_END PI LEN

EQU

; (*) необходимо запретить прерывания, т.к. если процедура

; их обработки будет вызвана между возвратом из _RET и/или

; DEC-ами SP, то адрес LABEL будет безвозвратно потерян...

(Аплодисменты. Зрители рыдают.)

; Дело в том, что после возврата из подпрограммы _RET адрес

; LABEL лежит по адресу SP-2, а при вызове процедуры обработки

; прерываний адрес возврата будет записан туда же...

PART2 LD HL,-OFFSET ; DE - адрес

ADD HL,DE ; загрузки файла,

LD DE,P1_LEN ; HL - начало

EX DE,HL ; кода настроенной

ADD HL,DE ; программы

аналогичный трюк см. выше

CD LEN

EQU $+1

LD ВС,0

ПРОФЕССИОНАЛЬНЫЙ

ПОДХОД

адрес начала свободного пространства за настроенной программой

LDIR

LD

LD

B,D

C,E

POP RET EI RET

AF PO

P2 END-PART2

P2END P2 LEN

EQU

К сему мы хотим сказать, что главным критерием при написании этой программы был минимальный размер части, присоединяемой к настраиваемому коду (т.е. размер PART1 и PART2). Именно поэтому эти фрагменты получились не совсем читабельными.

Для облегчения работы с нашей процедурой мы состряпали небольшой BASIC-интерфейс:

10 REM *** RPMU Interface **** 20 GO ТО VAL"100" 30 BORDER NOT PI: PAPER NOT PI: INK VAL"7"

40 CLEAR VAL"29999"

50 LOAD "RPMU.COM" CODE VAL"3E

4"

60 PRINT "RMPU vl.00 installed "'"Use memory from 30300." 70 STOP

99 REM ***********************

100 CLS

110 INPUT "BLOCK1 ORG value : " ;D

120 A^VAL"30002":GO SUB VAL"1E3

и

130 INPUT "BLOCK1 loading addre ss: ";B1A 140 A-A+VAL"2":LET D»B2A:GO SUB VAL"1E3"

150 INPUT "BLOCK2 loading addre ss: ";D

160 A=A+VAL"2":GO SUB VAL"1E3" 170 INPUT "Length of BLOCKS : " ;D

180 AaA+VAL"2":GO SUB VAL"1E3"

190 LET D-USR VAL"3E4"

200 PRINT "Adjusting complited.

M

300 PRINT "Use SAVE ""name"" CO DE ";B1A-VAL"36";",";D

310 STOP

999 REM *********************** 1000 RANDOMIZE D 1010 POKE A,PEEK VAL "xx" : POKE

A+SGN PI,PEEK VAL "ХХ+1" 1020 RETURN

Примечание: xx - адрес системной переменной SEED.

Мы старались не привязываться к конкретному накопителю, поэтому не вставили в BASIC-программу команд. выгрузки полученного и загрузки исходных файлов. Единственная накопителезависимая (извините за выражение) команда - это LOAD в строке 50. Поэтому если Вы используете не ленту, а какое-нибудь другое внешнее устройство (например, дисковод, принтер, плоттер, мышь или настольную лампу) , то замените команду загрузки "RMPU.COM" на нужную. Кстати, запись НАШЕЙ BASIC-программы необходимо производить командой: SAVE "RMPU" LINE 30 (или аналогичной, которую "понимает" Ваш DOS или Микродрайв).

Ну а теперь рассказ о самом интересном: как же пользоваться тем, что мы тут накропали. Итак, чтобы получить релоцируемую программу нужно:

1. Откомпилировать Ваш ассемблерный файл с двумя различными значениями ORG, причем у этих значений должны отличаться и младшие байты. После этого необходимо запомнить (а кому трудно, записать) длину полученного кода и значение ORG одного из блоков.

Потом эти значения потребуются для работы RMPU.

2. Отгрузить полученные модули на внешний носитель.

3. Загрузить RPMU.

4. После того, как появится сообщение: "RMPU vl.00- installed..." загрузить Ваши модули в свободную память.

5. Запустить RPMU командой RUN и чистосердечно ответить на вопросы программы, введя ранее запомненные значения и адреса загрузки .

6. Сообщение: "Adjusting computed. " известит Вас о том, что настройка закончена и готовую программу можно сохранить на внешнем носителе. Для выгрузки на ленту надо использовать указанную команду (если у Вас другой носитель, то скорректируйте ее соответствующим образом).

Итак, Вы получили релоцируемую версию некоторой программы. Возникает резонный вопрос: "А как же ей пользоваться?" Все очень просто. Загружаете ее в память:

LOAD "name" CODE addr

И запускаете: LET A~USR addr

Программа вернется в BASIC и в результате в переменной А будет адрес первой свободной ячейки памяти, а по адресу addr разместится Ваша настроенная программа. Теперь Вы можете запускать ее обынной командой:

RANDOMIZE USR addr

Обратите внимание, что полученная программа содержит только то, что Вы написали. Процедура и таблица настройки удалены так как они больше не нужны.

Теперь нам хотелось бы остановиться на модификации программ, для которых у Вас нет исходного текста (пример: Memory Editor). В этом случае придется воспользоваться директивой "Т" монитора MONS для его получения. К сожалению этот способ подходит только для опытных пользователей, но другого пути на наш взгляд нет.

Наконец, несколько слов еще об одном варианте использования предлагаемой программы. На компьютере IBM PC есть очень интересная возможность организации своих процедур - OBJ файлы. Нечто подобное можно попытаться сделать и на SPECCY. Тут поможет предложенная Вами идея КЕРНАЛЯ. Делать это надо примерно так:

- Вы создаете свои кодовые процедуры так, чтобы они "общались" между собой только через КЕРНАЛЬ.

- Каждая процедура делается перемещаемой при помощи RPMU.

- Программа, которая должна их использовать, сама загружает эти процедуры в память (причем в любой комбинации) и запускает настройщик. Затем адреса размещения процедур заносятся в определенные ячейки КЕРНАЛЯ.

Таким образом Вы полуавтоматически можете создать кодовую часть будущей программы даже из BASICa. Это будет выглядеть примерно так:

10 DATA "NAME1", "NAME2", ...

20 CLEAR 29999 : LET ADR=3E4

30 FOR 1=1 TO NOF : REM NOF -оличество подключаемых файлов. десь должны находиться строки, оторые отвечают за запись адреса ачала очередной процедуры (т.е. DR ) В КЕРНАЛЬ.

80 READ N$

90 LOAD N$ CODE ADR : LET ADR» USR ADR 100 NEXT I

Мы понимаем, что предложенная идея не отличается особой элегантностью, но ведь это только идея. И может быть кто-то, опираясь на нее, создаст что-то действительно стоящее.

* * *




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



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

Похожие статьи:
Кодинг - Совет вождей (сказочка). Кошка в зоопарке.
Четыре килобайта - Прошло 3 недели и...
BBS - список станций BBS ZXNet.

В этот день...   14 октября