УМ-М: Регистры и косвенная адресация

Один из способов повысить эффективность работы программы — предусмотреть небольшое количество выделенных быстродействующих ячеек памяти — регистров общего назначения

Регистровая учебная машина УМ-Р

16 регистров позволяют проводить довольно сложные вычисления без дополнительного обмена с ОП. В начале программы данные загружаются из памяти, в конце — результаты выгружаются в память.

Пример: максимум из трёх чисел (воспользуемся совместимой с УМ-Р поддерживаемой эмулятором архитектурой УМ-М)

TODO отладить

.cpu mm-r

.input 0x20, 0x22, 0x24
.output 0x26

.code
      00 1 0 0020 ; 00 ; загрузка A в R1
      00 2 0 0022 ; 02 ; загрузка B в R2
      00 3 0 0024 ; 04 ; загрузка C в R3
      20 4 1      ; 06 ; R4:=R1
      25 4 2      ; 07 ; Сравнить R4 и R2
      84 0 0 000B ; 08 ; Если ⩾, обойти следующую инструкцию
      20 4 2      ; 0A ; R4:=R2
      25 4 3      ; 0B ; Сравнить R4 и R3
      84 0 0 000F ; 0C ; Если ⩾, обойти следующую инструкцию
      20 4 3      ; 0E ; R4:=R3
      10 4 0 0026 ; 0F ; выгрузить R4 в D
      99 0 0      ; 11 ;

.enter 32 24 22

В регистрах можно эффективно хранить границы и изменения циклов.

Пример: ввести M и N, посчитать сумму квадратов от M2+(M+1)2+…+(N-1)2+N2

.cpu mm-r

.input 0x1000, 0x1002
.output 0x1004

.code
        00 1 0 1000 ; 00; R1:=M
        00 2 0 1002 ; 02; R2:=N
        22 3 3      ; 04; R3-=R3 (обнуление)
        00 A 0 0013 ; 05; RA:=1
        25 1 2      ; 07; сравнить R1 и R2
        86 0 0 0010 ; 08; Переход если >
        20 4 1      ; 0A; R4:=R1
        23 4 1      ; 0B; R4*=R4
        21 3 4      ; 0C; R3+=R4
        21 1 A      ; 0D; R1+=RA (+=1)
        80 0 0 0007 ; 0E; продолжение цикла
        10 3 0 1004 ; 10; выгрузить R4 в S
        99 0 0      ; 12; стоп
        00 0 0 0001 ; 13; 1

.enter 5 10

Косвенная адресация. Модификация адреса

Как было ранее показано, работа с массивами данных при помощи самомодификации кода имеет серьёзные недостатки, в частности, трудность отладки неработающей программы. Переменный размер команды эти трудности только усугубляет.

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

Косвенная адресация — механизм доступа к оперативной памяти, при котором в инструкции указывается не сам адрес, а место хранения этого адреса (например, регистр).

В некоторых архитектурах разрешена косвенная адресация по памяти (из ячейки памяти считывается адрес другой ячейки памяти, по которому берётся значение) и даже двойная косвенная адресация (адрес адреса!).

Очевидный недостаток косвенной адресации — усложнение такта работы машины: извлечение адреса плюс чтение значения вместо одного чтения. Косвенная адресация по «медленной» памяти вдобавок сильно замедляет такт.

Машина с модификацией адреса УМ-М

Поскольку работа с массивами данных происходит в оперативной памяти, в УМ-М косвенная адресация используется в командах вида «Регистр-Память». Второй операнд — регистр команд вида «регистр-память» — называется смещением.

Исполнительный адрес (актуальный адрес, с которым работает инструкция), — сумма поля адреса и содержимого регистра смещения.

Такт работы операции вида «КК П С АААА», где КК — код операции, П — номер регистра-приёмника, С — номер регистра-смещения, AAAA — базовый адрес в УМ-Р

  1. Считать ячейку «КК П С»
  2. Проанализировать размер инструкции
  3. Считать ещё одну ячейку «АААА»
  4. Вычислить адрес операнда, равный AAAA + содержимое регистра № С

  5. Загрузить операнд
  6. Вычислить операцию КК
  7. Результат поместить в регистр П
  8. Вычислить адрес следующей команды

Исключение: значение 0 в поле смещения означает прямую адресацию (без смещения). Регистр R0, таким образом, невозможно использовать для смещения.

Пример: Прибавить константу 0x123 к четырём (количество произвольное) ячейкам памяти, начиная с адреса 0x1000.

.cpu mm-m

.input 0x1000, 0x1002, 0x1004, 0x1006
.output 0x1000, 0x1002, 0x1004, 0x1006

