+12.71
Рейтинг
39.16
Сила
  • avatar SAA
  • 0
«интереснее» в том что от стандартных корок, которые не тянули, зато имели свои инструменты вроде ЯВУ и трансляторов ассемблера, пришлось перейти к собственной архитектуре которая обеспечивала приличную пропускную, но потребовала написать и трансляторы и компилятор и отладчики. Это все очень интересно, любительство оно такое в чем то беспощадное и требовательное, азартное :)

Суть же телодвижений с спрайт-тайтлами, ну в какой то момент показалось что имея аппаратное наложение слоев друг на друга, можно наслаждаться отрисовкой в квази-фрейм буфере (можно так группировать тайтлы/спрайты что в области графического представления, в области спрайтов — будете работать с массивом небольшой размерности, скажем 256х128 или 256х256. И это казалось что ли убер экономией видеопамяти, казалось конечно… Ну аппаратное наложение слоев конечно сильно экономило время и такты, но вот отрисовка в области спрайтов какой либо анимации… все равно вылилось в полноценный фрейм-буфер, который из блочного ОЗУ ПЛИС само собой переполз в SDRAM.
  • avatar SAA
  • 1
Да все отлично описали! Особенно пункт б) автоскролинг реализованный на счетчике адреса чтения данных спрайта со смещением — классика, там по сути делать ничего не надо, пиши смещение и весь экран горизонтально или вертикально скролируется. Но :) хотелось чего то очень продвинутого и универсального, без заморочек, настолько быстрого что бы можно было писать в ЯВУ, даже без оптимизации — и в итоге для нас это оказался clear render для frame buffer. Рискну предположить почему так, потому что читать про преодолевание трудностей это одно — здорово, очень интересно, испытываешь кураж прошедшего головоломное приключение человека и гордишься им. Однако читая ты не можешь оставить без анализа те трудности которые пришлось преодолевать другому и заранее закладываешь себе дорожку без них. С одной стороны — красивый хак, с другой — универсальность и избежание хака. Ну это так мысли в слух, возможно я не прав :)
  • avatar SAA
  • 1
Таки нашел, глубоко в историю ушло :) Если мне опять же память не изменяет то тут 6809, и конечно с одним единственным танком он справляется. Танк состоит из 4 спрайтов. Все в одном слое, видео с наложением слоев не нашел, только фото монитора. Видно что на один такой примитив да успевает отрабатываться.
Наложение с прозрачностью
Демо вращение гусениц в 4 спрайтах
  • avatar SAA
  • 1
Спрайт и Тайл — думал это одно и тоже. Представим себе массивы 32 x 24 таких массивов 3 штуки.

char layer[3][32*24];

