Переменный размер команды и стек

Цель: экономия памяти

Задача: система команд, в которых нет неиспользуемых полей

УМ_П: переменный размер команды

Решение: ввести переменный размер команды, зависящий от КОП

Пример: Вычисление факториала для учебной машины УМ-П. Обратите внимание на минимальный размер команды в один байт (halt), как следствие — на адресацию в байтах и на то, что целое занимает 40 битов, как в УМ-2. Кроме того, в УМ-П

.cpu mm-v

.input 0x100
.output 0x10A

.code
      00 0105 0100 ; 00; Заносим N в счётчик
      00 010A 0020 ; 05; Заносим 1 в результат
      05 0105 0020 ; 0A; Сравниваем счётчик и 1
      85 001F      ; 0F; Если ≤, цикл окончен
      03 010A 0105 ; 12; Домножаем результат на счётчик
      02 0105 0020 ; 17; Уменьшаем счётчик на 1
      80 000A      ; 1C; Переход на начало цикла
      99           ; 1F; конец
      00 0000 0001 ; 20; 1

.enter 6

FrBrGeorge/MyDict/speech_balloon_question.png Потребление памяти по сравнению с УМ-2 сократилось. Насколько возросло потребление адресов по сравнению с УМ-2 (если считать в адресуемых ячейках, а не в байтах)?

Порядок байтов

Целое число состоит из нескольких адресуемых байтов. В каком порядке эти байты располагаются в памяти?

Число: 0xA1B2C3D4

Представление

D4*0x01 + C3*0x100 + B2*0x10000 + A1*0x1000000

Порядок от младшего к старшему

остроконечный (little-endian)

0xD4, 0xC3, 0xB2, 0xA1

Порядок от старшего к младшему

тупоконечный (big-endian)

0xA1, 0xB2, 0xC3, 0xD4

Смешанный порядок

(mixed-endian)

0xB2, 0xA1, 0xD4, 0xC3

Тупоконечный порядок нагляднее, зато в нём интерпретация значения целого зависит от размера целого в битах, даже если значение этого целого мало. Допустим, первые 4 байта памяти равны 03 00 00 00. Тогда по адресу 0

При остроконечном порядке все три значения равны 3.

Смешанный порядок возникает, когда размер целого превышает размер машинного слова. В результате и байты в машинном слове и машинные слова в целом числе имеют порядок little-endian.

В УМ-П — сорокаразрядное целое, байты располагаются в порядке адресации (big-endian)

Ошибочная интерпретация инструкций.

Допустим, в примере выше в результате ошибки безусловный переход был запрограммирован на адрес 0008 вместо 000A. (80 0008). Как бы тогда выполнялась программа?

Отладка таких конструкций представляет известную трудность

Стековые машины

Цель: минимизировать обмен с оперативной памятью

Аппаратный стек — это хранилище данных, в котором предусмотрены две операции

Таким образом стек реализует абстракцию LIFO (Last In, First Oout).

В стеке может быть предусмотрена также и адресация относительно его вершины:

Стековая машина УМ-С

Дополним машину УМ-1 аппаратным стеком и ограничим все операции над над данными

Пример: вычисление b2-4*a*c

.cpu mm-s

.input  0x1006, 0x1000, 0x1009
.output 0x100c
.code
5A 1000 ; загрузить b ; b
5C      ; дублировать ; b b
03      ; умножить    ; b*b
5A 1003 ; загрузить 4 ; b*b 4
5A 1006 ; загрузить a ; b*b 4 a
5A 1009 ; загрузить c ; b*b 4 a c
03      ; умножить    ; b*b 4 a*c
03      ; умножить    ; b*b 4*a*c
02      ; вычесть     ; b*2-4*a*c
5b 100C ; выгрузить d ; b*2-4*a*c
99      ; halt

.code 0x1003
00 0004 ; 4

.enter  3 5 2

Более сложные алгоритмы требуют более сложных операций над стеком.

