The post has been translated automatically. Original language: Russian
The number of processor instructions is often perceived as an indicator of its capabilities. The more instructions there are, the richer the architecture seems. But in practice, it is not the number of commands itself that is more important, but how fully the necessary calculations can be built from them and how simply these commands are implemented in hardware.
This is clearly seen when comparing Donald Knuth's MIX and Memora8.
MIX was created as a universal learning machine for parsing algorithms. Its commands are quite expressive: a single instruction can combine an address, indexing, and additional parameters that affect its behavior. Memora8 is built differently. It is a 32-bit processor with a small set of simple instructions, designed for direct implementation in programmable logic.
Memora8 uses 28 active commands. This is enough for arithmetic, logical operations, shifts, comparisons, transitions, calling routines, and working with memory.
The small set was not made out of an effort to set a record for minimalism. This is a consequence of a different approach to the boundary between software and hardware.
How does MIX gain expressiveness
In MIX, the machine command contains more than just the opcode. It contains an address, an indication of the index case, and a field that can change the meaning of the action.
Due to this, one common mechanism is used for several behaviors at once. Depending on the value of the additional field, the command can work with different parts of the machine word, check different conditions, or perform different variants of the same operation.
It's convenient for a programmer. A fairly complex action can be expressed in one instruction.
But this expressiveness means that inside the processor you have to sort out several components of the command and take into account their combination. The operation code no longer always fully determines what exactly needs to be performed.
For MIX, this is justified. Knuth needed a compact and at the same time rich computer model suitable for describing a large number of algorithms.
Memora8 has a different goal.
One instruction is one clear action.
In Memora8, the opcode takes up five bits. In total, this format allows you to have 32 values, but four of them are left free. The rest are used by the current teams.
Most of the instructions make pretty direct sense.
ADD adds the values of two registers. The SUB performs the subtraction. AND, OR, and XOR are responsible for logical operations. For some actions, there are options with a direct value, when the second operand is stored directly inside the command.
Working with memory is organized just as explicitly. There are separate commands for reading and writing bytes, half words, and 32-bit words.
As a result, a single Memora8 instruction can usually do less than a more complex instruction of another architecture could. But its operation is easier to understand and easier to carry out through the hardware implementation.
If a complex operation consists of two or three simple commands, this is not considered a disadvantage in itself.
The main question is how often, in order to reduce a program by one instruction, the entire processor has to be complicated.
Comparison without hidden flags
A good example of this approach is the comparison of values.
In many architectures, the comparison command sets special processor flags. The next transition instruction checks these flags and makes a decision.
"""MIX" uses a separate comparison indicator that stores the status of "less than", "equal to", or "greater than". Transition commands then work with this state.
There is no separate set of comparison flags in Memora8.
The CMP and CMPI commands write the result to a regular register. It contains signs of equality, as well as the results of signed and unsigned comparisons.
After that, the program can select the desired feature using the usual logical operation and perform the transition using BNZ or BEQZ.
It turns out several separate actions: first, compare, then, if necessary, process the result and only then change the course of the program.
At the machine code level, this sometimes requires more instructions. But each of them uses an already existing common mechanism.
The comparison creates a regular value. The logical command works with the usual value. The transition checks the regular register.
The processor does not need an additional hidden object, the state of which should accompany the execution of the program.
Why several simple commands can be more profitable than one complex one
Imagine an instruction that immediately compares two numbers and performs a transition if the first is less than the second.
This is convenient for a programmer: one line instead of several.
But such a command adds a new behavior to the processor. You need to get two values, perform a comparison, determine the condition, calculate the new address, and correctly process the change in the execution flow.
If you then need transitions based on other conditions, the number of options starts to grow.
In Memora8, comparison and transition are separated. Due to this, the same several mechanisms can be combined in different ways, without creating a separate team for each case.
This approach is especially useful in a small processor, where not only the area of the circuit is important, but also the clarity of its operation.
The fewer special cases there are, the easier it is to verify that each team is interacting correctly with the others.
Working with memory also remains explicit.
Memory access is another area where you can make an instruction very powerful.
Theoretically, one instruction could simultaneously calculate a complex address, select the data size, convert the value, and change one of the registers involved in addressing.
Memora8 does not do this.
The load and save commands use the base case and offset. The size of the data is determined by the command itself: a byte, half a word, or a full 32-bit word.
If the address needs to be calculated more difficult, the program does it in advance with the usual arithmetic operations.
The same principle occurs again. It is not necessary to put the maximum possibilities inside a single instruction, if the desired result can be obtained by combining existing simple actions.
Why is the number 28 not an end in itself?
The Memora8 set could theoretically be reduced even more.
Some convenient instructions can be eliminated and replaced with sequences of other commands. Some of the work can be transferred to the compiler. We could try to make the processor even more minimal.
But too small a set also creates problems.
If the simplest common operation constantly requires a long sequence of commands, the savings in hardware may no longer be justified. The size of the program increases, the number of executed instructions increases, and the compiler's work becomes more complicated.
Therefore, the task was not to minimize the number of teams at any cost.
We needed a set that remains small, but covers the main classes of operations directly: arithmetic, logic, working with constants, memory, and flow control.
The number 28 was the result of this compromise.
Fixed command length
All Memora8 instructions are 32 bits long.
This is more important than it might seem at first glance. The processor does not have to determine where one instruction ends and the next begins. The command is selected from memory as a single word and immediately transmitted for analysis.
The main fields are located in pre-determined locations. The opcode is in the lower five bits, and the register numbers also have a fixed position.
Several formats differ in which data occupies the rest of the instruction, but the general principle remains.
This format works well with a small set of commands. The decoder does not have to first determine the length of the instruction, assemble it from several parts, or disassemble a large number of exceptions.
The simplicity of the instruction set continues in the processor device.
What happens inside Memora8
The Memora8 team goes through a five-stage pipeline.
First, it is selected from memory. Then the command is parsed and registers are read. After that, the stage of working with memory is performed, then the actual calculation, and at the end the result is finally fixed in the architectural state of the processor.
This means that every new feature of the command system must go through this entire path.
If you add a new operand type, you need to get it and pass it between stages. If it appears a new way to record the result, it must be processed correctly at the end. If the command changes the course of the program, you need to cancel the already selected instructions that should no longer be executed.
Therefore, the complexity of the instruction does not remain only in the command table.
It applies to the device of the entire processor.
This is where a small and regular set of operations begins to give a practical advantage.
Two approaches to the same task
MIX and Memora8 are good examples of two different ways to make a processor compact and intuitive enough.
MIX uses expressive instructions. Additional fields allow a single command to perform several types of actions. This makes the machine language dense and well suited for a universal learning machine.
Memora8 prefers a more direct model. A separate instruction performs a relatively simple action, and a more complex operation is constructed as a sequence of them.
In the first case, part of the complexity lies within the team.
In the second case, it's a combination of commands.
For Memora8, the second approach turned out to be more convenient, because the processor is designed not only as an abstract architecture. It must exist as a real digital circuit, where every exception and every additional behavior option requires hardware support.
Therefore, it is not the 28 teams themselves that are of interest.
The important principle behind this number is to leave a small set of operations in the processor, sufficient for building programs, and not add a new hardware mechanism just to shorten a separate sequence by one or two instructions.
It was this boundary between the simplicity of the hardware and the expressiveness of the program that defined the Memora8 command system.
Количество команд процессора часто воспринимается как показатель его возможностей. Чем больше инструкций, тем богаче кажется архитектура. Но на практике важнее не само число команд, а то, насколько полно из них можно построить нужные вычисления и насколько просто эти команды реализуются в аппаратуре.
Это хорошо видно при сравнении MIX Дональда Кнута и Memora8.
MIX создавался как универсальная учебная машина для разбора алгоритмов. Его команды достаточно выразительны: одна инструкция может сочетать адрес, индексирование и дополнительные параметры, влияющие на её поведение. Memora8 построен иначе. Это 32-битный процессор с небольшим набором простых инструкций, рассчитанный на непосредственную реализацию в программируемой логике.
В Memora8 используется 28 действующих команд. Этого достаточно для арифметики, логических операций, сдвигов, сравнения, переходов, вызова подпрограмм и работы с памятью.
Небольшой набор получился не из стремления установить рекорд минимализма. Это следствие другого подхода к границе между программой и аппаратурой.
Как MIX получает выразительность
В MIX машинная команда содержит не только код операции. В ней есть адрес, указание индексного регистра и поле, которое может изменять смысл действия.
Благодаря этому один общий механизм используется сразу для нескольких вариантов поведения. В зависимости от значения дополнительного поля команда может работать с разными частями машинного слова, проверять разные условия или выполнять различные варианты одной операции.
Для программиста это удобно. В одной инструкции можно выразить довольно сложное действие.
Но такая выразительность означает, что внутри процессора приходится разбирать несколько составляющих команды и учитывать их сочетание. Код операции уже не всегда полностью определяет, что именно предстоит выполнить.
Для MIX это оправданно. Кнуту была нужна компактная и одновременно богатая модель вычислительной машины, пригодная для описания большого числа алгоритмов.
У Memora8 другая цель.
Одна инструкция — одно понятное действие
В Memora8 код операции занимает пять бит. Всего такой формат позволяет иметь 32 значения, но четыре из них оставлены свободными. Остальные используются действующими командами.
Большинство инструкций имеют довольно прямой смысл.
ADD складывает значения двух регистров. SUB выполняет вычитание. AND, OR и XOR отвечают за логические операции. Для части действий существуют варианты с непосредственным значением, когда второй операнд хранится прямо внутри команды.
Работа с памятью организована так же явно. Есть отдельные команды чтения и записи байта, половины слова и 32-битного слова.
В результате отдельная инструкция Memora8 обычно умеет меньше, чем могла бы более сложная команда другой архитектуры. Зато её действие проще понять и проще провести через аппаратную реализацию.
Если сложная операция собирается из двух или трёх простых команд, это не считается недостатком само по себе.
Главный вопрос состоит в другом: насколько часто ради сокращения программы на одну инструкцию приходится усложнять весь процессор.
Сравнение без скрытых флагов
Хороший пример такого подхода — сравнение значений.
Во многих архитектурах команда сравнения устанавливает специальные флаги процессора. Следующая инструкция перехода проверяет эти флаги и принимает решение.
В MIX используется отдельный индикатор сравнения, который хранит состояние «меньше», «равно» или «больше». Команды перехода затем работают с этим состоянием.
В Memora8 отдельного набора флагов сравнения нет.
Команды CMP и CMPI записывают результат в обычный регистр. В нём содержатся признаки равенства, а также результаты знакового и беззнакового сравнения.
После этого программа может выделить нужный признак обычной логической операцией и выполнить переход с помощью BNZ или BEQZ.
Получается несколько отдельных действий: сначала сравнить, затем при необходимости обработать результат и только потом изменить ход программы.
На уровне машинного кода это иногда требует большего числа инструкций. Но каждая из них использует уже существующий общий механизм.
Сравнение создаёт обычное значение. Логическая команда работает с обычным значением. Переход проверяет обычный регистр.
Процессору не нужен дополнительный скрытый объект, состояние которого должно сопровождать выполнение программы.
Почему несколько простых команд могут быть выгоднее одной сложной
Представим инструкцию, которая сразу сравнивает два числа и выполняет переход, если первое меньше второго.
Для программиста это удобно: одна строка вместо нескольких.
Но процессору такая команда добавляет новый вариант поведения. Нужно получить два значения, выполнить сравнение, определить условие, вычислить новый адрес и правильно обработать изменение потока исполнения.
Если затем понадобятся переходы по другим условиям, количество вариантов начинает расти.
В Memora8 сравнение и переход разделены. Благодаря этому одни и те же несколько механизмов можно сочетать по-разному, не создавая отдельную команду для каждого случая.
Такой подход особенно полезен в небольшом процессоре, где важна не только площадь схемы, но и понятность её работы.
Чем меньше особых случаев, тем проще проверить, что каждая команда правильно взаимодействует с остальными.
Работа с памятью тоже остаётся явной
Доступ к памяти — ещё одна область, где можно сделать инструкцию очень мощной.
Теоретически одна команда могла бы одновременно вычислять сложный адрес, выбирать размер данных, преобразовывать значение и изменять один из регистров, участвующих в адресации.
Memora8 этого не делает.
Команды загрузки и сохранения используют базовый регистр и смещение. Размер данных определяется самой командой: байт, половина слова или полное 32-битное слово.
Если адрес требуется вычислить сложнее, программа делает это заранее обычными арифметическими операциями.
Снова возникает тот же принцип. Не обязательно помещать максимум возможностей внутрь одной инструкции, если нужный результат можно получить сочетанием уже существующих простых действий.
Почему число 28 не является самоцелью
Набор Memora8 теоретически можно было бы уменьшить ещё сильнее.
Некоторые удобные инструкции можно исключить и заменить последовательностями других команд. Часть работы можно перенести в компилятор. Можно было бы попытаться сделать процессор ещё более минимальным.
Но слишком маленький набор тоже создаёт проблемы.
Если простейшая распространённая операция постоянно требует длинной последовательности команд, экономия в аппаратуре может перестать оправдываться. Увеличивается размер программы, возрастает число выполняемых инструкций, усложняется работа компилятора.
Поэтому задача состояла не в минимальном числе команд любой ценой.
Нужен был набор, который остаётся небольшим, но покрывает основные классы операций непосредственно: арифметику, логику, работу с константами, памятью и управлением потоком исполнения.
Число 28 стало результатом этого компромисса.
Фиксированная длина команды
Все инструкции Memora8 имеют длину 32 бита.
Это важнее, чем может показаться на первый взгляд. Процессору не приходится определять, где заканчивается одна инструкция и начинается следующая. Команда выбирается из памяти как единое слово и сразу передаётся на разбор.
Основные поля располагаются в заранее известных местах. Код операции находится в младших пяти битах, номера регистров также имеют фиксированное положение.
Несколько форматов различаются тем, какие данные занимают оставшуюся часть инструкции, но общий принцип сохраняется.
Такой формат хорошо сочетается с небольшим набором команд. Декодер не должен сначала определять длину инструкции, собирать её из нескольких частей или разбирать большое число исключений.
Простота системы команд продолжается в устройстве процессора.
Что происходит внутри Memora8
Команда Memora8 проходит через пятистадийный конвейер.
Сначала она выбирается из памяти. Затем происходит разбор команды и чтение регистров. После этого выполняется стадия работы с памятью, затем собственно вычисление, а в конце результат окончательно фиксируется в архитектурном состоянии процессора.
Это означает, что каждая новая особенность системы команд должна пройти через весь этот путь.
Если добавить новый тип операнда, его нужно получить и передать между стадиями. Если появляется новый вариант записи результата, его необходимо правильно обработать в конце. Если команда изменяет ход программы, нужно отменить уже выбранные инструкции, которые больше не должны выполняться.
Поэтому сложность инструкции не остаётся только в таблице команд.
Она распространяется на устройство всего процессора.
Именно здесь небольшой и регулярный набор операций начинает давать практическое преимущество.
Два подхода к одной задаче
MIX и Memora8 хорошо показывают два разных способа сделать процессор достаточно компактным и понятным.
MIX использует выразительную инструкцию. Дополнительные поля позволяют одной команде выполнять несколько разновидностей действия. Это делает машинный язык плотным и хорошо подходит для универсальной учебной машины.
Memora8 предпочитает более прямую модель. Отдельная инструкция выполняет сравнительно простое действие, а более сложная операция строится как их последовательность.
В первом случае часть сложности находится внутри команды.
Во втором — в сочетании команд.
Для Memora8 второй подход оказался удобнее, потому что процессор проектируется не только как абстрактная архитектура. Он должен существовать в виде реальной цифровой схемы, где каждое исключение и каждый дополнительный вариант поведения требуют аппаратной поддержки.
Поэтому интерес представляют не сами 28 команд.
Важен принцип, который стоит за этим числом: оставить в процессоре небольшой набор операций, достаточный для построения программ, и не добавлять новый аппаратный механизм только ради того, чтобы сократить отдельную последовательность на одну или две инструкции.
Именно такая граница между простотой аппаратуры и выразительностью программы определила систему команд Memora8.