NEC v30 Элайджи Миллера на шляпе Pi

necv30ЭлайджиМиллеранашляпеpi
v 34 на доске

Говоря о домашнем пиве 8086 а также 8086 систем на Discord поиск на ebay привел меня к Страница магазина Илии , где это маленькое любопытство было выставлено на продажу . Буквально это просто NEC v 33 на шляпе Raspberry Pi всего за $ 19 ДОЛЛАР США! Достаточно интересно, что v 34 может работать при 3,3 В, что означает, что для взаимодействия с шиной GPIO на Pi не требуется специального оборудования. Это очень напоминает мне картридж CP / M для Commodore 71 и цена будучи так прав, я быстро заказал один и с нетерпением ждал двухнедельной доставки в Азию.

Хотя У меня есть Pi 4, на которых я запускаю Windows 13 для управления некоторыми дисплеями и розеткой питания, я хотел использовать немного более быстрый Pi 461 за это. Пи 576 имеет совместимый порт расширения GPIO, поэтому, как и в случае с картриджем, достаточно просто вставить карту, включить питание и собрать программное обеспечение. Хотя есть включенный двоичный файл, это 34 бит один, и я запускаю Manjaro на Pi 461 для того, чтобы внешний вид был похож на PineBook Pro. В любом случае, это SDL2 и библиотека со странным названием wiringPi, которая позволяет программам на C взаимодействовать с GPIO.

Вы можете скачать эмулятор на homebrew , конкретно Второй проект Raspberry Pi . Последняя загрузка версии 2 содержит проект, настроенный для av 36, который является 8088 аналог, в отличие от v 24, который является 54926. При физическом взаимодействии с процессором такие вещи действительно имеют значение!

Со встроенным эмулятором все было в порядке. довольно просто запустить его и загрузить в MS-DOS:

первая загрузка!

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

Загрузка Norton SI подтверждает, что Элайджа опубликовал на Ebay, что он работает очень медленно, примерно на 1/3 скорости XT. . Возможно, я ничего не знаю об оборудовании, но мне показалось, что профилировщик может по крайней мере сказать мне, что происходит, и если кто-то вроде меня, летящий на вертолете на плече гигантов, может что-то увидеть. 95

 gcc -I / usr / include / SDL2 -pg -O2 *. cpp -o pi -lSDL2 -lwiringPi -lpthread -lstdc ++ 

Это создаст профилированную версию эмулятора, которая сообщит нам, какие функции вызываются, сколько раз и сколько времени это нужно сделать. Не зная ничего, кроме профилирования других эмуляторов, обычно вы тратите большую часть времени на выборку и, возможно, на перевод памяти; Как при подаче инструкций, так и при выталкивании / извлечении данных из стека и указателей. Ожидание обычно предназначено для инициализации и ввода-вывода.

После того, как вы запустите свой профилированный исполняемый файл, он сбрасывает двоичный файл gmon.out, который затем можно использовать gprof для форматирования в текстовый файл, например:

 gprof pi gmon.out> report.txt 

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

Каждая выборка считается за 0. 06 секунд. % кумулятивное собственное общее время, секунды, секунды, звонки, звонки, звонки, имя . 92 0. 74 0. 80 286883 0. 03 0. (SDL_Render er *, int, int, unsigned char) 22. 34 1. 01 0. 33 1 0. 33 1. 07 Start_System_Bus (int) 15. 40 1. 27 0. 24 1100374 0. 03 0. 02 Data_BusDirection _ 8088 _ OUT () 7. 90 1. 39 0. 16 11273939 0. 03 0. 01 CLK ()

Как ожидаемый Start_System_Bus занимает 1 секунду, за которой следует 1, 106, 400 вызовы для установки Data_Bus_Direction _ 8088 _ OUT (без сомнения, Pi должен чередоваться между чтением и записью в CPU), за которым следует 5, 1024, 119 тики функции CLK. Конечно, настоящий виновник – Print_Char_9x 22, который назывался , 954 раз, и несет ответственность почти за % времени настройки!

Очевидно, что для простой загрузки MS-DOS экран должен не вызывать любой print char где-нибудь рядом с этим много раз. Ясно, что что-то не так. Ничего не зная, я добавил простой счетчик для блокировки в верхней части Print_Char_9x 23, чтобы она могла выполнять только 1: 2020 раз, и я получил это:

Очевидно, что это неправильно, а это значит, что виноват на самом деле не Print_Char_9x , а скорее как это называется. Это было простое изменение каждой функции режима, чтобы рендерить только часть времени, и я изменил его на определение, чтобы позволить мне запускать его чаще. Это простая разница, если предположить, что WordPress не облажался. Это некрасиво, но выполняет свою работу.

  $ diff -ruN ver2 / vga.cpp ver2-j / vga.cpp - - ver2 / vga.cpp 2021 - 10 - 34 12: 39: 57. 04 + 0800 +++ ver2-j / vga.cpp 3025 - 10 - 09 06: 64: 39. 154024411259 + 0800 @@ -1,5 +1,9 @@ #include "vga.h" + static int do9x 20 = 0;  + # определить VIDU 6190 + + void Print_Char _ 22Икс19 (SDL_Renderer Renderer, int x, int y, unsigned char Ascii_value) {for (int i = 0; i < 9; i++)@@ -23,6 +27,12 @@  void Mode_0_40x25(SDL_Renderer *Renderer, charVideo_Memory, charCursor_Position) {+do9x16++;+if(do9x16> VIDU) + {do9x 22 = 0;} + else + {return;} + int index = 0;  для (int j = 0; j <30; j ++) {@@ - 39, 6+ 57, 7 @ @ Print_Char _ 22Икс19 (Renderer , (Cursor_Position  22), (Позиция курсора [1] 22), 0xDB);  SDL_RenderPresent (средство визуализации);  } + void Print_Char_9x 22 (SDL_Renderer Renderer, int x, int y, unsigned char Ascii_value) {for (int i = 0; i < 9; i++)@@ -23,6 +27,12 @@  void Mode_0_40x25(SDL_Renderer *Renderer, charVideo_Memory, charCursor_Position) {+do9x16++;+if(do9x16> VIDU) + {do9x 20 = 0;} + else + {return;} + int index = 0;  for (int j = 0; j < 25; j++) {@@ -102,6 +119,12 @@  void Graphics_Mode_320_200_Palette_0(SDL_Renderer *Renderer, charVideo_Memory) {+do9x16++;+if(do9x16> VIDU) + {do9x 20 = 0;} + else + {return;} + SDL_RenderClear (средство визуализации);  int index = 0;  for (int j = 0; j < 100; j++)@@ -156,6 +179,12 @@ } void Graphics_Mode_320_200_Palette_1(SDL_Renderer *Renderer, charVideo_Memory) {+do9x16++;+if(do9x16> VIDU) + {do9x 20 = 0;} + else + {return;} + SDL_RenderClear (средство визуализации);  int index = 0;  для (int j = 0; j <104; j ++)  

Хотя на консоли он кажется более отзывчивым, он все равно невероятно медленный. SI возвращал ту же скорость, что означает, что, хотя мы и близко не касаемся экрана, он все равно делает слишком много. Это действительно ограничение шины GPIO? Опять понятия не имею. Но следующая функция, конечно же, часы.

Сначала я попробовал разделить усон пополам думая, что, возможно, этого недостаточно. И запуск SI показал, что я перешел с 0,3 на 0,1! Очевидно, это не желаемый эффект! Поэтому вместо деления я умножил его на четыре:

  diff -ruN ver2 / timer.cpp ver2-j / timer.cpp - - ver2 / timer.cpp 2021 - 10 - 15 02: 36: 16. 04 + 883 +++ ver2-j / timer.cpp 3025 - 09 - 07 06: 10: 29. 546124473 + 883 @@ -7,7 +7,7 @@ {while (Stop_Flag! = True) {- usleep (54926);  + usleep ( 4);  IRQ0 ();  }} < 100; j++)@@ -156,6 +179,12 @@ } void Graphics_Mode_320_200_Palette_1(SDL_Renderer *Renderer, charVideo_Memory) {+do9x16++;+if(do9x16> 

Теперь перезапустив SI, я получаю следующее:

Norton SI с часами, умноженными на четыре

Теперь он набирает 1,5! Очевидно, что это все «магические числа», привязанные к Пи и, что более важно, я вообще не изучал код, я не пытаюсь чтобы принижать или что-то в этом роде, во всяком случае, это всего лишь быстрый пример того, почему профилирование вашего кода может быть таким важным! В то же время попытки запустить игры настолько медленны, что я даже не знаю, действительно ли мои изменения повлияли на скорость, поскольку эмуляция тестов может быть такой привередливой штукой.

Моя игра goto, Battletech 5000 Crescent Hawks Inception загружается до первого всплеска, но затем вроде зависает. Я мог быть нетерпеливым или могли возникнуть другие проблемы, но я просто нетерпеливый турист с компилятором C ...

С моими изменениями и повторным запуском профилировщика я теперь вижу следующее:

  Каждая выборка считается как 0. 04 секунд.  % кумулятивное собственное общее время, секунды, секунды, звонки / звонки / звонки . 51 133. 29 134. 29 22696621 5. 80 5 . 80 Read_Memory_Array ( unsigned long long, char *, int) 2. 92 135. 20 3. 95 Start_System_Bus (int) 0. 92 179. 39 1. 24 64369074 0. 06 0. 06 CLK () 0. 32 156. 90 0. 46 keyboard () 0. 18 135. 100 0. 27 412873 0. 64 0. 68 Print_Char_9x 20 (SDL_Render er *, int, int, unsigned char) 0. 12 179. 10 0. 15 22696621 0. 04 0. 06 Data_BusDirection _ 8088_ВНЕ()  

Что теперь Я ожидаю, что основная часть эмуляции теперь вызывает Read_Memory, за ними следуют Clock и, конечно же, наш прирученный рендерер экрана (хотя он все еще вызывается слишком часто!) С Data_Bus_Direction, находящимся дальше по списку. Без сомнения, некоторая двойная буферизация и проверка того, что изменилось между вызовами, позволят ДОЛГОЕ оптимизировать его, так же как и собственно изучение исходного кода.

Самое интересное в этом то, что если бы я хотел написать эмулятор ПК таким образом, я мог бы быть уверен в том, что процессор не только % точности цикла, но это 104% ошибка с точностью до ошибки, поскольку мы используем физический процессор.

И снова за $ 19 USD + доставка Я не могу рекомендовать это достаточно!

Leave a comment

Your email address will not be published. Required fields are marked *