Создание пользовательского машинного кода с нуля

#machine-code

#машинный код

Вопрос:

Недавно я начал работать с дизайном на уровне логики как любитель, но теперь столкнулся с программным обеспечением, в котором я гораздо менее компетентен. Я завершил проектирование пользовательского 4-разрядного процессора в Logisim, свободно основанного на статье Этьена Сикарда «Очень простой микропроцессор». Теперь, когда он выполняет очень ограниченные функции, которые я в него встроил (сложение, логическое И, ИЛИ и XOR), без каких-либо более обнаруживаемых ошибок (скрещивание пальцев) Я столкнулся с проблемой написания программ для него. Logisim обладает функциональностью импорта скрипта с шестнадцатеричными числами в модуль RAM или ROM, чтобы я мог писать для него программы, используя свой собственный код микроинструкции, но с чего мне начать? Я буквально нахожусь на самом базовом возможном уровне разработки программного обеспечения и действительно не знаю, куда двигаться дальше. Есть хорошие предложения по ресурсам для изучения этого низкого уровня программирования или предложения о том, что я должен попробовать отсюда? Заранее большое спасибо, я знаю, что это, вероятно, не самый напрямую применимый вопрос, когда-либо задававшийся на этом форуме.

Комментарии:

1. Голосуйте за закрытие, потому что слишком широкое. Возможно, вас заинтересует nand2tetris.org/course.php

Ответ №1:

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

Очевидно, я бы выбрал b. Это потребует от вас немного переключить передачу и выполнить некоторое высокоуровневое программирование. То, что вы собираетесь написать, — это программа на ассемблере, которая выполняется на ПК и преобразует какой-нибудь простой язык ассемблера в ваш пользовательский машинный код. Сам ассемблер будет высокоуровневой программой, и как таковой, я бы рекомендовал написать его на высокоуровневом языке программирования, который хорош как для работы со строками, так и с двоичными файлами. Я бы порекомендовал Python.

По сути, вы хотите, чтобы ваш ассемблер мог читать в текстовом файле, подобном этому:

     mov a, 7
foo:
    mov b, 20
    add a, b
    cmp a, b
    jg foo
  

(Я только что придумал эту программу; это чепуха.)

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

Итак, я предлагаю вам:

  1. Создайте (на бумаге) язык ассемблера, который представляет собой простое письменное представление для каждого из кодов операций, поддерживаемых вашей машиной (возможно, вы уже сделали это),
  2. Изучите простой Python,
  3. Напишите скрипт на Python, который считывает по одной строке за раз ( sys.stdin.readline() ), определяет, какой это код операции и какие значения он принимает, и выводит соответствующий машинный код в стандартный вывод.
  4. Напишите некоторый ассемблерный код на вашем языке ассемблера, который будет выполняться на вашем процессоре.

Звучит как забавный проект.

Комментарии:

1. Очень полезно, но на самом деле это на шаг или два впереди того, на чем я сейчас нахожусь. Я уверен, что это будет полезно в будущем, но прямо сейчас меня действительно интересует просто что делать, а не как это сделать. Возможно, есть примеры того, как выполнять логические или математические операции в существующем ISA, которые я мог бы использовать, чтобы выяснить, что можно сделать в моем машинном коде. Оттуда я мог бы разработать дальнейшие улучшения, такие как сборка ассемблера или добавление дополнительных операций. Как вы упомянули, я разработал свой язык (пока только 6 операционных кодов и один — NOOP), но пытаюсь понять, что с ним делать. Еще раз спасибо!

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

Ответ №2:

Я сделал нечто подобное, что может показаться вам интересным. Я также создал с нуля свой собственный дизайн процессора. Это 8-разрядный многоцикловый RISC-процессор на базе архитектуры Harvard с инструкциями переменной длины.

Я начал в Logisim, затем закодировал все в Verilog, и я синтезировал это в FPGA.

Чтобы ответить на ваш вопрос, я создал простой и рудиментарный ассемблер, который переводит программу (инструкции, т. е. мнемонику данные) на соответствующий машинный язык, который затем может быть загружен в память программы. Я написал его в shell script и использую awk, что меня вполне устраивало.

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

Вы можете увидеть весь проект, включая ассемблер, задокументированный здесь:https://github.com/adumont/hrm-cpu

Ответ №3:

Поскольку ваш набор команд настолько мал и основан на потоке из ответа mguica, я бы сказал, что следующий шаг — продолжить и / или полностью протестировать ваш набор команд. у вас есть флаги? у вас есть инструкции по переходу. На данный момент просто вручную сгенерируйте машинный код. Флаги — сложная штука, в частности бит overflow (V). Вы должны изучить перенос и выполнить на сумматоре msbit, чтобы все получилось правильно. Поскольку набор команд достаточно мал, вы можете попробовать различные комбинации последовательных инструкций, за которыми следует or и за которыми следует xor, за которыми следует add, или за которыми следует and или за которыми следует xor и т.д. И смешивание в ветвях. вернемся к флагам, если xor и или, например, не касаются переноса и переполнения, то убедитесь, что вы видите, что перенос и переполнение равны нулю и не затронуты логическими инструкциями, а перенос и переполнение равны единице и не затронуты, а также независимо показывают, что перенос и переполнение являются отдельными, один на один выключены, не затронуты logical и т.д. убедитесь, что все условные ветви работают только с этим одним условием, ведут в различные условные ветви с битами флага, которые игнорируются в обоих состояниях, гарантируя, что условная ветвь их игнорирует. Также убедитесь, что если условная ветвь не должна их изменять, то она этого не делает. аналогично, если условие не вызывает ветвления, то условные флаги не затрагиваются…

Мне нравится использовать рандомизацию, но это может потребовать больше работы, чем вам нужно. Мне нравится самостоятельно разрабатывать программный симулятор набора команд, который, как я нахожу, проще в использовании, поскольку логику также иногда проще использовать при пакетном тестировании. затем вы можете рандомизировать некоторый короткий список инструкций, изменяя инструкцию и регистры, естественно, протестировать тестировщик вручную, вычисляя некоторые результаты, как состояние регистров после завершения теста, так и состояние битов флага. Затем сделайте этот рандомизированный список длиннее, в какой-то момент вы можете взять длинный список команд и запустить его в симуляторе логики и посмотреть, выдает ли логика те же результаты регистрации и биты флага, что и симулятор набора команд, если они отличаются, выясните, почему. Если не попробовать другую случайную последовательность, и еще одну. Заполнение регистров простыми числами перед началом теста — очень хорошая идея.

вернемся к тестированию отдельных инструкций и флагов, проходящих через все угловые случаи 0xFFFF 0x0000 0xFFFF 1, подобные вещи размещаются по обе стороны от и справа от операндов и результатов, которые находятся на расстоянии одного отсчета от того, где флаг изменяется в точке, где флаг изменяется, и только с другой стороны от этого. для логических элементов, например, если они используют флаг нуля, то имеют различные шаблоны данных, которые проверяют результаты, которые находятся по обе стороны от нуля 0x0000, 0xFFFF 0xFFFE 0x0001 0x0002 и т.д. И равны нулю. Возможно, в результате также появятся ходячие 0x0001, 0x0002, ox0004 и т.д.

надеюсь, я понял ваш вопрос и не указал на очевидное или на то, что вы уже сделали до сих пор.