Работа с внешними устройствами и MMIO
Долги за прошлую лекцию
Имитация исключений
С одной стороны, на все случаи жизни исключения предусмотреть нельзя.
С другой стороны, это очень эффективный механизм обращения к ОС почти (или совсем) без дополнительных действий, в отличие от syscall. Системный обработчик по адресу 0x80000180 вызывается немедленно одной инструкцией, атомарно с операцией сравнения.
teq $t1,$t2 |
Trap if equal |
Trap if $t1 is equal to $t2 |
teqi $t1,-100 |
Trap if equal to immediate |
Trap if $t1 is equal to sign-extended 16 bit immediate |
tge $t1,$t2 |
Trap if greater or equal |
Trap if $t1 is greater than or equal to $t2 |
tgei $t1,-100 |
Trap if greater than or equal to immediate |
Trap if $t1 greater than or equal to sign-extended 16 bit immediate |
tgeiu $t1,-100 |
Trap if greater or equal to immediate unsigned |
Trap if $t1 greater than or equal to sign-extended 16 bit immediate, unsigned comparison |
tgeu $t1,$t2 |
Trap if greater or equal unsigned |
Trap if $t1 is greater than or equal to $t2 using unsigned comparision |
tlt $t1,$t2 |
Trap if less than |
Trap if $t1 less than $t2 |
tlti $t1,-100 |
Trap if less than immediate |
Trap if $t1 less than sign-extended 16-bit immediate |
tltiu $t1,-100 |
Trap if less than immediate unsigned |
Trap if $t1 less than sign-extended 16-bit immediate, unsigned comparison |
tltu $t1,$t2 |
Trap if less than unsigned |
Trap if $t1 less than $t2, unsigned comparison |
tne $t1,$t2 |
Trap if not equal |
Trap if $t1 is not equal to $t2 |
tnei $t1,-100 |
Trap if not equal to immediate |
Trap if $t1 is not equal to sign-extended 16 bit immediate |
Таким образом гарантируется, что за время, прошедшее между сравнением и обращением к системе, никакие данные не изменятся (потому что этого времени нет ).
Назначение инструкций типа trap:
- Отладочный останов программы (т. н. assertion). Допустим, в регистре не должно получаться 0, а если ошибочно получится, то лучше пускай система обработает эту ошибку, чем программа будет дальше считать. Например, можно проверить, что изменение счётчика в цикле вообще происходит:
teqi $t1 0 subu $t0 $t0 $t1 bgez $t0 loop
- Обработка исключительной ситуации, если аппаратного обнаружения её нет. Обычно используется команда R-типа, в которой можно в неиспользуемом аппаратно поле хранить причину исключения. Ср. архитектура RISCore:
TEQ - Ловушка по равенству
31
25
20
15
5
SPECIAL 000000
rs
rt
code
TEQ 110100
Содержимое поля code игнорируется системой и может быть использовано для кодированиия информации для системного ПО. Для получения этой информации программа должна загрузить слово команды из памяти (адрес хранится в регистре EPC сопроцессора 0). В ассемблере Mars эта возможность отсутствует, обидно.
Свойства макроассемблера MARS
Замечания авторов MARS относительно их макроассемблера:
Макроопределение должно идти в тексте программы до соответствующей макрокоманды (иначе пришлось бы анализировать текст дважды)
Макроопределение локально в пределах одного файла. Это с очевидностью вытекает из самого процесса макроподстановки перед трансляцией. Если нужно, чтобы один и тот же макрос был виден из нескольких файлов, используйте .include
Вложенные макросы не поддерживаются, т. е. внутри макроопределения не может встречаться директива .macro
- Внутри макроопределения, как и в тексте программы, могут встречаться только ранее определённые макрокоманды, искать их определения далее по тексту никто не будет
Все метки меняются в процессе макроподстановки, превращаясь в метка_M№
(Замечание от меня: нет, на все! если передать метку в качестве параметра, а потом написать что-то вроде %label: , _M№ к ней не припишется. Не знаю, как и зачем это можно использовать…)
- Несколько макроса с одинаковым именем, но разным количеством параметров, считаются различными, и их можно использовать все
- Повторное определение макроса с тем же именем и тем же количеством параметров игнорируется, макрокоманда раскрывается в первое определение
Параметром макроса (в силу ограниченной реализации) может быть только атомарная лексема языка ассемблера. Например, параметром не может быть "4($t0)", потому что это две лексемы, а не одна
- Макросредства ассемблера не входят ни в какой стандарт и остаются на усмотрение авторов ассемблера
В больших многофайловых проектах принято все макросы складывать в отдельный файл и включать их в код программы с помощью директивы .include файл_с_макросами . Подпрограммы при этом складываются в другой файл (возможно. не один), т. н. «библиотеку», и подключаются посредством многофайловой сборки. На предыдущем примере: Файл с программой prog.asm:
.include "macro.inc" .globl main .text main: input "First input" $t0 input "Second input" $t1 print "First result" $t0 print "Second result" $t1 exit
Файл с подпрограммами lib.asm:
.globl _print _input .text _input: # $a0 — message / $v0 — input value li $v0 4 syscall li $v0 5 syscall jr $ra _print: # $a0 — message, $a1 — number li $v0 4 syscall move $a0 $a1 li $v0 1 syscall li $a0 10 li $v0 11 syscall jr $ra
На забываем метки всех подпрограмм, которые понадобятся в других файлах, объявлять как .globl Файл с макросами macro.inc (имя файла не заканчивается на .asm в знак того, что его не нужно транслировать отдельно):
.macro input %msg %reg .data msg: .ascii %msg .asciiz ": " .text la $a0 msg jal _input move %reg $v0 .end_macro .macro print %msg %reg .data msg: .ascii %msg .asciiz ": " .text la $a0 msg move $a1 %reg jal _print .end_macro .macro exit li $v0 10 syscall .end_macro
Чего нет в Mars
Макроассемблер Mars вполне достаточен для учебных целей, но не реализует много из того, что есть в промышленных средствах программирования на ассемблере
Библиотека макросов и подпрограмм. Чтобы написать большую программу, потребуется множество подпрограмм, реализующих стандартные приёмы работы — ввод-вывод, работа с дисками, управление внешними устройствами и т. п. Для этого в профессиональных инструментариях, типа NASM или Gnu Assebmler, имеются заранее подготовленные библиотеки макросов и подпрограмм. А мы пишем их сами
- «Настоящий» макропроцессор. Макропросессор в Mars опирается только на лексемы языка ассемблера и не имеет собственного языка. Это почти не мешает, но временами (как в примере с 4($t0) ) слегка неудобно
Адресная арифметика. Вычисление некоторых значений. смещений и размеров на основании уже известных адресов находится в ассемблере Mars в зачаточном состоянии.
Переменные периода трансляции. полезно уметь назначать мнемонические имена результатам таких вычислений, чего Mars делать тоже не умеет
Например SIZE=ArrEnd-Arr, и потом использование константы SIZE
⇒ Вычисление выражений с использованием переменных и констант. В промышленных макропроцессорах возможно вычисление любых арифметических выражений и задание констант для них. Ещё раз напомним, что всё это происходит до трансляции, и в оттранслированный код попадает результат таких вычислений
Условная трансляция. Наиболее полезное свойство вычислений в период трансляции — это возможность транслировать или не транслировать части текста в зависимости от результата этих вычислений. Например можно вставить исходный текст отладочные сообщения, но транслировать их только если определена некоторая переменная периода трансляции DEBUG. Как-нибудь так:
.if DEBUG код, транслируемый только если существует константа DEBUG .endif
- Генерация макроопределений. Если разрешить создавать макросы внутри макросов, можно развёртывать целые семейства определений в зависимости от исходного параметра внешнего макроса
Конкатенация.. Иногда необходимо, чтобы результат постановки нескольких макросов интерпретировался затем как одна лексема языка (например, строка label##suffix##index превращалась при наличии констант suffix=_M и index=5 в label_M5). В Mars такого механизма нет
- Бывает очень полезно ограничить видимость меток сильнее, чем просто внутри файла. Например, если в файле задано несколько подпрограмм, в каждой из них хотелось бы иметь возможность использовать метки типа start, finish, loop или стандартные имена переменных. Это можно было бы сделать, введя особенный синтаксис временных меток или ограничить видимость меток специальной конструкцией «локальное пространство имён» и т. п. В целом макроассемблер Mars достаточен для написания программ среднего объёма, а написание действительно крупных проектов на языке ассемблера выходит за рамки данного курса