Цель: Изучение основ работы ассемблера, создание простой программы генерации кода виртуального процессора. 1. Краткие теоретические сведения В общих случаях предложения языка Ассемблера состоят из следующих компонент: метка или имя; мнемоника; операнды; комментарии. Метка или имя является необязательным компонентом. Не во всех языках Ассемблеров эти понятия различаются. Если они различаются (например, MASM), то метка - точка программы, на которую передается управление, следовательно, метка стоит в предложении, содержащем команду; имя - имя переменной программы, ячейки памяти, следовательно, имя стоит в предложении, содержащем псевдокоманду резервирования памяти или определения константы. В некоторых случаях метка и имя могут отличаться даже синтаксически, так, в MASM/TASM после метки ставится двоеточие, а после имени - нет.
Написать программу генерации машинных команд. Входной файл содержит мнемоники команд и их аргументы, а также метки. Выходной файл – полный отчет о распознании (смотри пример ниже).
Первый в строке идентификатор с помощью бинарного поиска (смотри лабораторную работу номер 1) проверяется на принадлежность командам.
Если идентификатор не найден среди команд, происходит поиск среди имен (меток). Если имя не найдено там, оно добавляется в таблицу имен, иначе это ошибка - повторное определение. Для хранения имен необходимо использовать перемешанные таблицы (хеширование) – смотри лабораторную работу номер 2.
Если идентификатор найден среди команд, то из таблицы команд берем шаблон распознания аргументов для этой команды (число). В зависимости от шаблона ищем далее во входной строке имена регистров, константы или имена меток. Шаблоны, которые необходимо реализовать (как минимум):
Имя шаблона | Распознание аргументов |
---|---|
Арифметическая команда (RRR) | Регистр, Регистр, Регистр |
Обращение в память – база+смещение (RRC) | Регистр, Регистр, 16-битовая константа |
Команда сдвига (RRS) | Регистр, Регистр, 5-битовая константа |
Сравнение с нулем и переход (RM) | Регистр, Метка |
Сравнение двух регистров и переход (RRM) | Регистр, Регистр, Метка |
Действие | Команда | Шаблон |
---|---|---|
Сложить (регистры) | ADD | RRR |
Сложить с иммедиатой | ADDI | RRC |
Вычесть (регистры) | SUB | RRR |
Вычесть иммедиату | SUBI | RRC |
Умножить | MUL | RRR |
Загрузить 1-байтовое число | LD1 | RRC |
Загрузить 4-байтовое число | LD4 | RRC |
Перейти если регистр равен нулю | BEQZ | RM |
Перейти если равны два регистра | BEQ | RRM |
Перейти если меньше два регистра | BLT | RRM |
Сдвиг влево | SHL | RRS |
Сдвиг вправо | SHR | RRS |
Сохранить как 1-байтовое число | ST1 | RRC |
Сохранить как 4-байтовое число | ST4 | RRC |
Вызвать функцию | CALL | RM |
После успешного распознания аргументов ассемблерной команды, необходимо построить двоичное представление для машинной инструкции.
Для примера покажем как это выглядит в простейшей системе команд типа RISC, где одна команда – одно целое число.
биты машинной инструкции
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
код операции | регистр | регистр | регистр | не используем (ноль) | |||||||||||||||||||||||||||
код операции | регистр | регистр | сдвиг | не используем (ноль) | |||||||||||||||||||||||||||
код операции | регистр | регистр | константа | ||||||||||||||||||||||||||||
код операции | регистр | константа |
В процесс распознания необходимо проверять выход констант за допустимые пределы (согласно двоичного представления для машинной инструкции): регистры – от r0 до r31, сдвиг – от 0 до 31, константа – от -32768 до 32767. Место, предназначенное для меток, не заполняем (оставляем для второго прохода ассемблера).
Например: ADD r5,r20,r14
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
код ADD (6) | 5 | 20 | 14 | 0 |
Результат: 6*226+5*221+20*216+14*211
SUBI r5,r12,1230
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
код SUBI (16) | 5 | 12 | 1230 |
Результат: 16*226+5*221+12*216+1230*20
Получившееся число нужно вывести в 16-ичном и двоичном виде.
Например: Для входного файла
ADD r5,r12,r30
QWERTY
MUL r5,r12,r30
DIV r5,r12,r30
MYLABEL
SUBI r5,r12,1230
XOR r5,r12,r30
AND r5,r12,r30
OR r5,r12,r30
LDD r5,r12,5030
BEQ r15, MYLABEL
Результат мог бы выглядеть так
ADD keyword, opcode=6
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x13452689 или 1000101… (все 32 бита)
ОК!
QWERTY new name, hash=24
MUL keyword, opcode=2
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
DIV keyword, opcode=18
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
MYLABEL new name, hash=13
ОК!
SUBI keyword, opcode=16
шаблон = REG,REG,IMM
первый регистр - 5
второй регистр - 12
константа – 1230
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
XOR keyword, opcode=26
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
AND keyword, opcode=11
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
OR keyword, opcode=31
шаблон = REG,REG,REG
первый регистр - 5
второй регистр - 12
третий регистр – 30
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
LDD keyword, opcode=12
шаблон = REG,REG,IMM
первый регистр - 5
второй регистр - 12
константа – 5030
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!
BEQ r15, MYLABEL
шаблон = REG,LABEL
первый регистр - 5
метка – есть метка
Результат:0x3DАF4568 или 1000101… (все 32 бита)
ОК!