Пример: вычисление факториала в УМ-С: сначала стек наполняется значениями от N до 1 с шагом -1, затем все они перемножаются:

.cpu mm-s

.input 0x100
.output 0x103

.code
5A 0100 ; 00 ; Заносим N                          ; N
5C      ; 03 ; дублируем перед проверкой          ; N … K K
5A 0025 ; 04 ; Заносим 1                          ; N … K K 1
05      ; 07 ; Сравниваем                         ; N … K
85 0013 ; 08 ; Если ≤, цикл окончен → 13          ; N … K
5C      ; 0B ; Дублируем                          ; N … K K
5A 0025 ; 0C ; Заносим 1                          ; N … K K 1
02      ; 0F ; Вычитаем                           ; N … K K-1
80 0003 ; 10 ; Продолжаем набивать стек → 03      ; N … K K-1
5D      ; 13 ; Посмотрим очередной сомножитель    ; N … Res K
5C      ; 14 ; Дублируем для сравнения            ; N … Res K K
5A 0100 ; 15 ; Заносим N                          ; N … Res K K N
05      ; 18 ; Сравниваем, не конец ли            ; N … Res K
81 0020 ; 19 ; Нашли N, готово → 20               ; N … Res K
03      ; 1C ; Умножим                            ; N … Res*K
80 0013 ; 1D ; Продолжим умножение → 13           ; N … Res*K
03      ; 20 ; Умножим в последний раз            ; Res N
5B 0103 ; 21 ; Выгрузим                           ; Res
99      ; 24 ; Halt                               ; Res
00 0001 ; 25 ; 1

.enter 6

Ещё раз обратим внимание на то, что одно целое занимает три однобайтовых ячейки памяти.

Непосредственная и относительная адресация

Попытка реализовать факториал обычным циклом с умножением на УМ-С приведёт с усиленному использованию операций обмена с памятью. Причина: невозможность работать с содержимым стека глубже двух ячеек.

Безадресная машина УМ-0*

(не поддерживается modelmachine)

TODO Роль ввода в эмуляторе может выполнять предварительное заполнение стека, а вывода — выдача стека по окончании работы.

Рассмотрим архитектуру, единственной памятью для хранения данных которой является стек.

Фактически получается не-фоннеймановская архитектура: различие памяти для команд и для данных в стеке.

В командах работы с ячейками стека номер ячейки является, по сути, адресом относительно текущей вершины стека, или относительным адресом. В подавляющем большинстве случаев (если не происходит работы с массивами данных произвольного размера) относительный адрес сравнительно невелик (имеет такой же порядок, что и количество различных переменных в формуле).

Константы, задаваемые непосредственной адресацией, могут быть отрицательными. Например, 40 FE кладёт на вершину стека число -2. Таким образом, диапазон констант, которые можно использовать в УМ-0, варьируется от -128 до 127. Чтобы задать числа вне этого диапазона (например, 0xfe = 254), пришлось бы ввести дополнительные команды типа «заполнить старший байт вершины стека», но для простоты мы этого делать не станем.

Подобно операциям со стеком, операции перехода также крайне редко бывают «дальними»: при организации, например, цикла его начало скорее всего отстоит от конца не более, чем на несколько сотен инструкций. Таким образом, введя относительную адресацию переходов (вперёд и назад), мы можем сократить размер команд перехода (который в машинном коде довольно много). Такое сокращение эффективно на «настоящих» современных архитектурах с длинным полным адресом (как правило, 64 бита).

Договоримся, что в УМ-0 операнд команды перехода — это смещение относительно адреса текущей команды, то есть относительно содержимого регистра СК.

FrBrGeorge/MyDict/speech_balloon_question.png Что не так с программным кодом, в котором начала цикла отстоит от его конца на многие тысячи инструкций? Как программисты справляются с такими ситуациями?

