Лекция 2
Предполагаем, что с двоичной и шестнадцатеричной системами счисления все знакомы.
Сегодня поговорим уже собственно про архитектуру MIPS.
Идея MIPS - эффективная реализация в железе плюс система команд, достаточная для написания систем общего назначения.
Эти же люди придумали RISC (?)
Команды должны были быть максимально просты и эффективны, всё остальное программист напишет сам, особенно если: 1. он знает, на каком железе работает; 2 он знает, какие алгоритмы для его задач подходят больше.
В архитектуре самого процессора отсутствуют вычислительно сложные операции (например, в MIPS-32 любая команда занимает одно машинное слово, 4 байта)
В отличие от архитектур из начала 80-ых, где экономили всё-всё-всё, в MIPS регистровой памяти должно (по идее) хватать на всё, что потребуется (в разумных пределах)
Сразу 32 регистра общего назначения (РОН) - это никак не влияет на быстродействие, но делает вычисления удобнее
Общее свойство RISC-архитектур: память считается медленным устройством (и это правда так), и чтобы результирующий код получился эффективным, команды взаимодействия с памятью ограничиваются командами выгрузки/загрузки в память, а все вычислительные операции проводятся на регистрах.
С моей точки зрения, особенность MIPS - не только в аппаратной специфике, но и в том, на кого она ориентирована (но это неточно).
Поясняю: Архитектура MIPS принципиально трёхоперандная (используется ещё термин "трёхадресная", но, строго говоря, он не совсем корректен). При этом с учётом того, что вычислительные операции не используют память, мы ничего не теряем
Память команд и память данных разделены (отсылка к Гарвардской архитектуре?). Это можно рассматривать как нарушение одного из принципов архитектуры фон Неймана, однако программисты избегают самомодифицирующийся код многие десятилетия, так что это не важно.
Сама система команд MIPS и некоторые её особенности оптимизированы под конвейеризацию.
В практике программирования на MIPS используется термин псевдоинструкция - инструкция, обозначающая некоторую полезную операцию, но с точки зрения реализации являющаяся некоторым "хаком"
В MIPS есть регистр ноль, в котором всегда лежит ноль
Таким образом, можно заключить, что создатели MIPS считали, что язык MIPS-ассемблера должен быть удобен, в первую очередь, для написания людьми
Насколько удобен для чтения код для MIPS-ассемблера, настолько же неудобен для чтения полученный машинный код
В MIPS не используются флаги (регистра флагов даже нет, атомарные операции и так хорошо работают, и счёт производится быстрее)
32-разрядное машинное слово
Т.к. размер инструкций фиксирован, мы всегда знаем, в каких битах что лежит
См. слайд с описанием полей
Даже одного машинного слова из 32 битов на команду - это много
Ни одно из функциональных полей машинного слова не кратно 4
R-тип команд - команды, работающие с регистрами
Отдельный тип команд - Immediate, где Immediate - это некое число, которое играет роль... числа
J-тип команд
В поле target подразумевается наличие ещё двух битов в конце (которые всегда имеют значение ноль, ведь адресовать менее 4 байт у нас не выйдет)
См. слайд с арифметическими командами
Знаковые и беззнаковые операции отличаются вызовом исключения при переполнении (что довольно неожиданно): в простой операции сложения может возникнуть исключение, а в беззнаковой - нет
Т.е. мы не работаем с какими-то специальными данными, знаковыми или беззнаковыми; просто есть два типа сложения, учитывающие знак или не учитывающие (сделали ошибку в последнем - сами виноваты)
В ассемблере MIPS общий вид команды - куда, откуда, что
Проблемы с умножением: 1. операция долгая 2. в знаковом умножении знать знак числа важно 3. переполнение - для этого в MIPS придумали регистры HI и LO (обратите внимание, что операция умножения здесь - двухоперандная, третий операнд не используется)
Вы даже сами определиться, что вам дальше делать с полученными числами
Есть отдельные команды переноса полученных значений из регистров HI и LO в rd - MFHI (move from HI), MFLO (move from LO)
В MIPS, несмотря на отсутствие регистра флагов, иногда всё-таки нужно проводить некоторые условные операции
ПРОВЕРИТЬ ИНФОРМАЦИЮ ПРО КОМАНДУ MOVN Всё-таки в ней destination на втором месте
Есть команда ИЛИ-НЕ (см. слайды с командами)
Я сейчас рассказываю про все эти команды, чтобы вы примерно представляли, с чем мы примерно будем работать; всё равно в процессе выполнения домашних заданий с этим придётся разбираться более подробно и детально, а в MARS-е есть подробный help со всеми командами
В MARS-е обязательно сохраняйте код, иначе кнопка запуска программы будет работать некорректно
Область кода всегда начинается с адреса 0x00400000
В лекции см. пример разбора команды в машинном коде
По моему мнению, разработчики MIPS не предполагали, что бинарный код будут читать нормальные люди
LB rt, offset(rs) - в такой команде используется косвенная адресация (опять же, см. слайды)
Относительно команд перехода:
J target, JR (jump с return-ом)
Работа с памятью возможна со словами, с полусловами, с байтами
Адреса во всех случаях должны быть кратными (выравнивание)
В MIPS нельзя положить что-либо 4-байтное по не кратному 4 адресу
Одни из немногих нарушений принципа регистров общего использования - r0 и rd
Команды условного перехода имеют уже другой вид (BEQ, BNE, BLEZ,..)
Обратите внимание, среди этих команд нет инструкций сравнения двух регистров на больше-меньше
Но есть сравнение на равенство и сравнение с нулём
Операция сравнения двух значений - это 2 операции
Поскольку у нас АЛУ одно, мы не можем за одно действие сравнить два числа (сначала нужно одно из другого вычесть, потом сравнить с нулём)
Расскажу всё-таки про псевдоинструкции
См. в лекции пример программы (некоторые формы операции li - load immediate - псевдоинструкции)
Поговорим о том, как писать домашние задания MARS нас обеспечивает очень простыми системными вызовами
Будем решать один тип задач - ввели числа, обработали, вывели
Для ввода числа в регистр v0 нужно положить число 5 (это номер системного вызова) и произвести системный вызов:
li $v0 5 syscall
Для вывода числа - в a0 положить число, в v0 положить 1, произвести системный вызов
Пример:
li $a0 42 li $v0 1 syscall
Для завершения программы:
li $v0 10 syscall