Я перешел на Си, что было понятно о чем я. Это не сам спрайт, разумеется. Но, ячейка массива содержит число от 0 до 255, являющееся индексом (idx) спрайта. До спрайта мы еще не добрались, так как лежит он в каком то линейном участке памяти по смещению idx*sizeof(sprite). Если спрайт 32х32 8 бит (говорю «если» потому что уже не помню точно что там было) цвета каждой точки то его sizeof = 1024 байта или 1Кб. Вопрос доступны ли они все в пространстве процессора? Конечно нет, так как даже 64 таких спрайта займут все адресное пространство к примеру 6502. Но железке на это наплевать так как считанный адрес спрайта, например address = layer[0][idx] << 10, это просто шина нужной разрядности с которой адрес приходит на ША блочного ОЗУ ПЛИС. Конечно там начинается сложность с тем что развертка по строке движется линейно по экрану, переходя с одного спрайта на другой и поэтому часть адреса постоянна, а часть обновляется счетчиком — но не об этом сейчас. И так у нас три канала по которым летят данные из области ОЗУ с битмапом спрайта, с его непосредственным содержимым. Эти три канала D0,D1,D2 мы можем перемешать по принципу встреченного байта прозрачности (для простоты просто нуля). Если в D0 в данном такте появился 0 то значит нужно выдать ARGB из канала D1, но если и в канале D1 у нас тоже прозрачный пиксель (ноль), то берем ARGB из канала D2. Это все довольно просто описывается в HDL, не в ЯВУ.
Имея такую аппаратную примочку мы освобождаем процессор только от маскирования потока из областей спрайтов и необходимости постоянно лить данные спрайтов, когда они меняются на экране, в фрейм-буфер. Для Спрайтового движка понятия фрейм-буфер я считаю не применимо. Это по сути текстовый экран только буква не 8х8 а 32х32 и летит не с знакогенератора (который может быть и в ПЗУ), а из области ОЗУ со спрайтом. Мы меняем код спрайта в этом слое и в течении 12 тактов, без каких либо затрат на пересылку байт битмапа 32х32 (а это прилично!) получаем смену изображения. Все что нам нужно это просто загодя (за 12 тактов раньше) начать формировать растр по строке. Так как данные будут сыпаться в этой конвейер друг за другом, перерывов между ними не будет и работать можно с дичайшим FPS меняя спрайты всего экрана чуть ли не 100 раз за кадр. Заменить 768 байт на новые — это не вопрос даже для 6502 с 1,75МГц клока.
Но… грустно становится когда нам надо произвести скролинг внутри знакоместа, ну кого к примеру устроит резкий прыжок танчика на 32 точки вперед? И вот тут бывает так что танчик то не 32х32, а нечто побольше. Мало того хотелось бы двигать его не только прямо, а скажем по диагонали или вообще под произвольным углом. И мы начинаем копировать байты внутри спрайтовой области ОЗУ. А поскольку плана у нас три, и мы хотели бы что то еще двигать на задних планах, траву, кроны деревьев, какие либо еще произвольные объекты. То увы даже загнав имеющуюся корку 6502 на 50МГц (выше не получалось) удается разве что 20FPS обеспечить. Разговор конечно не про один объект, а про кучу различных. И даже 6809 выдавал до 30FPS на сценах, как ниже. Не из за одного «танчика», конечно это все затевалось. У меня к сожалению ничего продемонстрировать не осталось из того проекта, так как уже на симулях стало понятно — что это все не то. Но можно показать к чему пришли.
В итоге это все вылилось в фрейм-буфер 1024х768х8бит с двойной буферизацией, и то что там применено в качестве процессора :) отдельная песня — вывозило только 75% заполнения экрана. Но вот даже такая демка, которую для этой аппаратуры писал мой товарищ — 8 битные корки и даже 16 битные корки процессоров ложило наглухо. А это наверное 10%-20% имеющейся пропускной.
Demo clear render
  • avatar SAA
  • 0
т.е. стандартно мы бы накладывали поверх спрайт скажем с травой, спрайт с дорогой, поверх которой накладывался бы «танчик». Т.е. все три спрайта находятся в своих пространствах по одной координате X,Y. Но маскируются друг другом с приоритетом по каналу. Канал 1 перекрывает канал 2, канал 2 перекрывает канал 3, пиксель с цветом «прозрачный» может вылезти на канал 1 из любого канала. Наверное я все еще сильней запутал, но смысл очень простой. Есть три экрана спрайтов заданных байтом 32х24 знакоместа. Это три независимых плана, которые контроллер объединяет в один видимый на экране монитора. Спрайты находятся в своих областях ОЗУ как объекты 32х32х256. Прозрачный цвет не маскируется (допустим 0x00) и через него мы можем увидеть любой пиксель спрайта нижнего плана (2,3).
  • avatar SAA
  • 1
Спасибо за статейку! Читать такие вещи очень интересно, на хабре читал про 6809 с огромным интересом и продолжение про apple вполне себе дополняет. Особенно интересно было узнать что у 65С816 есть такие инструкции и DP.

Как то с другом мы обсуждали возможности в разрезе Clear reander engine vs Sprite/Tile Engine. Ну и закусились на тему, а что было бы если бы у бабушки были бы… если бы было три спрайтовых плана с приоритетом отрисовки (подобие Z-буфера). Было бы мол тогда интересней программить в ЯВУ те же танчики? Ну сказано сделано, примерно недели две возни с верилогом и готовый адаптер для отрисовки такой графики появился. Повозится конечно пришлось, что бы выйти на времянки 1024х768 (ЕМНИП). 12 стадий в конвейере устройства, причем регистры на стадии в ФУ конвейера, вовсе нет — простенькие мультиплексоры даже пришлось огораживать. Какое то время мне казалось что я не вывожу на этой ПЛИС задачу и как бы вообще на голую шину не пришлось ставить регистры в отрезки между ФУ. Но нет обошлось 12 стадиями. Жуткое чудо юдо.


