P.S.
Вообще однооперандные инструкции это такие в которых X пропускаясь через АЛУ перед записью в Y никак не зависит от Y.
Т.е. (используется синтаксис операторов похожий на синтаксис Си-подобных языков):
R0 = R1; move
R0 =+1 R1; inc1
R0 =<< R2; сдвиг влево на 1 бит
R0 =+2 R3; inc2
R0 ~= R4; побитовая инверсия
и так далее. т.е. над SRC производится операция и записывается в DST
Двухоперандные инструкции берут и SRC и DST и пропустив их оба через АЛУ записывают результат в DST:
R0 += R1; сумма R0 и R1 записывается в R0
R0 <<= R2; R0 сдвигается влево на R2 бит (существенное отличие с однооперандным аналогом!)
R0 cmp R3; R0 и R3 сравниваются — неизменность R0 достигается за счёт того, что АЛУ именно его (Y) выдаёт на выходе
и так далее.
это можно делать одновременно, и TI не нужен
если не понадобится Y, так не понадобится
Одновременно считывать из памяти не получится. Заполнение Y реально может быть не просто ненужным, но и времязатратным процессом.
(кстати, у тебя какой порядок слов-байтов? little/big endian?)
А тут это исключительно как захочет программист. Ячейка памяти 16-битная, регистры — 16-битные, если нужно компоновать 32-битные, то это пользовательский код целиком.
то inc1+inc2 ничем не лучше add3
Я сильно над этим не задумывался еще, но т.к. есть 16 однооперандных инструкций (и они никак не обязаны быть теми же что и 16 двухоперандных), то возможно там будет и dec/inc-1/2/3/4 и возможно что-то еще.
также не совсем понимаю смысл необходимости флага TI — почему бы всегда не производить копирование в X,Y?
Если TI=0, то общая схема инструкции следующая:
1. SRC загружается в X
2. X пропускается через АЛУ с кодом инструкции и TI=0 (т.е. полный код инструкции это TI+INSTR — оба поля)
3. результат сохраняется в DST
Если TI=1, то общая схема инструкции следующая:
1. SRC загружается в X
2. DST загружается в Y
3. X и Y пропускаются через АЛУ с кодом инструкции и TI=1 (т.е. полный код инструкции это TI+INSTR — оба поля)
3. результат сохраняется в DST
Т.е. TI — это признак двухоперандной инструкции — в таковой нужно больше действий в первой фазе по загрузке второго входного аргумента в Y и поэтому лучше по одному биту сразу понимать надо или не надо этот шаг делать.
Но можно так же воспринимать это так, что TI+INSTR формируют 5-битный код инструкции половина из которых однооперандная, а половина — двухоперандная.
Подумай, так ли тебе нужно писать что-то в следующий опкод.
Так и есть — это пожалуй единственная «бесполезная» комбинация аргументов которая осталась и действительно сделать её каким то особым случаем выглядит привлекательно.
Но лично мне не нравится, что ломается общая схема работы Simpleton-а.
Однако есть еще одно соображение — генерация исключений. Сколько ни думаю, но получается что при генерации исключений реально схемотехника должна быть вот настолько замороченной — нужно уметь и PC и FLAGS сохранить в стек да еще и переход совершить. Надо еще подумать, возможно из-за того что такая схемотехника просто нужна для прерываний, то и команда такая не будет обременением по итогу. Посмотрим, когда будет интерес и время подумать еще над этим помозгую. Немного смотрел в ARM-ы и вроде как там генерация прерываний не сохраняет никак флаги, типа это должно быть первой инструкцией обработчика прерываний… В общем возможны варианты.
(а еще лучше адресацию [R+imm], но она не лезет в такую схему)
Да, нифига не лезет. Да и норм. На деле дотянуться до произвольной переменной в стеке не так уж и трудно:
R5 = offset
R5 += SP
и вот тут уже мы можем совершать с параметром действия типа R0 += [R5] — мы получили на него указатель двумя командами и тремя словами.
ИТЕРИРОВАНИЕ подразумевает использование каждого элемента
Ну зависит от задачи. Например, если первый элемент нам подходит — переходим к его хвосту, а если нет — пропускаем до следующего элемента.
в какой массе? перечисли несколько хотя бы разумных случаев?
Главное откуда оно возникло — это минимизация и ускорение реализации CALL, как я писал, но помимо этого может быть применён для, допустим, быстрой адресации переменных близких к вершине стека (наряду с =+1) и может быть полезен при итерировании по массивам с таким размером ячейки и тому подобное.
второй случай можно закодировать через номер регистра флагов
(потому что, ну кому и зачем мб нужен косвенный доступ через флаги))
А вот это очень прикольная идея! Действительно косвенная индексация через FLAGS не просто ненужная операция, но по сути своей — UB в чистом виде. Вообще работать с регистром флагов надо очень осторожно во избежание проблем с будущими совместимостями и главная задача вынесения его в РОН — это упрощение системы команд при соблюдении легкости сохранения через [SP] = FLAGS и восстановления через FLAGS = [SP], а так же манипуляцией бита Enable Interrups через FLAGS ~= mask и FLAGS |= mask. Но схемотехнически и архитектурно как чтение так и запись неиспользуемых его битов — это Undefined Behaviour для возможностей будущих расширений, поэтому ни о какой разумной индирекции в таких условиях речи быть не может.
Логично!
Тогда действительно ничего не мешает сказать, что R7 это и есть наш FLAGS (остальные регистры сохраняют нумерацию описанную в посте) и когда indirect=0, то ведет себя как этот регистр.
Но когда встречаются indirect=1 и R7 (т.е. все биты ответственные за DST и/или SRC единичны), то схема переключается в режим immediate+indirect в точности как описано в посте.
И действительно тогда чистый immediate лучше реализовать как чтение PC+indirect, тогда остаётся только заметить, что любое косвенное чтение через PC приводит всегда к его инкременту — это и логично и схемотехнически резонно, а не какой то «исключительный случай». Т.е. fetch через PC — он и в африке fetch через PC откуда бы действие не происходило.
И внезапно выходит, что действительно, у нас высвободился регистр R4 и неисключительных регистров теперь пять штук — R0-R4. При этом исчезло понятие «запрещенной комбинации DST», но и на этом фоне еще и высвободился регистр.
Очень удачная идея! Спасибо!
Самое интересное, что чисто схемотехнически такое усовершенствование насколько я понимаю вообще не несёт практически никакого пенальти по сравнению с первоначальным вариантом — добавочных линий и логических элементов похоже что вообще не нужно вводить в схему — они уже все и в первом проекте были, просто немного по другому затусованы.
например, обмен координат в однонаправленном алгоритме
сортировка пузырьком с условным обменом
Не стоит того чтобы из-за этого ломать систему команд. Даже в ЯВУ swap как правило записывается проброской данного через tmp, так и тут — ну пробросим через регистр, несущественно и нужно очень редко чтобы из-за этого огород целый городить.
Это довольно эзотерично чтобы мне захотелось таким пользоваться в жизни. ;D
тем, что повторяет функцию универсального сложения с любой константой
Как и inc1 это экономия на imm в ряде случаев когда это часто нужно.
собс-но, даже инкремент на 1 — пережиток неортогональных восьмибитных процов
Так это и есть по духу 8-битный проц, но такой чтобы как можно более приятнее. inc2 лично мне приятно, экономит код, данные и скорость в массе случаев. Но, например, у этого проца очень слабая поддержка локальных переменных на стеке, а это вполне в духе 8-битных процессоров — в этом я даже нахожу какое то очарование, что система команд тяготеет к тому чтобы побольше всего вытаскивать в глобалки и минимизировать по настоящему комплексные адресации. Потому что по духу он и должен быть таким вот «восьмибитником».
только частных случаев очень много и эффект заметный от ускорения
Такую оптимизацию можно проделать в существующей системе команд и без swap:
; вызов процедуры
R3 =+2 PC; адрес возврата (inc2) (команда без immediate)
PC = proc_addr; вызов процедуры
; возврат из процедуры
PC = R3
Как видно никакого SWAP и стека в таких хвостовых функциях можно реально не использовать.
Теоретически можно было бы использовать какую нибудь из запрещенных комбинаций регистров в операндах или псевдо-nop-ы как расширители команд, но повторюсь — мне здесь нравится именно простота.
а для «компа мечты» должно быть эффективней и удобнее, а не «проще»
В том то и дело, что я нахожу такую систему команд довольно эффективной и удобной. Тут само мышление прямолинейно как стрела — всё есть пересылка данных с опциональной операцией с высокой степенью ортогональности. Не нужно задумываться над перекладыванием данных по регистрам как в Z80 или тем какие режимы адресации есть а каких нет и в каких операциях как в 6502.
ну, уродливый inc2 протащил же :)
Чем же он уродлив? inc2 (реализовать можно как ADD с загрузкой константы 2 в Y на первых фазах выполнения команды) часто нужен, потому и сделан — экономит код программы хорошо. Он полностью укладывается в парадигму X ?= Y и уродливым в ней быть не может — inc1 разве чем то уродлив? Тоже часто нужен, потому и полезен — абсолютно та же фигня и даже в тот же профиль.
с чего такой вывод?
В эти годы уже просто для поддержки CP/M могли целый процессор Z80 засадить в какой нибудь Commodore XXL (не помню точно модели) — какая уж тут гонка за примитивизмом?
например, девайс подкидывает байты высоким темпом, нужно успевать выгребать
Я уже говорил — команда типа SWAP BYTES внутри регистра (опять таки с логикой Y = swap bytes of X) делает такой сценарий маловероятным. Ну и вообще соединять комп с устройством которое плюётся данными быстрее чем или даже соизмеримо чем скорость с которой процессор отрабатывает memfill — это сомнительно и вряд ли такое вообще есть/было на практике.
Когда load [PC] (на одном операнде X рассмотрим) отрабатывает он сперва в X грузит PC, а уже потом из-за бита indirect замещает X на [X]. Это совсем не то же самое что сперва загрузить в X [PC++], а потом заменить X на [X]. Система команд очень прямолинейна.
поэтому нужен swap
Swap регистров обычно нужен когда они существенно неортогональны и ради аккумулятора надо перестановки делать. Тут такой острой необходимости нет.
Регистров на самом деле хватает чтобы сделать memmove/memcopy полностью на регистрах, а это я считаю эталонным кодом для проверки на нехватку регистров.
Зря лишил себя одного регистра из-за непосредственного операнда, всё равно его читаешь через PC, вот и кодируй (с индиректом) тоже через PC.
Immediate сам может быть тоже indirect и таким образом операции можно проводить непосредственно над памятью не загрязняя регистры. С чтением через PC так уже не получится и поэтому такой лишний регистр, имхо, только усложнит в итоге программы.
И зачем адрес возврата обязательно пихать в память?
Т.к. регистров действительно немного, а в самом обобщённом случае Link Register таки нужно пихать в стек, то делать так всегда имхо проще для системы команд. В ней есть только одна операция X ?= Y с лёгкими вариациями. Swap уже не пролазит и не знаю как его протащить без вреда для внутренней красоты. :D
Для 80-х все шины ширины 16 (и все вспомогательные микрухи!) в домашней машине дороговато
Только до тех лет когда начали делать 128Кб-ные машины.
Далее получилось что истинным 16-биткам уже не хватало и этого и в них перекочевали банкинги и свитчинги в лучшем случае реализованные как сегментные регистры. Байты, правда, конечно, уже к тому времени стали вещью безальтернативной.
плюс доступную восьмибитную периферию трудно или невозможно использовать
Да ну, не вижу ни трудностей ни тем более невозможностей.
Несомненно раз уж есть мощная 8-битная предыстория, то разумно предусмотреть команды типа обмена/сдвига/зануления в слове байт, но уж куда подвести проводки от 8-битной периферии к 16-битной шине данных — по моему дело просто вкуса, но никак не трудностей.
Это уже был бы компик моей мечты))
Вот можно было бы и послушать, если есть конкретные образы и идеи. :)
На форуме откуда я пришёл — gamedev.ru есть энтузиасты написавшие продолжение игры Gradius: gamedev.ru/projects/forum/?id=244656
Это само по себе было бы не особо интересно даже, если бы не список платформ на которые это продолжение было портировано.
Кроме очевидных Windows и Linux есть порт на DOS (!) (причём использующий DOS4/GW о чём отдельная песня у них есть: gamedev.ru/flame/forum/?id=245611)
Но кроме этого есть два порта на самодельные (!) портативные игровые консоли Black Prism и EMU Pocket! :D
Не удивлюсь если это широко известные люди в узких кругах. :D
Есть, кстати, у такого процессора еще одна забавная особенность — на нём можно оперировать ячейками памяти практически избегая «загрязнения регистров», т.е. делать команды вида:
[addr1] = [addr2]
или:
[addr1] += [addr2]
вообще минуя регистры общего назначения. за счёт того, что машинный цикл команды допускает сразу и многократные чтения из памяти и запись в память в конце команды — нет крайне часто встречаемого запрета в процессорах на то чтобы оба аргумента инструкции были в памяти.
казалось бы это должно сильно усложнить логику процессора, но как по мне — так нет. Тут как говорится «всё познаётся в сравнении».
Ведь возьмём такой популярный 8-битный процессор как i8080 на котором базирована командная система Z80.
В нём есть такая забавная инструкция как LHLD imm16, которая в синтаксисе Z80 выглядит как LD HL,(addr). Т.е. в регистровую пару HL считывается слово лежащее по адресу указанному в самой инструкции (imm16).
А что это с точки зрения машины состояний процессора? Во первых процессор 8-битный и все считывания 8-битные.
Поэтому сперва считывается код инструкции, потом считываются последовательно два байта imm16, потом считываются последовательно 2 байта лежащие по этому адресу и они уже рассовываются по регистрам H и L. Т.е., на минуточку, кроме считывания опкода в одной инструкции происходит 4 считывания из которых 2 меняют PC. Это уже по нагрузке на схемотехнические разбивки на шаги чтения/записи равно по сложности самому сложному возможному варианту возможных шагов чтения/записи единственной команды у Simpleton (только одно чтение меняется на запись), только вот у последнего команда может делать гораздо больше действий.
Ну, строго говоря тут не совсем «процессор с одной инструкцией». Если смотреть под немного другим углом — то в нём столько инструкций сколько есть кодов у поля INSTR + бит TI, просто все инструкции соблюдают строго один и тот же принцип — они берут X и возможно Y, проводят над ними операцию и записывают в Y. Ввиду того, что всё состояние процессора находится полностью в его РОН — такая схема достаточна для реализации практически всего что машине как правило надо — а то что прямо в это не укладывается (как CALL) вполне можно реализовать двумя операциями.
Т.е. любая команда это взятие операнда X или операндов X и Y, заталкивание их в ALU и записывание результата обратно во операнд Y. С аскетичностью и эзотеричностью даже One Instruction Set Computer это имеет мало чего общего. Здесь скорее преследуется цель сделать всё как можно более прямолинейным и простым для программирования — как можно более полная ортогональность (за исключением регистра SP) и никаких особых случаев и команд которые надо специально помнить.
Насчёт старых компьютеров у меня есть статья так что я в курсе. :)
Да у меня у самого руки зачесались чего нибудь сделать под это железо пока читал спеки для статьи, но главным образом из-за немного экзотичного процессора — вроде и наследник Z80/i8080, но от Z80 по большей части только JR (включая условные переходы) и битовые инструкции префикса $CB, а от i8080 даже флаги P и M отвалились. В то же время появились такие вкусности как
LD (HL+), A
LD HL, SP+imm8
ADD SP, imm8
то есть можно забавно адресовать локальные переменные на стеке, и
LD ($FF00+imm8), A
LD ($FF00+C), A
предполагают, что в 127 байтах быстрой памяти можно чего то интересного сохранять для более быстрой работы.
В общем было бы интересно пощупать этого мутанта в продакшене так сказать. :)
А ведь в линуксах вывод этих однострочных программ на Си просто в консоли направляется куда то типа в /dev/sound и вообще не нужны никакие дополнительные программы. :) Так что страдают самые беззащитные — маководы и виндозеры. ;)
Обожаю вообще генеративные вещи, псевдорандомы, динамический хаос и фрактальные вещи любой природы и эти программки — один из изумительнейших представителей направления.
В качестве бонуса еще приведу свой пост с gamedev.ru по поводу сабжа:
Те кто читал внимательно предыдущую статью про архитектуру Atari Jaguar могли заметить, что описывая блиттер консоли были упомянуты и затенение по Гуро и Z-буфер, но ничего не было сказано про текстурирование. И это действительно так — блиттер не умел текстурирование вообще.
А ведь даже современник — 3DO знал текстурирование (хотя наоборот не знал Z-буфер), но там другая история и весьма иной аппаратный ускоритель.
Действительно, если посмотреть на игру идущую в комплекте с консолью на её старте, то можно воочию увидеть, что всё 3D состояло из освещённых, но нетекстурированных треугольников:
Тем не менее игры с текстурированием были, но как можно понять по недолгому размышлению в этом случае игра уже не использовала аппаратный блиттер, а целиком возлагала процесс рисования треугольников на программируемое RISC-ядро чипа Том (а может даже припрягала в помощь к нему подобное ядро из чипа Джерри).
В связи с этим было интересно натолкнуться на интервью зарубежного компьютерного журнала о видеоиграх Edge с Джоном Кармаком после того как был анонсирован выход игры Doom на Atari Jaguar, но процесс разработки был еще в середине процесса.
Приведу лишь ссылки на скрины (т.к. они сами довольно большие по размеру):
Если вкратце, то Кармак весьма нахваливал консоль и даже выразился о ней как о лучшей игровой платформе на тот момент времени.
По его словам у него заняло две недели от начала портирования Doom на Atari Jaguar прежде чем хоть какая то картинка на телевизоре появилась, но с ужасным фреймрейтом.
Он подтверждает, что основной процессор m68k слишком медленный, но дополнительные RISC-ядра — это вестчь! Но ему требуется время чтобы переписать код так, чтобы он помещался стадиями в маленькие кусочки памяти RISC-ядер (см. описание Atari Jaguar выше если еще не).
Причём сперва он любопытства ради начал портировать движок Wolf 3D со SNES на Atari Jaguar и когда «пятнадцатью сидиромов позднее» у него получилась картинка — он послал результат в Atari и они дали добро на порт Doom.
Но до этого Джон потратил три недели чтобы сделать лучший порт Вольфеншейна среди всех прочих — он работает на 30 фпс с вчетверо большей деталистичностью чем на ПК и даже звук звучит на 22 кГц, что в трое больше чем на ПК. В общем этим портом он остался доволен.
Но Doom на момент написания статьи всё еще представлялся тёмной лошадкой — Кармак говорит, что текстурирование будет выполнятся не совсем так же как на ПК, но по схожему принципу. Консоль весьма хвалит называя лучшим игровым железом на тот момент на рынке. Положительно отзывается о процессоре объектов, т.к. тот и картинку позволяет легко компоновать из разных сюрфейсов так и спрайты поверх выводить в совершенно свободном виде. Так же он хвалит модель цвета CRY (нечто вроде CMY(k), как я писал в статье) и вообще называет её лучшей в мире моделью цвета для видеоигр ибо затенение (освещение) становится тривиальной штукой (просто канал яркости это отдельный канал яркости). Ну а 16-битный цвет сам по себе должен был позволить выйти за рамки ограничений ПК-шной 256-цветной палитры намного и всерьёз.
Однако уже здесь Кармак признавал, что Jaguar не удастся добиться плавности в 30 фпс какую Doom имел на процессоре класса Pentium — тут он на тот момент разводил руками: «посмотрим как получится».
Здесь же он упоминает, что несмотря на то, что он сперва был недоволен тем, что Atari пытаются разработать собственные RISC-процессоры, но в итоге он нашёл их довольно быстрыми и приятными в разработке — единственный минус — малый объём локальной памяти. Ну и то, что в них есть баги (лол!).
Так что единственная аппаратная ошибка которую, по его мнению, совершила Atari — это то, что в качестве ЦП в консоли стоит m68k. RISC-ядра по его мнению в 20 раз быстрее.
Далее разговор немного отходит от консоли, но просто ради фана упомяну интересные моменты. В этот момент Кармак говорит, что в разработке находятся три игры на движке Doom: Doom II, некая Druid от Raven Software («фентезийная версия Doom») и Strife от Sygnus Studio. И что уже неиллюзорно пахнет Quake-ом.
Просто факты о том о сём: разработка графического ядра Wolf 3D заняла месяц, разработка графического ядра Doom заняла уже 3 месяца, но потом пришлось потратить 2 месяца на полное переписывание его под новый центральный алгоритм. Одним из наиболее трудоёмких алгоритмов к реализации в движке Doom он называет определение видимости по лучу в лабиринте карты, в то время как параллакс неба называет весьма простой штукой.
Ну и напоследок высказывается (как со стороны журналиста так и Кармака) пренебрежение к Full-Motion-Video играм (виртуальным тирам) и Кармак прямо говорит, что до тех пор пока Nintendo не прекратит бегать со своей ханженской моралью вряд ли что-то от id software появится еще на их консолях (отсылка к тому, что в Wolf3D пришлось убрать свастики).
P.S.
Ах, да. Doom на Atari Jaguar получился всё-таки мыльноватым, хотя фпс нормальный для того времени:
«Некоторые видеопроцы используют другой способ вывода видео. Каждую строку изображения дисплей лист выполняется полностью, но отрисовывается не фреймбуфер (битмап 1024х512, например), а буфер строки (1024х1)»
Пишу сейчас про Atari Jaguar (а это ровесница Playstation 1) — там как раз по сути синтез таких подходов.
На видеовыход подаётся информация именно из строки-буфера в то время как в теневую такую же рисуются пиксели для следующей строки. При этом видеоадаптер для построения следующей строки проходит по командам дисплейного списка, где типовыми объектами являются битмапы из которых один сканлайн за раз переносится со всевозможными наложениями и прозрачностями диктуемыми порядком отрисовки. Таким образом это могут быть как спрайты из атласа так и почти полноэкранными битмапами в которые рендерил специализированный процессор. Черновик статьи с этим можно сейчас посмотреть тут: gamedev.ru/flame/forum/?id=231027&page=7&m=4976559#m95
Немного еще посмотрел в то как именно работают палетризированные цвета в Saturn, ибо там сразу было видно, что дела обстоят непростым образом, но за ворохом слов было не очень понятно.
Оказалось что и с палитрами там всё очень сложно.
Во первых — при рендере текстурированного спрайта в 16-битный фреймбуфер данные о цвете его битмапа могут быть представлены непосредственно как 16-битные значения, а могут быть 4-битными слотами в 16-цветной палитре 16-битных же цветов. Но в любом случае во фреймбуфер попадает 16-битное данное у которого верхний бит контролирует является оно RGB или является цветом в палитре.
Так вот когда оно является цветом в палитре существует 8 глобально выставляемых форматов того какие биты в нём отвечают за:
а) номер цвета в палитре (до 10 бит)
б) порядок отрисовки между задними фонами (до 3-х бит)
в) номер расширенного вычисления цвета (до 3-х бит)
г) бит «тени»
Кроме того для 8-битного режима фрембуфера есть так же 8 форматов как эти же биты, но уже в урезанном количестве распределены по 8-битному данному о цвете в нём.
И там такая битовая мешанина из сотен регистров управляющих всякими этими битовыми комбинациями, что как говорится «без поллитры не разберешься».
Реально ловлю то же самое ощущение что было в похожем по духу эпохи 3DO — вся эта битово-палитрово-слоевая мехника переусложнена до жути.
Мегадрайв от той же фирмы был просто гимном лаконичности и простоты своей графической начинки — одна палитра, один палитровый режим и для спрайтов и для тайлов, но пасаран. Тут же прям им крышу сорвало на то как впихать в биты как можно большее число всяких палитр, режимов и цветов. Сомневаюсь что даже половина этих всех вещей была в реальности востребована.
А вот дальше еще на другом форуме подсказали, что была такая весьма забавная видеокарта как Diamond Edge 3D — согласно википедии первая видеокарта на пользовательском рынке ПК с аппаратным ускорением (95-ый год). Так вот это был некий синтез видео- и звуковых чипов из Sega Saturn в виде платы для IBM PC. И вот тут всё стало совсем интересно, ведь Diamond Edge 3D в сердце своём содержит чип NV1 который разработала, да, nVidia.
Похоже, что это был одна из первых попыток этой фирмы продвигать 3Д в массы.
В статье про Sega Saturn этому посвящена всего одна строчка (мой перевод):
Шеффилд сказал, что использование Сатурном четырёхугольников подорвало поддержку системы третьими сторонами, но поскольку «nVidia вложилась в четырёхугольники» в то же время есть «отдалённая возможность», что они смогут «стать стандартом вместо треугольников», если каким то образом, магически, Сатурн стал бы самой популярной консолью той эпохи".
И удивительно, но в русском википедии есть гораздо больше интересной информации, чем в английской: ru.wikipedia.org/wiki/NV1
Использование квадратичных поверхностей было элегантным и абсолютно новым решением в 3D-мире. Хотя теория была придумана давно, в железе такого до этого никто не делал. Главная идея заключалась в том, что в 1995 году реализовать операцию деления 1/z в железе была невероятно сложно и дорого, и в результате этого имплементировать перспективную проекцию было очень трудно. Классический подход, заключавшийся в линейной аппроксимации гиперболической функции, имел свою проблему аппроксимации в окрестности нуля и требовал сложного программного обеспечения. Квадратичная теория, использованная в NV1, аппроксимировала функцию 1/z параболической интерполяцией и такая интерполяция по качеству превосходила кусочно-линейную, которая была использована в решениях конкурентов.
…
Попытка исследовательской группы NVIDIA портировать квадратичную технологию на API Microsoft провалилась. Попытка выполнить наложения текстуры и операцию clipping приводило к уравнениям 5 степени невычислимыми в радикалах. Хотя демонстрационные примеры с квадратичными поверхностями выглядели довольно неплохо, работа с ними оказалась чрезвычайно трудной.
…
Дальнейшая разработка NV1 под новым названием — NV2 была остановлена, NV1 производство было свернуто. В 1996 году Nvidia начала разработку классического акселератора.
Хех, оказывается за этими квадратностями стояла никто иная, как сама nVidia и это был её первый блин комом.
А в чём проблема то?
Вообще однооперандные инструкции это такие в которых X пропускаясь через АЛУ перед записью в Y никак не зависит от Y.
Т.е. (используется синтаксис операторов похожий на синтаксис Си-подобных языков):
R0 = R1; move
R0 =+1 R1; inc1
R0 =<< R2; сдвиг влево на 1 бит
R0 =+2 R3; inc2
R0 ~= R4; побитовая инверсия
и так далее. т.е. над SRC производится операция и записывается в DST
Двухоперандные инструкции берут и SRC и DST и пропустив их оба через АЛУ записывают результат в DST:
R0 += R1; сумма R0 и R1 записывается в R0
R0 <<= R2; R0 сдвигается влево на R2 бит (существенное отличие с однооперандным аналогом!)
R0 cmp R3; R0 и R3 сравниваются — неизменность R0 достигается за счёт того, что АЛУ именно его (Y) выдаёт на выходе
и так далее.
А тут это исключительно как захочет программист. Ячейка памяти 16-битная, регистры — 16-битные, если нужно компоновать 32-битные, то это пользовательский код целиком.
Я сильно над этим не задумывался еще, но т.к. есть 16 однооперандных инструкций (и они никак не обязаны быть теми же что и 16 двухоперандных), то возможно там будет и dec/inc-1/2/3/4 и возможно что-то еще.
1. SRC загружается в X
2. X пропускается через АЛУ с кодом инструкции и TI=0 (т.е. полный код инструкции это TI+INSTR — оба поля)
3. результат сохраняется в DST
Если TI=1, то общая схема инструкции следующая:
1. SRC загружается в X
2. DST загружается в Y
3. X и Y пропускаются через АЛУ с кодом инструкции и TI=1 (т.е. полный код инструкции это TI+INSTR — оба поля)
3. результат сохраняется в DST
Т.е. TI — это признак двухоперандной инструкции — в таковой нужно больше действий в первой фазе по загрузке второго входного аргумента в Y и поэтому лучше по одному биту сразу понимать надо или не надо этот шаг делать.
Но можно так же воспринимать это так, что TI+INSTR формируют 5-битный код инструкции половина из которых однооперандная, а половина — двухоперандная.
Так и есть — это пожалуй единственная «бесполезная» комбинация аргументов которая осталась и действительно сделать её каким то особым случаем выглядит привлекательно.
Но лично мне не нравится, что ломается общая схема работы Simpleton-а.
Однако есть еще одно соображение — генерация исключений. Сколько ни думаю, но получается что при генерации исключений реально схемотехника должна быть вот настолько замороченной — нужно уметь и PC и FLAGS сохранить в стек да еще и переход совершить. Надо еще подумать, возможно из-за того что такая схемотехника просто нужна для прерываний, то и команда такая не будет обременением по итогу. Посмотрим, когда будет интерес и время подумать еще над этим помозгую. Немного смотрел в ARM-ы и вроде как там генерация прерываний не сохраняет никак флаги, типа это должно быть первой инструкцией обработчика прерываний… В общем возможны варианты.
Да, нифига не лезет. Да и норм. На деле дотянуться до произвольной переменной в стеке не так уж и трудно:
R5 = offset
R5 += SP
и вот тут уже мы можем совершать с параметром действия типа R0 += [R5] — мы получили на него указатель двумя командами и тремя словами.
Ну зависит от задачи. Например, если первый элемент нам подходит — переходим к его хвосту, а если нет — пропускаем до следующего элемента.
Логично!
Тогда действительно ничего не мешает сказать, что R7 это и есть наш FLAGS (остальные регистры сохраняют нумерацию описанную в посте) и когда indirect=0, то ведет себя как этот регистр.
Но когда встречаются indirect=1 и R7 (т.е. все биты ответственные за DST и/или SRC единичны), то схема переключается в режим immediate+indirect в точности как описано в посте.
И действительно тогда чистый immediate лучше реализовать как чтение PC+indirect, тогда остаётся только заметить, что любое косвенное чтение через PC приводит всегда к его инкременту — это и логично и схемотехнически резонно, а не какой то «исключительный случай». Т.е. fetch через PC — он и в африке fetch через PC откуда бы действие не происходило.
И внезапно выходит, что действительно, у нас высвободился регистр R4 и неисключительных регистров теперь пять штук — R0-R4. При этом исчезло понятие «запрещенной комбинации DST», но и на этом фоне еще и высвободился регистр.
Очень удачная идея! Спасибо!
Самое интересное, что чисто схемотехнически такое усовершенствование насколько я понимаю вообще не несёт практически никакого пенальти по сравнению с первоначальным вариантом — добавочных линий и логических элементов похоже что вообще не нужно вводить в схему — они уже все и в первом проекте были, просто немного по другому затусованы.
Не стоит того чтобы из-за этого ломать систему команд. Даже в ЯВУ swap как правило записывается проброской данного через tmp, так и тут — ну пробросим через регистр, несущественно и нужно очень редко чтобы из-за этого огород целый городить.
Это довольно эзотерично чтобы мне захотелось таким пользоваться в жизни. ;D
Как и inc1 это экономия на imm в ряде случаев когда это часто нужно.
Так это и есть по духу 8-битный проц, но такой чтобы как можно более приятнее. inc2 лично мне приятно, экономит код, данные и скорость в массе случаев. Но, например, у этого проца очень слабая поддержка локальных переменных на стеке, а это вполне в духе 8-битных процессоров — в этом я даже нахожу какое то очарование, что система команд тяготеет к тому чтобы побольше всего вытаскивать в глобалки и минимизировать по настоящему комплексные адресации. Потому что по духу он и должен быть таким вот «восьмибитником».
; вызов процедуры
R3 =+2 PC; адрес возврата (inc2) (команда без immediate)
PC = proc_addr; вызов процедуры
; возврат из процедуры
PC = R3
Как видно никакого SWAP и стека в таких хвостовых функциях можно реально не использовать.
Теоретически можно было бы использовать какую нибудь из запрещенных комбинаций регистров в операндах или псевдо-nop-ы как расширители команд, но повторюсь — мне здесь нравится именно простота.
В том то и дело, что я нахожу такую систему команд довольно эффективной и удобной. Тут само мышление прямолинейно как стрела — всё есть пересылка данных с опциональной операцией с высокой степенью ортогональности. Не нужно задумываться над перекладыванием данных по регистрам как в Z80 или тем какие режимы адресации есть а каких нет и в каких операциях как в 6502.
Чем же он уродлив? inc2 (реализовать можно как ADD с загрузкой константы 2 в Y на первых фазах выполнения команды) часто нужен, потому и сделан — экономит код программы хорошо. Он полностью укладывается в парадигму X ?= Y и уродливым в ней быть не может — inc1 разве чем то уродлив? Тоже часто нужен, потому и полезен — абсолютно та же фигня и даже в тот же профиль.
В эти годы уже просто для поддержки CP/M могли целый процессор Z80 засадить в какой нибудь Commodore XXL (не помню точно модели) — какая уж тут гонка за примитивизмом?
Я уже говорил — команда типа SWAP BYTES внутри регистра (опять таки с логикой Y = swap bytes of X) делает такой сценарий маловероятным. Ну и вообще соединять комп с устройством которое плюётся данными быстрее чем или даже соизмеримо чем скорость с которой процессор отрабатывает memfill — это сомнительно и вряд ли такое вообще есть/было на практике.
Swap регистров обычно нужен когда они существенно неортогональны и ради аккумулятора надо перестановки делать. Тут такой острой необходимости нет.
Регистров на самом деле хватает чтобы сделать memmove/memcopy полностью на регистрах, а это я считаю эталонным кодом для проверки на нехватку регистров.
Т.к. регистров действительно немного, а в самом обобщённом случае Link Register таки нужно пихать в стек, то делать так всегда имхо проще для системы команд. В ней есть только одна операция X ?= Y с лёгкими вариациями. Swap уже не пролазит и не знаю как его протащить без вреда для внутренней красоты. :D
Только до тех лет когда начали делать 128Кб-ные машины.
Далее получилось что истинным 16-биткам уже не хватало и этого и в них перекочевали банкинги и свитчинги в лучшем случае реализованные как сегментные регистры. Байты, правда, конечно, уже к тому времени стали вещью безальтернативной.
Да ну, не вижу ни трудностей ни тем более невозможностей.
Несомненно раз уж есть мощная 8-битная предыстория, то разумно предусмотреть команды типа обмена/сдвига/зануления в слове байт, но уж куда подвести проводки от 8-битной периферии к 16-битной шине данных — по моему дело просто вкуса, но никак не трудностей.
Вот можно было бы и послушать, если есть конкретные образы и идеи. :)
Это само по себе было бы не особо интересно даже, если бы не список платформ на которые это продолжение было портировано.
Кроме очевидных Windows и Linux есть порт на DOS (!) (причём использующий DOS4/GW о чём отдельная песня у них есть: gamedev.ru/flame/forum/?id=245611)
Но кроме этого есть два порта на самодельные (!) портативные игровые консоли Black Prism и EMU Pocket! :D
Не удивлюсь если это широко известные люди в узких кругах. :D
[addr1] = [addr2]
или:
[addr1] += [addr2]
вообще минуя регистры общего назначения. за счёт того, что машинный цикл команды допускает сразу и многократные чтения из памяти и запись в память в конце команды — нет крайне часто встречаемого запрета в процессорах на то чтобы оба аргумента инструкции были в памяти.
казалось бы это должно сильно усложнить логику процессора, но как по мне — так нет. Тут как говорится «всё познаётся в сравнении».
Ведь возьмём такой популярный 8-битный процессор как i8080 на котором базирована командная система Z80.
В нём есть такая забавная инструкция как LHLD imm16, которая в синтаксисе Z80 выглядит как LD HL,(addr). Т.е. в регистровую пару HL считывается слово лежащее по адресу указанному в самой инструкции (imm16).
А что это с точки зрения машины состояний процессора? Во первых процессор 8-битный и все считывания 8-битные.
Поэтому сперва считывается код инструкции, потом считываются последовательно два байта imm16, потом считываются последовательно 2 байта лежащие по этому адресу и они уже рассовываются по регистрам H и L. Т.е., на минуточку, кроме считывания опкода в одной инструкции происходит 4 считывания из которых 2 меняют PC. Это уже по нагрузке на схемотехнические разбивки на шаги чтения/записи равно по сложности самому сложному возможному варианту возможных шагов чтения/записи единственной команды у Simpleton (только одно чтение меняется на запись), только вот у последнего команда может делать гораздо больше действий.
Т.е. любая команда это взятие операнда X или операндов X и Y, заталкивание их в ALU и записывание результата обратно во операнд Y. С аскетичностью и эзотеричностью даже One Instruction Set Computer это имеет мало чего общего. Здесь скорее преследуется цель сделать всё как можно более прямолинейным и простым для программирования — как можно более полная ортогональность (за исключением регистра SP) и никаких особых случаев и команд которые надо специально помнить.
Насчёт старых компьютеров у меня есть статья так что я в курсе. :)
LD (HL+), A
LD HL, SP+imm8
ADD SP, imm8
то есть можно забавно адресовать локальные переменные на стеке, и
LD ($FF00+imm8), A
LD ($FF00+C), A
предполагают, что в 127 байтах быстрой памяти можно чего то интересного сохранять для более быстрой работы.
В общем было бы интересно пощупать этого мутанта в продакшене так сказать. :)
Обожаю вообще генеративные вещи, псевдорандомы, динамический хаос и фрактальные вещи любой природы и эти программки — один из изумительнейших представителей направления.
Те кто читал внимательно предыдущую статью про архитектуру Atari Jaguar могли заметить, что описывая блиттер консоли были упомянуты и затенение по Гуро и Z-буфер, но ничего не было сказано про текстурирование. И это действительно так — блиттер не умел текстурирование вообще.
А ведь даже современник — 3DO знал текстурирование (хотя наоборот не знал Z-буфер), но там другая история и весьма иной аппаратный ускоритель.
Действительно, если посмотреть на игру идущую в комплекте с консолью на её старте, то можно воочию увидеть, что всё 3D состояло из освещённых, но нетекстурированных треугольников:
youtu.be/dfCcgwFjlxE
Тем не менее игры с текстурированием были, но как можно понять по недолгому размышлению в этом случае игра уже не использовала аппаратный блиттер, а целиком возлагала процесс рисования треугольников на программируемое RISC-ядро чипа Том (а может даже припрягала в помощь к нему подобное ядро из чипа Джерри).
В связи с этим было интересно натолкнуться на интервью зарубежного компьютерного журнала о видеоиграх Edge с Джоном Кармаком после того как был анонсирован выход игры Doom на Atari Jaguar, но процесс разработки был еще в середине процесса.
Приведу лишь ссылки на скрины (т.к. они сами довольно большие по размеру):
i.imgur.com/rLUV4OZ.jpg
i.imgur.com/He5c9rd.jpg
i.imgur.com/zTcX50I.jpg
i.imgur.com/adZsajM.jpg
Если вкратце, то Кармак весьма нахваливал консоль и даже выразился о ней как о лучшей игровой платформе на тот момент времени.
По его словам у него заняло две недели от начала портирования Doom на Atari Jaguar прежде чем хоть какая то картинка на телевизоре появилась, но с ужасным фреймрейтом.
Он подтверждает, что основной процессор m68k слишком медленный, но дополнительные RISC-ядра — это вестчь! Но ему требуется время чтобы переписать код так, чтобы он помещался стадиями в маленькие кусочки памяти RISC-ядер (см. описание Atari Jaguar выше если еще не).
Причём сперва он любопытства ради начал портировать движок Wolf 3D со SNES на Atari Jaguar и когда «пятнадцатью сидиромов позднее» у него получилась картинка — он послал результат в Atari и они дали добро на порт Doom.
Но до этого Джон потратил три недели чтобы сделать лучший порт Вольфеншейна среди всех прочих — он работает на 30 фпс с вчетверо большей деталистичностью чем на ПК и даже звук звучит на 22 кГц, что в трое больше чем на ПК. В общем этим портом он остался доволен.
Но Doom на момент написания статьи всё еще представлялся тёмной лошадкой — Кармак говорит, что текстурирование будет выполнятся не совсем так же как на ПК, но по схожему принципу. Консоль весьма хвалит называя лучшим игровым железом на тот момент на рынке. Положительно отзывается о процессоре объектов, т.к. тот и картинку позволяет легко компоновать из разных сюрфейсов так и спрайты поверх выводить в совершенно свободном виде. Так же он хвалит модель цвета CRY (нечто вроде CMY(k), как я писал в статье) и вообще называет её лучшей в мире моделью цвета для видеоигр ибо затенение (освещение) становится тривиальной штукой (просто канал яркости это отдельный канал яркости). Ну а 16-битный цвет сам по себе должен был позволить выйти за рамки ограничений ПК-шной 256-цветной палитры намного и всерьёз.
Однако уже здесь Кармак признавал, что Jaguar не удастся добиться плавности в 30 фпс какую Doom имел на процессоре класса Pentium — тут он на тот момент разводил руками: «посмотрим как получится».
Здесь же он упоминает, что несмотря на то, что он сперва был недоволен тем, что Atari пытаются разработать собственные RISC-процессоры, но в итоге он нашёл их довольно быстрыми и приятными в разработке — единственный минус — малый объём локальной памяти. Ну и то, что в них есть баги (лол!).
Так что единственная аппаратная ошибка которую, по его мнению, совершила Atari — это то, что в качестве ЦП в консоли стоит m68k. RISC-ядра по его мнению в 20 раз быстрее.
Далее разговор немного отходит от консоли, но просто ради фана упомяну интересные моменты. В этот момент Кармак говорит, что в разработке находятся три игры на движке Doom: Doom II, некая Druid от Raven Software («фентезийная версия Doom») и Strife от Sygnus Studio. И что уже неиллюзорно пахнет Quake-ом.
Просто факты о том о сём: разработка графического ядра Wolf 3D заняла месяц, разработка графического ядра Doom заняла уже 3 месяца, но потом пришлось потратить 2 месяца на полное переписывание его под новый центральный алгоритм. Одним из наиболее трудоёмких алгоритмов к реализации в движке Doom он называет определение видимости по лучу в лабиринте карты, в то время как параллакс неба называет весьма простой штукой.
Ну и напоследок высказывается (как со стороны журналиста так и Кармака) пренебрежение к Full-Motion-Video играм (виртуальным тирам) и Кармак прямо говорит, что до тех пор пока Nintendo не прекратит бегать со своей ханженской моралью вряд ли что-то от id software появится еще на их консолях (отсылка к тому, что в Wolf3D пришлось убрать свастики).
P.S.
Ах, да. Doom на Atari Jaguar получился всё-таки мыльноватым, хотя фпс нормальный для того времени:
youtu.be/04Q_Jl7nGdM
Пишу сейчас про Atari Jaguar (а это ровесница Playstation 1) — там как раз по сути синтез таких подходов.
На видеовыход подаётся информация именно из строки-буфера в то время как в теневую такую же рисуются пиксели для следующей строки. При этом видеоадаптер для построения следующей строки проходит по командам дисплейного списка, где типовыми объектами являются битмапы из которых один сканлайн за раз переносится со всевозможными наложениями и прозрачностями диктуемыми порядком отрисовки. Таким образом это могут быть как спрайты из атласа так и почти полноэкранными битмапами в которые рендерил специализированный процессор. Черновик статьи с этим можно сейчас посмотреть тут: gamedev.ru/flame/forum/?id=231027&page=7&m=4976559#m95
Оказалось что и с палитрами там всё очень сложно.
Во первых — при рендере текстурированного спрайта в 16-битный фреймбуфер данные о цвете его битмапа могут быть представлены непосредственно как 16-битные значения, а могут быть 4-битными слотами в 16-цветной палитре 16-битных же цветов. Но в любом случае во фреймбуфер попадает 16-битное данное у которого верхний бит контролирует является оно RGB или является цветом в палитре.
Так вот когда оно является цветом в палитре существует 8 глобально выставляемых форматов того какие биты в нём отвечают за:
а) номер цвета в палитре (до 10 бит)
б) порядок отрисовки между задними фонами (до 3-х бит)
в) номер расширенного вычисления цвета (до 3-х бит)
г) бит «тени»
Кроме того для 8-битного режима фрембуфера есть так же 8 форматов как эти же биты, но уже в урезанном количестве распределены по 8-битному данному о цвете в нём.
И там такая битовая мешанина из сотен регистров управляющих всякими этими битовыми комбинациями, что как говорится «без поллитры не разберешься».
Реально ловлю то же самое ощущение что было в похожем по духу эпохи 3DO — вся эта битово-палитрово-слоевая мехника переусложнена до жути.
Мегадрайв от той же фирмы был просто гимном лаконичности и простоты своей графической начинки — одна палитра, один палитровый режим и для спрайтов и для тайлов, но пасаран. Тут же прям им крышу сорвало на то как впихать в биты как можно большее число всяких палитр, режимов и цветов. Сомневаюсь что даже половина этих всех вещей была в реальности востребована.
А вот дальше еще на другом форуме подсказали, что была такая весьма забавная видеокарта как Diamond Edge 3D — согласно википедии первая видеокарта на пользовательском рынке ПК с аппаратным ускорением (95-ый год). Так вот это был некий синтез видео- и звуковых чипов из Sega Saturn в виде платы для IBM PC. И вот тут всё стало совсем интересно, ведь Diamond Edge 3D в сердце своём содержит чип NV1 который разработала, да, nVidia.
Похоже, что это был одна из первых попыток этой фирмы продвигать 3Д в массы.
В статье про Sega Saturn этому посвящена всего одна строчка (мой перевод):
И удивительно, но в русском википедии есть гораздо больше интересной информации, чем в английской: ru.wikipedia.org/wiki/NV1
Хех, оказывается за этими квадратностями стояла никто иная, как сама nVidia и это был её первый блин комом.