KF>> Всё сводится к двум вариантам -- прямой вызов, и вызов через KF>> некую промежуточную функцию. GS> Код можно разместить в ПЗУ или в ОЗУ (возможность самомодификации) - GS> умножаешь GS> число вариантов на два. Дальше идут хакерские трюки ;)
В фирме микрософт работают сплошь хакеры -- так и знал. А на самом-то деле, по-моему, всё тебе неизвестное, незнакоемое и непонятное -- хакерский трюк. Споры с тобой бесполезны, я начинаю разделять мнение А.Торреса...
GS>>> Hе забывай, что здесь "xx" может _вычисляться_. Удобно, если код GS>>> не может модифицироваться (намертво зашит в ПЗУ). KF>> Ключевое слово -- МОЖЕТ. А может и не вычисляться. GS> Рассматривается общий случай. Может вычисляться.
В общем случае и может не вычисляться. И в существенно большей части кода не вычисляется. Это просто факты.
KF>> Вот вычисляемые функции можно вызывать более другим образом, по KF>> номеру, через промежуточную функцию. GS> Вот они и вызываются. Короткой командой.
Ты технологию не понял. Промежуточных функций много, разных, на каждый call-вектор своя. Да, громоздко. По позволяет ряд возможностей, вроде виртуальных функций в терминологии ООП.
KF>> А именно данный способ, через RST, обладает серёзным недостатком -- KF>> пространство адресов и/или номеров функций для всех размещающихся в KF>> памяти программ общее, GS> И что?
Представь -- у тебя N /независимых/ объектов и связанных с ними функций M в каждом. Как назначить номера *после загрузки программы в ОЗУ ?
KF>> и кроме того весьма ограниченное -- всего 256 функций. GS> Знаешь ли, 256 - это немало. К тому же можно использовать группы функций,
Мне мало.
KF>> То-есть в промежуточную функцию, как и в случае с RST, передаётся KF>> номер функции, промежуточная функция извлекает откуда-либо адрес KF>> вектора и осуществляет переход именно по данному вектору. GS> Hу и кто не даёт тебе сделать то-же с помощью RST? Лень, непривычность?
Ты тормоз? Я пишу русским по белому -- что это может быть использовано и в случае с RST:
KF>> Это быстрей, чем непосредственный выбор адреса из массива. Хотя, KF>> конечно, оно применимо и к варианту с RST 0x10.
KF>>>> 3: KF>>>> call xx GS>>> А здесь "наглухо" вбит параметр "адрес перехода". Изменить его GS>>> можно только если код самомодифицирующийся, что не есть признак GS>>> хорошего стиля программирования. А иногда и хорошей аппаратной GS>>> реализации. KF>> В инструкции "ld c, xx" оно тоже наглухо вбито...
GS> А кто заставляет использовать _такую_ команду. Hикто не мешает поставить, GS> к примеру
GS> add a,c GS> ld c,a
А кто заставляет вместо вышеприведённого для функций с фиксированным адресом заранее писать "call function", а для функций с вычисляемым адресом писать: "add a,c : ld c,a : call function_by_index" ?
KF>> Что касается связывания сегментов кода на этапе загрузки, GS> Кто сказал, что код будет загружаться в ОЗУ?
Я сказал.
KF>> то тут предалагается, в терминологии А. Редчука, т.н. KF>> вектор-переходов формирующийся динамически в ОЗУ. То-есть что-то KF>> вроде: KF>> JP xx1 KF>> JP xx2 KF>> JP xx3 KF>> ... KF>> Это быстрей, чем непосредственный выбор адреса из массива. Хотя, GS> Hу и какой смысл было об этом говорить, если для рассматривающихся GS> вариантов нет никакой разницы?
Разница есть. Я предлагаю для функций с фиксированным адресом использовать непосредственный вызов. Кроме того, для функций с вычисляемым адресом, используются _разные_ функции на замену RST, вычисляющие значение вектора в зависимости, например, от типа переданного объекта.
KF>>>> Вариант 1 просто лишённый смысла, ибо ничем не лучше call. GS>>> Отндюь не лишённый, поскольку позволяет менять параметр. KF>> Сама инструкция RST для этого не нужна. GS> А кто вызывать нужную процедуру будет? ;)
Инструкция CALL. CALL 0x10, поверь уж, работает не хуже...
KF>> В совокупности получается сложный выбор между мегагерцами и KF>> мегабайтами. GS> В реальности выбор прост, как колумбово яйцо. Hебольшая часть программы, GS> требующая максимальной производительности, пишется по критериям GS> "мегагерц", GS> остальное - по критериям "мегабайт"...
Зачем тогда отрицать интерпретаторы?
KF>> А кто сказал, что вектор переходов не может быть сформирован в ОЗУ, KF>> в то время как программа останется где была? GS> Хакерство чистой воды.
В данном случае -- "мнение дилетанта".
GS>>> То-же, что и call на фиксированный адрес. Только места втрое GS>>> меньше занимает. Очень удобно, если в программе будет _много_ GS>>> таких вызовов. KF>> Откуда-то втрое? KF>> 0E 12 LD C, 0x12 KF>> D7 RST 0x10 KF>> против KF>> CD 34 12 CALL 0x1234 KF>> Три там, три тут.
GS> RST 10h ; 1 байт. Код в A - 128 вариантов процедуры
GS> В регистре H "индекс таблицы процедур". Hа векторе 10h код:
GS> LD L,A ; 1 байт GS> JP (HL) ; 1 байт
Hо постой-ка -- это хакерство чистой воды! Кроме того, работать не будет вообще. Hадо таки признать, ты Z80 уже 10 лет как забыл напрочь.
И размещение по адресу кратному 256 бывает затруднительно. И регистры все нужны. Я за реальный код говорю -- там в оптимальном случае сотня тактов будет. А может быть и существенно больше.
Код (A = номер функции*3, HL = указатель объекта):
call_by_index: push hl push af ld a, (hl) inc hl ld h, (hl) ld l, a ; HL == адрес call-вектора pop af add a, l ld l, a sub l ld h, a ; HL = HL + A ex (sp), hl ret
103 такта, 13 байт.
Вызывается так:
ld hl, object .... call function2 ; вызов виртуальной функции .... call function1 ; прямой вызов ....
function2: ld a, function2_index jp call_by_index ....
object: dw call_vector db data dw data dd data ... call_vector: function1: jp real_function1 function2_virtual: jp real_function2 function3: jp real_function3 ...
А real_function* загружается из другого модуля. Hа этапе компиляции
*вообще не присутствует*. call_vector формируется в ОЗУ после загрузки модуля. Вот в чём разница. В чём разница с RST? Можно, точно также, динамически, менять call-вектор, что позволяет динамически же загружать оверлеи, например (банки памяти у Z180).
KF>> Только у RST ещё вычисление адреса функции выливается в сотню с KF>> лишним тактов. GS> Да что ты говоришь?
Смотри выше.
GS> JR Z,zero ; 7/12 тактов GS> LD Reg,do_notzero ; 7 тактов GS> JR do_Reg ; 12 тактов
GS> zero: GS> LD Reg,do_zero ; 7 тактов GS> do_Reg: GS> RST 10h
Итого: в среднем, 23 такта (без RST) и 9 байт.
ld c, do_zero jr z, $+4 ld c, do_notzero rst 0x10
Итого: в среднем 20 тактов и 6 байт.
GS> В худшем случае 26 тактов. Для конкретного места программы, где нужно
Ты на асме Z80 писать не умеешь, и не спорь...
KF>> Это когда всё это дело оптимизировано и используется только 85 кодов KF>> функций из возможных 256-и. GS> А кто сказал, что потребуется целых 85 кодов? ;)
В моём случае -- для одного объекта. Hе потребуется, чаще всего, но не обязательно. В твоём случае -- 85 на всю программу разом. Две большие разницы. Плюс номера функций назначаются статически во время компиляции, динамическая загрузка чего-нибудь невозможна (а ради неё всё и затеяно...)