Да, но это я к тому что вот это вот убер устройство хотели повесить на корку 6502, ну и оно не потянуло. Потом я рассматривал 6809 и тоже нет, не потянуло :) Потому что, если бы только проблемы отрисовки спрайтов поверх других спрайтов с аппаратным перекрытием, а ведь еще и скроллинг этих спрайтов попиксельно в памяти самих спрайтов. После этого как то утвердился в мысли о том что такого рода аппаратура мало что даст для программера, нужен именно прокаченный процессор и желательно такой который может за такт не одну инструкцию, и при это еще бы и асинхронно.
  • avatar SAA
  • 1
Заход все равно получился не плохой, про триады вполне себе! Безусловно тема с ик145 требует огромных усилий.
  • avatar SAA
  • 1
Вот так сукины дети прикрутили CRC вместо счетчика. Так то оно конечно и быстрей и меньше ресурсов сдвигать и хорить. Ай да сукины дети! Примерно такая же идея мне пришла по поводу расчета CRC ключевых слов Форта или Бейсика, для того что бы не проводить поиск по токену, а иметь всегда уникальное смещение в результате расчета по токену из строки полинома CRC-16, да можно даже CRC-8 :)

P.S. Спасибо за статью! Порадовали. Где то в комментах проскакивало, что Вы собираетесь пойти крестовым походом на 6502, буду рад почитать.
  • avatar SAA
  • 1
Ощущаю что мы где то рядом с пониманием. :) Поэтому вот такую еще картинку предлагаю глянуть что бы быть уверенным что мы об одном и том же говорим.
Распределение внутренних ресурсов в кольце МК61

Условно я разбил кольцо на пакеты по 42 тетрады каждая. 15 пакетов или еще можно сказать фреймов. Фрейм используется для хранения данных и кода одновременно. Т.е. в одном фрейме-пакете залегает например регистр Р0 и код для шагов 0-7 программы. Либо например регистр стека Х1, регистр Р9 и 64-70 шаг программы.
На что я хочу обратить внимание (сейчас я прям очень грубо представлю) — что за один проход по кольцу сложить X и Y нельзя, ну потому что мы схватили младший разряд X, добрались до следующего фрейма в котором лежит младший разряд Y и уже прошло 42 такта (из 4 фаз). После сложения этих разрядов у нас сформировался перенос CARRY, но применить его к следующему разряду X+Y+CARRY мы сможем только после прокрутки кольца полностью. Но что меня особенно заботит это то что так складывать два числа без учета их порядка занятие совершенно бессмысленное, мантису надо сдвинуть таким образом что бы порядки чисел совпадали. Я специально утрировал ситуацию что бы показать как бы надо было бы действовать если бы не было внутренних регистров колец внутри ИК145, а они есть R и ST, адресация в них привязана к такту, т.е. не независимая, но что характерно меняется по кольцу от 0 до 41, поскольку R и ST это тоже замкнутые кольца величиной 42 тетрады.

С таким буфером как R и ST уже можно себе позволить заглотить мантису и порядок X из магистрального кольца целиком, впрочем поскольку мантиса лежит не подряд то и в буфер она тоже ляжет не подряд а с разрывом. Как правило на этом месте у меня уже начинает дергаться глаз :) поскольку при подходе к Y магистральное кольцо синхронизируются с внутренним регистром R и ST то сложение X[i]+Y[i]+CARRY становится возможным. Но блин надо же еще нормализовать мантису по порядку. :)
  • avatar SAA
  • 1
Да вполне резонно утверждаете. Но из схемы выбивается последняя последовательность 0,1,2,3,4,5 — заключительная, следующая после 6-7-8.

В кольце залегают группы тетрад, обратите внимание 1 р-р мантиссы внутренних регистров 0… Е, лежат первой тетрадой в кольце, затем следуют тетрада X1, X, Y, Z, T. Поэтому если к примеру мы работаем с регистром X осуществляя какую то операцию над ним, то выглядит это так:
1 такт из 4 фаз работаем над 1 разрядом мантиссы X- пропуск 2 тактов из 4 фаз каждый — 1 такт из 4 фаз работаем над 2 разрядом мантиссы Х — и так далее. Как тут быть? Мне кажется кольцо гоняется по кругу дикое кол-во раз ради прохождения одной операции к примеру сложения.
  • avatar SAA
  • 1
