2022 Demodulation (17-18 September) photos.app.goo.gl/gfXgDxYZbb3u8Njo8
  • avatar nodeus
  • 0
Репа переехала на Гитхаб
  • avatar Vinnny
  • 2
2022 Undefined Summer (20-21 August) photos.app.goo.gl/zH5iQPW6DFZZZVcGA
Отличный выбор человека для интервью, очень интересно узнать о нём хотя бы немного.
Возможно те, кому лень пролистать статью на английском языке даже не заинтересованы этой тематикой?
  • avatar frog
  • 0
Возможно стоит в самом верху написать по-русски, что перевод ниже. Или оформить перевод отдельным постом. Потому что сейчас те, кто не знают английского посчитают, что перевода в посте нет.
Невероятно крутая демо! Спасибо за статью!
  • avatar Vinnny
  • 2
2022 DiHalt Summer Camping (29-31 July) photos.app.goo.gl/HQG8nniH5hv4EbA29
  • avatar sq
  • 0
diver4d , пора писать статью про гамма-чанки, в чём была их идея (и почему мы (я) налажали, и из-за этого никто ничего не понял :)
  • avatar sq
  • 0
Статья не моя, поэтому все плюсы и слава должны достаться MixailV aka Monster^Sage :)
В онлайн-эмуляторе aa-dav.github.io/ доступен новый исходник «bitmap noise» — выбираем пункт и нажимаем «Compile and Run».
После выполнения инициализирующего кода в simple_lib.inc раскладка памяти следующая:
С адреса $8000 располагаются 16384 _слов_ (ячейки памяти 16-битные) описывающие 16-цветный битмап 256x256 (на экране без записи в регистры скроллинга видны верхние 192 строки). Т.к. ячейки — слова, то в каждое слово влезает четыре пикселя по 4 бита цветности на каждый. Раскладка линейная.
Но еще с адреса $C000 располагается зона атрибутов размером 32x32 знакоместа. Атрибут имеет маску PPPP00NN NNNNNNNN, где N это номер выводимого знакоместа в соответствующей квадратной сетке, а PPPP — верхние биты палитры из 256 цветов которые будут добавляться к цветам пикселя. Т.е. номер субпалитры в 16 слотов из 16 субпалитр. Чтобы записать в палитру надо записать в ячейку/порт $FFF6 номер слота палитры (0..255), а в $FFF7 RGB-значение в формате 0RRRRRGGGGGBBBBB.
Таким образом на экране может быть одновременно 256 цветов, но не более чем 16 цветов в знакоместе.
Более того — отображение в знакоместах можно перенастраивать на другие знакоместа через таблицу атрибутов реализуя быстро работающие текстовые видеорежимы ну или игровое тайловое поле с аппаратным скроллингом.
Размышляя на досуге о том не присобачить ли какой-нибудь ЯВУ вместо ассемблера просто подумал — а насколько архитектура Simp4 хорошо ложится на крайне минималистичный язык Forth?
А ведь есть весьма хороший из-за симметричности системы команд уровень «природной» совместимости.
Во первых т.к. в Simp4 нет байтов, а и регистры и ячейки памяти есть 16-битные, то Forth уже удачно ложится по своей безтиповой стековой модели. Угу.
Во вторых если рассмотреть базовые арифметические операции Forth — они производятся над двумя величинами в вершине стека которые замещаются одним результатом.
Но в Simp4 этому крайне удачно соответствует сам принцип работы со стеком и в точности, например, операция форта + будет реализована всего одной машинной командой размером в минимальное одно слово:

[ sp ] = [ sp ] + [ sp ] ; считываем со стека два операнда (с пост-декрементом), суммируем и отправляем обратно в стек (с пост-инкрементом)


Вместо + может быть и минус и вся прочая базовая арифметика процессора.
Такая адресация используя специфичное поведение регистра sp при косвенной адресации реализует именно что базовую модель форта — со стека снимаются два аргумента, помещаются на вход АЛУ (sp каждый раз при «изъятии» пост-декрементируется) и далее результат из АЛУ записывается обратно в стек с пред-инкрементом. Ровно то, что нужно форту.

