Ассемблер 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                       # останов — обязательная строка

Этапы роста: какая команда с какого уровня

УровеньНовые командыНовые псевдо
51add sub sll slt sltu xor srl sra or and, addi slti sltiu xori ori andi slli srli sraili, mv, nop
52lw, sw—
53beq—
54bne—
55luili учится большим константам (lui + addi)
56jal, jalrj, ret

На уровне 51 вместо схемного упражнения есть «Слово из полей»: соберите машинное слово из полей команды и сравните его с ассемблером побитно — так видно, где в слове живёт каждый операнд.

Форматы команд

Каждая команда — одно 32-битное слово. Поля в слове раскладываются по одному из шести форматов:

ФорматКомандыПример записи
Rарифметика регистр-регистрadd x7, x5, x6
Iaddi slti sltiu xori ori andi slli srli srai, lw, jalraddi x5, x0, 7, lw x6, 4(x5)
Sswsw x7, 8(x5)
Bbeq, bnebeq x5, x6, done
Uluilui x10, 0xFFFFF
Jjaljal ra, sum

Для памяти пишется пара «смещение(регистр)»: lw x6, 0(x5) читает слово по адресу x5 + 0. Смещение — от −2048 до 2047, у lui — от 0 до 0xFFFFF.

Псевдоинструкции

Псевдо — удобные сокращения, которые ассемблер превращает в настоящие команды:

ПсевдоВо что превращается
li x5, 7addi x5, x0, 7; большое число с 55-го уровня — lui + addi (2 слова)
mv x6, x5addi x6, x5, 0
nopaddi x0, x0, 0
j loopjal x0, loop
retjalr x0, 0(ra)
.endjal x0, 0 — слово останова

li с большим числом занимает два слова — адреса меток ниже сдвигаются на 4. На уровнях до 55 большое li даёт ERR_ASM_IMM_RANGE.

Переходы и метки

Ошибки сборки

Ошибки показываются под редактором с номером строки. Текст объясняет, что проверить:

КодЧто случилось и что делать
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 кнопка «Собрать» не только строит слова, но и исполняет программу на эталонном одноцикловом ядре: одна команда — один такт. В блоке «Исполнение» видны регистры, счётчик тактов и трасса команд. Ошибки времени исполнения (невыровненный адрес, чтение из окна команд, выход за окно памяти) замораживают машину с кодом ошибки — они описаны в справке вкладки «Исполнение».

Пример: цикл с 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.