Эволюция ISA…
Предлагаю вам перевод цикла The EightThirtyTwo ISA Part 6: Evolving the ISA… автора Alastair M. Robinson.
В моих детских шагах к рабочему бэкэнду VBCC у меня теперь есть достаточно полный функционал, по крайней мере, для того чтобы отправить текст “Hello world!” в UART. Еще предстоит пройти долгий путь, прежде чем он (а) заработает и (б) создаст код, который даже отдаленно эффективен, но это только начало. В процессе я нашел и устранил некоторые недостатки в ISA, а также внес несколько изменений в набор команд и кодирование.
Самое главное, что для кода C на процессоре с относительно небольшим количеством регистров крайне важно, чтобы значения могли эффективно проиндексированы в фрейме стека. Eighthirtytwo ISA не был хорошо настроен для этого. Первоначально я рассматривал возможность придания r5 особого значения в качестве индексного регистра и связанного с каждой инструкции “load indexed” и “store indexed”, что добавляло бы r5 к выбранному регистру для создания эффективного адреса, но это оказалось неуклюжим и неэлегантным.
Я уже решил включить инструкцию “addt”, а также “add”. Эти инструкции будут одинаковыми, за исключением того, что “addt” помещает результат во временный регистр вместо выбранного регистра. Мой первоначальный план состоял в том, чтобы сделать эту особенность специально для r7, чтобы “addt r[0-6]” выполнял простое сложение, помещая результат в temp, но “addt r7” помещал результат в r7, а старое содержимое r7 + 1 в регистр temp. Это фактически станет инструкцией “jsr”, использующей temp в качестве регистра ссылок; подпрограмма сохраняет temp в стеке и извлекает его в r7, когда она завершается.
[С тех пор я заметил, что особый вариант “addt r7” делает его полезным для вычисления относительных адресов переменных ПК, поэтому вместо этого я буду использовать особый вариант “add r7”, чтобы поместить старое значение + 1 в temp. Возможно, это имеет больше смысла в любом случае, поскольку в этом случае мы просто добавляем функциональность для особого варианта, а не полностью меняем его.]
Однако мне пришло в голову, что я смогу лучше использовать “addt”, если смогу загружать и хранить, используя временный регистр в качестве адреса, вместо того, чтобы использовать выбранный регистр, поэтому я определил инструкцию загрузки и хранения для этой цели. Вместе с addt они образуют небольшой кластер инструкций, которые работают в направлении, противоположном основному набору инструкций. Поскольку мы вряд ли будем использовать вычисленный адрес во второй раз до того, как он будет вытеснен из temp, я определил только предварительные и постинкрементные версии этих инструкций загрузки и хранения, поскольку это наиболее полезно при работе со стеком. С помощью этих инструкций мы теперь можем сохранять и восстанавливать регистры в функции prologue / epilogue, не сортируя каждый из них по очереди во временный регистр, как это:
function:
exg r6 // Адрес возврата в temp
stmpdec r6 // записать адрес возврата
stmpdec r1
stmpdec r2
stmpdec r3
stmpdec r4
mr r6 // запись адреса возврата и четырез регистров - 7 байт.
...
li 24
addt r6
ldtmpinc r1 // Загрузить значения 24 последних байт стека - 3 байта.
...
mt r6
ltmpinc r4
ltmpinc r3
ltmpinc r2
ltmpinc r1
ltmpinc r6
exg r6
mr r7 // Восстановить четыре регистра, и перейти на адрес возврата - 8 байт
Я также понял, что мне нужно иметь дело с разницей между сравнениями со знаком и без знака: хотя двоичная арифметика одинакова для двух, семантика операндов будет определять, какой из них мы считаем большим, и, следовательно, как мы интерпретируем бит переноса/заёма. Мне пришло в голову, что если я добавлю инструкцию “sgn”, которая устанавливает флаг, заставляющий следующее сравнение рассматриваться как со знаком, я могу использовать этот флаг и в других контекстах. Например, я могу использовать его, чтобы указать, что сдвиг следует рассматривать как арифметический, а не логический – таким образом, устраняя необходимость иметь инструкции “asr” и “lsr” – или, возможно, указывая, что байтовые или полусловные загрузки должны быть расширены со знаком.
Еще одна вещь, которая поразила меня, заключается в том, что, поскольку r7 является счетчиком программ, многие инструкции никогда не будут использоваться с r7 в качестве операнда – например, “and”, “or”, “xor”, “ror”, “shl”, “shr” и некоторые другие, я мог бы потенциально перегрузить эти коды операций, предоставив мне потенциальное пространство для кодирования некоторых инструкций с нулевым операндом, таких как предлагаемая инструкция “sgn”, упомянутая выше.
Набор инструкций в его нынешнем виде выглядит следующим образом:
Инструкции перемещения:
- li – непосредственная загрузка.
- mr – переместить temp в регистр.
- mt – переместить регистр в temp.
- exg – обменять регистр с temp.
Инструкции с памятью загрузки/сохранения:
- ld – загрузить из памяти адрес в регистре, результат в temp.
- ldinc – то-же, что ld, но с постинкрементом регистра.
- ldbinc – то-же, что ldinc но однобайтное чтение.
- ltmpinc – загрузить из памяти по адресу в temp, результат в регистре.
- st – сохранить temp по адресув регистре.
- stdec – то-же, что st но с предекрементом регистра.
- sth – сохранить половину слова в temp по адресу в регистре.
- stbinc – сохранить байт в temp по адресу в регистре с постинкрементом.
- stmpdec – сохранить содержимое регистра по адресу вtemp, с предекрементом.
Арифметические инструкции:
- add – сложить temp с регистром, результат в регистре. (Если регистр r7,старое значение+ 1 записать в temp.)
- addt – сложить temp с регистром, результат в temp.
- sub – вычесть temp из регистра, результат в регистре.
- cmp – вычесть temp из регистра, результат отбросить.
- mul – умножить temp с регистром, результат в регистре.
Побитовые/Логические инструкции:
- and - И.
- or - ИЛИ.
- xor – исключающее ИЛИ.
- shl – сдвиг влево.
- shr – сдвиг вправо.Если предидущая инструкция “sgn” сделать арифметический сдвиг.
- ror – циклический сдвиг вправо.
Прочие инструкции:
- cond – начать условное выполнение, операнд один из EX, NEX, EQ, NEQ, GE, SGT, LE, SLT.
- “cond EX” возвратиться к безусловному выполнению – aкак и все что записывается в r7.
- “cond NEX” сейчас используется для отключения симулятора.
Без операнда, перегружаемые инструкции:
- sgn – указывает, что следующей инструкции (sub, cmp, shr, mul) следует рассматривать ее операнды как знаковые, а не беззнаковые.
- ldt – загрузить по адресу в temp, результат в temp.
В следующий раз я посмотрю на сделанный вручную ассемблерный код EightThirtyTwo…
Адрес для контактов : imax9@narod.ru
Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.
При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.
© Максим Ильин 2022г.