Сначала отметим, что:
- блоки кода загружаются в последовательные регистры для выполнения
- каждый регистр содержит 39 битов: либо слово данных, либо две инструкции.
Компьютер Pegasus поддерживал вызовы подпрограмм / функций, передавая две инструкции (в одном регистре) о том, как вызываемый объект должен возвращать - как данные, то есть как параметр. Затем вызываемый объект получит этот параметр, сохранит его в своей локальной памяти и передаст ему управление, тем самым выполняя две инструкции, которые были переданы вызывающей стороной в виде данных.
Вызывающая сторона сделает это:
load instructions from L1: into X1 register
... pass other parameters ...
load callee's first block into U0
transfer control to U0 at start
L1: load instructions from main block back into U0
transfer control to U0 at desired caller resume point (e.g. L2)
L2: ...caller resumes...
И вызывающая сторона сделает это:
... compute something ...
store X1 into L4 # writes the two instruction pair in X1 into L4
transfer control to L4 # may or may not be present depending on location of L4
L4: # starts empty but contains the two instructions that came from L1
# so control is transferred back to the caller
Последовательность в L1 никогда не выполняется напрямую, а скорее является шаблоном для инструкций, которые должны передаваться в качестве параметра вызываемому абоненту, который сохранит этот шаблон в своей локальной памяти и затем выполнит эту копию.
Они используют термин «cue» для вызова (вызов подпрограммы, например, передача управления вызываемому объекту) и «link» для возврата (передача управления из подпрограммы обратно соответствующему вызывающему объекту) (и термин «заказы» для инструкций машинного кода).
Таким образом, когда они говорят о «самомодифицирующемся соединении», они означают, что механизм для подпрограмма для возврата к вызывающей стороне использует код авто-модификации , который является кодом, который изменяет свои собственные инструкции при выполнении, то есть код th при записи в память команд для предстоящего исполнения. Здесь это запись X1 (копия кода в L1) в L4, которая выполняется сразу после записи.
Самоизменяющийся код в эти дни обычно не одобряется как у него есть несколько негативных последствий: кеш команд должен храниться в синхронизации c с самомодификацией, что влияет на производительность, а память команд должна быть доступной для записи, что мешает безопасности.
Самостоятельная модификация часто использовалась в старых процессорах из-за того, что мы сегодня можем считать отсутствующими инструкциями в наборе команд - например, PDP-8 имел инструкции ввода-вывода, но порт ввода-вывода был жестко запрограммирован в инструкции - не было никакого косвенного доступа к порту ввода / вывода. Таким образом, для того, чтобы подпрограмма получила доступ к порту ввода-вывода, взятому в качестве параметра, для создания инструкции ввода-вывода для этого порта использовался самоизменяющийся код. (Можно было бы также использовать большой оператор switch, но он занимал бы немного больше кода.) Эти более старые процессоры не имели кэшей инструкций, а программисты и средства разработки не обеспечивали отделение секций инструкций от секций данных (т.е. попытка защитить инструкции).
В случае с Pegasus нет никаких косвенных ответвлений, препятствующих возможности возврата по адресу, указанному вызывающим абонентом. Как выясняется, код вызывающего абонента также может быть фактически перекрыт - возможность передать две инструкции для выполнения вызываемого абонента позволила как восстановить блок регистров для кода вызывающего абонента, так и переход в этом блоке.
(Передача полностью сформированных инструкций в качестве параметра привела к меньшему количеству инструкций для обратной связи, чем к другой самоизменяющейся альтернативе: динамически создать инструкцию перехода по заданному параметру адреса - для этого потребовалось бы еще несколько инструкций для выполнения операций с битовыми полями.)
https://en.wikipedia.org/wiki/Ferranti_Pegasus http://bitsavers.org/pdf/ferranti/pegasus/PegasusProgrammingMan_1962.pdf