Общая структура системы команд MIPS
- Принципы RISC:
- отсутствие вычислительно сложных инструкций,
- фиксированная длина инструкции,
- большое количество регистров общего назначения,
- ограничения на работу непосредственно с оперативной памятью как с медленным устройством
- …и их реализация в MIPS:
- + отсутствие дублирующих инструкций, псевдоинструкции
- + трёхадресность,
- + разделение памяти данных и команд,
- + оптимизация под конвейер (см. далее)
- …
удобство чтения/написания инструкций ассемблера и неудобство чтения машинного кода человеком (упаковка битов, псевдоинструкции и т. п.)
- т. н. исключения — нормальное состояние программы, а не ошибка
- некоторые регистры более специальные, чем другие:
$0 всегда равен 0, $31 используется для подпрограмм и т. п.
- Организация системы команд MIPS32:
(на лекции не перечисляются все команды, даются только примеры, подбор команд по таблице — это ДЗ)
32 регистра общего назначения, доступа к специализированным регистрам нет (в т. ч. нет регистра флагов, даже на аппаратном уровне!)
- 3 базовых типа команд
биты слова
31…26
25…21
20…16
15…11
10…6
5…0
6
5
5
5
5
6
R-Тип (Register)
op
rs
rt
rd
sa
funct
I-Тип (Immediate)
op
rs
rt
immediate
J-Тип (Jump)
op
target
op — код операции (6 битов)
rs — № регистра-источника (5 битов)
rt — № регистра-опреанда (для команд типа I — регистра-назначения) (5 битов)
immediate — непосредственный знаковый операнд (16 битов), используется для логических операндов, арифметических знаковых операндов, для смещений в адресе загрузки/сохранения, для команд условного ветвления («близкого» перехода)
target — адрес перехода (26 битов); в действительности это 28 битов без последних 2, т. к. адрес перехода всегда кратен 4 (то же верно и для «близкого» перехода, 18 битов вместо 16)
rd — № регистра-назначения (5 битов)
sa — величина сдвига для команд побитового сдвига регистра (5 битов достаточно )
funct — поле функции (6 битов), используется для разных команд, у которых код операции одинаковый. Например, все команды типа R, кроме работающих с сопроцессором, имеют opcode=0 (SPECIAL), а различаются полем funct. По-видимому, для эффективной реализации R-команд в конвейере удобнее не декодировать опкод, а по-быстрому сравнить его с нулём, и получать значения регистров, параллельно декодируя функцию, чтобы потом её применить.
- Знаковая и беззнаковая арифметика; регистры LO/HI для умножения/деления; побитовые операции
- Для некоторых операций (например, сложения) «знаковость» означает, что при переполнении будет возникать исключение, а «беззнаковость» — что не будет.
- Условная пересылка и сравнение
сравнение только с 0 или = / ≠, сравнение на > / < двух регистров потребовало бы двух арифметических операций!
Работа с памятью (в т. ч. псевдоинструкции типа li)
Суть понятия псевдоинструкции на примере li регистр, число:
Нет никакой инструкции «положить I-число в регистр», зато есть инструкция «сложить I-число с $0 (с нестираемым нулём) и положить результат в регистр»
Если число в li больше 16 битов, псевдоинструкция раскладывается в две:
- записать старшую половину большого числа (это I-число) в старшую половину регистра (младшая при этом обнуляется),
- побитово добавить (OR) младшую половину большого числа в регистр
Соответственно, программисту не надо в уме прикидывать, влезает ли число в 16 битов, и распиливать его, если не влезает
это — наиболее эффективные способы реализации команды li регистр, число
Обратите внимание на использование $1 для хранения промежуточных данных
переходы: короткие (типа I) и длинные (типа J)
- Короткие — условные
сравнения бываюто только на = / ≠ и с нулём, остальное — (например, сравнение двух регистров на >/<) — псевдоинструкции:
(повторение ) непосредственно указываемый адрес в командах типа I и J усекается на 2 бита справа, потому что они всё равно всегда равны 0, ибо адрес команды всегда кратен 4
- Короткие — условные
- Пример программы для Mars
- будем использовать пока что магические системные вызовы ввода и вывода десятичных чисел, находящихся в регистре
- Ввести два числа, вывести результат:
1 li $v0 5 # Системный вызов №5 — ввести десятичное число 2 syscall # Результат — в регистр $v0 (он же $2) 3 move $t0 $v0 # Сохраняем результат в $t0 (он же $8) 4 li $v0 5 # $v0 нам нужен для ввода ещё одного числа 5 syscall 6 add $a0 $v0 $t0 # Складываем ввод с сохранённым, записываем в $a0 (он же $4) 7 li $v0 1 # Системный вызов №1 — вывести число из регистра $a0 8 syscall 9 li $v0 10 # Системный вызов №10 — останов программы 10 syscall
Д/З
Зарегистрироваться в EJudge, если кто не
EJudge: DoubleSum 'Двойная сумма'
Ввести в столбик четыре целых числа. Первое сложить со вторым, третье — с четвёртым. Вывести две полученные суммы в строчку, без пробелов.
12 23 -23 7
12 23 -23 7
35-16