Если повсеместно использовать относительную и непосредственную адресацию, код приобретает два полезных свойства:

  1. Код программы получается позиционно-независимым: он может быть загружен и выполнен в произвольном месте оперативной памяти.

  2. Код программы не зависит также и от представления целого числа: оно может занимать и два байта, то есть совпадать с размером команды, а может, например, и 4

Договоримся, что размер целого числа УМ-0 — два байта, хотя это и маловато. Тогда характеристики УМ-0 получаются такими:

Напомним, что свойство «Размер ячейки оперативной памяти — 2 байта» означает, что в УМ-0, как в УМ-3 и УМ-2, операнд команды перехода — это количество команд, а не количество байтов.

Фиксированный размер команд также предполагает, что у арифметических команд, команд сравнения и команд работы со стеком есть операнд — номер ячейки в стеке. В отличие от УМ-С такие команды снимают одно значение с вершины стенка, а второе, указанное в операнде, не трогают.

Команда

Описание

Содержимое стека

Код

Результат

40 NN

Положить число NN на вершину стека

4 5 6 7 8

40 09

4 5 6 7 8 9

5B II

Снять и выбросить II элементов стека

4 5 6 7 8 9

50 04

4 6 7 8 9

5C II

Дублировать II-й элемент стека на его вершине

4 5 6 7 8 9

54 04

4 5 6 7 8 9 5

5D II

Обменять местами вершину стека и II-й его элемент

4 5 6 7 8 9

56 04

4 9 6 7 8 5

05 II

Сравнить (вычесть) вершину стека из II-го элемента, выставить флаги

4 9 6 7 8 5

05 04

4 9 6 7 8

02 II

Вычесть вершину стека из II-го элемента

4 9 6 7 8

01 04

4 9 6 7 -4

?? II

(Другая операция ∀) вершину стека из II-го элемента

9 6 7 -4

?? 02

9 6 7 6∀-4

80 AA

Перейти по смещению AA относительно текущей команды

80 FB

… (на 5 команд назад)

… (другие команды перехода)

Пример вычисления факториала на УМ-0 (исходное число N уже находится на вершине стека, результат также должен оказаться на вершине)

cpu mm-0

.input 1
.output 1

.code
40 01   ; 00 ; Будущий результат        ; N Res=1
40 01   ; 01 ; Ещё 1 (счётчик)          ; N Res i=1
5C 00   ; 02 ; Сдублируем счётчик       ; N Res i i
05 03   ; 03 ; Сравним с N              ; N Res i
85 09   ; 04 ; Если i>=N, -> +9 (конец) ; N Res i
40 01   ; 05 ; Занесём 1                ; N Res i 1
01 01   ; 06 ; Прибавим 1 к i           ; N Res i i+1
5D 02   ; 07 ; Обменяем местами         ; N i+1 i Res
03 02   ; 08 ; Умножим                  ; N i+1 i Res*(i+1)
5D 02   ; 0A ; Вернём на место          ; N Res*(i+1) i (i+1)
5D 01   ; 0B ; Обменяем i и i+1         ; N Res*(i+1) (i+1) i
5B 01   ; 0C ; Удалить i                ; N Res i
80 F6   ; 0D ; Продолжим цикл -> -10    ; N Res i
5B 01   ; 0E ; Выбросим i               ; N Res
99 00   ; 0F ; Конец                    ; N Res

.enter 6

В действительности абсолютный адрес необходим только в одном случае: когда при трансляции программы заранее неизвестно (или сложно рассчитать), где именно в памяти окажется адресуемый объект после запуска. Например, если пробовать хранить глобальные данные на дне стека, смещение до них от текущей вершины будет всё время меняться. Много проблема начнётся при использовании подпрограмм.

Описанным выше путём «заполнения ячейки по частям» можно сформировать в ячейке абсолютный адрес, а затем обратиться к области памяти, находящейся по этому адресу. Такой механизм называется косвенной адресацией — мы рассмотрим его в следующей лекции.

LecturesCMC/ArchitectureAssemblerProject/04_VarAddressing (последним исправлял пользователь FrBrGeorge 2024-09-26 14:41:32)