Сложнее, например, с командой DUP. Она должна продублировать вершину стека. Но записать это как

[ sp ] <- [ sp ]; аналог move с нулём в качестве второго операнда режима сложения с inplace-immediate


нельзя, т.к. sp без альтернатив при косвенном считывании пост-декрементируется, а при записи пост-инкрементируется, это его неизменная фича. А значит мы останемся ни с чем. Просто холостая проброска без изменения состояния машины.
А значит нужно сперва нацелить другой регистр на вершину стека и действие выполняется в две 16-битных инструкции:

r0 <- sp + 0
[ sp ] <- [ r0 ]


Т.е. загоняем в r0 копию указателя на вершину стека и вот её уже дублируем.
Но интересно тут то, что константа 0 в используемом тут режиме inplace-immediate (может полностью отсутствовать с кодом операции + как во второй инструкции) может быть любым числом от -8..+7, что позволяет уложить этот режим в одно слово инструкции. А значит во первых — ряд инструкций которым нужно дотянуться неглубоко в стек (до -8 от вершины) могут использовать этот сокращённый режим инструкции, а форту больше и не надо.

Однако такое удобство стека в Simp4 вроде бы как ставит подножку — использовать его уже понятно, что очень удобно для форта, но как же переходы в другие участки кода (CALL-ы) которые тоже используют стек?
А вот как: в Simp4 на самом деле по архитектуре нет команды CALL, а она эмулируется последовательностью из, например, таких инструкций:

[ sp ] <- pc + 2
pc <- proc_address


Т.е. да, в Simp4 мы всегда сами помещаем в стек «вручную» адрес возврата предварительно его вычислив где опять таки пригождается сложение в режиме inplace-immediate, т.к. желательно уложится в одно слово и потом мы совершаем безусловный прыжок куда надо. Такая последовательность инструкций занимает уже три слова, т.к. proc_address как полное 16-битное слово надо размещать за последней инструкцией. Поэтому в стек в первой команде помещается pc+2 — на момент выполнения этой инструкции pc уже смотрит в начало следующей которая занимает 2 слова, вот мы и вычисляем адрес возврата сразу за ними.
Окей, но это для нас значит, что мы можем вообще изменить стратегию вызова процедур в режиме форта и не использовать для неё регистр sp!
Можно применить совмещённую стратегию программного стека и LINK REGISTER. Договоримся, что регистр r4 указывает на слово сразу под вершиной программного стека для вызова процедур и тогда вызов будем писать так:

[ r4 ] <- pc + 2
pc <- proc_address


Это всё те же три слова инструкций, но регистр r4 сам не декрементируется. У него в отличие от sp такой фичи нет.
Поэтому если процедура не вызывает другие процедуры (leaf-node) она просто не портит r4 и делает возврат тривиально:

pc <- [ r4 ]


И ячейка на которую указывает [ r4 ] снова готова к использованию без какого либо пенальти по сравнению с классическим стилем вызова процедур в Simp4 как я его использую.
Но если процедура собирается вызывать другие процедуры, то она должна начинаться с тривиального пролога:

r4 <- r4 - 1


Просто при входе декрементируем r4 в одну инструкцию-слово.
И возврат из такой процедуры усложняется всего лишь на одно слово:

r4 <- r4 + 1 ; увеличиваем r4 на 1
pc <- [ r4 ] ; переходим по адресу возврата в ячейке куда указывает r4


И вуаля! Такой отказ от использования аппаратного стека для подпрограмм совсем не выглядит катастрофой, обременительные расходы совсем небольшие, а для leaf-node-процедур вообще нулевые. Зато гибкость аппаратного стека для арифметики самого Форта выглядит солидным достижением.

Забавно, забавно. Simp4 оказывается сравнительно дружелюбная архитектура к Форту хотя таковой совсем не планировалась. :)
  • avatar MixailV
  • 0
