Arduino Nano - эмулятор компьютера под управлением ОС CP/M

В далекие 90-е годы у меня был "бытовой" (как он тогда назывался) компьютер "Байт" - брестский клон популярнейшего в те времена на просторах СНГ ZX Spectrum 48K. Но на нем невозможно было запустить операционную систему CP/M, бывшую де-факто стандартом для восьмибитных компьютеров (в середине 1980-х годов эта ОС запускалась на свыше 300 моделях микрокомпьютеров). И вот я решил восполнить это упущение и создать свой компьютер под управлением ОС CP/M 2.2 на базе ... Arduino Nano 3.0 (с микроконтроллером ATmega328)!

Подобные проекты часто именуются "homebrew computer" и технологии их построения весьма разнообразны - от электромагнитных реле до FPGA.

cpm4nano Mk IV

Схема:
cpm4nano emulator i8080

Внешний вид прототипа:
cpm4nano эмуляция 8080

В процессе его создания я решил две задачи:

  • создал симулятор процессора Intel 8080, а также реализовал эмуляцию оперативной памяти и дисковой системы;
  • адаптировал к созданному устройству ОС CP/M;
  • реализовал вывод текстовой информации на экран телевизора;
  • разработал преобразователь PS/2-UART для подключения клавиатуры.

GitHub
Исходный код
(текущая версия 0.4) (распространяется по лицензии GPL 3.0) и доступен в репозитарии GitHub - https://github.com/Dreamy16101976/cpm4nano

Конфигурация системы

Минимальная конфигурация:
Arduino Nano 3.0;
SD-карточка с адаптером;
преобразователь USB-UART для связи через последовательный порт;
дополнительные аксессуары - компьютер, используемый в качестве терминала.

Стандартная конфигурация:
Arduino Nano 3.0;
SD-карточка с адаптером;
преобразователь USB-UART для связи через последовательный порт или преобразователь PS/2-UART для подключения клавиатуры;
один или несколько чипов FM24C256;
пассивный адаптер для подключения телевизора через композитный вход;
дополнительные аксессуары - PS/2-клавиатура и телевизор с композитным входом.

Термины "эмулятор" и "симулятор" часто употребляются как синонимы. Вот интересная статья в Wikipedia с объяснением этих терминов - https://en.wikipedia.org/wiki/Emulator.

Симулятор процессора i8080

Я создал симулятор для Arduino Nano, способный воспроизводить все команды процессора i8080.
При этом под i8080 я понимаю  все семейство  X8080Y, не имевшее программных отличий.

Мой симулятор представляет собой интерпретатор, последовательно выполняющий команды микропроцессора i8080.

Kelly Smith Test
Этот тест был разработан в 1980 году и тестирует большую часть флагов, режимов и инструкций (за исключением операций ввода-вывода, обработки прерываний, также тестируются не все постусловия инструкций).
Эмулятор проходит проверку с помощью теста MICROCOSM ASSOCIATES 8080/8085 CPU DIAGNOSTIC VERSION 1.0 (C) 1980 ("Kelly Smith Test"):
MCTEST

От ввода команды MCTEST до выдачи сообщения об успешном окончании теста проходит время около 18 секунд.

8080PRE
Также мой эмулятор процессора 8080 успешно прошел предварительный тест 8080PRE.COM от Ian Bartholomew:
8080PRE.COM

CPUTEST
Мой симулятор успешно прошел тест DIAGNOSTICS II V1.2 - CPU TEST от SUPERSOFT ASSOCIATES:
Supersoft Diagnostics CPU Test

Быстродействие симулятора

Я оценил длительность выполнения некоторых команд в симуляторе (в миллисекундах):

NOP -  0,35 (4 такта, экв. частота 11 кГц);
INR A - 0,35 (5 тактов, экв. частота 14 кГц);
INX B - 0,35 (5 тактов, экв. частота 14 кГц);
MOV A, M - 0,66 (7 тактов, экв. частота 11 кГц);
MVI A, d8 - 0,70 (7 тактов, экв. частота 10 кГц);
LHLD a16 -
1,60 (10 тактов, экв. частота 6 кГц).

Эмуляция оперативной памяти

Главная проблема в этом проекте - нехватка оперативной памяти, которой в ATmega328 только два килобайта! Для эмуляции оперативной памяти размером несколько десятков Кбайт я использую микросхемы FRAM и четырехгигабайтную SDHC-карточку, подключенную к Arduino Nano через переходник.

SD RAM

кэширование в эмуляторе

