Ассемблер RISC Lab
На уровнях «RISC Lab» (часть 5 курса) вкладка «Ассемблер» работает иначе, чем на уровнях 18–29. Теперь это настоящее подмножество RV32I: 32 регистра, 32-битные машинные слова, форматы R/I/S/B/U/J. Каждая строка программы собирается в слово, которое можно сверить с эталоном побитно, а с 54-го уровня — ещё и исполнить по тактам на эталонном ядре.
Ассемблер растёт вместе со схемой: соберёте на уровне новую команду — ассемблер её выучит. Так и задумано: если команда ещё не изучена, ошибка «неизвестная команда» — не поломка. Проверьте, с какого уровня она доступна.
Общий вид программы
# комментарий — всё после # игнорируется
loop: # метка — имя и двоеточие
addi x5, x0, 3 # команда и операнды через запятую
addi x5, x5, -1 # числа: десятичные или 0x…
bne x5, x0, loop # переходы — по метке
.end # останов — обязательная строка
- Одна инструкция на строку; пустые строки и строки-только-комментарии пропускаются.
- Комментарии начинаются с
#(наравне с ним работают//и;в шаблонах уровней). - Регистры:
x0…x31и имяra(≡ x1).x0всегда читается как 0. - Числа: десятичные (
-5) и шестнадцатеричные (0x1F). - Строка
.endобязательна — без неё сборка даётERR_ASM_NO_END. В середине программы она тоже допустима: слово останова встанет на своём адресе, а разбор продолжится дальше.
Этапы роста: какая команда с какого уровня
| Уровень | Новые команды | Новые псевдо |
|---|---|---|
| 51 | add sub sll slt sltu xor srl sra or and, addi slti sltiu xori ori andi slli srli srai | li, mv, nop |
| 52 | lw, sw | — |
| 53 | beq | — |
| 54 | bne | — |
| 55 | lui | li учится большим константам (lui + addi) |
| 56 | jal, jalr | j, ret |
На уровне 51 вместо схемного упражнения есть «Слово из полей»: соберите машинное слово из полей команды и сравните его с ассемблером побитно — так видно, где в слове живёт каждый операнд.
Форматы команд
Каждая команда — одно 32-битное слово. Поля в слове раскладываются по одному из шести форматов:
| Формат | Команды | Пример записи |
|---|---|---|
| R | арифметика регистр-регистр | add x7, x5, x6 |
| I | addi slti sltiu xori ori andi slli srli srai, lw, jalr | addi x5, x0, 7, lw x6, 4(x5) |
| S | sw | sw x7, 8(x5) |
| B | beq, bne | beq x5, x6, done |
| U | lui | lui x10, 0xFFFFF |
| J | jal | jal ra, sum |
Для памяти пишется пара «смещение(регистр)»: lw x6, 0(x5) читает слово по адресу x5 + 0. Смещение — от −2048 до 2047, у lui — от 0 до 0xFFFFF.
Псевдоинструкции
Псевдо — удобные сокращения, которые ассемблер превращает в настоящие команды:
| Псевдо | Во что превращается |
|---|---|
li x5, 7 | addi x5, x0, 7; большое число с 55-го уровня — lui + addi (2 слова) |
mv x6, x5 | addi x6, x5, 0 |
nop | addi x0, x0, 0 |
j loop | jal x0, loop |
ret | jalr x0, 0(ra) |
.end | jal x0, 0 — слово останова |
li с большим числом занимает два слова — адреса меток ниже сдвигаются на 4. На уровнях до 55 большое li даёт ERR_ASM_IMM_RANGE.
Переходы и метки
- Метка — имя из латинских букв, цифр и
_, начинающееся не с цифры; объявляется строкой видаимя:. - Смещение
beq/bne/jalсчитается в байтах от адреса самой команды перехода до метки. Его вычисляет ассемблер, вам нужен только адрес метки. - Цель перехода обязана быть чётной (адреса идут с шагом 4, поэтому нечётная цель — признак опечатки в метке): нечётная даёт
ERR_ASM_UNALIGNED_TARGET. - Диапазон
beq/bne— от −4096 до +4094 байт, уjal— ±1 МБ.
Ошибки сборки
Ошибки показываются под редактором с номером строки. Текст объясняет, что проверить:
| Код | Что случилось и что делать |
|---|---|
ERR_ASM_SYNTAX | Строку не удалось разобрать — проверьте формат: команда и операнды через запятую. |
ERR_ASM_UNKNOWN_MNEMONIC | Неизвестная команда — проверьте написание и этап: команда могла ещё не появиться. |
ERR_ASM_UNKNOWN_PSEUDO | Псевдо ещё не изучалась — напишите её через обычные команды. |
ERR_ASM_UNKNOWN_REG | Неверное имя регистра — используйте x0…x31 или ra. |
ERR_ASM_IMM_RANGE | Число не влезает в команду — проверьте диапазон (у addi — от −2048 до 2047, у lui — 0…0xFFFFF). |
ERR_ASM_UNALIGNED_TARGET | Метка стоит на нечётном адресе — переходить можно только на чётный адрес. |
ERR_ASM_UNDEF_LABEL | Метка не найдена — проверьте, что она объявлена и написана одинаково. |
ERR_ASM_DUP_LABEL | Метка с таким именем уже есть — имена должны быть уникальными. |
ERR_ASM_NO_END | Нет строки .end — добавьте её в конец программы. |
ERR_ASM_EMPTY | Программа пустая — напишите код в редакторе. |
Ассемблер собирает все ошибки сразу, а не только первую — исправляйте сверху вниз и пересобирайте кнопкой «Собрать» (Ctrl+Enter).
Исполнение с 54-го уровня
С уровня 54 кнопка «Собрать» не только строит слова, но и исполняет программу на эталонном одноцикловом ядре: одна команда — один такт. В блоке «Исполнение» видны регистры, счётчик тактов и трасса команд. Ошибки времени исполнения (невыровненный адрес, чтение из окна команд, выход за окно памяти) замораживают машину с кодом ошибки — они описаны в справке вкладки «Исполнение».
.end— останов: такта не добавляет, счётчик замирает.- Адреса памяти данных — окно
0x1000…0x13FF, шаг адреса дляlw/sw— ровно 4. - LED — адрес
0xFFFF_F000, только запись (появляется с 59-го уровня); тумблеры —0xFFFF_F004, только чтение.
Пример: цикл с bne (уровень 54)
# x5 = 3; повторить тело 3 раза
addi x5, x0, 3 # x5 = 3 — счётчик
loop:
addi x5, x5, -1 # x5 -= 1
bne x5, x0, loop # пока x5 != 0 — назад на метку
.end
Последний bne исполняется, но прыжка не делает: x5 уже 0. Цикл из N повторений стоит N×(тело + 1) тактов.
Пример: база данных через lui (уровень 55)
# читаем слово из массива данных по адресу 0x1000
lui x10, 0x1 # x10 = 0x1000 — база массива
lw x6, 0(x10) # x6 = первое слово массива
.end
lui кладёт число в старшие 20 бит: lui x10, 0x1 даёт 0x1000, потому что значение сдвигается на 12 бит влево.
Смежные материалы
Частые вопросы
Чем ассемблер RISC Lab отличается от ассемблера уровней 18–29?
Это настоящее подмножество RV32I: 32 регистра, 32-битные слова, форматы R/I/S/B/U/J и настоящие смещения переходов в байтах. Список команд растёт вместе с уровнями: каждый новый уровень схемы добавляет ассемблеру команды.
Что означает .end в конце программы?
Это останов. Ассемблер превращает .end в слово jal x0, 0 — прыжок на самого себя. Процессор исполняет его и замирает, счётчик тактов больше не растёт.
Почему команда не собирается, хотя написана правильно?
Ассемблер знает только команды уже пройденных уровней. Например, beq появляется на уровне 53, bne — на 54, lui — на 55. До этого уровня они дают ошибку ERR_ASM_UNKNOWN_MNEMONIC или ERR_ASM_UNKNOWN_PSEUDO.