Raison d’être - в далекие 90-е годы у меня был "бытовой" (как он тогда назывался) компьютер "Байт" - брестский клон популярнейшего в те времена на просторах СНГ ZX Spectrum 48K. Но на нем невозможно было запустить операционную систему CP/M, бывшую де-факто стандартом для восьмибитных компьютеров (в середине 1980-х годов эта ОС запускалась на свыше 300 моделях микрокомпьютеров). И вот я решил восполнить это упущение и создать свой компьютер под управлением ОС CP/M 2.2 на базе ... Arduino Nano 3.0 (с микроконтроллером ATmega328)!
В процессе его создания я решил несколько задач:
- создал симулятор процессора Intel 8080, а также реализовал эмуляцию оперативной памяти и дисковой системы;
- адаптировал к созданному устройству ОС CP/M;
- реализовал вывод текстовой информации на экран телевизора;
- разработал преобразователь PS/2-UART для подключения клавиатуры.
Подобные проекты часто именуются "homebrew computer" и технологии их построения весьма разнообразны - электромагнитные реле, дискретные транзисторы, микрсохемы логики, FPGA...
Текущая версия проекта - cpm4nano Mk VI (версия ПО - 0.7)
Проект постоянно развивается, поэтому описание может не успевать за текущим состоянием проекта!
Структурная схема проекта
Внешний вид проекта
Конфигурация системы
Симулятор процессора i8080
Эмуляция оперативной памяти
Эмуляция консоли
Эмуляция дисководов
Хранение данных на магнитной ленте
Адаптация операционной системы CP/M
Эксплуатация
Оценка быстродействия
Программирование
Игры
YouTube
Структурная схема проекта:
Внешний вид проекта:
Как все устроено -
Конфигурация системы:
- 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.
ТЕСТЫ ПРОЦЕССОРА i8080, УСПЕШНО ПРОЙДЕННЫЕ СИМУЛЯТОРОМ
Kelly Smith Test от MICROCOSM ASSOCIATES (версия 1.0, 1980 год) - подарен группе пользователей CP/M "SIG/M"
Этот тест тестирует большую часть флагов, режимов и инструкций (за исключением операций ввода-вывода, обработки прерываний, также тестируются не все постусловия инструкций).
8080PRE (предварительный (preliminary) тест) от Ian Bartholomew и Frank Cringles
DIAGNOSTICS I от SUPERSOFT ASSOCIATES (версия 1.2, 1980 год)
DIAGNOSTICS II от SUPERSOFT ASSOCIATES (версия 1.2, 1981 год)
Буквы A...Z обозначают пройденные тесты.
Согласно https://github.com/superzazu/8080/tree/master/cpu_tests на процессоре 8080 с тактовой частотой 2 МГц тест между BEGIN ... и END ... длится около двух минут.
В FAST-режиме мой "нанокомпьютер" выполняет этот тест за время около семи минут, что соответствует эквивалентной тактовой частоте около 500 - 600 кГц, в SLOW-режиме - за время около 26 минут (150 кГц). При отключенном кэше инструкций время выполнения теста намного возрастает - до 117 минут в FAST- режиме (35 кГц).
8080/8085 CPU Exerciser от Ian Bartholomew и Frank Cringles
Эмуляция оперативной памяти
Главная проблема в этом проекте - нехватка оперативной памяти, которой в ATmega328 только два килобайта! Для эмуляции оперативной памяти размером несколько десятков Кбайт я использую микросхемы FRAM.
Я использую две микросхемы FRAM (встречается сокращение и F-RAM) FM24C256-G:
Расшифровка обозначения чипа FM24C256-G:
FM - Cypress;
24 - I2C FRAM;
C - напряжение питания 4,5...5,5 В;
256 - емкость 256 Кбит;
G - 8-выводный SOIC-корпус
Для удобства применения таких микросхем я применил
SOIC-DIP адаптеры:
FRAM - это запоминающее устройство, построенное по сегнетоэлектрической (англ. ferroelectric) технологии. Микросхема FM24C256-G обладает емкостью 32 КБайта и подключается к шине I2C. Эта микросхема выпускается в SOIC-корпусе:
Назначение контактов:
Обозначение вывода |
Назначение вывода: |
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, задав их адреса как
0x50 (все адресные линии "плавающие");
0x51 (адресная линия A0 подключена к выводу VDD, остальные адресные линии - "плавающие").
Светодиод индицирует низкий уровень на линии SCL - при обращении к FRAM-памяти.
Кэширование
Для ускорения доступа к оперативной памяти я использую кэш инструкций - однолинейный, размером 8 байт с политикой "read ahead" (упреждающее чтение - считываются байты программы, расположенные после текущей инструкции) / "write through" (запись происходит всегда в ОЗУ).
Я оценил влияние размера кэша на быстродействие в разных задачах, определив время их выполнения:
расчет первого хода в Microchess:
нет кэша - 291 секунда;
кэш 4 байта - 580 секунд;
кэш 8 байт - 21 секунда;
кэш 16 байт - 26 секунд.
компиляция ADA-программы TOWERS (решение задачи о ханойской башне):
нет кэша - еще нет результатов;
кэш 4 байта - еще нет результатов;
кэш 8 байт - 71 минута;
кэш 16 байт - 87 минут.
Измерения выполнялись в FAST-режиме.
ТЕСТЫ ОПЕРАТИВНОЙ ПАМЯТИ, УСПЕШНО ПРОЙДЕННЫЕ СИМУЛЯТОРОМ
Популярным способом проверки памяти является запись/чтение в ячейки памяти/из ячеек памяти сначала байтов 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 от Mel Cruts (1980 год)
MEMR Rasmussen Memory Test от Lifeboat Associates (версия 2.2, 1980 год)
--- в процессе прохождения ---
Сохранение и загрузка образов памяти
Для записи образа памяти на карточку используется команда монитора Pxx, где xx - номер образа памяти (от 00 до 99), например, команда P01 записывает образ памяти с номером 01.
Для загрузки образа памяти с карточки используется команда монитора Uxx, где xx - номер образа памяти (от 00 до 99), например, команда U01 загружает образ памяти с номером 01.
Эмуляция консоли
Ввод/вывод через последовательный порт
Для связи Arduino Nano с компьютером через последовательный порт (для ввода-вывода команд и данных) я использую преобразователь USB-Serial и терминальную программу Tera 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.
Схема реализации вывода изображения на экран ТВ:
Формат вывода на экран ТВ - 20 строк x 45 колонок, шрифт 4 x 8.
Шрифт описан в виде битовых последовательностей - сначала первая (верхняя) строка для 256 символов, затем вторая и т.д.:
(шрифт описывается в старших 4 битах в инвертированном виде: 1 - черный пиксель, 0 - белый пиксель, младшие 4 бита должны быть равны 1).
Русские буквы и символы псевдографики (согласн RFC 1489) соответствуют кодировке КОИ-8 (KOI-8R):
...
Вот как выглядит набор символов шрифта:
на экране LCD-телевизора:
на экране ЭЛТ-телевизора:
Снимок экрана ЭЛТ-телевизора с результатами дампа памяти из монитора:
Пример снимка телевизионного экрана с выполненной командой DIR и тестами процессора:
Я преодолел ограничение физического разрешения видеовыхода моего «нанокомпьютера» (45 колонок) в стиле портативного микрокомпьютера Osborne 1 — используя два виртуальных экрана.
Содержимое текущего виртуального экрана хранится в видеопамяти (ОЗУ микроконтроллера), а содержимое другого виртуального экрана — в основной памяти (I2C-чипах).
Они расположены горизонтально, разрешение виртуального экрана — 40 колонок и 19 строк + 1 строка статуса:
Таким образом, виртуальные экраны эмулируют физический экран с 80 колонками (Osborne эмулировал физический экран со 128 колонками посредством перекрывающихся виртуальных экранов шириной 56 колонок).
Переключение режимов (физический экран/виртуальные экраны) осуществляется из программы-монитора.
Переключение между виртуальными экранами осуществляется комбинацией клавиш CTRL-O.
Пример:
левый виртуальный экран
правый виртуальный экран
Видеовыход моего компьютера оснащен раъемом "тюльпан" 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 - стирание части строки.
При выводе на экран телевизора позиция следующего символа указывается подстрочным "аппаратным" курсором:
Я сделал отображение видеопамяти в верхнюю часть адресного пространства процессора. Поэтому при записи в эту область памяти на экране появляется соответствующий символ:
Страничный режим вывода на экран
Если происходит полное заполнение экрана (после операции ввода с консоли), то вывод на экран и работа эмулятора приостанавливается, а в самой нижней строке экрана (она не используется для вывода текста) появляется приглашение ">>>" для нажатия какой-либо клавиши после просмотра информации на экране (страничный режим). После нажатия клавиши происходит прокрутка информации на экране и работа эмулятора продолжается до следующего полного заполнения экрана.
Ввод через PS/2-клавиатуру
В качестве альтернативы вводу из "большого" компьютера через последовательный порт я использую ввод с клавиатуры, подключенной через преобразователь PS/2-UART:
Я сделал такой преобразователь на основе микроконтроллера STM8S103F3P6. К PS/2-разъему этого преобразователя подключается клавиатура, оснащенная соответствующим разъемом, а выход преобразователя подключается к контактам UART Arduino Nano вместо преобразователя USB-UART.
Преобразователь получает скан-коды нажатых клавиш от клавиатуры, помещает их в кольцевой буфер и выдает их в последовательный порт, проверяя предварительно сигнал CTS.
Клавиша Scroll Lock на клавиатуре управляет режимом блокировки прокрутки - при включенной блокировке после непрерывного вывода 19 строк дальнейший вывод блокируется до нажатия любой клавиши на клавиатуре (при этом в самой нижней строке экрана отображается >>>):
Блокировка прокрутки управляется уровнем на выводе A3 Arduino ("1" - блокировка выключена, "0" - блокировка включена).
Индикация включенного режима блокировки прокрутки осуществляется желтым светодиодом SCROLL:
Переключение языка ввода (английский/русский в кодировке КОИ-8) осуществляется нажатием сочетания клавиш Alt-Shift:
Включение русского языка ввода индицируется синим светодиодом RUS:
При нажатии сочетания клавиш Ctrl-Alt-Del на клавиатуре выполняется перезагрузка (подается импульс низкого уровня на вход RST Arduino).
Эмуляция дисководов
Я эмулирую поведение дисковода гибких дисков на уровне обращений чтения/записи к портам.
Порты контроллера дисковода/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 секторов).
Для "общения" с карточкой используется шина SPI:
MISO - прием данных от карточки (D12)
MOSI - передача данных карточке (D11)
SCK - тактовый сигнал для карточки (D13)
SS - выбор устройства (D10)
3V3 - питание 3,3 В
GND - "земля"
ICSP-разъем Arduino Nano:
Я подключил microSD-карточку посредством специального адаптера:
Для индикации доступа к карте я подключил красный светодиод с балластным резистором между выводами 3V3 (анод светодиода) и SS (катод светодиода).
Для записи образа дискеты на карточку используется команда монитора 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.
Хранение данных на магнитной ленте
Я реализовал запись данных на магнитную ленту/в аудиофайл и считывание данных с магнитной ленты/аудиофайла.
Для согласования уровней я использую такую схему:
Она рассчитана на запись данных на линейный вход и считывание с линейного выхода.
Схема реализована в виде небольшой платы:
Для подключения внешних устройств служат два RCA-разъема:
При работе с магнитной лентой приостанавливается работа видеогенератора.
Формат данных:
Бит информации кодируется четырьмя импульсами: "1" - частотой 2400 Гц, "0" - частотой 1200 Гц (частоты соответствуют стандарту Kansas City standard (KCS)).
Байт данных содержит один стартовый бит ("0"), восемь бит данных, начиная с младшего, и два стоповых бита "1".
Данные записываются блоками - каждый блок начинается с пилот-тона (64 "1"), затем следует номер блока (два байта, начиная с младшего), адрес блока в памяти (два байта, начиная с младшего), 128 байт данных и байт контрольной суммы.
Вот как биты представлены в аудиофайле:
Эксперименты показали, что отклонение скорости проигрывания на 20% не влияет на надежность считывания данных.
Пример WAV-файла с записью трех блоков:
tape.wav
Подключение внешних устройств
Для подключения внешних устройств я планирую использовать I2C-платы расширения портов PCF8574:
Звуковой сигнал
Для подачи звукового сигнала я использую пьезоизлучатель, подключенный к выводу A0 Arduino Nano (этот же выход используется и для записи на магнитную ленту):
Включение/выключение звукового сигнала управляется портом 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 и указывается его восьмибитная контрольная сумма:
Программа, используя файл CPMDIFF.SYS, корректирует адреса в файле CPM00K.SYS, выполняя настройку на заданный объем оперативной памяти:
Размер получаемого файла, содержащего CCP и BDOS операционной системы CP/M версии 2.2, равен 5632 байта.
Я выбрал вариант с 62 Кбайтами ОЗУ (62K system).
Для записи операционной системы 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 байт в одном секторе карточки)
0x80000 ... 0x98FFF - образы памяти (доступно 100 образов, на образ выделено 1024 (0x400) сектора) (заняты первые 128 байт в секторах карточки)
ИТОГО: 306 МБайт
После включения Arduino Nano происходит детектирование FRAM-чипов памяти на шине I2C, проверка оперативной памяти:
Затем запускается монитор и отображается его приглашение к вводу команд ">".
Я реализовал для монитора следующие команды:
RXYY - запись байта YY (hex) в регистр X
DXXXX - отображение байта памяти по адресу XXXX (hex)
LXXXXYY - запись байта YY (hex) в память по адресу XXXX (hex)
W - включение/выключение режима отладки
G - запуск программы с адреса XXXX (hex)
QXXXX - установка точки останова (breakpoint) на адрес XXXX (hex)
F - загрузка двоичной информации из файла формата Intel HEX
BXXXX - загрузка двоичного файла в память с адреса XXXX (hex)
ZXYY - "вставка" дискеты с номером YY в дисковод X
XX - форматирование диска X (X = A,B,C,D) - область на карточке, отведенная для диска, заполняется байтами 0xE5
NXXXXXXXX - запись двоичного файла на карточку, начиная с блока XXXXXXXX (hex) (каждый фрагмент файла длиной 128 байт записывается в начало 512-байтного сектора карточки)
PRXX / PWXX - считывание (R) / запись (W) (карточка) образа памяти с номером XX
UR / UWAAAANN - считывание (R) / запись (W) (магнитная лента или аудиофайл) образа памяти, начиная с адреса AAAA (hex), размером NN (hex) блоков по 256 байт
C - загрузка ОС CP/M
HX - включение 8-битного (X=8) или 7-битного (X=7) режима вывода символов
V - вывод информации о текущем состоянии эмулятора
Запуск программ в мониторе
В мониторе можно запустить монитор от микрокомпьютера "Altair 8800".
После запуска появляется приглашение в виде точки:
Вводя команду D, получаем дамп области памяти:
Загрузка операционной системы CP/M
При вводе команды "C" происходит загрузка операционной системы с SD-карточки (длится 7,8 секунды - в FAST-режиме / 11,7 секунды - в SLOW-режиме)):
64K SYSTEM - размер памяти, используемый системой, в килобайтах
CBASE - адрес начала CCP
FBASE - адрес начала BDOS
BIOS - адреса процедур BIOS
После загрузки отображается приглашение операционной системы "A>".
В ОС CP/M встроено несколько команд:
DIR - вывод списка файлов
REN - переименование файла
ERA - удаление файлов
SAVE - сохранение области памяти в файл
TYPE - вывод содержимого текстового файла
USER - задание текущего номера пользовательской области
Пример результата выполнения команды DIR:
При нажатии клавиш CTRL + / при запросе ввода с консоли происходит возврат из ОС CP/M в монитор.
Загрузка файлов в эмулятор
Для загрузки файлов в эмулятор посредством терминальной программы (я покажу этот процесс на примере программы Tera Term) используется протокол XMODEM.
Для загрузки в оперативную память двоичного файла используется команда монитора Bxxxx , где xxxx - начальный адрес в памяти для загрузки файла (в 16-ричной форме):
После ввода команды монитор ожидает начала передачи от компьютера, передавая символы C.
Затем в программе Tera Term необходимо выбрать пункт меню для передачи файла по протоколу XMODEM:
Затем следует выбрать файл для загрузки (например, CPU.COM):
После выбора файла начинается его загрузка:
После окончания загрузки происходит возврат в монитор:
При этом отображается количество принятых байт.
Для загрузки в оперативную память текстового файла используется команда монитора Txxxx , где xxxx - начальный адрес в памяти для загрузки файла (в 16-ричной форме). Если в загружаемом текстовом файле встречается байт со значением 0xA, то он считается символом конца файла и передаваемые дальше байты игнорируются.
Для загрузки в оперативную память данных из файла формата Intel HEX используется команда монитора F.
Для записи загруженных (с адреса 0x100) данных на диск необходимо загрузить ОС CP/M командой монитора C:
Затем следует записать содержимое памяти, начиная с адреса 0x100, на диск в файл FILE.COM командой
SAVE n FILE.COM , где n = [ размер файла FILE.COM / 256 ] + 1:
После окончания записи записанный файл CPU.COM отображается при просмотре директории командой DIR:
Системный диск
Я записал на эмулируемый флоппи-диск стандартные системные программы:
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
Оценка быстродействия
Прогон из монитора 32768 команд:
Команда |
FAST-режим |
SLOW-режим |
NOP |
1,7 секунды |
3,0 секунды |
MOV A, A |
1,8 секунды |
3,3 секунды |
DAA |
2,0 секунды |
3,6 секунды |
Выполнение теста DIAGNOSTICS II V1.2:
(между BEGIN TIMING TEST и END TIMING TEST):
FAST-режим |
SLOW-режим |
899 секунд |
1743 секунды (x1,94) |
Выполнение тестовой программы в TINYBASIC:
FAST-режим |
SLOW-режим |
123 секунды |
202 секунды (x1,64) |
Выполнение скомпилированной программы PRIMES, написанной на Ada (найдено 1899 простых чисел):
FAST-режим |
SLOW-режим |
10320 секунд |
16980 секунд (x1,65) |
Программирование
BASIC
TinyBASIC
Я запустил интерпретатор бейсика TinyBASIC (SHERRY BROTHERS TINY BASIC VER. 3.1):
Ссылка на скачивание zip-архива с исполняемым файлом TINYBAS.COM и файлом справки TINYBAS.DOC:
https://acdc.foxylab.com/sites/default/files/TINYBAS.ZIP
Altair BASIC
Мой "нанокомпьютер" успешно запустил BASIC (8 килобайт, версия 4.0) для микрокомпьютера Altair:
Файл 8kbas40.bin загружается в память с помощью команды монитора B000. Затем он запускается командой G0000 (предварительно нужно отключить режим отладки командой W). После загрузки можно сохранить образ памяти на SD-карточке с помощью команды PXX (XX - номер образа), и затем загружать его командой UXX.
Особенности этого диалекта BASIC:
Из-за использования старшего бита в служебных целях листинг получается искаженным:
Отображение в терминале можно исправить, предварительно включив 7-битный режим обмена с помощью команды монитора H7.
Для удаления последнего введенного символа необходимо ввести знак подчеркивания:
Программу можно загрузить с компьютера через терминальную программу из текстового файла такого формата:
Вот такой тест выполняется 110 секунд в FAST-режиме (162 секунды в SLOW-режиме):
Также я проверил работоспособность четырехкилобайтной версии Бейсика:
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
Результатом компиляции является объектный файл HELLO.REL.
Линковка программы выполняется командой
LK80 HELLO
Получаемый в процессе линковки исполнимый файл HELLO.COM успешно запускается командой
HELLO
и выводит сообщение:
FOCAL
Интерпретатор FOCAL для микрокомпьютера Altair успешно заработал на моем "нанокомпьютере":
C
BDS C
Я успешно испытал компилятор и линкер из пакета BD Software C:
Компиляция тестовой программы TAIL.C выполняется командой
CC TAIL
Результатом компиляции является объектный файл TAIL.CRL.
Линковка программы выполняется командой
CLINK TAIL
В результате создается исполняемый файл TAIL.COM.
FORTH
Я запустил FORTH-83 (файл f83.com доступен здесь):
В качестве примера я ввел и исполнил простую программу, выводящую сообщение HELLO, WORLD!:
Основные команды FORTH-83:
(при успешном выполнении команды выдается ok)
10 CREATE-FILE HL.BLK - создание файла HL.BLK размером 10 блоков
OPEN HL.BLK - открытие файла HL.BLK
1 LOAD - загружает из открытого файла экран 1 и компилирует содержащиеся в нем описания слов
HL - выполнение слова HL из открытого экрана
BYE - выход в CP/M
Можно вводить немедленно исполняемые команды:
2 2 + . - складывает 2 и 2 и выводит результат (4):
2 - 2 помещается в стек
2 - 2 помещается в стек
+ - складывает аргументы из стека и помещает результат в стек
1 3 - . - вычитает 1 из 3 и выводит результат (-2):
Ada
Я записал на эмулируемую дискету компилятор языка программирования Ada версии 2.10 от компаний Supersoft Inc. и Maranatha Software Systems.
Архив с компилятором можно скачать здесь - acdc.foxylab.com/ada.zip .
Компиляцию программы TOWERS.ADA решения задачи о "Ханойской башне" я выполнил командой:
ADA TOWERS
В процессе компиляции создаются временные файлы TOWERS.INT и TOWERS.OPT и результирующий файл TOWERS.COM.
Затем я запустил скомпилированную программу TOWERS.COM на выполнение командой
TOWERS
и получил решение задачи для трех колец:
LISP
Я запустил интерпретатор LISP/80 версии 1.1 от Thomas W. Yonkman:
Заметка об этом интерпретаторе в журнале BYTE:
Вот заметка об этом интерпретаторе в журнале BYTE:
MINOL
Также я проверил работоспособность интерпретатора языка программирования MINOL ("Tiny BASIC with Strings in 1.75K Bytes"), созданного для микрокомпьютера Altair :
Файл minol22.bin загружается в память с помощью команды монитора B000. Затем он запускается командой G0000 (предварительно нужно отключить режим отладки командой W). После загрузки можно сохранить образ памяти на SD-карточке с помощью команды PXX (XX - номер образа), и затем загружать его командой UXX.
Игры
Othello
А вот как выглядит запуск игры реверси (Othello), написанной на языке C (файл OTHELLO.C скомпилировался в файл OTHELLO.COM):
В этой партии я играю *, а "нанокомпьютер" - @.
Ходя заключается в указании расположения фишки, размещаемой на доске:
34 - третья строка, четвертый столбец:
Цель игры - окружить фишки соперника, при этом окруженные по горизонтали или вертикали фишки "переворачиваются".
На обдумывание хода у "нанокомпьютера" уходит около шести минут.
Star Trek
А вот как выглядит запуск игры Star Trek, написанной на языке Ada (файл STARTREK.ADA скомпилировался в файл STARTREK.COM):
в консоли:
на ЭЛТ ТВ:
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):
0 - дальнее сканирование (Long Range Scan);
1 - ближнее сканирование (Short Range Scan);
2 - фазеры (Phasors);
3 - навигация (Navigate).
При выполнении дальнего сканирования (команда 0) отображаются 9 квадрантов (в центре (E) - квадрант, в котором находится игрок). Для остальных квадрантов отображаются две цифры, например, 3 - 5:
(система координат: ось x - сверху вниз от 1, ось y - слева направо от 1).
При выполнении ближнего сканирования (команда 1) отображается карта текущего квадранта:
(система координат: ось x - сверху вниз от 1, ось y - слева направо от 1).
Для навигации (перемещения в космосе) (команда 3) необходимо указать тип двигателя (I (Impulse Drive) - перемещение в пределах квадранта между секторами, W (Warp Drive)- перемещение между секторами) и ввести новые координаты x и y:
перемещение между квадрантами:
перемещение внутри квадранта:
Для атаки корабля Клингонов фазером (команда 2) необходимо указать мощность выстрела. Фазер не требует прицеливания, но сила его удара уменьшается с удалением от цели. После выполнения атаки отображается урон, нанесенный вражескому кораблю, и полученный от вражеского корабля:
При нанесении достаточного урона корабль Клингонов уничтожается и исчезает с карты:
Microchess
Играющая в шахматы программа Microchess для микрокомпьютеров на процессоре Intel 8080 была разработана в 1977 году компанией MICRO-WARE LTD.:
Авторы программы: Peter R. Jennings (канадский исследователь) и T. O'Brien.
Эта была первая коммерчески успешная (было продано сыше 10 тысяч копий) шахматная программа для микрокомпьютеров. Код игры продавался напечатанным в брошюре, поэтому пользователь должен был сам ввести его в компьютер.
Вот как выглядит запуск этой программы на моем "нанокомпьютере":
Клетки доски пронумерованы таким образом:
На мой первый ход пешкой:
программа ответила ходом конем:
На обдумывание хода у "нанокомпьютера" ушло около трех минут.
Видео работы проекта в реальном времени:
18 октября 2019 года
https://youtu.be/Yuq1yEH-WZ8
23 марта 2017 года
https://youtu.be/LHFmt3qWAuY
Продолжение следует