Для "общения" с карточкой используется шина SPI:
MISO - прием данных от карточки (D12)
MOSI - передача данных карточке (D11)
SCK - тактовый сигнал для карточки (D13)
SS - выбор устройства (D10)
3V3 - питание 3,3 В
GND - "земля"

ICSP-разъем Arduino Nano:
ICSP Arduino

Я подключил microSD-карточку посредством специального адаптера:
адаптер для SD-карточки

Для индикации доступа к карте я подключил красный светодиод с балластным резистором между выводами 3V3 (анод светодиода) и SS (катод светодиода).

Для ускорения работы с эмулируемой памятью я использую кэш в ОЗУ Arduino Nano, состоящий из восьми линий, каждая размером 64 байта.
Для хранения содержимого оперативной памяти я выделил на карточке 2048 секторов от 0x000400 до 0x000BFF (в начале каждого 512-байтного сектора карточки размещаются байты, соответствующие одной линии кэша). В конце этой цепочки байт записывается байт LRC-контрольной суммы (выполняется операция "Исключающее ИЛИ" над всеми байтами цепочки), который считывается и проверяется при чтении данных с карточки:
LRC контрольная сумма

В качестве алгоритма кэширования я использую LRU (Least recently used). Использованные прежде данные вытесняются выше, к началу кэша - к линии 0, а самая нижняя линия кэша заполняется при промахе новыми данными. При попадании в кэш используемые данные смещаются ниже, обмениваясь позицией с находящимися там данными.

FRAM

Я использую две микросхемы FRAM (встречается сокращение и F-RAM) FM24C256-G:
FM24C256-G
Расшифровка обозначения чипа FM24C256-G:
FM - Cypress;
24 - I2C FRAM;
C - напряжение питания 4,5...5,5 В;
256 -  емкость 256 Кбит;
G - 8-выводный SOIC-корпус
Для удобства применения таких микросхем я применил
SOIC-DIP адаптеры:
SOIC-DIP адаптер

FRAM - это запоминающее устройство, построенное по сегнетоэлектрической (англ. ferroelectric) технологии. Микросхема FM24C256-G обладает емкостью 32 КБайта и подключается к шине I2C. Эта микросхема выпускается в SOIC-корпусе:
FM24C256-G
Назначение контактов:

Обозначение вывода Назначение вывода:
A0 бит 0 адреса
A1 бит 1 адреса
A2 бит 2 адреса
WP защита от записи
SDA данные/адрес (I2C)
SCL тактирование (I2C)
VDD напряжение питания 5 В
VSS "земля"

Подключение к Arduino Nano осуществляется так:

Вывод FRAM VDD SDA SCL VSS
Вывод Arduino Nano 5V A4 A5 GND

Схема подключения FRAM-памяти:
FRAM память I2C

Я использую два чипа FRAM, задав их адреса как
0x50 (все адресные линии "плавающие");
0x51 (адресная линия A0 подключена к выводу VDD, остальные адресные линии - "плавающие").

Тестирование быстродействия эмулируемой памяти

Я использовал тест последовательного и случайного доступа к ОЗУ.

Среднее время доступа при последовательном доступе к памяти составляет около 80 микросекунд.

Для генерации случайного адреса памяти я использую XOR-алгоритм генерации псевдослучайных чисел в диапазоне 0...65535 с начальным значением (seed) 0xABCD:

rnd ^= rnd << 2
rnd ^= rnd >> 7
rnd ^= rnd << 7

По результатам тестов время случайного доступа к эмулируемой картой оперативной памяти составило около 7 миллисекунд (при половинной скорости работы карты) и около 6 миллисекунд (при полной скорости).

Такое резкое различие при последовательном и случайном доступе обусловлено использованием системы кэширования.

Я использовал тест на Бейсике (в интерпретаторе TINYBASIC) для оценки влияния кэша на быстродействие в реальной задаче:
BASIC тест быстродействия
(размер линии кэша составляет 64 байта)

кол-во линий кэша время выполнения теста (секунды)
2 345
4 155
6 80
8 60

Тестирование памяти