Попытаюсь ответить хотя бы на первую часть вопроса. В ОЗУ кольца на самом деле тетрад нет, там однобитный кольцевой регистр ячейка которого конденсатор и транзистор. Этакий DRAM со сдвигом. Физически там двигается 1 бит в кольце и магистраль на выходе тоже однобитная. Для того что бы обработать тетраду (тетрада удобна для хранения BCD разряда (двоично-десятичного), хотя кое где используется и шестнадцатеричные разряды) в один такт составлен из 4 фронтов сигналов сдвинутых по фазе Ф0, Ф1, Ф2, Ф3. Это физические линии, получается что такт у калькулятора построен из 4 микротактов. Кстати такое в 6502 любили тоже использовать. За 4 этих фронта и происходит обработка тетрады, но строго побитно.
подключения клоковой группы сигналов
Структура тактового сигнала МК61
По поводу обработки мантисы я Фролова не понял, возможно потому что он цитирует Я. Трахименко xn--b1aph9d.xn--p1ai/biblioteka/cartochki/0036.htm, а там тоже не все прозрачно, я например не продрался.
В мантисе 8 разрядов, и по программе видно что каждый такт в объект эмулирующий К145ИК13хх посылается дополнительный параметр по которому считывается 32-битная микроинструкция. Каждый бит этой микроинструкции управляет функциональными устройствами К145ИК13хх. Вот следование этого параметра друг за другом.
0,1,2,3,4,5 3,4,5 3,4,5 3,4,5 3,4,5 3,4,5 3,4,5 6,7,8 0,1,2 3,4,5,6,7,8 0,1,2,3,4,5
И таких вызовов 42, за один цикл. В принципе тут усматривается система о которой пишет Фролов. Но последний 3,4,5 идет не подряд. Мантисса 8-разрядная а триады 3,4,5 следуют подряд только 7 раз. А вот такому рисунку,

который я делал когда искал смещение переменных внутри кольца, это больше соответствует. Может я плохо понял, то что Вас интересовало? Давайте попробуем решить этот ребус вместе еще раз :)
  • avatar SAA
  • 1
Да к сожалению алгоритм пока никто не сумел увидеть в потоке команд. Когда мне пришлось для повышения производительности переработать эмуляцию комплекта К145 для msp430, то посыпались ошибки как из рога изобилия. Что бы отловить где я ошибаюсь с пониманием внутренней архитектуры пришлось логировать каждое дыхание msp430 и emu61 на ПК. На один шаг DoStep в логах был выхлоп строк на 1000. А сбоило не всегда на первом DoStep. Я тогда чуть не «позеленел» от поиска несовпадений. :) Но в итоге уперся в то что не могу уже понять что то на уровне алгоритма, не охватываю. Мелочи какие то да, еще могу описать и осознать но что то большее 10-ка тактов выбивает из понимания того что происходит. Это как раз тот случай когда описать аппаратуру можно вплоть до гейта, микрокод снят до последнего бита, а в итоге понимания чуть больше чем 0.01% :) Мало того я ощутил ту черту производительности за которую я уже физически не могу выйти даже если я рассредоточу объекты по разным микроконтроллерам или переложу все в верилог и ПЛИС. Она мягко говоря будет отставать даже от МК161.

P.S. Как всегда надежда на свежую кровь в этом вопросе, может быть иной взгляд со стороны.
  • avatar SAA
  • 0
Spartan-6 XC6LX16 и старенький ACEX EP1K50 совсем в разных весовых категориях, у LX16 2278 слайсов, а слайс Xilinx грубо в 4 раза больше чем LE у Альтеры. При том что у Spartan-6 LUT — 6 входовой, а у ACEX 4-х. Ну и блочная память, тут совсем все неважно у ACEX. Даже если представить себе, что часть нововведений Z80N можно «пришить» сбоку имеющегося в Evo Z80, то скорее всего всю остальную часть логики засунуть уже будет некуда.
  • avatar SAA
  • 0
Спасибо за статью, очень интересно!

