Опубликован: 19.01.2025 | Доступ: свободный | Студентов: 0 / 0 | Длительность: 05:57:00
Лекция 6:

Лабораторная работа №5. Режимы адресации

< Лекция 5 || Лекция 6 || Лекция 7 >

5.1 Цель и задачи

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

Для достижения поставленной цели требуется решить следующие задачи:

  1. Изучить возможные способы адресации.
  2. Ознакомиться с основными типами машинных команд RISC-V.

Презентация к блоку "Ассемблер RISC-V"

5.2. Основные теоретические сведения

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

Далее каждый из способов адресации (доступа к данным) будет пояснен на примере операции add как одной из самых простых и интуитивно понятных. Эти объяснения обобщаются на все соответствующие типы инструкций.

5.2.1 Регистровая адресация

При регистровой адресации (Рис. 5.1) регистры используются для всех операндов-источников и операндов-назначений (иными словами - для всех операндов и результата). Все инструкции типа R используют именно такой режим адресации.

Регистровая адресация

Рис. 5.1. Регистровая адресация

Пример регистровой адресации:

add rd,rs1,rs2    	# rd = rs1 + rs2

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

При непосредственной адресации (Рис. 5.2) в качестве операндов наряду с регистрами используют константы (непосредственные операнды). Этот режим адресации используют некоторые инструкции типа I, такие как сложение с 12-битной константой (addi) и логическая операция andi.

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

Рис. 5.2. непосредственная адресация

Пример непосредственной адресации:

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.

Базовая адресация

Рис. 5.3. Базовая адресация

Пример базовой адресации:

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, поэтому тот адрес, куда будет осуществлен переход, называют адресом относительно счетчика команд.

Адресация относительно счетчика команд

Рис. 5.4. Адресация относительно счетчика команд

Инструкции перехода по условию (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. Задание к лабораторной работе

Напишите четыре программы на ассемблере, использующие различные режимы адресации.

  1. Регистровая адресация. Сложить 2 константы n1=511 и n2=-10, заданные с помощью директивы .equ. Константы разместить в регистрах t0 и t1, а результат сложения в регистре t2.
  2. Непосредственная адресация. Переслать константу n1=511 в регистр t0 и выполнить логическую операцию И с константой -8. Результат операции сохранить в регистре t1.
  3. Базовая адресация. Сложить n целых чисел sum = a[0] + a[1] + a[2] + ... + a[n-1], результат записать в память.

    Регистровый файл

    X1 Addr of a[i]
    X2 n
    X3 sum
    X10 100

    Память

    0 a[0]
    4 a[1]
    …. ….
    a[n-1]
    100 0
    104 n
    108 sum
  4. Адресация относительно счетчика команд. Найти c = max(a, b)

    Регистровый файл до выполнения программы

    a0 a
    a1 b

    Регистровый файл после выполнения программы

    a0 c

Каждую программу скомпилируйте программу и отладьте. Представьте результат в машинном коде.

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

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. Вопросы для контроля

  1. Что такое регистровый файл?
  2. В чем отличие регистров и основной памяти?
  3. Объясните методы адресации, используемые в RISC-V?
  4. Какие другие методы адресации Вы знаете? Почему в RISC-V они не используются
  5. В чем отличие команд перехода jal и jalr?
< Лекция 5 || Лекция 6 || Лекция 7 >