Популярным способом проверки памяти является запись/чтение в ячейки памяти/из ячеек памяти сначала байтов 0x55 (01010101), а затем байтов 0xAA (10101010), либо байтов 0xA5 (10100101)  и 0x5A (01011010).
В статье "Ram Tests" (http://www.ganssle.com/articles/ramtest.htm) советуется записывать последовательно в ячейки памяти и проверять при чтении случайную последовательность из, например, 257 байт. Для ускорения теста можно писать/читать, например, каждую третью или пятую последовательность.
Я использую этот метод с длиной последовательности 33 - на единицу больше размера линии кэша - из байтов:

0x3D 0x55 0x5F 0x15 0x23    0x47 0x1C 0x31 0x48 0x60  
0x35 0x11 0x4F 0x2F 0x2E    0x14 0x20 0x5B 0x39 0x26  
0x09 0x61 0x34 0x30 0x50    0x2B 0x4B 0x0F 0x63 0x1F   0x10 0x1E 0x36

(случайная последовательность сгенерирована с помощью сервиса RANDOM.ORG).

Сначала я выполняю проход теста с исходной последовательностью, а затем - с инвертированной.

Я выполнил 7400 проходов теста памяти (с помощью команды монитора "M") - ошибок обнаружено не было:
тест памяти

Также успешно было пройден тест памяти UMPIRE.COM:
тест памяти UMPIRE

Переключение банков памяти

Я сделал эмуляцию устройства управления памятью (Memory Management Unit - MMU), осуществляющего переключение банков памяти.

Я разделил адресное пространство 0x0000...0xFFFF на 16 блоков (с номерами от 0 до 15) размером по 4 КБайта. Каждый блок может быть отображен в один из банков с восьмибитным номером.

Старший бит номера банка определяет тип устройства памяти:
0xxxxxxx - SD RAM;
1xxxxxxx - FRAM.

SD RAM содержит 4 банка памяти размером 64 КБайта каждый, размещенных на SD-карте (1 сектор содержит одну линию кэша - 64 байта).

FRAM может быть представлена чипами (от одного до восьми), I2C-адреса которых определяют их соответствие банкам памяти размером 32 КБайт каждый (размер банка равен адресуемому размеру памяти -  64 КБайта):

I2C -адрес 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57
Номер банка 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87

Соответствие банков блокам определяется номерами банков в наборе из 16 регистров.
Для задания номера банка для определенного блока используются две команды вывода в порт:

OUT D0, номер_блока
OUT D1, номер_банка

Также для переключения блока памяти X на банк памяти Y можно использовать команду монитора YXY.

Для определения текущего банка для определенного блока используется команда:

IN D0, номер_блока

Сохранение и загрузка образов памяти

Для записи образа памяти на карточку используется команда монитора Pxx, где xx - номер образа памяти (от 00 до 99), например, команда P01 записывает образ памяти с номером 01.

Для загрузки образа памяти с карточки используется команда монитора Uxx, где xx - номер образа памяти (от 00 до 99), например, команда U01 загружает образ памяти с номером 01.

Эмуляция консоли

Ввод/вывод через последовательный порт

Для связи Arduino Nano с компьютером через последовательный порт (для ввода-вывода команд и данных) я использую преобразователь USB-Serial и терминальную программу TerraTermTera Term (https://osdn.net/projects/ttssh2/releases/).
Для настройки последовательного порта в программе Tera Term предназначен пункт меню Serial -> Serial port...:
эмуляция микрокомпьютера
Последовательный порт должен настраиваться на скорость 115200 бод, 8 бит данных, без бита четности, 1 стоп-бит и аппаратное управление потоком:
эмулятор настройка

Для управления потоком используется вывод CTS преобразователя USB-Serial, который соединен с выводом D2 Arduino. "Низкий" уровень на этом выводе разрешает передачу данных от компьютера к эмулятору.

Я сделал эмуляцию коммуникационных портов в соответствии с архитектурой компьютера "Altair 8800" , созданного Micro Instrumentation Telemetry Systems (MITS) :

основной последовательный порт Arduino отображается одновременно на порты двух плат:
плата SIO-A (88SIO) (чип COM2502):
порт 0x00 (0): порт статуса (TTS) (R - бит 5 = 1 (0x20) - есть символы для ввода, бит 1 = 1 (0x02) - терминал готов к выводу символа)
порт 0x01 (1): порт данных  (TTI/TTO) (R/W)

первый порт платы 2SIO (88-2SIO) (чип EF6850 - 8-битный асинхронный адаптер коммуникационного интерфейса):
порт 0x10 (16): порт статуса (TTS) (R - бит 0 = 1 (0x01) - есть символы для ввода, бит 1 = 1 (0x02) - терминал готов к выводу символа)
порт 0x11 (17): порт данных  (TTI/TTO) (R/W)

Для настройки терминала в программе Tera Term предназначен пункт меню Serial -> Terminal...:

По умолчанию размер экрана терминала - 80 колонок, 25 строк:
терминал эмулятора

Вывод на экран телевизора

В качестве альтернативы выводу в последовательный порт я использую вывод на телевизионный экран через композитный вход.

Для вывода на ТВ используется таймер 1 и выводы D3 ... D9 Arduino.

Схема реализации вывода изображения на экран ТВ:
вывод на телевизор ATmega AVR

Формат вывода на экран ТВ - 20 строк x 45 колонок, шрифт 4 x 8.

Шрифт описан в виде битовых последовательностей - сначала первая (верхняя) строка для 256 символов, затем вторая и т.д.:
шрифт для вывода на телевизор
(шрифт описывается в старших 4 битах в инвертированном виде: 1 - черный пиксель, 0 - белый пиксель, младшие 4 бита должны быть равны 1).

Русские буквы и символы псевдографики (согласн RFC 1489) соответствуют кодировке КОИ-8 (KOI-8R):
...

Вот как выглядят символы шрифта на экране телевизора:
эмулятор вывод на телевизор

Пример снимка телевизионного экрана с выполненной командой DIR и тестами процессора:
эмуляция процессора Intel 8080

Видеовыход моего компьютера оснащен раъемом "тюльпан" RCA. Он подключается к композитному входу ("AV IN") телевизора - разъему "тюльпан" RCA ("VIDEO", желтый) или SCART:
генерация видеосигнала

Я использую вот такой кабель:
вывод на телевизор самодельного компьютера

При выводе информации на телевизор распознаются следующие ESC-последовательности:
ESC[nA - перемещение курсора на n позиций;
ESC[nB - перемещение курсора на n позиций;
ESC[nC - перемещение курсора на n позиций;
ESC[nD - перемещение курсора на n позиций;
ESC[r;cH - перемещение курсора в строку r колонку c;
ESC[nJ - стирание области дисплея;
ESC[nK - стирание части строки.

При выводе на экран телевизора позиция следующего символа указывается подстрочным "аппаратным" курсором:
cpm4nano вывод на телевизор

Страничный режим вывода на экран

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

Ввод через PS/2-клавиатуру

В качестве альтернативы вводу из "большого" компьютера через последовательный порт я использую ввод с клавиатуры, подключенной через преобразователь PS/2-UART:
PS/2 UART преобразователь STM8

Я сделал такой преобразователь на основе микроконтроллера STM8S103F3P6. К  PS/2-разъему этого преобразователя подключается клавиатура, оснащенная соответствующим разъемом, а выход преобразователя подключается к контактам UART Arduino Nano вместо преобразователя USB-UART.

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

Эмуляция дисководов

Я эмулирую поведение дисковода гибких дисков на уровне обращений чтения/записи к портам.

Порты контроллера дисковода/DMA:
порт 0xE0: порт статуса (R)/ команды (W)
порт 0xE1: порт  дорожки (R/W)
порт 0xE2: порт сектора (R/W)
порт 0xE3: порт выбора диска (R/W)
порт 0xE4: порт младшего байта адреса DMA-буфера (W)
порт 0xE5: порт старшего байта адреса DMA-буфера (W)

Для эмуляции дисководов гибких дисков (на дискете 77 дорожек с 26 128-байтными секторами на каждой = 2002 (0x7D2) сектора = 256256 байт = 250,25 Кбайт) я  использую ту же SDHC-карточку, выделив в ней области, начиная с сектора  0x0001000, для эмуляции 100 дискет (128-байтный сектор эмулируемой дискеты размещен в начале 512-байтного сектора карточки, для каждой дискеты отводится 0x1000 секторов).

Для записи образа дискеты на карточку используется команда монитора Nxxxxxxxx, где xxxxxxxx - номер сектора карточки, начиная с которого запиываются данные (в 16-ричной форме), например, команда N00001000 записывает образ дискеты с номером 00.

С помощью команды Z монитора я могу "вставить" дискету в любой из четырех дисководов A, B, C или D:
(например, команда ZB02 монтирует дискету с номером 02 в дисковод B)
эмуляция дисководов

Номера "вставленных" дискет запоминаются в EEPROM Arduino и восстанавливаются при повторном запуске эмулятора.
Дискету в дисководе можно отформатировать (заполнить байтами 0xE5) с помощью команды X монитора (например, XA - форматирование дискеты в дисководе A).

Файлы могут быть скопирован с одного диска на другой с помощью утилиты PIP:
например, PIP B:=A:TEST.COM копирует файл TEST.COM с диска A на диск B.

Звуковой сигнал

Для подачи звукового сигнала я использую пьезоизлучатель, подключенный к выводу A0 Arduino Nano:
эмуляция 8080

Включение/выключение звукового сигнала управляется портом 0xD7 - для включения сигнала следует записать в порт 1 (OUT D7, 1) , а для отключения - 0 (OUT D7, 0).

Питание

В качестве источника питания (выходное напряжение 5 В) я использую интегральный стабилизатор 7805, входное напряжение (9 В) на который подается с выхода компактного блока питания:
самодельный компьютер блок питания

Адаптация операционной системы CP/M

Операционная система CP/M ("Control Program for Microcomputers") версии 2.2 (однопользовательская и однозадачная) была выпущена компанией  Digital Research Inc. (DRI) под руководством Гарри Килдалла (Gary Kildall) и его жены  Дороти МакЭвен (Dorothy McEwen) в 1979 году.
Гарри Килдалл
Гарри Килдалл (Gary Kildall)

Руководство по ОС CP/M версии 2.2 (317 страниц!) можно загрузить здесь - http://www.cpm.z80.de/manuals/cpm22-m.pdf .

Система CP/M должна настраиваться под размер оперативной памяти конкретного компьютера - этот процесс называется "CP/M Alteration".
Для адаптации операционной системы под актуальный размер памяти я создал проект на Go getcpm (проект доступен на GitHub - https://github.com/Dreamy16101976/getcpm).
Для компиляции программы на Go в исполняемый файл getcpm.exe требуется использовать команду:

go build getcpm.go

При запуске программы getcpm.exe требуется указать размер памяти в килобайтах (XX) и желаемый серийный номер системы CP/M (6 байтов в 16-ричном виде, YYYYYYYYYYYY), после выполнения настройки создается файл CPMXXK.SYS и указывается его восьмибитная контрольная сумма:
настройка CP/M

Программа, используя файл CPMDIFF.SYS, корректирует адреса в файле CPM00K.SYS, выполняя настройку на заданный объем оперативной памяти:
настройка CP/M

Размер получаемого файла, содержащего CCP и BDOS операционной системы CP/M версии 2.2, равен 5632 байта.
Я выбрал вариант с 64 Кбайтами ОЗУ (64K system).

Ссылка на скачивание zip-архива с образом CP/M CPM64K.SYS:
https://acdc.foxylab.com/sites/default/files/CPM64K.ZIP

Для записи операционной системы CP/M на SD-карточку используется команда монитора N00000100, где 00000100 - номер сектора карточки (в 16-ричной форме), начиная с которого записывается образ системы.

После ввода команды монитор ожидает начала передачи от компьютера, передавая символы C. Затем в программе Tera Term необходимо выбрать для загрузки файл CPM64K.SYS с образом системы. После окончания загрузки происходит возврат в монитор.

CCP и BDOS располагаются на SDHC-карточке, начиная с сектора 256, откуда их  загружает написанный мной начальный загрузчик.

Карта памяти системы:

0xFFFF
BIOS - базовая система ввода-вывода
0xFA00
BDOS - базовая дисковая операционная система
0xEC06
CCP - интерпретатор командной строки (командный процессор)
0xE400
TPA (Transient Program Area) -
область программ пользователя
0x0100
cлужебная область операционной системы
0x0000

Я сделал перехват функций BIOS (при обращении по соответствующим адресам памяти) и их реализацию:
(смещения указаны относительно адреса 0xFA00)

Смещение Функция
0x00 BOOT - "холодная" перезагрузка
0x03 WBOOT - "горячая" перезагрузка
0x06 CONST - получение состояния консоли
0x09 CONIN - ввод из консоли
0x0C CONOUT - вывод в консоль
0x0F LIST
(не реализовано)
0x12 PUNCH
(не реализовано)
0x15 READER
(не реализовано)
0x18 HOME - переход в начало диска
0x1B SELDSK - выбор диска
0x1E SETTRK - выбор дорожки
0x21 SETSEC - выбор сектора
0x24 SETDMA - установка адреса DMA-буфера
0x27 READ - чтение сектора
0x2A WRITE - запись сектора
0x2D LISTST - получение состояния устройства вывода листинга
(не реализовано)
0x30 SECTRAN - трансляция логических (с 0)/физических (с 1) секторов

Для ввода/вывода информации в CP/M используются такие логические устройства:
CONSOLE - консоль;
LIST - устройство для вывода листинга (обычно принтер);
READER - устройство ввода c перфоленты;
PUNCH - устройство вывода на перфоленту.

Оптимизация проекта

Для оптимизации исходного кода проекта я использую утилиту avr-nm, которая позволяет отобразить размеры кода функций и областей памяти, занимаемых переменными:

avr-nm.exe --size-sort -r -C -S -t decimal cpm4nano-master.ino.elf >> size.txt

Указанная команда выводит результирующую информацию (о скомпилированном файле проекта cpm4nano-master.ino.elf) в файл size.txt.

Пример результата работы утилиты:

00019622 00005138 T call(unsigned int)
00024760 00003708 T loop
00001792 00002048 t font
00016792 00001700 T __vector_13
00014562 00001302 T _IPL()
00018492 00001130 T setup
08389186 00000800 B vram
........................................................

Вторая колонка указывает размер объекта в памяти в байтах, а третья - его тип (буква в нижнем регистре - локальный объект, в верхнем - глобальный):
T  t - текст (код или данные во флэш-памяти),  B b - неинициализированные данные в ОЗУ; D d - инициализированные во флэш-памяти данные, скопированные в ОЗУ; W - ???.

Имя объекта указывается в четвертой колонке.

Эксплуатация эмулятора

Карта памяти SD-карточки (сектора карточки имеют размер 512 байт)

0x100 ... 0x12B  - CCP & BDOS (загружаемая ОС CP/M) (44 сектора, в каждом секторе заняты первые 128 байт)
0x1000 ... 0x64FFF - данные эмулируемых дискет (доступно 100 дискет, на дискету выделено 4096 (0x1000) секторов) (сектор дискеты занимает первые 128 байт в одном секторе карточки)
0x70000 ...  0x70FFF - 4 банка памяти (1 линия кэша занимает первые 64 байта в секторе карточки, 1 банк занимает 1024 (0x400) сектора)
0x80000 ... 0x98FFF - образы памяти (доступно 100 образов, на образ выделено 1024 (0x400) сектора) (заняты первые 128 байт в секторах карточки)
ИТОГО: 306 МБайт

После включения Arduino Nano происходит детектирование FRAM-чипов памяти на шине I2C, инициализация памяти на SD-карте, автоконфигурация MMU, проверка оперативной памяти:
загрузка самодельного компьютера

Затем запускается монитор и отображается его приглашение к вводу команд ">".
Я реализовал для монитора следующие команды:
RXYY - запись байта YY (hex) в регистр X
DXXXX - отображение байта памяти по адресу XXXX (hex)
LXXXXYY - запись байта YY (hex) в память по адресу XXXX (hex)
AXX - заполнение памяти байтом XX (hex)

IXX - чтение байта из порта XX (hex)
OXXYY - запись байта YY (hex) в порт XX (hex)
W - включение/выключение режима отладки
G - запуск программы с адреса XXXX (hex)
QXXXX - установка точки останова (breakpoint) на адрес XXXX (hex)
F
- загрузка двоичной информации из файла формата Intel HEX
TXXXX - загрузка текстового файла в память с адреса XXXX (hex)
BXXXX - загрузка двоичного файла в память с адреса XXXX (hex)
ZXYY - "вставка" дискеты с номером YY в дисковод X
XX - форматирование диска X (X = A,B,C,D) - область на карточке, отведенная для диска, заполняется байтами 0xE5
NXXXXXXXX
- запись двоичного файла на карточку, начиная с блока XXXXXXXX (hex) (каждый фрагмент файла длиной 128 байт записывается в начало 512-байтного сектора карточки)
PXX - сохранение образа памяти с номером XX
UXX
- загрузка образа памяти с номером XX
C - загрузка ОС CP/M
J - запуск интерпретатора Brainfuck

H - перезагрузка Arduino
M
- тест памяти
YXY - переключение блока памяти X на банк памяти Y
V
- вывод информации о текущем состоянии эмулятора

При вводе команды "C" происходит загрузка операционной системы с SD-карточки (длится около 11 секунд):
загрузка CP/M
64K SYSTEM - размер памяти, используемый системой, в килобайтах
CBASE - адрес начала CCP
FBASE - адрес начала BDOS
BIOS - адреса процедур BIOS
После загрузки отображается приглашение операционной системы "A>".

В ОС CP/M встроено несколько команд:
DIR - вывод списка файлов
REN - переименование файла
ERA - удаление файлов
SAVE - сохранение области памяти в файл
TYPE - вывод содержимого текстового файла
USER - задание текущего номера пользовательской области

Пример результата выполнения команды DIR:
CP/M команда DIR
При нажатии клавиш CTRL + / при запросе ввода с консоли происходит возврат из ОС CP/M в монитор.

Загрузка файлов в эмулятор

Для загрузки файлов в эмулятор посредством терминальной программы (я покажу этот процесс на примере программы Tera Term)  используется протокол XMODEM.

Для загрузки в оперативную память двоичного файла используется команда монитора Bxxxx , где xxxx - начальный адрес в памяти для загрузки файла (в 16-ричной форме):
эмулятор 8080 загрузка файлов

После ввода команды монитор ожидает начала передачи от компьютера, передавая символы C.

Затем в программе Tera Term необходимо выбрать пункт меню для передачи файла по протоколу XMODEM:
загрузка файла по протоколу XMODEM

Затем следует выбрать файл для загрузки (например, CPU.COM):
передача файла в эмулятор

После выбора файла начинается его загрузка:
загрузка файлов в эмулятор CP/M

После окончания загрузки происходит возврат в монитор:
запист файлов в эмулятор
При этом отображается количество принятых байт.

Для загрузки в оперативную память текстового файла используется команда монитора Txxxx , где xxxx - начальный адрес в памяти для загрузки файла (в 16-ричной форме). Если в загружаемом текстовом файле встречается байт со значением 0xA, то он считается символом конца файла и передаваемые дальше байты игнорируются.

Для загрузки в оперативную память данных из файла формата Intel HEX используется команда монитора F.

Для записи загруженных (с адреса 0x100) данных на диск необходимо загрузить ОС CP/M командой монитора C:
загрузка CP/M

Затем следует записать содержимое памяти, начиная с адреса 0x100, на диск в файл FILE.COM командой
SAVE n FILE.COM , где n = [ размер файла FILE.COM / 256 ] + 1:
команда SAVE CP/M

После окончания записи записанный файл CPU.COM отображается при просмотре директории командой DIR:
команда DIR CP/M

Системный диск

Я записал на эмулируемый флоппи-диск стандартные системные программы:
ASM.COM - ассемблер
DDT.COM - отладчик
DUMP.COM - вывод файла в 16-ричном виде
ED.COM - текстовый редактор
LOAD.COM - создание COM-файла из HEX-файла
PIP.COM - копирование файлов
STAT.COM - вывод состояния диска
SUBMIT.COM - выполнение SUB-файла
XSUB.COM - выполнение расширенной программы SUBmit

Также я разместил на диске интерпретатор бейсика TINYBAS.COM и тестовые программы TEST.COM, 8080PRE.COM, 8080EX1.COM.

Ссылка на скачивание zip-архива с образом этого диска DISK_1.BIN:
https://acdc.foxylab.com/sites/default/files/DISK_1.ZIP

Запуск программ в эмуляторе

В эмуляторе можно запустить эмулятор от микрокомпьютера "Altair 8800".

После запуска появляется приглашение в виде точки:
эмуляция Altair 8800

Вводя команду D, получаем дамп области памяти:
эмулятор Альтаира

Программирование в эмуляторе

BASIC

TinyBASIC

Я запустил интерпретатор бейсика TinyBASIC (SHERRY BROTHERS TINY BASIC VER. 3.1):
TinyBASIC CP/M

Ссылка на скачивание zip-архива с исполняемым файлом TINYBAS.COM и файлом справки TINYBAS.DOC:
https://acdc.foxylab.com/sites/default/files/TINYBAS.ZIP

Для тестирования скорости работы эмулятора я использовал программу, содержащую цикл:
тестирование скорости BASIC

Длительность выполнения программы составила 90 секунд:
скорость эмулятора 8080

CBASIC

Также я записал на эмулируемую дискету компилятор CBASIC от компании Digital Research, который представлен такими файлами:

CB80.COM - компилятор
LK80.COM - линкер
CB80.OV1 - оверлей
CB80.OV2 - оверлей
CB80.OV3 - оверлей
CB80.IRL - ран-тайм библиотека

В моем эмуляторе успешно выполняются компиляция и линковка программ.

Вот какую тестовую программу я записал в файл HELLO.BAS:

100 REM HELLO, WORLD
200 PRINT "HELLO, WORLD!"

Компиляция тестовой программы выполняется командой

CB80 HELLO

CBASIC компилятор

Результатом компиляции является файл HELLO.REL.

Линковка программы выполняется командой

LK80 HELLO

CBASIC линкер

Получаемый в процессе линковки исполнимый файл HELLO.COM успешно запускается командой

HELLO

и выводит сообщение:
компилятор бейсика CP/M

Ada

Я записал на эмулируемую дискету компилятор языка программирования Ada версии 2.10 от компаний Supersoft Inc. и Maranatha Software Systems.
Архив с компилятором можно скачать здесь - acdc.foxylab.com/ada.zip .
Компиляцию программы TOWERS.ADA решения задачи о "Ханойской башне" я выполнил командой:

ADA TOWERS
компилятор Ada

В процессе компиляции создаются временные файлы TOWERS.INT и TOWERS.OPT и результирующий файл TOWERS.COM.

Затем я запустил скомпилированную программу TOWERS.COM на выполнение командой

TOWERS

и получил решение задачи для трех колец:
задача о Ханойской башне

В качестве теста производительности я использовал программу PRIMES.ADA.
Для выполнения программы на эмуляторе потребовалось 4 часа 25 минут, при этом было найдено 1899 простых чисел:
PRIMES.ADA

Star Trek

А вот как выглядит запуск игры Star Trek, написанной на языке Ada (файл STARTREK.ADA скомпилировался в файл STARTREK.COM):
игра Star Trek CP/M

Star Trek представляет собой симулятор пилота космического корабля USS Enterprise (NCC-1701) во вселенной "Звездного пути" ("Star Trek Universe"). Автор оригинальной версии игры - Mike Mayfield, (дата выпуска - 20 октября 1972 года).
Игровое поле (галактика) имеет размеры 8 x 8 квадрантов (quadrant), причем каждый квадрат состоит из 8 x 8 секторов (sector).

Цель игры - уничтожить с помощью фазеров космические корабли Клингонов - инопланетной цивилизации гуманоидов-воинов.
Флаг Клингонской империи (Klingon Empiry):
флаг Клингонской империи

Quadrant, Sector - текущее местоположение игрока
Stardate - текущая звездная дата

Метки на карте текущего квадранта:
E -  космический корабль Enterprise игрока;
B - база звездного флота (starbase);
K - корабль Клингонов (Klingon ship);
* - звезда (star)

Команды:
(список команд отображается при вводе номера команды, отличного от 0,1,2,3):
команды игры Star Trek
0  - дальнее сканирование (Long Range Scan);
1 - ближнее сканирование (Short Range Scan);
2 - фазеры (Phasors);
3 - навигация (Navigate).

При выполнении дальнего сканирования (команда 0) отображаются 9 квадрантов (в центре (E) - квадрант, в котором находится игрок). Для остальных квадрантов отображаются две цифры, например, 3 - 5:
карта игры Star Trek
(система координат: ось x - сверху вниз от 1, ось y - слева направо от 1).

При выполнении ближнего сканирования (команда 1) отображается карта текущего квадранта:
карта в игре Star Trek
(система координат: ось x - сверху вниз от 1, ось y - слева направо от 1).

Для навигации (перемещения в космосе) (команда 3) необходимо указать тип двигателя (I (Impulse Drive) - перемещение в пределах квадранта между секторами, W (Warp Drive)- перемещение между секторами) и ввести новые координаты  x и y:
перемещение между квадрантами:
навигация в игре Star Trek
перемещение внутри квадранта:
навигация в игре Star Trek

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

При нанесении достаточного урона корабль Клингонов уничтожается и исчезает с карты:
уничтожение корабля Клингонов

Обзор игры в журнале The Dragon Magazine #38 за 1980 год:
...

Brainfuck

Я встроил в монитор моего "нанокомпьютера" интерпретатор языка Brainfuck.

Программа на языке Brainfuck может размещаться в памяти в диапазоне адресов 0x0000 ... 0x7FFF, ячейки данных (30 000 ячеек) располагаются в диапазоне 0x8000 ... 0xF52F.

Командами языка Brainfuck являются восемь символов:
+
-
<
>
.
,
[
]

Мой интерпретатор игнорирует любые другие символы, кроме байт со значениями 0x00 и 0x1A, которые трактуются как конец программы.

Вот пример тестовой программы:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
------.--------.>+.>. 

Дамп этой программы, размещенной в памяти:
интерпретатор Brainfuck

Для запуска программы используется команда монитора J.

Тестовая программа выводит сообщение:
интерпретатор Brainfuck

YouTube
Видео работы проекта в реальном времени:
https://youtu.be/LHFmt3qWAuY

Площадки для обсуждения проекта cpm4nano

русскоязычные:
Arduino.ru - http://arduino.ru/forum/proekty/arduino-nano-emulyator-kompyutera-pod-upravleniem-os-cpm
HYPE - http://hype.retroscene.org/blog/689.html
nedoPC.org - http://www.nedopc.org/forum/viewtopic.php?f=46&t=17239

англоязычные:
Arduino Project Hub - https://create.arduino.cc/projecthub/FoxyLab/arduino-nano-as-cp-m-compatible-computer-c09d4e
Arduino Forum - https://forum.arduino.cc/index.php?topic=464410.30
ATMEL COMMUNITY - https://community.atmel.com/projects/cpm-computer-arduino-nano-30

Продолжение следует

Яндекс.Метрика