Важная информация

User Tag List

Показано с 1 по 7 из 7

Тема: Делаем игровую приставку. Как я собрал ретроконсоль в домашних условиях

  1. #1
    Junior
    Регистрация
    08.10.2006
    Адрес
    Cheboksary
    Сообщений
    24
    Спасибо Благодарностей отдано 
    1
    Спасибо Благодарностей получено 
    2
    Поблагодарили
    1 сообщение
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию Делаем игровую приставку. Как я собрал ретроконсоль в домашних условиях

    Оригинал статьи первоисточника (бесплатное чтение)

    Это перевод статьи Сержиу Виейры, впервые опубликованной в его блоге. Перевела Алёна Георгиева.

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

    Как все начиналось

    Меня зовут Сержиу Виейра, и я португальский парень, выросший в 80–90-е. Я всегда ностальгировал по ретроконсолям, особенно третьего и четвертого поколений. Несколько лет назад я решил глубже изучить электронику и попробовать собрать свою собственную видеоконсоль. Работаю я инженером софта, и ранее никакого опыта работы с электроникой у меня не было — если не считать сборки и апгрейда моего десктопа (что, конечно, не считается). Но, несмотря на отсутствие опыта, я сказал себе: «Почему нет?», купил несколько книг и комплектов электроники — и начал учиться.

    Я хотел собрать консоль, похожую на те, что всегда мне нравились, — что-то среднее между NES и Super Nintendo или между Sega Master System и Mega Drive. Все эти консоли обладали CPU, кастомным видеочипом (тогда он еще не назывался GPU) и аудиочипом — интегрированным или выделенным. Игры к ним распространялись на картриджах, которые обычно представляли собой аппаратные расширения с чипом ПЗУ и иногда другими компонентами.

    Моим изначальным планом было собрать консоль со следующими характеристиками.

    Никакой эмуляции, все игры/программы должны запускаться на реальном железе — не обязательно железе «из тех времен», просто достаточно шустром для своих задач.
    Настоящий ностальгический «ретрочип» процессора.
    Вывод на телевизор (аналоговый сигнал).
    Способность производить звук.
    Поддержка двух контроллеров.
    Прокрутка фона и движущиеся спрайты.
    Поддержка платформеров в стиле «Марио» (и, конечно, других типов игр).
    Возможность запускать игры и программы с карты SD.

    Я решил использовать поддержку SD-карт вместо картриджей, потому что запускать игры с карты намного практичней — к тому же на нее проще копировать файлы с компьютера. Используй я картриджи, пришлось бы накрутить куда больше железа и завести отдельное железо для каждой программы.

    Собираем консоль

    Видеосигнал

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

    Итак, я начал с микроконтроллера ATmega644, работающего на частоте 20 МГц, который посылал PAL-сигнал на телевизор. Поскольку сам по себе микроконтроллер этот формат не поддерживает, пришлось добавить внешний ЦАП.



    Наш микроконтроллер выдает восьмибитную цветность (RGB332: три бита на красный, три бита на зеленый и два — на синий), а пассивный ЦАП конвертирует всю эту красоту в аналоговый RGB. По счастью, в Португалии внешние устройства к телевизору чаще всего подключают через разъем SCART — и большая часть телевизоров принимает RGB-сигнал через него же.

    Подходящая графическая система

    Поскольку первый микроконтроллер я хотел использовать исключительно для передачи сигнала на телевизор (я назвал его VPU — Video Processing Unit), для графики в целом я решил использовать способ двойной буферизации.

    Я взял второй микроконтроллер для PPU (Picture Processing Unit) — ATmega1284 тоже на 20 МГц. Он должен генерировать изображение на чип ОЗУ (VRAM1), после чего первый микроконтроллер передаст содержимое другого чипа оперативки (VRAM2) на телевизор. После каждого кадра (два кадра в PAL или 1/25 секунды) VPU переключает чипы ОЗУ и передает изображение с VRAM1 на телевизор, пока PPU генерирует новое на VRAM2.

    Видеоплата получилась довольно сложной: мне пришлось использовать дополнительное железо, чтобы дать микроконтроллерам доступ к обоим чипам ОЗУ, а также ускорить доступ к памяти, которая к тому же используется для вывода видеосигнала методом битбэнга. Для этого я добавил в цепочку несколько чипов 74-й серии в качестве счетчиков, линейных селекторов, трансиверов и прочего.

    Прошивка для VPU и особенно для PPU тоже вышла довольно сложной, поскольку мне нужно было написать чрезвычайно производительный код — если я хотел получить все искомые графические возможности. Изначально я все писал на ассемблере, позже кое-что кодил на C.



    В итоге мой PPU генерировал изображение 224 × 192 пикселя, которое VPU транслировал на экран телевизора. Разрешение может показаться слишком низким, но на самом деле оно немногим меньше, чем у консолей-прототипов — они обычно имели разрешение 256 × 224. Зато более низкое разрешение позволило мне втиснуть больше графических фич в тот временной отрезок, что уходил на отрисовку каждого кадра.

    Прямо как в старые добрые времена, у моего PPU есть «фиксированные» графические возможности, которые можно настроить. Фон рендерится из символов размером 8 × 8 пикселей (их еще иногда называют тайлами). Это значит, что размер всего фона — 28 × 24 тайла. Для попиксельной прокрутки и возможности плавного обновления фона я сделал четыре виртуальных экрана 28 × 24 тайла — все они смежные и «обтекают» друг друга.



    Поверх фона PPU рендерит до 64 спрайтов шириной и высотой от 8 до 16 пикселей (то есть один, два или четыре символа), которые можно повернуть по вертикали, горизонтали или по обеим осям. Еще над фоном можно отрендерить оверлей — такую плашку размером 28 × 6 тайлов. Она пригодится для игр, где нужны элементы интерфейса поверх основного экрана (HUD), фон скроллится, а спрайты используются не только для подачи информации, но и для других целей.

    Другая «продвинутая» фича — возможность прокручивать фон в разных направлениях по отдельным строкам, что позволяет добавить эффекты типа ограниченного параллакс-скроллинга или разделенного экрана.

    Еще есть таблица атрибуции, которая позволяет задать каждому тайлу значение от 0 до 3. А дальше можно, например, назначить все тайлы с определенным значением на конкретную тайловую страницу или увеличить номер их символа. Это полезно, когда конкретные элементы фона постоянно меняются, — в таком случае CPU не нужно обновлять каждый тайл в отдельности, он может просто передать команду вроде «все тайлы со значением 1 увеличивают свое значение на 2». Разными способами этот подход используется, например, в играх с Марио, где на фоне двигаются знаки вопроса, или в других играх с постоянно льющимися водопадами.

    Процессор

    Когда была готова функциональная видеоплата, я приступил к работе над CPU — для своей консоли я выбрал Zilog Z80. Помимо того что Z80 — просто крутой ретропроцессор, у него есть отдельно 16 бит на память и 16 бит для I/O, чем другие подобные восьмибитные процессоры, например знаменитый 6502, похвастаться не могут. У того же 6502 есть только 16 бит памяти, а значит, эти 16 бит придется делить между собственно памятью и дополнительными устройствами: аудио, видео, ввода и прочими. Если же у нас есть отдельный участок для I/O, то он возьмет на себя все внешние устройства, а 16 бит памяти (то есть 64 Кбайт кода или данных) мы сможем использовать по прямому назначению.

    Для начала я соединил свой CPU с EEPROM, добросив немного тестового кода. Еще я прикрутил к CPU через участок I/O микроконтроллер, который связывается с ПК по RS-232, — чтобы проверить, нормально ли работает мой процессор и все остальные соединения. Этот микроконтроллер (двадцатимегагерцевый ATmega324) должен был стать IO MCU (микроконтроллером ввода-вывода) и отвечать за доступ к игровым контроллерам, карте SD, клавиатуре PS/2 и коммуникацию с компом через RS-232.


    Потом я прикрутил к процессору чип ОЗУ на 128 Кбайт, из которых были доступны 56 (это может показаться пустой тратой ресурса, но у меня были чипы ОЗУ только по 128 и по 32 Кбайт). Таким образом, вся память процессора состоит из 8 Кбайт ПЗУ и 56 Кбайт ОЗУ.

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

    Соединяем CPU и PPU

    Пришло время реализовать взаимодействие между CPU и PPU. Для этого я нашел «простое решение» — чип ОЗУ с двойным портом (то есть тот, который можно одновременно подключить по двум разным шинам). Он спас меня от накручивания новых микросхем типа линейных селекторов — и к тому же сделал доступ к оперативной памяти для обоих чипов практически одновременным. Также PPU связывается с CPU напрямую каждый кадр, активируя его немаскируемое прерывание (NMI). Это значит, что каждый кадр процессор прерывается (ценное умение для синхронизации и своевременного обновления графики).

    Каждый кадр взаимодействие между CPU, PPU и VPU развивается по следующему сценарию:

    - PPU копирует информацию с внешнего ОЗУ (на рисунке ниже обозначено как PPU RAM) на встроенное ОЗУ.
    - PPU посылает CPU сигнал немаскируемого прерывания.

    Одновременно с этим:

    -CPU немедленно обращается к функции немаскируемого прерывания и обновляет в PPU RAM информацию о состоянии графики в следующем кадре (программа должна выйти из прерывания до его начала);
    - PPU рендерит изображение, основываясь на информации, которую перед этим скопировал в один из двух ОЗУ графической системы (VRAM1 или VRAM2);
    - VPU посылает изображение с другого VRAM на телевизор.

    Примерно в то же время я добавил поддержку игровых контроллеров. Изначально я хотел использовать контроллеры Super Nintendo, но их разъем проприетарный — и достать его непросто. Поэтому я выбрал совместимые шестикнопочные контроллеры Mega Drive/Genesis: они используют стандартные, распространенные и доступные разъемы DB-9.


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

    У меня был процессор с поддержкой игровых контроллеров, который мог управлять PPU и загружать программы с SD-карты, так что… пришло время сделать игру. Я написал ее, конечно, на языке ассемблера Z80 — это заняло у меня пару дней (исходный код игры).


    Добавляем кастомную графику

    Все отлично, у меня есть рабочая консоль, но… этого недостаточно. Игры пока не могут использовать кастомную графику — только ту, что хранится в прошивке PPU. А единственный способ поменять встроенную графику — обновить прошивку. Поэтому я решил добавить отдельный чип ОЗУ с графикой (символьное ОЗУ, Character RAM) — он должен быть доступен PPU и загружать графику согласно инструкциям, пришедшим из CPU. При этом нужно было использовать как можно меньше новых компонентов, потому что консоль уже получилась довольно большой и сложной.

    Я нашел следующий выход: доступ к новому ОЗУ будет только у PPU, а CPU станет передавать ему информацию через PPU. И пока эти данные передаются, наше новое ОЗУ не будет использоваться для графики — его функции временно возьмет на себя встроенная графика.

    После передачи данных процессор переключится из режима встроенной графики в режим работы с символьным ОЗУ (CHR RAM на схеме ниже), и PPU сможет использовать кастомную графику. Возможно, это не идеальное решение, но оно работает. В итоге новое ОЗУ имело объем 128 Кбайт и могло хранить 1024 символа размером 8 × 8 пикселей для фона и 1024 символа того же размера для спрайтов.


    И наконец, звук

    Реализацию звука я оставил на финал. Изначально я собирался дать своей консоли те же звуковые возможности, что у Uzebox, и встроить микроконтроллер, который генерировал бы четыре канала PWM-звука. Однако я выяснил, что можно относительно легко достать винтажные чипы, — и заказал несколько чипов YM3438, работающих на принципе [частотно-модуляционного синтеза] (https://en.wikipedia.org/wiki/Freque...tion_synthesis). Они полностью совместимы с YM2612, которые установлены в Mega Drive/Genesis. Установив этот чип, я получаю музыку качества Mega Drive и звуковые эффекты, которые производит контроллер. CPU управляет звуковым модулем (я назвал его SPU, Sound Processor Unit, — он отдает команды YM3438 и сам производит звуки) снова через маленькое ОЗУ с двойным портом, на сей раз емкостью всего в 2 Кбайт.

    Так же как у графического, у звукового модуля есть 128 Кбайт на хранение звуковых патчей и семплов PCM. Процессор же выгружает информацию в эту память через SPU. Таким образом, процессор может как велеть SPU воспроизводить команды из этого ОЗУ, так и обновлять команды для SPU каждый кадр.

    CPU управляет четырьмя PWM-каналами через четыре кольцевых буфера, которые есть в специальном ОЗУ (SPU RAM на схеме ниже). SPU проходит через эти буферы и выполняет имеющиеся в них команды. Таким же образом работает еще один кольцевой буфер в SPU RAM — он обслуживает чип частотно-модуляционного синтеза (YM3438).

    Взаимодействие между процессором и звуковым модулем похоже на историю с графикой — и устроено по следующей схеме:

    - SPU копирует информацию из SPU RAM во встроенную оперативку.
    - SPU ждет сигнала NMI от PPU (для синхронизации).

    Одновременно с этим:

    - процессор обновляет буферы PWM-каналов и чипа частотно-модуляционного синтеза;
    - SPU выполняет команды в буферах согласно информации, сохраненной во встроенной памяти.

    Пока все это происходит, SPU непрерывно обновляет PWM-звук с частотой 16 кГц.


    Конечный результат

    После разработки всех модулей я поместил некоторые из них на макетные платы. Для модуля CPU я сумел придумать и заказать кастомную плату. Не знаю, буду ли делать то же самое для других модулей, — полагаю, мне довольно сильно повезло получить рабочую кастомную плату с первой попытки. Только звуковой модуль пока что остается в виде макета.

    Вот как выглядит консоль на момент написания этого текста:


    Архитектура

    Схема ниже иллюстрирует, какие компоненты входят в каждый модуль и как они взаимодействуют друг с другом. Единственное, что не показано, — это сигнал в форме NMI, который PPU передает непосредственно процессору каждый кадр, а также аналогичный сигнал, передаваемый SPU.


    - CPU: Zilog Z80, работающий на частоте 10 МГц.
    - CPU-ROM: EEPROM на 8 Кбайт, содержит код загрузчика.
    - CPU-RAM: 128 Кбайт оперативной памяти (из них используются 56 Кбайт), содержит код и данные для программ/игр.
    - IO MCU: ATmega324, служит интерфейсом между CPU и RS-232, клавиатурой PS/2, игровыми контроллерами и файловой системой SD-карты.
    - PPU-RAM: двухпортовое ОЗУ на 4 Кбайт, это интерфейсное ОЗУ между CPU и PPU.
    - CHRRAM: 128 Кбайт оперативки, содержит кастомные тайлы фона и спрайтовую графику (8 × 8 пикселей каждый символ).
    - VRAM1, VRAM2: 128 Кбайт оперативки (используются 43 008 байт), служат для хранения кадрового буфера; информацию в них записывает PPU, а считывает — VPU.
    - PPU (Picture Processing Unit): ATmega1284, отрисовывает кадр и отправляет его в кадровый буфер.
    - VPU (Video Processing Unit): ATmega324, считывает кадровый буфер и генерирует RGB и PAL-сигнал.
    - SPU-RAM: двухпортовое ОЗУ на 2 Кбайт, служит интерфейсом между CPU и SPU.
    - SNDRAM: 128 Кбайт оперативки, содержит патчи PWM, семплы PCM и блоки инструкций для частотно-модуляционного синтеза.
    - YM3438: одноименный чип частотно-модуляционного синтеза.
    - SPU (Sound Processing Unit): ATmega644, генерирует звук на базе PWM и управляет YM3438.

    Итоговые характеристики

    Процессор:

    - восьмибитный CPU Zilog Z80 с частотой 10 МГц;
    - 8 Кбайт постоянной памяти для загрузчика;
    - 56 Кбайт оперативной памяти.

    Ввод/вывод (I/O):

    - чтение данных с SD-карт файловых систем FAT16/FAT32;
    - чтение и запись на порт RS-232;
    - два игровых контроллера, совместимых с Mega Drive/Genesis;
    - клавиатура PS/2.

    Видео:

    - разрешение 224 × 192 пикселя;
    - 25 кадров в секунду;
    - 256 цветов (схема RGB332);
    - виртуальное фоновое пространство 2 × 2 (448 × 384 пикселя) с двунаправленной попиксельной прокруткой, которое описывают четыре именные таблицы;
    - 64 спрайта высотой и шириной 8 или 16 пикселей с возможностью развернуть их как по вертикали, так и по горизонтали;
    - фон и спрайты, состоящие из символов 8 × 8 пикселей каждый;
    - символьное ОЗУ с 1024 фоновыми и 1024 спрайтовыми символами;
    - независимая горизонтальная прокрутка фона по кастомным строкам на 64;
    - независимая вертикальная прокрутка фона по кастомным строкам на 8;
    - наложение плашки размером 224 × 48 пикселей с прозрачностью или без нее;
    - таблица атрибуции для фона;
    - RGB и композитный PAL-вывод через разъем SCART.

    Звук:

    - генерируемый с помощью PWM восьмибитный четырехканальный звук с заранее заданными формами волны (меандр, синусоида, пилообразная, шумовая и так далее);
    - восьмибитные и восьмикилогерцевые семплы PCM на одном из каналов PWM;
    - чип частотно-модуляционного синтеза YM3438 с обновляемыми инструкциями на частоте 50 Гц.

    Разработка софта для консоли

    Первый кусок софта, написанный для консоли, — это загрузчик. Он хранится в постоянной памяти процессора и занимает до 8 Кбайт. Он же использует первые 256 байт оперативки процессора. Загрузчик — первый софт, запускаемый на процессоре. Его цель — показать программы, доступные на SD-карте. Эти программы хранятся в файлах, которые содержат скомпилированный код и могут также содержать данные кастомной графики и звука.

    После выбора программы она загружается в оперативку процессора, символьное ОЗУ и ОЗУ звукового модуля. Там соответствующая программа выполняется. Код программ, загружаемых на консоль, может занимать до 56 Кбайт памяти — за исключением первых 256 байт; также, конечно, нужно учитывать объем стека и оставлять место для данных.

    И загрузчик, и программы для этой консоли разрабатываются похожим образом. Коротко поясню, как я их сделал.

    Маппинг памяти и ввода-вывода

    При разработке для консоли следует обратить особое внимание на то, как CPU может получить доступ к другим модулям, поэтому представление памяти и ввода-вывода имеет решающее значение.

    Процессор обращается к своему загрузчику на ПЗУ и ОЗУ через память. Представление памяти выглядит так:


    К PPU-RAM и SPU-RAM, а также к IO MCU он обращается через участок ввода-вывода. Представление участка ввода-вывода процессора будет таким:


    Внутри представления участка ввода-вывода IO MCU, PPU и SPU имеют свои конкретные адреса.

    Управление PPU

    Мы можем управлять PPU с помощью записи на PPU-RAM, а доступ к PPU-RAM, как мы знаем из таблицы выше, организован через участок ввода-вывода с адресов от 1000h до 1FFFh.

    Вот как выглядит этот диапазон адресов, если представить его более подробно:


    Состояние PPU (PPU Status) может принимать следующие значения:

    0 — режим встроенной графики;
    1 — режим кастомной графики;
    2 — режим записи в символьное ОЗУ;
    4 — запись закончена, ожидание подтверждения от CPU.

    А вот пример того, как можно работать со спрайтами. Консоль может рендерить до 64 спрайтов одновременно. Информация об этих спрайтах передается через адреса с 1004h по 1143h (320 байт), по 5 байт информации на каждый спрайт (5 × 64 = 320 байт):

    Смешанный байт (каждый его бит — это флаг: Active, Flipped_X, Flipped_Y, PageBit0, PageBit1, AboveOverlay, Width16 и Height16).
    Символьный байт (какой символ является спрайтом на странице, описанной соответствующими флагами смешанного байта).
    Байт хромакея (описывает, какой цвет будет прозрачным).
    Байт позиции по горизонтали (оси X).
    Байт позиции по вертикали (оси Y).

    Итак, чтобы сделать спрайт видимым, мы должны присвоить флагу Active значение 1, а также установить координаты, при которых он станет видимым (координаты x = 32 и y = 32 разместят спрайт в левый верхний угол экрана; если значения x и y будут меньше, то спрайт окажется за пределами экрана — частично или полностью). Затем мы можем присвоить ему символ и определить, какой цвет спрайта будет прозрачным.

    Например, если мы хотим сделать видимым десятый спрайт, мы должны присвоить адресу ввода-вывода 4145 (1004h + (5 x 9)) значение 1. Затем устанавливаем координаты спрайта — скажем, x = 100 и y = 120, — присвоив адресу 4148 значение 100, а адресу 4149 — значение 120.

    Кодим на ассемблере

    Один из способов написать программу для нашей консоли — использовать язык ассемблера.

    Ниже — пример кода, который заставляет первый спрайт двигаться и сталкиваться с углами экрана:

    PHP код:
    ORG 2100h

    PPU_SPRITES
    EQU $1004
    SPRITE_CHR
    EQU 72
    SPRITE_COLORKEY
    EQU $1F
    SPRITE_INIT_POS_X
    EQU 140
    SPRITE_INIT_POS_Y
    EQU 124

    jp main

    DS 
    $2166-$

    nmi:
        
    ld bcPPU_SPRITES 3
        ld a
    , (sprite_dir)
        and 
    a1
        jr z
    subX
        in a
    , (c) ; increment X
        inc a
        out 
    (c), a
        cp 248
        jr nz
    updateY
        ld a
    , (sprite_dir)
        xor 
    a1
        ld 
    (sprite_dir), a
        jp updateY

    subX
    :
        
    in a, (c) ; decrement X
        dec a
        out 
    (c), a
        cp 32
        jr nz
    updateY    
        ld a
    , (sprite_dir)
        xor 
    a1
        ld 
    (sprite_dir), a

    updateY
    :
        
    inc bc
        ld a
    , (sprite_dir)
        and 
    a2
        jr z
    subY
        in a
    , (c) ; increment Y
        inc a
        out 
    (c), a
        cp 216
        jr nz
    moveEnd
        ld a
    , (sprite_dir)
        xor 
    a2
        ld 
    (sprite_dir), a
        jp moveEnd

    subY
    :
        
    in a, (c) ; decrement Y
        dec a
        out 
    (c), a
        cp 32
        jr nz
    moveEnd
        ld a
    , (sprite_dir)
        xor 
    a2
        ld 
    (sprite_dir), a

    moveEnd
    :
        
    ret

    main
    :
        
    ld bcPPU_SPRITES
        ld a
    1
        out 
    (c), a  Set Sprite 0 as active
        inc bc
        ld a
    SPRITE_CHR
        out 
    (c), a  Set Sprite 0 character
        inc bc
        ld a
    SPRITE_COLORKEY
        out 
    (c), a  Set Sprite 0 colorkey
        inc bc
        ld a
    SPRITE_INIT_POS_X
        out 
    (c), a  Set Sprite 0 position X
        inc bc
        ld a
    SPRITE_INIT_POS_Y
        out 
    (c), a  Set Sprite 0 position Y

    mainLoop
    :    
        
    jp mainLoop

    sprite_dir
    :     DB 0 
    Используем инструменты C

    Еще можно писать программы для консоли на C, используя компилятор SDCC или другие кастомные инструменты. Разработка так идет быстрее, хотя производительность кода, конечно, падает.

    В качестве примера покажу код на С, который выполняет ту же самую задачу, что и приведенный выше ассемблерный. Чтобы облегчить обращение к PPU, я здесь использовал библиотеку.

    PHP код:
    #include <console.h>

    #define SPRITE_CHR 72
    #define SPRITE_COLORKEY 0x1F
    #define SPRITE_INIT_POS_X 140
    #define SPRITE_INIT_POS_Y 124

    struct s_sprite sprite = { 1SPRITE_CHRSPRITE_COLORKEYSPRITE_INIT_POS_XSPRITE_INIT_POS_Y };
    uint8_t sprite_dir 0;

    void nmi() {
        if (
    sprite_dir 1)
        {
            
    sprite.x++;
            if (
    sprite.== 248)
            {
                
    sprite_dir ^= 1;
            }
        }
        else
        {
            
    sprite.x--;
            if (
    sprite.== 32)
            {
                
    sprite_dir ^= 1;
            }
        }

        if (
    sprite_dir 2)
        {
            
    sprite.y++;
            if (
    sprite.== 216)
            {
                
    sprite_dir ^= 2;
            }
        }
        else
        {
            
    sprite.y--;
            if (
    sprite.== 32)
            {
                
    sprite_dir ^= 2;
            }
        }

        
    set_sprite(0sprite);
    }

    void main() {
        while(
    1) {
        }

    Кастомная графика

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

    Цель в том, чтобы перевести всю необходимую графику в двоичную форму — в таком виде загрузчик консоли сможет грузить ее в символьное ОЗУ. Чтобы этого добиться, я начал с нескольких изображений уже нужного размера — в данном случае они предназначены для фона сразу в нескольких игровых ситуациях.


    Поскольку кастомная графика состоит из четырех страниц по 256 символов размером 8 × 8 пикселей для фона и четырех таких же страниц для спрайтов, я преобразовал графику с картинки выше в файл PNG для каждой страницы, используя специальный инструмент (за исключением повторяющихся результирующих символов).


    А следом я использовал еще один инструмент, чтобы сконвертировать результат в бинарник с символами 8 × 8 пикселей в цветовой схеме RGB332.


    В результате получаются двоичные файлы, состоящие из символов 8 × 8 пикселей (символы в памяти являются смежными, каждый занимает 64 байта).

    Звук

    Образцы звуковых волн конвертируем в восьмибитные и восьмикилогерцевые семплы PCM. Патчи звуковых эффектов и музыки PWM можно составить, используя заранее определенные инструкции. Что касается ямаховского чипа частотно-модуляционного синтеза YM3438, то для него я нашел приложение DefleMask. С помощью DefleMask делают синхронизированную с PAL музыку для звукового чипа YM2612 от Genesis, который совместим с нашим YM3438.

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

    Бинарники со всеми тремя видами звуков объединяются в один двоичный файл, который загрузчик потом сможет загрузить в ОЗУ звукового модуля (SNDRAM).


    Соберем все вместе

    Программный бинарник, графику и звук нужно соединить в один файл формата PRG. В файле PRG есть заголовок, который сообщает, использует ли программа кастомную графику и/или звук и каков размер каждого из этих компонентов. Там же содержится вся прочая соответствующая двоичная информация.

    Затем получившийся файл можно разместить на карту SD, загрузчик консоли оттуда его прочитает, пошлет всю необходимую информацию соответствующим ОЗУ и запустит программу.


    Используем эмулятор для разработки

    Чтобы облегчить разработку софта для консоли, я написал на C++ эмулятор, используя wxWidgets. Чтобы эмулировать процессор, я обратился к библиотеке libz80.

    Я добавил в эмулятор несколько отладочных функций. В частности, я могу оказаться в конкретной точке останова и пройти из нее по всем ассемблерным инструкциям. Также есть связь с исходным кодом, если программа была скомпилирована на C. Что касается графики, то тут я могу проверить, что хранится на страницах тайлов и в именных таблицах (представление фона размером в четыре экрана), а также что находится в символьном ОЗУ (CHRRAM).

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


    Демонстрируем работу консоли

    Видео из этого раздела — это съемка экрана электронно-лучевого телевизора на камеру телефона. Прошу прощения, что качество не очень высокое.

    Запускаем с помощью бейсика и клавиатуры PS/2. На этом видео я — сразу после создания первой программы — записываю напрямую в ОЗУ графического модуля (PPU-RAM) через участок ввода-вывода команды включить и настроить спрайт, а в конце переместить его.



    Далее -> Окончание и оригинал статьи первоисточника (бесплатное чтение)
    Последний раз редактировалось !dx; 14.10.2019 в 15:12.

  2. Эти 2 пользователя(ей) поблагодарили !dx за это полезное сообщение:

    Black Cat / Era CG (16.10.2019), murgatroid_79 (18.10.2019)

  3. #1
    С любовью к вам, Yandex.Direct
    Размещение рекламы на форуме способствует его дальнейшему развитию

  4. #2
    Veteran Аватар для bigral
    Регистрация
    12.07.2006
    Адрес
    Kiev/Ukraine
    Сообщений
    1,878
    Спасибо Благодарностей отдано 
    1
    Спасибо Благодарностей получено 
    20
    Поблагодарили
    16 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    очередной soft эмулятор по типу avr spectrum-a, странно что автор не додумался 20 avr-ов поставить мотивируя конструкцию дешевой ценой на микроконтроллеры

  5. #3
    Veteran Аватар для Eltaron
    Регистрация
    16.01.2005
    Адрес
    Ekaterinburg
    Сообщений
    1,364
    Спасибо Благодарностей отдано 
    3
    Спасибо Благодарностей получено 
    25
    Поблагодарили
    19 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от bigral Посмотреть сообщение
    очередной soft эмулятор по типу avr spectrum-a, странно что автор не додумался 20 avr-ов поставить мотивируя конструкцию дешевой ценой на микроконтроллеры
    Ну охренеть, давайте ещё и устройства с железным z80 называть софт-эмуляторами! Как далеко вообще может зайти этот безумный пуризм?
    Последний раз редактировалось Eltaron; 17.10.2019 в 20:05.
    Граф Дракула наш кумир, патамушта он вомпир!

  6. #4
    Guru Аватар для NEO SPECTRUMAN
    Регистрация
    22.05.2011
    Адрес
    Дзержинск
    Сообщений
    3,253
    Спасибо Благодарностей отдано 
    38
    Спасибо Благодарностей получено 
    41
    Поблагодарили
    32 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от !dx Посмотреть сообщение
    Вот как выглядит консоль на момент написания этого текста:
    вот это дичь

    Цитата Сообщение от !dx Посмотреть сообщение
    как собирал
    скорей всего как делал\строил

    "собирал" ассоциируется с "собирал по схеме уже давно изобретенное"

    - - - Добавлено - - -

    что примечательно
    там где наши кинули бы жгуты мгтф-а

    тут ide-шные или флоповодные (на глаз не отличу)
    шлейфа о_О



    что примечательно 2
    когда у нас по 15 лет не эмулируются железки
    ихние пилятели компьютеров БЕЗ ПРОЦЕССОРА
    ВНЕЗАПНО вмесете с постоянно вкидывают и эмулятор своего бессмысленного поделия...

  7. #5
    Veteran Аватар для Eltaron
    Регистрация
    16.01.2005
    Адрес
    Ekaterinburg
    Сообщений
    1,364
    Спасибо Благодарностей отдано 
    3
    Спасибо Благодарностей получено 
    25
    Поблагодарили
    19 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от NEO SPECTRUMAN Посмотреть сообщение
    ВНЕЗАПНО вмесете с постоянно вкидывают и эмулятор своего бессмысленного поделия...
    Почему внезапно? Довольно очевидно, что он именно на эмуляторе всё и делал-отлаживал. Не с железкой же тыкаться, это непродуктивно.
    Граф Дракула наш кумир, патамушта он вомпир!

  8. #6
    Guru Аватар для JV-Soft
    Регистрация
    14.05.2015
    Адрес
    г. Харьков, Украина
    Сообщений
    2,489
    Спасибо Благодарностей отдано 
    31
    Спасибо Благодарностей получено 
    58
    Поблагодарили
    16 сообщений
    Mentioned
    2 Post(s)
    Tagged
    1 Thread(s)

    По умолчанию

    !dx, прекрасный пример творчества !

    Арфы нет ,возьмите бубен
    Безумие это повторение одного и того же в ожидании другого результата.


    Сайт http://p-45.zzz.com.ua
    Amiga A500
    Восстановлен(2018) дополнен и в строю - Pentagon (1991) 1024k (256kb ROM 4 конфигурации ПЗУ)/turbo 7 мгц/кеш 32кб/covox/ TS /AY mouse/fdd 3.5" /Nemo-Ide/10gb HDD (DNA-OS)
    Восстановлен(2015) и в строю - Харьков 128
    Восстановлен(2016) ZX-Дигитайзер

    Ждут паяльника - пентагон 48 , pentagon 128.
    [свернуть]

  9. #7
    Junior
    Регистрация
    08.10.2006
    Адрес
    Cheboksary
    Сообщений
    24
    Спасибо Благодарностей отдано 
    1
    Спасибо Благодарностей получено 
    2
    Поблагодарили
    1 сообщение
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от JV-Soft Посмотреть сообщение
    !dx, прекрасный пример творчества !
    Тут мое творчество в том, что статья эта здесь "зеркально" разместилась, ибо автор сего не я. Ссылки на оригинальный ресурс вверху и внизу "репоста". Но, спасибо, "репост" тоже творчество ))

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Похожие темы

  1. Ответов: 0
    Последнее: 23.04.2019, 17:09
  2. [Москва] Продам игровую приставку Atari Jaguar
    от sergi в разделе Барахолка (архив)
    Ответов: 1
    Последнее: 29.12.2011, 09:51
  3. продам игровую приставку 8 бит
    от p@lex в разделе Барахолка (архив)
    Ответов: 2
    Последнее: 23.12.2010, 19:03
  4. Ответов: 4
    Последнее: 18.05.2007, 12:56

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •