Цель такова, что вот есть N релоцируемых программ. Мы их загружаемСообщение от Vitamin
в память, настраиваем, и они каким-то образом начинают вызывать функции друг друга (из других программ, модулей, библиотек). При
том, что до загрузки ничего не известно, что по каким адресам располагается. Существует три, уже 100 раз тут писалось, метода:
1) используя функцию диспетчер, аргументом которой является
"идентификатор функции" в "чужом" программном модуле.
Это так называемый вызов через RST в том числе. Хотя можно
и через CALL, как, например, в CP/M -- CALL 0x05, а в регистре
C -- номер функции. Детали реализации скрываются за
функцией-диспетчером -- поэтому, возможно, это слишком
общий метод и его сравнивать с остальными не совсем
корректно. Но вот тормозной он -- это точно.
2) Прямой вызов. Для этого все CALL, JP, и даже LD HL, xxx и т.п.
должны быть пропатчены в вызывающей программе после
загрузки всех модулей. Требует формирования сложных
таблиц для патчей создаваемых сложной системой макросов
поверх ассемблера, или специальным ассебмлером. Требует
дисковой памяти порядка sum(Ki)+sum(Mi), где Ki -- число
вызовов внешних функций в i-том модуле, а Mj -- общее
число экспортирующихся функций в j-том модуле. Памяти
ОЗУ после настройки не требует. Но Ki -- достаточно велико.
3) Вызов через функцию-перенаправитель. Для каждой функции
из вызываемого модуля в вызывающий модуль ещё на этапе
компиляции включается специальная функция, состоящая
из одной команды JP xxx. Все вызовы к функциям "чужого"
модуля адресуются к этим функциям-перенаправителям.
А сами функции-перенаправители патчатся, после загрузки,
так, чтобы инструкция JP xxx указвала на действительный адрес
функции из вызываемого модуля. Данный способ отличается
от предыдущего тем, что вызов тормозней на 10 тактов и
памяти ОЗУ требует порядка sum(Mi)+sum(Nj), где Mi -- общее
число экспортируемых функций в каждом i-том вызываемом
модуле, Nj -- число внешних вызываемых функций в каждом
j-том вызывающем модуле.
Считаю, способ N3 наиболее перспективный. Он позволяет относительно легко в любом ассемблере получить нужный код,
он не накладывает чрезмерных накладных расходов на совершения
вызова функций, не требует много памяти как дисковой, так и ОЗУ.
Хотя по использованию ОЗУ он, проигрывает остальным методам.
Со способом N2 сравнивать бесполезно (0 байт), против способа
N1 проигрыш составляет порядка трёх байт на каждую вызываемую
внешнюю функцию в каждом модуле. При общем числе функций
порядка десятков, максимум сотни-другой, это вполне допустимый,
с моей точки зрения, расход памяти.
Способ N2 исключительно сложен в реализации, и требует
специальной поддержки от ассемблера, поэтому, с моей точки
зрения, в общем случае не применим и имеет смысл в каких-то
специфических случаях, например, когда исключительно критично
время вызова функции.
Способ N1 даёт слишком большие накладные расходы на вызов
функий и поэтому, аналогично, имеет смысл, опять же с моей точки
зрения, исключительно в специфических ситуациях. Например,
где время некритично, но очень критичен объём занимаемой
программной памяти.




Ответить с цитированием
Размещение рекламы на форуме способствует его дальнейшему развитию 
[