Спасибо за такой подробный и развернутый ответ!
Кодить что-то своё наврядли буду, просто было интересно )
Про PDM даже мысли в голову не приходили. По идее следующий шаг после PWM — это реализовать дельта-сигма модуляцию!
Забыл ответить точнее на твои варианты. Движков через AND или XOR я не знаю (AND звучит как неважная идея, XOR может и заработать, не пробовал). Идея с чередованием — предпоследняя в моём списке, так делают многие старые движки, да и новые тоже. Недостаток этой идеи: трудно сделать много каналов, т.к. часто делаем out, причём такая схема требует высокой частоты дискретизации (из-за частого переключения порта, мы производим больше высокочастотных помех). Из-за этого, например, движок Wham так сильно свистит.

Squeeker смешивает звуки по OR, что может звучать странно, но работает неплохо. Мой Octode XL как бы складывает каналы вместе и использует сумму как итоговую скважность (фактически, PWM).

Было бы интересно сделать биперный движок на PDM. К сожалению, не знаю как синтезировать PDM с нужной скоростью.
Есть много подходов. Вот прямо из головы несколько:
* Можно выводить звук каждого канала тонкими иголками (тонкими = намного уже периода цикла дискретизации). Тогда громкость можно изобразить меняя ширину иголки. Так были устроены движки Фоллина, и это довольно популярный подход.
* Так как иголки сравнительно узкие, а периоды нот сравнительно длинные, большую часть времени ничего выводить не нужно. Поэтому можно сделать иголки пошире, например шириной в период дискретизации, или даже в несколько. Это идея довольно редкая, но так устроен, например, Octode XL.
* Можно думать об этом как о чём-то вроде ШИМ. Тогда можно просто сделать движок как что-то типа ШИМ движка, и кормить ему сэмплы, как в обычном цифровом движке. utz делал несколько современных движков по такому принципу, и наверное не он один (но я не вспомню сейчас их названия).
* Можно выводить текущее состояние каждого канала звука раз в цикл дискретизации. Типа, 2 канала выдают прямоугольники, каждый со своим периодом, тогда выводим в звуковом цикле поочерёдно состояние каждого из каналов. Так работают Wham the Music Box, Savage, из новых движков Tritone, ну и многие др., это чуть ли не самая «модная» стратегия.
* Можно смешивать подход с иголками и подход с прямоугольниками. Это тоже довольно редкая идея, но Squeeker устроен как раз так. Плюс такой идеи в том, что прямоугольники лучше воспроизводят басы, а иголки дают более разнообразное звучание.
Вроде ничего фундаментального я не забыл, но пишу второпях. Надеюсь, что Shiru дополнит/поправит, если что.
  • avatar MixailV
  • 0
Никогда не вникал в эту тему, но стало интересно. А как смешивают каналы? Складывают через and, ксорят, или чередуют, выделяя некий квант времени на проигрывание каждого?
просто интересно, чтобы ты сказал о других классических биперах, как-то: cybernoid-2, last ninja, robin of the wood, sooty and sweep, hate, renegade, soldier of fortune, flying shark… для необразованного меня в те года все движки делились на два типа: «вхам» и «не-вхам» (и этот второй еще на две разновидности: «тихие» и «громкие»)))
Вот список треке в старом сборнике, кот я делал в 1994: SAVAGE 1, SAVAGE 2, SAVAGE 3, MiG-29, GOLDEN AXE, CHRONOS, RAMPARTS, SOLDIER OF FORTUNE, FAIRLIGHT, SABOTEUR 2, FOX FIGHTS BACK, GRYZOR, RACE AGAINST TIME.

В 2013 я переделал этот сборник, существенно расширив его. Но Cybernoid 2 у меня всё равно не попал. И даже если бы я брал что-то на этом движке, я бы 100% взял титульный трек из Stormlord.
  • avatar aa-dav
  • 0
Но в остальном, скажу честно, у меня там наверное больше несовпадений со списком Shiru, чем совпадений.

Cybernoid 2 присутствует? :)
  • avatar Shiru
  • 0
Мимо он не прошёл, был на кассете с Ping-Poing и Thanatos. И я действительно вспоминал обоих Saboteur'ов, когда делал список, но всё же не включил. Но да, там тоже очень интересное звучание и сама музыка.