Лабораторная работа №5. Режимы адресации
5.1 Цель и задачи
Целью работы является разработка простой программы (программ) преобразования данных для приобретения практических навыков программирования на языке ассемблера и закрепления знаний по режимам адресации.
Для достижения поставленной цели требуется решить следующие задачи:
- Изучить возможные способы адресации.
- Ознакомиться с основными типами машинных команд RISC-V.
Презентация к блоку "Ассемблер RISC-V"
5.2. Основные теоретические сведения
В данном практикуме адресация понимается как способ доступа к данным в рамках той или иной управляющей инструкции. Поскольку ассемблерные программы имеют мало выразительных синтаксических средств, то способы работы с данными могут существенно отличаться от программ на языке высокого уровня. В архитектуре RISC-V используются четыре режима адресации: регистровый, непосредственный, базовый и относительно счетчика команд. Первые три режима (регистровый, непосредственный и базовый) определяют способы чтения и записи операндов. Последний режим (относительно счетчика команд) определяет способ записи счетчика команд.
Далее каждый из способов адресации (доступа к данным) будет пояснен на примере операции add как одной из самых простых и интуитивно понятных. Эти объяснения обобщаются на все соответствующие типы инструкций.
5.2.1 Регистровая адресация
При регистровой адресации (Рис. 5.1) регистры используются для всех операндов-источников и операндов-назначений (иными словами - для всех операндов и результата). Все инструкции типа R используют именно такой режим адресации.
Пример регистровой адресации:
add rd,rs1,rs2 # rd = rs1 + rs2
5.2.2 Непосредственная адресация
При непосредственной адресации (Рис. 5.2) в качестве операндов наряду с регистрами используют константы (непосредственные операнды). Этот режим адресации используют некоторые инструкции типа I, такие как сложение с 12-битной константой (addi) и логическая операция andi.
Пример непосредственной адресации:
addi rd,rs1,12 # rd = rs1 + 12 andi rd,rs1,-8 # rd = rs1 & 0xFF8
Чтобы использовать константы большего размера, используйте инструкцию непосредственной записи в старшие разряды lui (load upper immediate), за которой следует инструкция непосредственного сложения addi. Инструкция lui загружает 20-битное значение сразу в 20 старших битах и помещает нули в младшие биты:
lui s2, 0xABCDE # s2 = 0xABCDE000 addi s2, s2, 0x123 # s2 = 0xABCDE123
При использовании многоразрядных непосредственных операндов, если указанный в addi 12-битный непосредственный операнд отрицательный, старшая часть постоянного значения в lui должна быть увеличена на единицу. Помните, что знак addi расширяет 12-битное непосредственное значение, поэтому отрицательное непосредственное значение будет содержать все единицы в своих старших 20 битах. Поскольку в дополнительном коде все единицы означают число -1, добавление числа, у которого все разряды установлены в 1, к старшим разрядам непосредственного операнда приводит к вычитанию 1 из этого числа. Пример иллюстрирует ситуацию, когда мы хотим в s2 получить постоянное значение 0xFEEDA987:
lui s2, 0xFEEDB # s2 = 0xFEEDB000 (число, которое нужно записать в старшие 20 разрядов (0xFEEDA), предварительно увеличено на 1)
addi s2, s2, ?1657 # s2 = 0xFEEDA987 (0x987 - это 12-битное представление числа -1657)(0xFEEDB000 + 0xFFFFF987 = 0xFEEDA987)
5.2.3 Базовая адресация
Инструкции для доступа в память, такие как загрузка слова(чтение памяти) (lw) и сохранение слова(запись в память) (sw), используют базовую адресацию (Рис. 5.3). Эффективный адрес операнда в памяти вычисляется путем сложения базового адреса в регистре rs1 и 12-битного смещения с расширенным знаком, являющегося непосредственным операндом. Операции загрузки (lw) - это инструкции типа I, а операции сохранения (sw) - инструкции типа S.
Пример базовой адресации:
lw rd,36(rs1) # rd = M[rs1+imm][0:31]
Поле rs1 указывает на регистр, содержащий базовый адрес, а поле rd указывает на регистр-назначение. Поле imm, хранящее непосредственный операнд, содержит 12-битное смещение, равное 36. В результате регистр rd содержит значение из ячейки памяти rs1+36
sw rs2,8(rs1) # M[rs1+imm][0:31] = rs2[0:31]
Инструкция сохранения слова sw демонстрирует запись значения из регистра rs2 в слово памяти, расположенное по адресу rs1+8
5.2.4. Адресация относительно счетчика команд
Инструкции условного перехода, или ветвления, используют адресацию относительно счетчика команд для определения нового значения счетчика команд (Рис. 5.4) в том случае, если нужно осуществить переход. Смещение со знаком прибавляется к счетчику команд (PC) для определения нового значения PC, поэтому тот адрес, куда будет осуществлен переход, называют адресом относительно счетчика команд.
Инструкции перехода по условию (beq, bne, blt, bge, bltu, bgeu) типа B и jal (переход и связывание) типа J используют для смещения 13- и 21-битные константы со знаком соответственно. Самые старшие значимые биты смещения располагаются в 12- и 20-битных полях инструкций типа B и J. Наименьший значащий бит смещения всегда равен 0, поэтому он отсутствует в инструкции.
beq rs1,rs2,imm # if(rs1 == rs2) PC += imm jal rd,imm #rd = PC+4; PC += imm
Инструкция jal может быть использована как для вызова функций, так и для простого безусловного перехода. Соглашение, используемое в RISC-V таково, что адрес возврата должен быть сохранён в адресе возврата ra ( x1).
Инструкция jal не имеет достаточного места для кодирования полного 32-битного адреса. Это означает, что вы не можете сделать переход куда-либо в коде, если ваша программа больше максимального значения смещения. Но если адрес перехода хранится в регистре, вы можете сделать переход на любой адрес (инструкция jalr типа I).
jalr rd,imm(rs1) # rd = PC + 4, PC = rs1 + imm
Большая разница состоит в том, что переход jalr не происходит относительно PC. Вместо этого он происходит относительно rs1. Для этого можно использовать addi для установки значения регистра rs1, но на практике поступают иначе. Для того, чтобы адрес был относителен к программному счётчику (РС) применяется специальная инструкция auipc. Инструкция auipc (Add Upper Immediate to Program Counter) типа U (сложить старшие разряды константы смещения с PC) также использует адресацию относительно счетчика команд.
auipc rd,imm # rd = PC + imm[31:12] << 12
Инструкция auipc является очень важной при работе с подпрограммами. Об этом подробнее будет рассказано "Лабораторная работа №6. Адресация элементов массива, организация цикла" .
5.3. Задание к лабораторной работе
Напишите четыре программы на ассемблере, использующие различные режимы адресации.
- Регистровая адресация. Сложить 2 константы n1=511 и n2=-10, заданные с помощью директивы .equ. Константы разместить в регистрах t0 и t1, а результат сложения в регистре t2.
- Непосредственная адресация. Переслать константу n1=511 в регистр t0 и выполнить логическую операцию И с константой -8. Результат операции сохранить в регистре t1.
- Базовая адресация. Сложить n целых чисел sum = a[0] + a[1] + a[2] + ... + a[n-1], результат записать в память.
Регистровый файл
Память
- Адресация относительно счетчика команд. Найти c = max(a, b)
Регистровый файл до выполнения программы
Регистровый файл после выполнения программы
Каждую программу скомпилируйте программу и отладьте. Представьте результат в машинном коде.
В качестве варианта повышенной сложности, можно организовать работу всех четырех типов заданий в рамках одной программы.
5.3.1. Описание последовательности выполнения работы
Скомпилируйте исходную программу и убедитесь в ее работоспособности в RISC-V ОС, либо через qemu-riscv64.
С помощью утилиты objdump дизассемблируйте бинарный файл программы
5.3.2. Пример выполнения задания на защиту
.text start: .global _start n1 EQU 511 n2 EQU -10 # Регистровая адресация addi t0, zero, n1 addi t1, zero, n2 add t2, t0, t1 # Непосредственная адресация addi t0, zero, n1 andi t1, t0,-8 # Базовая адресация # s10=100 lw s2, 0x0(s10) lw s3, 0x4(s10) add s4, zero, zero loop: lw s5, 0x0(s2) add s4, s4, s5 addi s2, s2, 4 addi s3, s3, -1 bnez s3, loop sw s4, 0x8(s10) #Адресация относительно счетчика команд max: blt a0, a1, second # if a0 < a1 then a1 is larger jal zero, done second: add a0, zero, a1 # make a1 the return value done:
5.4. Вопросы для контроля
- Что такое регистровый файл?
- В чем отличие регистров и основной памяти?
- Объясните методы адресации, используемые в RISC-V?
- Какие другие методы адресации Вы знаете? Почему в RISC-V они не используются
- В чем отличие команд перехода jal и jalr?