Таким образом переход к виртуальной машине дает возможность переноса на множество других платформ. А никогда не возникало ощущения, что аппаратные возможности платформы (ядра процессора) могли бы способствовать эффективному исполнению инструкций ВМ? Например у DEC в его последнем CISC-е VAX новые инструкции могли вносится как микрокод и нативно расширяли систему команд. В случае VM это конечно в большей степени касается вызова шитого кода.
Не могу точно сказать насчет первенства в JIT компиляции, DEC тоже делала шаги в эту сторону, кроме всего прочего снабжая ядро виртуальной машины профилировщиком, принимающим решение на определенной итерации начать «инлайнить код», то есть приложение становилось шустрее и шустрее, в зависимости от времени его работы.
У МК-85, приходилось читать Отрохова, разработчик Бейсика пошел на применение шитого кода для плавающей арифметики. Формально это тоже некая часть VM. Я хочу сказать что в определенный момент, когда сложность написания процедур, даже в такой удобной и интуитивно понятной
системе команд как PDP-11, начинает достигать пределов возможностей человеческого мозга. В таком случае уйти в форт-машину или еще какую либо VM, в конечном счете связано даже не с борьбой за плотность или переносимость кода, а за возможность продолжать вести разработку.
  • avatar SAA
  • 0
стандарты разложения растров
Как Вы и отметили растра 4:3, получить количество точек по горизонтали можно умножив кол-во строк на 4/3. Для PAL и SECAM это 800 элементов в строке. Не спорю конечно кинескоп и качество исполнения видеотракта дадут свои ограничения, на эти возможности.

У меня есть несколько вопросов, буду признателен за ответы.
1. Почему именно телевизионный растр?
2. Чем вызваны Ваши предпочтения накладываемые на видеоадаптер по разрешению и цветности, только ли ограничениями ТВ растра?
  • avatar SAA
  • 0
Да с текстом претензии к видеоадаптерам возрастают. Если предположить что 256х256 это всего лишь массив для отображения на экране, тогда можно пойти чуть дальше и представить, что для его отображения используется 2х2 точки в растре разрешением 800х600. Ну а раз есть такое разрешение то можно сделать еще один шажок с наложением текстового слоя с знакоместом 8х8 как 80х60 из области ~5Кб но точкой для него 1х1. Два растра точно конечно не совмещаются, но в этом может быть и нет необходимости. В принципе добиться включения слоя с текстом в слой с графикой можно меняя размеры знакоместа.
  • avatar SAA
  • 0
Трудно не согласится! Любое ограничение играет в пользу творческого процесса. Можно по взрослым платформам лицезреть как доступность ресурсов работает против них… черт возьми снова законы диалектики :) Было бы интересно увидеть размышления на тему полезного ограничения в разрешающей способности видеоадаптеров для платформы 64К. Есть ли золотая середина?
  • avatar SAA
  • 0
Монитор попроще чем виртуальная машина Возняка. Ваше исследование почитаю с удовольствием! :)
  • avatar SAA
  • 0
Хорошее замечание, включу в пример, часть кода. LINK — это регистр в который попадает значение IP для следующей инструкции, в случае записи в IP. То есть в случае с переходом (записью адреса перехода в IP) следующая инструкция на которую будет указывать IP это туда куда надо вернутся.

#label -> IP  ; Переход на адрес label и запись адреса следующей инструкции в LINK
R1 -> ALU.A
.........
label:
..............
LINK -> IP         ; возврат на команду R1 -> ALU.A


Можно сохранять LINK как и в случае с LR в ARM либо в ОЗУ, либо один из регистров общего назначения. Допустим R4 это стек.

; push LINK
R4 -> ADDR
LINK -> DATA    ; LINK 16 битный регистр, будет записан в память как слово за два такта
R4 -> ALU.A
#2 -> ALU.B
ADD -> R4       ; R4 += 2
; pop LINK
R4 -> ALU.A
#-2 -> ALU.B
ADD -> R4
ADD -> ADDR
DATA -> IP       ;  IP - 16-битный регистр, буден считан за два такта
  • avatar SAA
  • 1
А вот очень хороший пример! Действительно либо адрес в 16бит и борьба за каждый байт (ЕМНИП, Стив Возняк неплохо это показал запихнув в 256 байт монитор для Apple-I), либо 24/32 бита — «счастье всем и никто не уйдет обиженным». Адресного пространства так много что ARM выделяет в нем области для манипуляцией битами. :)

P.S. Статья про SuperFX будет крайне интересна!