.code
22 4 4      ; 00 ; обнуление регистра (вычтем R4 из R4)
00 2 4 1000 ; 01 ; загрузим R4-й элемент массива (поначалу нулевой) в R2
01 2 0 000e ; 03 ; добавим 123 к R2
10 2 4 1000 ; 05 ; запишем обратно
01 4 0 0010 ; 07 ; добавим 2 к R4
05 4 0 0012 ; 09 ; сравним с последним индексом массива
95 0 0 0001 ; 0B ; переход обратно, если не больше
99 0 0      ; 0D ;
00000123    ; 0E ; 0x123
00000002    ; 10 ; 2
00000006    ; 12 ; 6

.enter 123 234 345 456

Здесь R4 играет роль индекса массива: инструкции 00 и 10 содержат 0x1000 в качестве базового адреса, к которому при чтении и, соответственно, записи, прибавляется содержимое R4. Размер целого в УМ-М — 4 байта, т. е. две ячейки, поэтому к R4 в цикле прибавляется константа 2.

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

Однако команда 00 1 0 1008 выполняется слегка по-другому, т. к. содержимое R0 не используется, и сложения не производится.

Смещение присутствует и в командах перехода УМ-М. Это позволяет вычислять и изменять адрес перехода, не модифицируя код программы.

FrBrGeorge/MyDict/speech_balloon_question.png А зачем это может быть нужно?

В других архитектурах механизмы адресации могут быть и другие:

Пример: «Транспонирование матрицы». 12 чисел в памяти трактуются как элементы двумерного массива 3×4:

Ввести это массив построчно, после чего транспонировать его и вывести построчно:

Для этой цели организуем «честный» вложенный цикл — сначала по X, а внутри — по Y, будем считывать элемент массива A[X][Y] = A[X + Y*3] и записывать элемент массива B[Y][X] = B[Y + X*4]. Не забудем также про адресную арифметику: Размер целого — это две ячейки памяти.

.cpu mm-m

.input  0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116
.output 0x300, 0x302, 0x304, 0x306, 0x308, 0x30a, 0x30c, 0x30e, 0x310, 0x312, 0x314, 0x316

.code
22 2 2      ; 00 ; R2 ← 0 (вычтем R2 из R2), это Y
22 1 1      ; 01 ; R1 ← 0 (вычтем R1 из R1), это X
20 5 2      ; 02 ; R5 ← R2
03 5 0 0206 ; 03 ; R5 *= 3
21 5 1      ; 05 ; R5 += R1
03 5 0 0202 ; 06 ; R5 *= 2 (смещение в первой матрице)
00 6 5 0100 ; 08 ; R6 ← 0x100(R5)
20 5 1      ; 10 ; R5 ← R1
03 5 0 0204 ; 11 ; R5 *= 4
21 5 2      ; 0D ; R5 += R2
03 5 0 0202 ; 0E ; R5 *= 2 (смещение во второй матрице)
10 6 5 0300 ; 10 ; R6 → 0x300(R5)
01 1 0 0200 ; 12 ; R1 += 1
05 1 0 0206 ; 14 ; R1 ? 3
83 0 0 0002 ; 16 ; R1 < 3? ⇒ 0002
01 2 0 0200 ; 18 ; R2 += 1
05 2 0 0204 ; 1A ; R2 ? 4
83 0 0 0001 ; 1C ; R2 < 4? ⇒ 0001
99 0 0      ; 1D ;

.code 0x200
00000001    ; 00 ; 1
00000002    ; 02 ; 2
00000004    ; 04 ; 4
00000003    ; 06 ; 3

.enter 1 2 3 4 5 6 7 8 9 10 11 12

Программирование в машинных кодах и язык ассемблера

Цель: небессмысленно выполнить произвольную программу на ЭВМ

Задача: ввести готовую программу и данные в ОЗУ, запустить программу, получить данные

Недостатки программирования в кодах

Ассемблер

Цель: упростить программирование в машинных кодах

Задача: сделать более удобным представление программы/данных и их размещение в памяти, в том числе ввести именованные обозначения вместо чисел

Решение: написать программу в более удобной форме, после чего транслировать в формат, пригодный для непосредственного исполнения

Ассемблер: транслятор, преобразующий исходный текст некоторой программы из представления, удобного для человека, в машинные коды

Язык программирования, с которого происходит трансляция, называется языком ассемблера.

Свойства этого языка:

LecturesCMC/ArchitectureAssemblerProject/05_RegistersIndexing (последним исправлял пользователь FrBrGeorge 2024-09-05 19:25:53)