Как написать X86_64 _assembler_? - PullRequest
5 голосов
/ 19 июня 2010

Цель: я хочу написать ассемблер X86_64. Примечание: помечено как вики сообщества

Справочная информация: я знаком с C. Я уже писал сборку MIPS. Я написал некоторую сборку x86. Однако я хочу написать ассемблер x86_64 - он должен выводить машинный код, к которому я могу перейти, и начать выполнение (как в JIT).

Вопрос: как лучше всего подойти к этому? Я понимаю, что эта проблема выглядит довольно большой для решения. Я хочу начать с базового минимального набора:

  • Загрузить в регистр
  • Арифметические операции с регистрами (только целые числа - это хорошо, пока не нужно связываться с FPU)
  • 1012 * Conditionals *
  • Прыгает

Просто базовый набор, чтобы сделать его полным по Тьюрингу. Кто-нибудь сделал это? Предложения / ресурсы?

Ответы [ 2 ]

8 голосов
/ 19 июня 2010

Ассемблер, как и любой другой «компилятор», лучше всего писать как лексический анализатор, подающий в процессор грамматики языка.

Язык ассемблера, как правило, проще, чем обычные скомпилированные языки, так как вам не нужно беспокоиться о конструкциях, пересекающих границы строк, и формат обычно является фиксированным.

Я написал ассемблер для (вымышленного) процессора около двух лет назад для образовательных целей, и он в основном рассматривал каждую строку как:

  • необязательный ярлык (например, :loop).
  • операция (например, mov).
  • операнды (например, ax,$1).

Самый простой способ сделать это - убедиться, что токены легко различимы.

Вот почему я сделал правило, что метки должны были начинаться с : - это значительно облегчило анализ строки. Процесс обработки строки был:

  • убрать комментарии (сначала ; вне строки до конца строки).
  • Извлечь этикетку, если имеется.
  • первое слово - это операция.
  • остальные операнды.

Вы можете легко настаивать на том, что разные операнды также имеют специальные маркеры, чтобы сделать вашу жизнь проще. Все это предполагает, что у вас есть контроль над форматом ввода. Если вам необходимо использовать формат Intel или AT & T, это немного сложнее.

Я подошел к этому так, что была вызвана простая функция для каждой операции (например, doJmp, doCall, doRet), и эта функция определяла, что разрешено в операндах.

Например, doCall допускает только цифры или метки, doRet ничего не позволяет.

Например, вот фрагмент кода из функции encInstr:

private static MultiRet encInstr(
    boolean ignoreVars,
    String opcode,
    String operands)
{
    if (opcode.length() == 0) return hlprNone(ignoreVars);
    if (opcode.equals("defb"))  return hlprByte(ignoreVars,operands);
    if (opcode.equals("defbr")) return hlprByteR(ignoreVars,operands);
    if (opcode.equals("defs"))  return hlprString(ignoreVars,operands);
    if (opcode.equals("defw"))  return hlprWord(ignoreVars,operands);
    if (opcode.equals("defwr")) return hlprWordR(ignoreVars,operands);
    if (opcode.equals("equ"))   return hlprNone(ignoreVars);
    if (opcode.equals("org"))   return hlprNone(ignoreVars);

    if (opcode.equals("adc"))   return hlprTwoReg(ignoreVars,0x0a,operands);
    if (opcode.equals("add"))   return hlprTwoReg(ignoreVars,0x09,operands);
    if (opcode.equals("and"))   return hlprTwoReg(ignoreVars,0x0d,operands);

Функции hlpr... просто принимают операнды и возвращают байтовый массив, содержащий инструкции. Они полезны, когда многие операции имеют схожие требования к операндам, такие как adc , add and и все они требуют двух операндов регистра в приведенном выше случае (второй параметр контролирует, какой код операции был возвращен для инструкции).

Делая типы операндов легко различимыми, вы можете проверить, какие операнды предоставляются, являются ли они допустимыми и какие последовательности байтов генерировать. Разделение операций на их собственные функции обеспечивает хорошую логическую структуру.

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

Для того, чтобы правильно создать код в ЦП, который допускает инструкции переменной длины, лучше всего делать это в два прохода.

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

6 голосов
/ 19 июня 2010

Не отговаривать вас, но уже есть много сборщиков с различными прибамбасами.Пожалуйста, рассмотрите возможность участия в существующем проекте с открытым исходным кодом, например elftoolchain .

...