Лекция 3

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

Сегодня поговорим про регистры.

Регистр 1 зачастую неявно используется в псевдоинструкциях, так что использовать его следует с аккуратностью.

Кроме того, на уровне архитектуры несколько отличаются от остальных регистров регистр 0 (в нём хранится нестираемый ноль) и регистр 31 (он используется в операции перехода).

Регистры (их 32 штуки) для удобства программиста поименованы.

Регистр 1 (at)

Если вы не знаете в точности, как работает используемая вами инструкция, не используйте в программе at - в нём часто хранятся временные значения.

Регистры 2 и 3 (v0 и v1) используются для системных вызовов (как для хранения номера системного вызова перед непосредственно вызовом, так и для возврата результата).

Регистры 4 - 7 (a0 - a7) используются для передачи параметров системного вызова.

Регистры типа t, 8 - 15, 24 и 25 (t0 - t7, t8, t9) - temporary, временные; не стоит рассчитывать, что после вызова подпрограммы содержимое этих регистров не изменится.

В отличие от них, регистры типа s, 16 - 23, гарантированно (согласно ABI) должны быть восстановлены к исходным (перед вызовом) значениям после завершения вызова подпрограммы.

k0, k1 - для случаев, когда ваша программа внезапно попадет в режим работы ядра (ваша программа вообще не должна трогать эти регистры, они для ядра).

Регистр 31 - для адреса возврата из подпрограммы

Регистр 29 - stack pointer, указатель на вершину стека

Регистр 30 - frame pointer - регистр кадра (сюда иногда сбрасывается stack pointer, мы об этом поговорим позже)

Регистр 28 - global pointer - через него передаются данные при запуске программы

Большинство из указанных выше функций различных регистров - части ABI (application binary interface), физически почти все регистры одинаковы. Однако следование ABI позволяет сделать программирование на языке ассемблера заметно более удобным.

Регистра флагов в MIPS нет.

Регистры HI и LO - используются в командах деления и умножения; из них, по сути, данные напрямую можно только извлекать

Плоская модель памяти.

См. табличку на странице занятия - она красивая и информативная

С точки зрения программы эта память однородная, с точки зрения архитектуры - не совсем

1. С адреса 0 по примерно 4 MB - зарезервированная область

обращение сюда вызывает исключение

Это нулевая страница памяти

Одна из задач этой штуки - если вы разыменовываете (как указатель) какое-то небольшое число, это наверняка ошибка, и должно быть вызвано исключение

2. Далее - область программного кода - примерно 1/16 часть памяти

Изначально MIPS подразумевала раздельное хранение кода и данных, причём в область кода ничего нельзя было писать, а читать только по адресам, кратным 4

Инструкции типа j используют 26 бит, и больше вам как раз и не нужно (вы как раз можете адресовать всю доступную память)

3. Далее - область данных - примерно половина адресуемых в 32-разрядной модели 4 GB

Здесь находятся стек и куча (heap), которые вы используете для хранения данных вашей программы

heap - для динамических данных

Навстречу куче растёт стек (куча растёт снизу вверх, стек растёт сверху вниз)

4. Все старшие 2 GB памяти - область ядра (область кода и область данных)

Эта область недоступна обычным программам

5. Верхний кусочек памяти (MMIO) - эти адреса вообще не соответствуют никакой оперативной памяти в принципе

Эти адреса соответствуют внешним устройствам

Как с ней работать - зависит от конкретных устройств

В MIPS есть виртуальная память, но, к сожалению, в эмуляторе её нет, поэтому продемонстрировать её потом придётся отдельно

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

Плюс нет инструкций, с которыми непонятно в момент декодирования машинного слова, что именно должно быть сделано

Область статических данных очень маленькая: предполагается, что данные большего размера вы не будете адресовать вручную; обычно если уж у вас есть большой объём данных, вы его подгружаете из некоего файла в динамическую память

Директивы - специальные команды ассемблера

.data - для задания переменных (.word, .half и т.д.)

В MARS можно выбрать, какой endian использовать

Процессор MIPS имеет программный переключатель endianness

По умолчанию используется little-endian

Метки: произвольный идентификатор + двоеточие

Далее на метку можно ссылаться

Наличие псевдоинструкций приводит к несколько менее эффективному коду

Выравнивание.

Адрес каждой переменной должен быть кратен 4, поэтому если мы будем при объявлении переменных смешивать .word, .byte, .half, у нас будут оставаться "дырки" в памяти

.align - позволяет выполнить выравнивание вручную (0 - байт, 1 - полуслово, 2 - слово, 3 - двойное слово)

В принципе, вы можете перемешивать секции .text и .data

В секции .data можно указать адрес, с которого нужно производить размещение:

.data 0x10010100

Директиву .asciiz мы пока не рассматриваем просто потому, что ещё не говорили о соответствующем системном вызове

Адресация в секции кода:

попытка перехода на адрес, не кратный четырём, вызывает ошибку

Ещё одна особенность MIPS - для некоторых случаев просто нет подходящих простых команд (прибавить -1 проще, чем вычесть 1)

Пример цикла: TODO

Массивы - просто некая область памяти, адресуемая через смещение от начала массива

LecturesCMC/ArchitectureAssembler2019/03_RegistersMemory/conspect (последним исправлял пользователь RomanKrivonogov 2019-03-15 19:36:58)