гм. очень даже интересная идея. но имеет один недостаток- она нереентерабельна. да и по памяти лишние траты (особенно после настройки). думаю, имеет смысл разработчикам софта почитать посты отсюда и применять идеи на практике в своих программах
Вид для печати
гм. очень даже интересная идея. но имеет один недостаток- она нереентерабельна. да и по памяти лишние траты (особенно после настройки). думаю, имеет смысл разработчикам софта почитать посты отсюда и применять идеи на практике в своих программах
На самом деле, я не вижу причин, почему надо выбирать только один и самый быстрый при этом способ вызова системных функций. Надо подходить к этому избирательно. Для часто вызываемых функций, скорость работы которых не столь важна, подошли бы RST. Для прямого и очень при этом быстрого вызова простая таблица с jp f1;jpf2;... самое нормальное. Предолженный выше мной вариант на самом деле не так хорош например в случае, когда быстрая работа функции требуется уже при первом вызове. А вместо этого первый вызов начинает выполнять всю перечисленную галиматью, теряя драгоценные такты. Еще надо посчитать, сколько раз в программе должна вызваться эта функция, чтобы за счет экономии 10 тактов на jp окупить тот расход, который был съеден на первом обращении.
Предлагаю подумать больше не о способе вызова системных функций, а о способе автоподстройки "приложений" по месту загрузки. У меня вот такая идейка есть. Выделить RST под операцию подстройки, которая бы подстраивала 2хбайтное слово через 1 байт после вызова. Так:
rst R ; R-заранее заданное любое 1..6
defb ?;мнемоника операции
defw ?;подстраиваемый адрес
Все что нужно знать подстройщику - адрес, на который настроена программа и адрес, с которого она загружена. Точнее разницу.
Подстраиваться может адрес практически в любой команде. Еще подстройщик может анализировать следующий байт за вызовом rst на наличие префиксов dd/fd и подстраивать тогда слово еще на байт дальше.
По окончании подстройки сам вызов RST R заменяется на nop, чтобы подстройка больше не вызывалась для этого адреса.
Преимущества: дополнительная таблица не нужна, годится любой имеющийся компилятор с асма, подстройка выполняется по мере надобности, загружать программу можно с любого байта в ОЗУ (кроме области занятой под систему и стек, разумеется).
Недостаток: +1 байт в программе на каждую команду требующую подстройки. В принципе, эти байты можно задействовать иногда для самомодифицирующегося кода, но это уже для спецов.
Попробую привести код:
org R*8
ex (sp),ix
push hl
push de
push af
jp reloc_1
reloc_1:
ld de,(reloc_difference)
ld a,(ix+0)
and a,0DFh
cp a,0DDh
push af
jr nz,@1
inc ix
@1:ld L,(ix+1):ld H,(ix+2)
add hl,de
ld (ix+1),L:ld (ix+2),H
pop af
jr nz,@2
dec ix
@2:ld (ix-1),0
pop af
pop de
pop hl
ex (sp),ix
ret
2vitamin. а при чем там реентерабельность? сначала стояло call 103h, после первого срабатывания на этом месте уже стоИт call fun2. Никакие регистры предложенный выше способ не портит. Но он длинный и эффект от него - только при большом и частом вызове подстраиваемой таким образом функции, о чем я уже сказал в предыдущем посте.
или предполагается, что выполнение системы может в этот момент прерваться, и каким-то макаром может случиться так, что эта же программа будет вызвана (через callback?) в этом же месте еще раз, и случится бяка?
бяка будет если функция прервется после того, как часть адреса будет скорректирована и будет вызов второй раз. тогда программа улетит в нирвану. и вместе с ней вся ось.
насчет релокации смотри http://zxdocs.fatal.ru/coding/module.zip
2Vladimir Kladov> Универсальный метод не "канает" только потому что не факт что система находится в ПЗУ. Вообще говоря, система может быть где угодно, и в указанном месте (в местах ретрансляции) будут тоже находится трапы, но хитрые: если это большая и долгая функция - то переход на другую страницу ОЗУ и т.д, если это быстрая и ответственная фукнкция, то в нижней части ОЗУ.
А вызов системных функций - дело тонкое, потому лучше именно через Call напрямую согласно указанному методу.
Здесь незря в процессе обсуждения были подняты такие вещи, как менеджмент памяти, оганизация вызовов и структура программы. Это три очень тесно завязанных друг на друга элемента, и самое оптимальное решение с точки зрения развития, переносимости и возможности оптимизации под конкретную платформу, является именно это.
я так и подумал. Но мне кажется, так быть не должно. Даже если делать на спеке многозадачную систему, я полагаю, каждая задача должна оставаться однопоточной и тогда никаких проблем с реентерабельносью. Если ось прерывает задачу, чтобы отдать кадр другой задаче, она должна будет продолжить эту задачу только с этого самого места, и ни с какого другого.
А если это не так, то оба мои эссе неприменимы. Может быть, для такого конкретно случая. Только не очень ясно-понятно, что это за случай такой. Например, драйвер мыши, который может вызываться "в контексте" любой задачи? Хм. Очень даже похоже, если сам драйвер как загружаемый кусок кода, а не часть оси. Но драйвер такого рода мог бы работать в запрещенных прерываниях. Тогда страшен только случай NMI.
В общем, мой код #1 для автоподстройки для эфемерной экономии 10 тактов просто наглядно демонстрирует ненужность такого израта, и очевидное преимущество простой таблиы из jp f1;jp f2; .. Такая таблица кстати, делались в "оси" на микроше. В 1983-84 г. будучи на службе в СА я принимал участие в разработке микрошеподобной машины на базе кр580 или как он там назывался. На мне был весь софт (ось, ассемблер, драйверы устройств, прикладное по). За 3 месяца все это было сбацано, и представляло из себя многозадачную (!) ось, которая могла одновременно обслуживать несколько периферийных устройств (часы точного времени, перфосчитывать 1500 байт/с, перфоратор (десятки байт /с), телетайп, быстрый принтер (IBM-какой-то, матричный принтер, до 10 строк /с), модем 9600 бод (прием онли), клавиатуру от дисплея IBM/360. Экран был ч/б, но графический, 640х384 или 80х24 символа матрицей 16х8. Я мог одновременно компилировать, перфорировать, печатать, набирать текст в редакторе... Да, когда шел прием с модема, все останавливалось, тут скорости уже не хватало. Хватало только на экран выводить мозаику. Да, отвлекся, мой первый "Осьминог" припомнил... Интересно, чем сейчас Марат Вениаминович занимается (не буду называть фамилию, неказисто она звучит по-русски :) ), и где прочие участники той команды.
Код №2 больше пригож для случая утилит и прикладных задач, тоже не очень похоже, что таким способом должны пользоваться драйверы или какие-то другие вещи, требующие реентерабельности. На крайняк, можно было бы подумать о том, чтобы система прежде чем забирать кадр у задачи, анализировала бы, что там сейчас делается, и если выполняется потенциально опасный участок кода из самой системы, просто не пыталась прервать исполнение. Самое простое, что приходит в голову.
скачал, посмотрел. Очень все серьезно (не многовато кода?). Я бы делал проще :) Но интересно посмотреть, что тут получится. Ассемблер должен поддерживать макросы, как минимум, и не просто макросы, а переменные. Асм без макросов не прокатит. Сдругой строны, таблицы не занимают место и могут выбрасываться после того, как отработаны, что конечно, гуд. Сразу линковщик, с именами. Ну что ж, правда интересно становится, что получится.
ассемблеры сейчас все с макросами делают. без них работа на порядок труднее.Цитата:
Сообщение от Vladimir Kladov
а че там кода много? только самое необходимое! и что бы ты упростил? поделись идеями %)
А вообще, ваш метод годится при наличии небольшой доп. утилиты для любого компилятора, хоть даже и с паскаля или С. Достаточно откомпилировать с 2х разных адресов, и подать на вход такой утилиты, и совсем несложно выявить слова, которые отличаются на смещение, которое должно наблюдаться (start2-start1).
Ээээ. Товарищи, какой RST? Давайте подумаем о том, что стандартное ПЗУ мы модифицировать пока не собираемся ;) Иначе толку от этой ОСи на реальных машинах? Конечно, "кеш" есть, но все-таки совместимость бы надо оставить...
И еще - зачем автоподстройка? Именно подстройка при загрузке. Тогда и механизм DLL реализуется "на ура".