= Общая структура системы команд MIPS = [[https://moodle.cs.msu.ru/mod/lesson/view.php?id=1864|базовая лекция Moodle]] * Принципы 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 регистр, число`: {{attachment:li.png}} * Нет никакой ''инструкции'' «положить I-число в регистр», зато есть инструкция «сложить I-число с `$0` (с нестираемым нулём) и положить результат в регистр» * Если число в `li` больше 16 битов, псевдоинструкция раскладывается в две: 1. записать старшую половину большого числа (это I-число) в старшую половину регистра (младшая при этом обнуляется), 1. побитово добавить (OR) младшую половину большого числа в регистр * Соответственно, программисту не надо в уме прикидывать, влезает ли `число` в 16 битов, и распиливать его, если не влезает * это — наиболее эффективные способы реализации команды `li регистр, число` * Обратите внимание на использование '''$1''' для хранения промежуточных данных * переходы: короткие (типа '''I''') и длинные (типа '''J''') * Короткие — условные * сравнения бываюто только на = / ≠ и с нулём, остальное — (например, сравнение ''двух'' регистров на >/<) — псевдоинструкции: {{attachment:ble.png}} * (''повторение'' :) ) непосредственно указываемый адрес в командах типа I и J усекается на 2 бита справа, потому что они всё равно всегда равны 0, ибо адрес команды всегда кратен 4 * Пример программы для Mars * будем использовать пока что магические системные вызовы ввода и вывода десятичных чисел, находящихся в регистре * Ввести два числа, вывести результат: {{{#!highlight nasm li $v0 5 # Системный вызов №5 — ввести десятичное число syscall # Результат — в регистр $v0 (он же $2) move $t0 $v0 # Сохраняем результат в $t0 (он же $8) li $v0 5 # $v0 нам нужен для ввода ещё одного числа syscall add $a0 $v0 $t0 # Складываем ввод с сохранённым, записываем в $a0 (он же $4) li $v0 1 # Системный вызов №1 — вывести число из регистра $a0 syscall li $v0 10 # Системный вызов №10 — останов программы syscall }}} == Д/З == * [[Python/PsyPython2018/EjudgeRegistration|Зарегистрироваться в EJudge]], если кто не * <>