Пилим восьмибитный процессор. Часть пятая: ассемблер, тесты, запуск.

Mar 05, 2013 12:40

11:42 05.03.2013

Пилим восьмибитный процессор. Часть пятая: ассемблер, тесты, запуск.

Небольшое предисловие, которое должно было быть в самой первой статье по этой теме:
Я понятия не имею, как правильно проектировать процессоры. И как неправильно - тоже. Я просто играюсь в конструктор, делаю ошибки, перекраиваю схемы на лету и наслаждаюсь этим забавным и увлекательным процессом.
Перечитывая предыдущие части, я нахожу в них заметное количество косяков и откровенных глупостей. Скажем, я так и не определился с архитектурой процессора, вроде сначала задумав сделать что-то RISC-подобное, но временами замахиваясь на CISC, и огребая от сложности реализации откатывался обратно. В итоге же получается, что я во многом копирую единственную знакомую мне архитектуру x86.
Поэтому: эти статьи рассказывают о том, как делаю я, а не о том, как надо.

Но окей, перейду к делу.
Поскольку теперь у нас есть контроллер памяти и минимально необходимые для функционирования ядра схемы, нужно попытаться собрать их все вместе и оттестировать. Пока набор инструкций крайне мал, тестирование процессора можно производить так: подать ему на вход все возможные наборы инструкций и посмотреть, как он их обработает. При этом следует учесть, что большая часть ошибочных инструкций не отлавливается - подай мы на вход несуществующую команду или несовместимый набор параметров - процессор поведёт себя непредсказуемо. Но отлов ошибок оставляю на потом, сейчас важнее и интереснее проверить работу на корректном наборе инструкций.
Итак, как выглядит вся схема в сборе:



Не очень-то впечатляет =). Но если раскрыть схемы, и представить всё это добро вместе, получится вот так:



Клик для увеличения


Я не стал раскрывать все подсхемы (в которых, в свою очередь, есть ещё подсхемы) - по-моему и так внушительно. Даже Шмигирилову показать не стыдно.
Несколько комментариев к схеме.
- Выход Execute end сделан на будущее. В дальнейшем он будет сигнализировать о том, что процессор выполнил очередь команд и ждёт новых инструкций. Пока же все инструкции выполняются за один так, так что смысла в подобной сигнализации нет.
- Выход ERR - это зачатки системы контроля ошибок. Сейчас на него подаётся сигнал в единственной ситуации: если попытаться работать с памятью на ввод и вывод одной инструкцией (пример: MOV [addr],[addr]).
- В процессе отладки (который я ещё буду описывать) была найдена незначительная ошибка: в блоке команд не стояло несколько согласующих резисторов, отчего невозможны были операции с памятью. Эта ошибка присутствует на схемах, приведённых в предыдущей статье.
- Индикаторы, если кто ещё не догадался, показывают значения регистров и предназначены только для увеличения удобства.
- Память и ядро работают на одной частоте, несмотря на то, что тактовые генераторы у них разные. Теоретически, ничто не должно мешать работать им на разных частотах (если память будет работать быстрее - нужно будет ввести простенький механизм ожидания выполнения), но logisim не позволяет задавать разные частоты двум элементам.

Пожалуй, можно переходить к тестированию, которое, как решено выше, должно заключаться в передаче процессору всех вариантов корректных команд. Такой набор можно сгенерировать вручную, а можно - и даже нужно - написать ассемблер, переводящий читаемые команды в машинный код.
На реализацию этого ассемблера на PHP ушло около часа, побаловаться можно здесь Meighty assembler (название "Meighty" у меня получилось из объединения слов might и eight, так я и назвал свой процессор). Команды вводить в чёрное окошко, жать compile. При удачной трансляции слева от окна появится двоичный байткод, снизу - его шестнадцатеричное представление, которое можно копипастить в logisim (буквально, его компонент ОЗУ корректно воспримет вставку таких данных), при ошибке выдастся описание проблемы.



Клик для увеличения
Ассемблер имеет intel-like синтаксис, регистр значения не имеет, все лишние пробелы игнорируются. Числовые значения задаются только в шестнадцатеричной системе счисления, десятичная и двоичная, возможно, будут сделаны потом. Операнды перечисляются через запятую, и задаются следующим образом:
- константа: значениеH, например 10H, FDH, 7h;
- регистр: идентификатор регистра от A до H;
- порт: @номер портаH, например @03H, @88h;
- адрес в памяти: [адресH], например [12h], [FFH], [0H];
Ассемблер пытается отлавливать ошибки, как синтаксические, так и логические, можете поиграться. Комментарии не реализованы. Порты, как понимаете, тоже отсутствуют, выполнение команд чтения-записи для них смысла не имеет.
Пример программы (абсолютно бессмысленной, её цель только продемонстрировать работу):
MOV A,10h
MOV B,A
SUM B,3h
MOV [20h],B
MOV C,[20h]
SHL [20h],2h
XOR A,[20h]
NOP
и т.д.
Меня КРАЙНЕ ломает делать гифку, которая продемонстрировала бы, что эта, да и все другие допустимые программы, выполняются корректно. Гифкоклепание займёт не меньше часа, так что предлагаю просто скачать logisim, взять мою схему и посмотреть всё самим.

Что дальше? Примерно вот что:
- реализация восьми дополнительных служебных регистров вдобавок к восьми имеющимся, которые останутся пользовательскими. Служебные регистры будут предназначены, прежде всего, для организации работ программ в памяти (требуется разделять код и данные - значит нужны аналоги CS и DS) и возможного её увеличения (для адресации за пределами двух килобайт можно ввести сегментацию, тогда понадобится регистр, указывающий на текущий сегмент), а также для выполнения команд условного перехода (команда будет сравнивать два определённых регистра), ну и давно задуманный флаговый регистр. Возможно - счётчик (это будет зависеть от того, как я реализую циклы).
- стек. Его я, как и было задумано ранее, всё-таки реализую на отдельной схеме - пусть будет хоть какое-то значимое отличие от x86. На реализацию четырёх стековых команд (стандартные POP, PUSH, не очень стандартная PEEK и совсем не стандартная POKE) понадобится всего один опкод (т.к. параметр у этих команд будет всегда один, два неиспользуемых бита можно отдать под команду).
- команды условного и безусловного перехода.
- дополнительные команды обработки данных (SUB, MUL, DIV, NOT).
- доработка ассемблера.
- отлов и обработка ошибок.
- ASCII-терминал.

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

read more at Журнал Великого Позитроника

rss2lj

Вера Игоревна одобряет этот пост, it, трансляция из blogger

Previous post Next post
Up