Возможно стоит в самом верху написать по-русски, что перевод ниже. Или оформить перевод отдельным постом. Потому что сейчас те, кто не знают английского посчитают, что перевода в посте нет.
В онлайн-эмуляторе 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 оказывается сравнительно дружелюбная архитектура к Форту хотя таковой совсем не планировалась. :)
Спасибо за такой подробный и развернутый ответ!
Кодить что-то своё наврядли буду, просто было интересно )
Про 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 дополнит/поправит, если что.
Никогда не вникал в эту тему, но стало интересно. А как смешивают каналы? Складывают через 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.
Мимо он не прошёл, был на кассете с Ping-Poing и Thanatos. И я действительно вспоминал обоих Saboteur'ов, когда делал список, но всё же не включил. Но да, там тоже очень интересное звучание и сама музыка.
Поскольку я очень любил бипер, а слушать бипер на реале можно только загружая софт, что медленно даже с дисководом, я делал себе сборник своей самой любимой выдранной биперной музыки. Возможно тебе будет приятно узнать, что Saboteur 2 у меня там был выдран. Но в остальном, скажу честно, у меня там наверное больше несовпадений со списком Shiru, чем совпадений. Просто не выберешь TOP 10 или ТОП 20 чтобы угодить всем.
Да, определённо раньше. В 1994-1995 я только осваивал Бейсик, и никакого системного софта у меня просто не было. В 1996 стал использовать MCODER2, стал писать первые процедурки в машкоде, потом добыл ZEUS, Gens. А полностью на ассемблере начал писать программы в 1997.
Я не то, чтобы извиняюсь, но иронизирую над проблемой восприятия. Это касается не только бипера, но любого чиптюна. Если вдруг меня спрашивают про занятия музыкой, мне очень трудно объяснить, что и почему я делаю, и почему оно звучит так необычно, не как из радио. Многие определяют этот диапазон звучания как 'что-то игрушечное', кому-то трудно воспринять это как музыку в принципе. С другой стороны, я в принципе не отношусь к чему-либо излишне серьёзно (музыка-игры-демки-разработка), несмотря на то, что это типа как мои основные занятия в жизни.
Согласен, главное качество звуковых чипов — унификация звучания, по крайней мере значительное снижение диапазона возможностей по звучанию, в обмен на облегчение жизни процессору. И у большинства чипов есть некоторые скрытые фишки, которые со временем были найдены, изучены, и существенно расширили потенциал. Огибающая у AY, коротко-периодический шум у SN76489, DPCM сэмплы тональных инструментов на NES, и так далее.
Также разделяю мысли, что бипер наиболее идеологически натуральное звуковое устройство для Спектрума, и наиболее интересное со стороны творческой свободы.
После выполнения инициализирующего кода в 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 нет байтов, а и регистры и ячейки памяти есть 16-битные, то Forth уже удачно ложится по своей безтиповой стековой модели. Угу.
Во вторых если рассмотреть базовые арифметические операции Forth — они производятся над двумя величинами в вершине стека которые замещаются одним результатом.
Но в Simp4 этому крайне удачно соответствует сам принцип работы со стеком и в точности, например, операция форта + будет реализована всего одной машинной командой размером в минимальное одно слово:
Вместо + может быть и минус и вся прочая базовая арифметика процессора.
Такая адресация используя специфичное поведение регистра sp при косвенной адресации реализует именно что базовую модель форта — со стека снимаются два аргумента, помещаются на вход АЛУ (sp каждый раз при «изъятии» пост-декрементируется) и далее результат из АЛУ записывается обратно в стек с пред-инкрементом. Ровно то, что нужно форту.
Сложнее, например, с командой DUP. Она должна продублировать вершину стека. Но записать это как
нельзя, т.к. sp без альтернатив при косвенном считывании пост-декрементируется, а при записи пост-инкрементируется, это его неизменная фича. А значит мы останемся ни с чем. Просто холостая проброска без изменения состояния машины.
А значит нужно сперва нацелить другой регистр на вершину стека и действие выполняется в две 16-битных инструкции:
Т.е. загоняем в r0 копию указателя на вершину стека и вот её уже дублируем.
Но интересно тут то, что константа 0 в используемом тут режиме inplace-immediate (может полностью отсутствовать с кодом операции + как во второй инструкции) может быть любым числом от -8..+7, что позволяет уложить этот режим в одно слово инструкции. А значит во первых — ряд инструкций которым нужно дотянуться неглубоко в стек (до -8 от вершины) могут использовать этот сокращённый режим инструкции, а форту больше и не надо.
Однако такое удобство стека в Simp4 вроде бы как ставит подножку — использовать его уже понятно, что очень удобно для форта, но как же переходы в другие участки кода (CALL-ы) которые тоже используют стек?
А вот как: в Simp4 на самом деле по архитектуре нет команды CALL, а она эмулируется последовательностью из, например, таких инструкций:
Т.е. да, в Simp4 мы всегда сами помещаем в стек «вручную» адрес возврата предварительно его вычислив где опять таки пригождается сложение в режиме inplace-immediate, т.к. желательно уложится в одно слово и потом мы совершаем безусловный прыжок куда надо. Такая последовательность инструкций занимает уже три слова, т.к. proc_address как полное 16-битное слово надо размещать за последней инструкцией. Поэтому в стек в первой команде помещается pc+2 — на момент выполнения этой инструкции pc уже смотрит в начало следующей которая занимает 2 слова, вот мы и вычисляем адрес возврата сразу за ними.
Окей, но это для нас значит, что мы можем вообще изменить стратегию вызова процедур в режиме форта и не использовать для неё регистр sp!
Можно применить совмещённую стратегию программного стека и LINK REGISTER. Договоримся, что регистр r4 указывает на слово сразу под вершиной программного стека для вызова процедур и тогда вызов будем писать так:
Это всё те же три слова инструкций, но регистр r4 сам не декрементируется. У него в отличие от sp такой фичи нет.
Поэтому если процедура не вызывает другие процедуры (leaf-node) она просто не портит r4 и делает возврат тривиально:
И ячейка на которую указывает [ r4 ] снова готова к использованию без какого либо пенальти по сравнению с классическим стилем вызова процедур в Simp4 как я его использую.
Но если процедура собирается вызывать другие процедуры, то она должна начинаться с тривиального пролога:
Просто при входе декрементируем r4 в одну инструкцию-слово.
И возврат из такой процедуры усложняется всего лишь на одно слово:
И вуаля! Такой отказ от использования аппаратного стека для подпрограмм совсем не выглядит катастрофой, обременительные расходы совсем небольшие, а для leaf-node-процедур вообще нулевые. Зато гибкость аппаратного стека для арифметики самого Форта выглядит солидным достижением.
Забавно, забавно. Simp4 оказывается сравнительно дружелюбная архитектура к Форту хотя таковой совсем не планировалась. :)
Кодить что-то своё наврядли буду, просто было интересно )
Про PDM даже мысли в голову не приходили. По идее следующий шаг после PWM — это реализовать дельта-сигма модуляцию!
Squeeker смешивает звуки по OR, что может звучать странно, но работает неплохо. Мой Octode XL как бы складывает каналы вместе и использует сумму как итоговую скважность (фактически, PWM).
Было бы интересно сделать биперный движок на PDM. К сожалению, не знаю как синтезировать PDM с нужной скоростью.
* Можно выводить звук каждого канала тонкими иголками (тонкими = намного уже периода цикла дискретизации). Тогда громкость можно изобразить меняя ширину иголки. Так были устроены движки Фоллина, и это довольно популярный подход.
* Так как иголки сравнительно узкие, а периоды нот сравнительно длинные, большую часть времени ничего выводить не нужно. Поэтому можно сделать иголки пошире, например шириной в период дискретизации, или даже в несколько. Это идея довольно редкая, но так устроен, например, Octode XL.
* Можно думать об этом как о чём-то вроде ШИМ. Тогда можно просто сделать движок как что-то типа ШИМ движка, и кормить ему сэмплы, как в обычном цифровом движке. utz делал несколько современных движков по такому принципу, и наверное не он один (но я не вспомню сейчас их названия).
* Можно выводить текущее состояние каждого канала звука раз в цикл дискретизации. Типа, 2 канала выдают прямоугольники, каждый со своим периодом, тогда выводим в звуковом цикле поочерёдно состояние каждого из каналов. Так работают Wham the Music Box, Savage, из новых движков Tritone, ну и многие др., это чуть ли не самая «модная» стратегия.
* Можно смешивать подход с иголками и подход с прямоугольниками. Это тоже довольно редкая идея, но Squeeker устроен как раз так. Плюс такой идеи в том, что прямоугольники лучше воспроизводят басы, а иголки дают более разнообразное звучание.
Вроде ничего фундаментального я не забыл, но пишу второпях. Надеюсь, что Shiru дополнит/поправит, если что.
В 2013 я переделал этот сборник, существенно расширив его. Но Cybernoid 2 у меня всё равно не попал. И даже если бы я брал что-то на этом движке, я бы 100% взял титульный трек из Stormlord.
Cybernoid 2 присутствует? :)
мимо-то он точно пройти не мог
Я не то, чтобы извиняюсь, но иронизирую над проблемой восприятия. Это касается не только бипера, но любого чиптюна. Если вдруг меня спрашивают про занятия музыкой, мне очень трудно объяснить, что и почему я делаю, и почему оно звучит так необычно, не как из радио. Многие определяют этот диапазон звучания как 'что-то игрушечное', кому-то трудно воспринять это как музыку в принципе. С другой стороны, я в принципе не отношусь к чему-либо излишне серьёзно (музыка-игры-демки-разработка), несмотря на то, что это типа как мои основные занятия в жизни.
Согласен, главное качество звуковых чипов — унификация звучания, по крайней мере значительное снижение диапазона возможностей по звучанию, в обмен на облегчение жизни процессору. И у большинства чипов есть некоторые скрытые фишки, которые со временем были найдены, изучены, и существенно расширили потенциал. Огибающая у AY, коротко-периодический шум у SN76489, DPCM сэмплы тональных инструментов на NES, и так далее.
Также разделяю мысли, что бипер наиболее идеологически натуральное звуковое устройство для Спектрума, и наиболее интересное со стороны творческой свободы.