%\tracingall
\XeTeXtracingfonts=1
\usepackage[a4paper,left=20mm,top=20mm,bottom=15mm,right=20mm,nohead,xetex]{geometry}
\usepackage[pdfencoding=unicode,psdextra,xetex]{hyperref}
\hypersetup{unicode=true,colorlinks=true}
\usepackage[indentafter,raggedright,newlinetospace,pagestyles]{titlesec}
%Доработанные шрифты с поддержкой перечеркнутого нуля
\setmainfont{CMUSerif.otf}[Path=./,UprightFont = *-Roman,BoldFont = *-Bold,Numbers=SlashedZero]
\setmonofont{CMUTypewriter.otf}[Path=./,UprightFont = *-Regular,Numbers=SlashedZero]
\setmathfont{Latin Modern Math}
% Точка после номера раздела в pdf bookmarks
% Оформление оглавления
\etocsetstyle{section}{}{}{\etocsavedsectiontocline{\etoclink{\numberline{\etocifnumbered{\etocnumber.}{}}\sloppy\MakeUppercase{\etocthename}}}{\etocpage}}{}
\etocsetstyle{subsection}{}{}{\etocsavedsubsectiontocline{\etoclink{\numberline{\etocifnumbered{\etocnumber.}{}}\sloppy\etocname}}{\etocpage}}{}
\etocsetstyle{subsubsection}{}{}{\etocsavedsubsubsectiontocline{\etoclink{\numberline{\etocifnumbered{\etocnumber.}{}}\sloppy\etocname}}{\etocpage}}{}
\setlist{nosep,after=\vspace{\baselineskip}}
\DeclareRobustCommand{\Cyrax}{\texorpdfstring{\(^\circledast\)}{\circledast}}
\setmainlanguage{russian}
\setotherlanguage{english}
\begin{document}
\begin{titlepage}
\begin{center}
\textbf{Recoded by Evgeny Muchkin 06.10.1998\\
SysOp of PALLY\_STATION tel: 176-74-19\\
Last update and correxion by Cyrax, Inc. 18.04.2002\\
Former GS programmer\ldots{} ;) email: reptyle@mail.ru}
\textbf{\LARGE\copyright~STINGER \&\\
\copyright~Cyrax, Inc.~--- \Cyrax\label{Cyrax}}
\medskip
\medskip
\textbf{\LARGE{}Руководство по программированию\\
General Sound}
\medskip
\medskip
\textbf{\Large{}Версия v1.04. Редакция 006}
(отредактировал в документ CHRV)
\medskip
Оставлен полностью авторский текст за исключением \\
форматирования и исправления ошибок в тексте.\\
Добавлено описание исправлений в GS ROM v1.05.
\medskip
(преобразовал в \XeLaTeX{} deathsoft 01.07.2018)
\medskip
\end{center}
\end{titlepage}
\section{Краткие технические характеристики GS}
\begin{description}
\item[Процессор:] Z80, 12MHz, без циклов wait
\item[RAM:] Static Ram 128k всего, 112k доступно для модулей и сэмплов в базовой версии
\item[Каналы:] 4 независимых 8-и битных канала, каждый с 6-и битным контролем громкости.
\end{description}
\section{Краткое описание GS, или много всякой лабуды}
GS --- музыкальная карточка, предназначенная для проигрывания музыкальных модулей и отдельных сэмплов (эффектов).
Модули для GS --- это стандартные Амижные и PCшные 4-х канальные MOD файлы, а сэмплы --- как Амижные signed sample, так и PCшные unsigned sample.
Проигрыватель MOD файлов в GS является практически полным аналогом ProTracker'а на Амиге и создавался при интенсивном использовании исходников ProTracker'a. (Исходники были из Protracker'а v2.1A by Lars ``ZAP'' Hamre --- Amiga Freelancers)
MOD Player поддерживает все команды Pro Tracker'а, за исключением двух:
\begin{itemize}
\item Е01 Filter On Амига-специфичная команда, включает фильтр высоких частот.
\item EFX Invert Loop я еще не видел плеера, который бы поддерживал эту команду. Возможно, она поддерживается на каких-то старых плеерах.
\end{itemize}
GS представляет из себя, по-сути, микропроцессорный комплекс со своим процессором, ПЗУ, ОЗУ и портами, и абсолютно не зависит от главного процессора Спектрума, что позволяет, например, загрузить свой любимый модуль, сбросить Спектрум, загрузить ассемблер и творить под любимую музыку. Soft внутри GS полностью берет на себя задачи проигрывания звука, интерпретации модуля и т.д. Программирование GS'а сводится к передаче байт за байтом модуля и/или сэмплов, а затем требуется только подавать команды типа: запустить модуль, установить глобальную громкость проигрывания модуля, запустить сэмпл \#09 в канале \#02 и т.д.
Если предполагается загрузить модуль вместе с сэмплами, то в GENERAL требуется загружать вначале модуль, а затем сэмплы.
При загрузке модуля очень рекомендуется оставить свободными 2к памяти, т.е. загружать модули длиной максимум 110K. Это условие не является необходимым, но его исполнение очень желательно в целях совместимости с последующими версиями.
Аналогично очень рекомендуется оставлять по 80 байт для каждого сэмпла, например, если требуется загрузить 63-х килобайтный модуль и 18 сэмплов, то имеем:
\[\mathrm{Total\_Sample\_Length}=112\cdotp1024-63\cdotp1024-2\cdotp1024-18\cdotp80=46688\ \mathrm{байт}\]
Это суммарная длина сэмплов, которые при таком положении вещей могут быть загружены.
Если же, например, требуется вычислить, сколько поместится в память GS'а 2-х килобайтных сэмплов, то это вычисляется следующим образом:
\(112\cdotp\frac{1024}{2048+80}=53\) сэмпла.
В GS'е имеются 4 физических канала, которые и проигрывают звук.
Каналы 0 и 1~--- левые, а 2 и 3~--- правые.
На мир GS смотрит при помощи 4 регистров:
\begin{enumerate}
\item \textbf{Command register}~--- регистр команд, доступный для записи порт по адресу 187 (\#BB). В этот регистр записываются команды.
\item \textbf{Status register}~--- регистр состояния, доступный для чтения порт по адресу 187 (\#BB).
Биты регистра:
\begin{description}
\item[7] — Data bit, флаг данных
\item[0] — Command bit, флаг команд
\end{description}
\end{minipage}
Этот регистр позволяет определить состояние GS, в частности можно ли прочитать или записать очередной байт данных, или подать очередную команду, и т.п.
\item \textbf{Data register}~--- регистр данных, доступный для записи порт по адресу 179 (\#B3). В этот регистр Спектрум записывает данные, например, это могут быть аргументы команд.
\item \textbf{Output register}~--- регистр вывода, доступный для чтения порт по адресу 179 (\#B3). Из этого регистра Спектрум читает данные, идущие от GS.
\end{enumerate}
Command bit в регистре состояний устанавливается аппаратно после записи команды в регистр команд. Сбрасываться в 0 он может только из GS, что сигнализирует об определенном этапе исполнения команды.
Data bit в регистре состояний может быть установлен или сброшен как по желанию Спектрума, так и по желанию GS: при записи Спектрумом в регистр данных он аппаратно устанавливается в 1, а после чтения GS'ом из этого регистра сбрасывается в 0. При записи GS в регистр вывода он (все тот же Data bit) аппаратно устанавливается в 1, а после чтения из этого порта Спектрумом сбрасывается аппаратно в 0.
Несмотря на то, что регистр данных и регистр вывода расположены в пространстве адресов портов по одному и тому же адресу и воздействуют на один и тот же бит данных, они являются двумя независимыми регистрами. Значение, один раз записанное в один из этих регистров, остается неизменным в нем до новой записи.
Состояние бита данных очень часто не определено, и если в спецификации команд не определены значения этого бита на определенных этапах исполнения команды, недопустимо делать какие-либо предположения относительно значения этого бита.
Вначале позволю себе небольшое отступление от собственно системы команд. GS, как известно, предназначен в основном для проигрывания модулей и сэмплов. В данной версии (1.04) GS ROM допускается загрузка одного модуля и/или до 32 сэмплов.
Каждый сэмпл при загрузке его в память получает свой уникальный идентификатор, который однозначно определяет обращение к данному сэмплу в командах, которые требуют номер сэмпла. Самый первый загруженный сэмпл получает номер (handle) = 1, следующий --- номер 2, и т.д.
То же самое применимо и к модулям, и этот единственный загруженный модуль будет иметь handle=1 после загрузки.
Особенностью данной версии является также то, что вначале требуется загружать модуль, а затем уже сэмплы.
Особенности описания команд:
Команды описываются следующим образом:
\begin{enumerate}
\item Выполняемые действия при исполнении команды
\item Комментарии к команде
\end{enumerate}
\end{minipage}
Формат команды описывается следующим образом:
\begin{verbatim}
GSCOM EQU 187
GSDAT EQU 179
SC #NN : Послать код команды в регистр команд
LD A,#NN
OUT (GSCOM),A
WC : Ожидание сброса Command bit
WCLP IN A,(GSCOM)
RRCA
JR C,WCLP
SD Data : Послать данные в регистр данных
LD A,Data
OUT (GSDAT),A
WD : Ожидание сброса Data bit, по сути, ожидание, пока GS не примет
посланные ему данные
WDLP IN A,(GSCOM)
RLCA
JR C,WDLP
GD Data : Принять данные из регистра данных
IN A,(GSDAT)
WN : Ожидание установки Data bit,по сути, ожидание очередных данных
от GS
WNLP IN A,(GSCOM)
RLCA
JR NC,WNLP
(*):
<PAUSE> - Просто небольшая задержка кадра два-три.
\end{verbatim}
\end{minipage}
И напоследок — небольшая последовательность против зависания (иногда помогает)
\begin{verbatim}
XOR A
OUT (#B3),A
OUT (#BB),A
IN A,(#BB)
\end{verbatim}
для верности можно и продублировать ;).
Сбрасывает флаги Data bit и Command bit.
\begin{verbatim}
SC #00
WC
(Data bit=0, Command bit=0)
\end{verbatim}
Выводит в ЦАПы всех каналов \#80. По сути устанавливает тишину.
\begin{verbatim}
SC #01
WC
\end{verbatim}
Устанавливает громкость ЦАПов всех каналов в ноль.
\begin{verbatim}
SC #02
WC
\end{verbatim}
Устанавливает громкость ЦАПов всех каналов в максимум.
\begin{verbatim}
SC #03
WC
\end{verbatim}
Устанавливает в `E' регистре GS 3 младших бита в соответствии с заданным значением (2 младших бита в сущности являются номером канала \#00~---~\#03).
\begin{verbatim}
SD Chan (#00-#07)
SC #04
WC
\end{verbatim}
Устанавливает громкость канала, номер которого содержится в `E', в указанное значение. (Команда срабатывает при условии, что `E' находится в пределах \#00~---~\#03)
\begin{verbatim}
SD Volume (#00-#3F)
SC #05
WC
\end{verbatim}
Выводит байт в ЦАП канала, указываемого по `E'.
\begin{verbatim}
SD Byte
SC #06
WC
\end{verbatim}
Выводит байт в ЦАП (`E') с заданной громкостью.
\begin{verbatim}
SD Byte
SC #07
WC
SD Volume
WD
\end{verbatim}
Установка громкости канала, номер которого задан в 2х старших битах.
\begin{verbatim}
SD Byte (ccvvvvvv)
SC #09
WC
\end{verbatim}
cc --- Номер канала\\
vvvvvv --- Его громкость
Еще один непосредственный вывод в ЦАП.
\begin{verbatim}
SD Byte
SC #0A
WC
SD Chan (#00-#03)
WD
\end{verbatim}
И наконец последний вывод в ЦАП с установкой громкости.
\begin{verbatim}
SD Fbyte
SC #0B
WC
SD Sbyte (ccvvvvvv)
WD
\end{verbatim}
Назначение битов Sbyte как и у команды~\hyperref[cmd:09]{\#09}.
Команды~\hyperref[cmd:01]{\#01}~---~\hyperref[cmd:0B]{\#0B} служат в основном для построения различных Covox'ов и проигрывателей, при этом не слишком углубляясь во внутреннюю структуру GS.
Вызывает режим четырехканального Ковокса, последовательно копирует регистр данных по каналам. Выход из режима автоматически после вывода четвертого байта.
\begin{verbatim}
SD CH1
SC #0C
WC
SD CH2
WD
SD CH3
WD
SD CH4
WD
\end{verbatim}
Вызывает режим универсального Ковокса, последовательно копирует регистр данных по каналам, число которых регулируется (1~---~4). В отличие от предыдущего варианта синхронизация не производится. Выход также производится автоматически по записи последнего байта.
\begin{verbatim}
SD CHANS
SC #0D
WC
SD CH1
SD CH2
SD CH3
SD CH4
\end{verbatim}
CHANS (4-е младших бита) указывает какие каналы будут задействованы --- для включения канала соответствующий бит нужно установить. Если канал выключен, то поступивший байт попадает на следующий включенный канал (если успеет :)
Переходит в режим одноканального Ковокса, напрямую копирует регистр данных в ЦАПы двух (правого и левого) каналов. Выход из этого режима --- запись \#00 в регистр команд.
\begin{verbatim}
SC #0E
WC
SD \
SD \
... Это вывод в ЦАПы
/
SD /
SC #00
WC
\end{verbatim}
Переходит в режим двухканального Ковокса, напрямую копирует регистр данных в ЦАПы одного канала, а регистр команд в ЦАПы второго канала. Выход из этого режима --- запись \#4Е в регистр данных, затем последовательно \#0F и \#AA в регистр команд.
\begin{verbatim}
SD #59
SC #0F
WC
SD \
SC \
SD \
SC Это вывод в ЦАПы
... /
SD /
SC /
SD #4E
WD
SC #0F
WC
SC #AA
WC
\end{verbatim}
Выводит байт вo внутренний порт GS (\#00~---~\#09).
\begin{verbatim}
SD Port
SC #10
WC
SD Data
WD
\end{verbatim}
Читает байт из внутреннего порта GS (\#00~---~\#09).
\begin{verbatim}
SD Port
SC #11
WC
GD Data
WN
\end{verbatim}
Выводит байт в порт конфигурации GS (\#00).
\begin{verbatim}
SD Data
SC #12
WC
\end{verbatim}
Передает управление по заданному адресу.
\begin{verbatim}
SD ADR.L
SC #13
WC
SD ADR.H
WD
\end{verbatim}
Загрузка блока кодов по указанному адресу с заданной длиной.
\begin{verbatim}
SD LEN.L
SC #14
<PAUSE> (мб WD)
SD LEN.H
WD
SD ADR.L
WD
SD ADR.H
<PAUSE>
SD \
WD \
SD \
WD Блок данных длиной LEN
... /
SD /
WD /
\end{verbatim}
Выгрузка блока кодов по указанному адресу с заданной длиной.
\begin{verbatim}
SD LEN.L
SC #15
<PAUSE> (мб WD)
SD LEN.H
WD
SD ADR.L
WD
SD ADR.H
<PAUSE>
GD \
WN \
GD \
WN Блок данных длиной LEN
... /
GD /
WN /
GD
\end{verbatim}
Записывает единичный байт по указанному адресу.
\begin{verbatim}
SD Byte
SC #16
WC
SD ADR.L
WD
SD ADR.H
WD
\end{verbatim}
Считывает единичный байт из указанного адреса.
\begin{verbatim}
SD ADR.L
SC #17
WD
SD ADR.H
GD Byte
\end{verbatim}
Загружает регистровую пару DE (относящуюся к GS, не путать с одноименной парой Main CPU) указанным словом.
\begin{verbatim}
SD Byte.E
SC #18
WC
SD Byte.D
WD
\end{verbatim}
Записывает байт по адресу указанному в DE.
\begin{verbatim}
SD Byte
SC #19
WC
\end{verbatim}
Считывает содержимое адреса, указываемого по DE.
\begin{verbatim}
SC #1A
WC
GD Byte
WN
\end{verbatim}
Увеличивает пару DE на единичку.
\begin{verbatim}
SC #1B
WC
\end{verbatim}
Записывает байт по адресу, старший байт которого равен \#20. Смысла команда никакого не имеет, так как по этим адресам находится ПЗУ карты.
\begin{verbatim}
SD ADR.L
SC #1C
WC
SD Byte
WD
\end{verbatim}
Читает байт с адреса, старший байт которого равен \#20.
\begin{verbatim}
SD ADR.L
SC #1D
WC
GD Byte
WN
\end{verbatim}
Получить общий объем доступной памяти на GS. (В базовой версии это 112к)
\begin{verbatim}
SC #20
WC
GD RAM.L(Младшая часть)
WN
GD RAM.M(Средняя часть)
WN
GD RAM.H(Старшая часть)
\end{verbatim}
\(\mathrm{Total\_RAM}=65536\cdotp\mathrm{RAM.H}+256\cdotp\mathrm{RAM.M}+\mathrm{RAM.L}\)
Получить общий объем свободной памяти на GS.
\begin{verbatim}
SC #20
WC
GD RAM.L(Младшая часть)
WN
GD RAM.M(Средняя часть)
WN
GD RAM.H(Старшая часть)
\end{verbatim}
\(\mathrm{Free\_RAM}=65536\cdotp\mathrm{RAM.H}+256\cdotp\mathrm{RAM.M}+\mathrm{RAM.L}\)
Получить значение переменной GS номер которой задан по Num. Переменные описывают текущее состояние карты, например номер паттерна, темп и т.п. Соответственно номеров переменных не приводится (в следствие несистематизированности и отрывочности данных, а так же по иным причинам).
\begin{verbatim}
SD Num
SC #22
WC
GD Variable
WN
\end{verbatim}
Получить число страниц на GS. (В базовой версии 3 страницы)
\begin{verbatim}
SC #23
WC
GD Number_RAM_Pages
\end{verbatim}
Установить громкость проигрывания модулей.
\begin{verbatim}
SD Module_Master_Volume [#00..#40]
SC #2A
WC
[GD Old_Master_Volume] - Старая громкость
\end{verbatim}
Маленький пример использования данной команды:
(Предполагается, что играется модуль)
\begin{verbatim}
LD B,#40
LOOP: LD A,B
OUT (GSDAT),A
LD A,#2A
OUT (GSCOM),A
EI
HALT
DJNZ LOOP
LD A,#32
OUT (GSCOM),A
\end{verbatim}
Вышеописанное плавно снижает громкость играющего модуля, а затем останавливает его.
Установить громкость проигрывания эффектов.
\begin{verbatim}
SD FX_Master_Volume [#00..#40]
SC #2B
WC
[GD Old_FX_Volume] - Старая громкость
\end{verbatim}
Аналогично предыдущей команде, но действует на сэмплы.
С помощью этих двух команд можно регулировать баланс громкости модуля и сэмплов, и т.п.
Установить текущий эффект. Просто присваивает переменной CURFX это значение.
Если какая-либо команда требует номер сэмпла (sample handle), то можно вместо этого номера подать ей \#00 и интерпретатор подставит вместо этого нуля значение переменной CURFX.
(См. команды~\hyperref[cmd:38]{\#38}, \hyperref[cmd:39]{\#39}, \hyperref[cmd:40]{\#40}~---~\hyperref[cmd:49]{\#49} для понимания вышеизложенного.)
\begin{verbatim}
SD Cur_FX
SC #2E
WC
\end{verbatim}
Загрузка модуля в память.
\begin{verbatim}
SC #30
WC
[GD Module_Handle]-номер модуля
(Command bit=0, Data bit=0)
SC #D1 (Open Stream-открыть поток)
WC
SD \
WD \
... Байты модуля
SD /
WD /
SC #D2 (Close Stream-закрыть поток)
WC
\end{verbatim}
Пример:
\begin{verbatim}
LD HL,Mod_adress
LD DE,0-Mod_length
LD C,GSCOM
LD A,#30
CALL SENDCOM
LD A,#D1
CALL SENDCOM
LD A,(HL)
LOOP: IN B,(C)
JP P,READY
IN B,(C)
JP M,LOOP
READY: OUT (GSDAT),A
INC HL
LD A,(HL)
INC E
JP NZ,LOOP
INC D
JP NZ,LOOP
WAIT: IN B,(C) ;Ждем принятия
JP M,WAIT ;последнего байта
LD A,#D2
CALL SENDCOM
IN A,(GSDAT) ; Номер модуля
OUT (GSDAT),A
LD A,#31
SENDCOM: OUT (GSCOM),A
WAITCOM: IN A,(GSCOM)
RRCA
JR C,WAITCOM
RET
\end{verbatim}
Проигрывание модуля.
\begin{verbatim}
SD Module_Handle - номер модуля
SC #31
WC
\end{verbatim}
Остановить проигрывание модуля.
\begin{verbatim}
SC #32
WC
\end{verbatim}
Продолжить проигрывание модуля после остановки.
\begin{verbatim}
SC #33
WC
\end{verbatim}
Установить громкость проигрывания модулей.
\begin{verbatim}
SD Module_Master_Volume [#00..#40]
SC #35
WC
[GD Old_Master_Volume] - Старая громкость
\end{verbatim}
Устанавливает регистр данных в \#FF.
\begin{verbatim}
SC #36
WC
[GD Data (#FF) ]
\end{verbatim}
Переустанавливает внутренние переменные в исходное состояние.
\begin{verbatim}
SC #37
WC
\end{verbatim}
Загрузка сэмпла эффекта в память. Загружает беззнаковые сэмплы (PC type)
\begin{verbatim}
SC #38
WC
[GD FX_Handle]-номер сэмпла
(Command bit=0, Data bit=0)
SC #D1 (Open Stream-открыть поток)
WC
SD \
WD \
... Байты сэмпла
SD /
WD /
SC #D2 (Close Stream-закрыть поток)
WC
\end{verbatim}
При загрузке каждого сэмпла, в памяти GS создается для этого сэмпла заголовок, в котором описываются различные параметры сэмпла. После загрузки эти параметры устанавливаются в определенные значения, как то: Note=60, Volume=\#40, FineTune=0, SeekFirst=\#0F, SeekLast=\#0F, Priority=\#80, No Loop и внутренняя переменная CurFX устанавливается равной FX\_Handle.
Затем командами~\hyperref[cmd:40]{\#40}, \hyperref[cmd:41]{\#41}, \hyperref[cmd:42]{\#42}, \hyperref[cmd:45]{\#45},
\hyperref[cmd:46]{\#46} и \hyperref[cmd:47]{\#47} можно эти значения по умолчанию сменить на свои.
Это требуется потому что команда~\hyperref[cmd:39]{\#39} для инициации проигрывания сэмпла использует значения
параметров из заголовка сэмпла.
В своем естественном виде сэмплы обычно плохо пакуются компрессорами, но сжимаемость обычно можно поднять,
если перевести сэмпл в Delta-вид, т.е. хранить не абсолютные значения сэмпла, а относительное смещение
относительно предыдущего байта. Примерно вот так вот можно перевести сэмпл в Delta-вид:
\begin{verbatim}
LD HL,Start_of_sample
LD DE,0-Length_of_sample
LD C,#00
LOOP: LD A,(HL)
SUB C
LD C,(HL)
LD (HL),A
INC E
JP NZ,LOOP
INC D
JP NZ,LOOP
\end{verbatim}
А вот как можно закачать сэмпл:
\begin{verbatim}
LD IX,Parameters
LD HL,Sample_address
LD DE,0-Sample_length
LD C,GSCOM
LD A,#38
CALL SENDCOM
LD A,#D1
CALL SENDCOM
LD A,(HL)
LOOP: IN B,(C)
JP P,READY
IN B,(C)
JP M,LOOP
READY: OUT (GSDAT),A
INC HL
ADD A,(HL)
INC E
JP NZ,LOOP
INC D
JP NZ,LOOP
WAIT: IN B,(C) ;Ждем принятия
JP M,WAIT ;последнего байта
LD A,#D2
CALL SENDCOM
; Теперь переопределяем параметры
; сэмпла по умолчанию своими
; значениями
LD A,(IX+#00)
OUT (GSDAT),A ; Нота
LD A,#40
CALL SENDCOM
LD A,(IX+#01)
OUT (GSDAT),A ; Громкость
LD A,#41
SENDCOM: OUT (GSCOM), A
WAITCOM: IN A,(GSCOM)
RRCA
JR C,WAITCOM
RET
\end{verbatim}
Проигрывание эффекта.
\begin{verbatim}
SD FX_Handle - номер сэмпла
SC #39
WC
\end{verbatim}
При исполнении этой команды происходит следующее: смотрятся каналы, указанные в SeekFirst параметре нашего сэмпла,
и если хотя-бы один из них свободен, в нем и проигрывается сэмпл, в противном случае смотрятся каналы,
указанные в SeekLast и если один из них свободен, в нем и играется сэмпл, если свободных нет,
то просматриваются все каналы, указанные SeekLast, из них выбирается канал с наименьшим приоритетом
и сравнивается с приоритетом нашего сэмпла (имеется в виду сэмпл, который мы хотим проиграть),
если у этого сэмпла будет больший приоритет, чем у сэмпла, уже играющего в канале,
то играющий в канале сэмпл будет остановлен, а наш сэмпл будет запущен в этом канале вместо старого сэмпла.
Вот такая вот приоритетная схема\ldots
Тогда сэмпл запускается в канале, то его нота, громкость и т.п. параметры записываются в область данных канала
из заголовка сэмпла.
В общем случае, чтобы проиграть сэмпл с нужными параметрами, вы можете установить эти параметры
после загрузки сэмпла и смело использовать команду~\hyperref[cmd:39]{\#39}.
Если же параметры должны меняться, то можно поступать следующим образом:
командой~\hyperref[cmd:2E]{\#2E} сделать текущим требуемый сэмпл, командами \#4x изменить его параметры,
а затем уже запускать его командой~\hyperref[cmd:39]{\#39}.
Альтернативный метод запуска сэмплов предоставляют команды~\hyperref[cmd:80]{\#80}---\hyperref[cmd:9B]{\#9B},
при исполнении этих команд вы прямо в коде команды указываете, в каком канале требуется запустить сэмпл,
и кроме этого, вы можете также указать с какой нотой и/или громкостью требуется запустить сэмпл.
Установка проигрывания эффектов в заданных каналах, которые указываются в маске каналов (Channel Mask). В ней единица в n-ном бите указывает на то, что эффект в n-ном канале требуется остановить
\begin{verbatim}
SD Channel_Mask
SC #3A
WC
\end{verbatim}
Описанное выше есть идеальный вариант работы данной команды, но к сожалению не все так просто в этом мире, и эта команда действует не так, а именно: единица в бите 7 останавливает сэмпл в нулевом канале, и т.п. В следующих версиях это будет исправлено, а пока я могу порекомендовать останавливать вообще все сэмплы маской \#FF.
Установить громкость проигрывания эффектов.
\begin{verbatim}
SD FX Volume [#00..#40]
SC #3D
WC
[GD Old_FX_Volume] - Старая громкость
\end{verbatim}
Загрузка сэмпла эффекта в память. Позволяет загружать сэмплы со знаком. (Amiga type)
\begin{verbatim}
SD #01 (Signed sample)
SC #3E
WC
[GD FX_Handle]-номер сэмпла
(Command bit=0, Data bit=0)
SC #D1 (Open Stream-открыть поток)
WC
SD \
WD \
... Байты сэмпла
SD /
WD /
SC #D2 (Close Stream-закрыть поток)
WC
\end{verbatim}
Установка ноты по умолчанию для текущего эффекта.
\begin{verbatim}
SD Note [0..95]
SC #40
WC
\end{verbatim}
\begin{verbatim}
Note=
0 C-0
1 C#0
12 C-1
24 C-2
36 C-3 (C-1 в Амиге)
48 C-4 (C-2 в Амиге)
60 C-5 (C-3 в Амиге)
72 C-6
84 C-7
\end{verbatim}
В данной версии Sound Generators Wave 2, 3 могут воспроизвести октавы 3, 4 и 5, поэтому допустимым значением параметра Note является диапазон от 36 до 71.
Установка громкости по умолчанию для текущего эффекта.
\begin{verbatim}
SD FX_Volume [#00..#40]
SC #41
WC
\end{verbatim}
Установка Finetune по умолчанию для текущего эффекта.
\begin{verbatim}
SD FX_Finetune [#00..#40]
SC #42
WC
\end{verbatim}
Установка приоритета для текущего эффекта. (См. Команду~\hyperref[cmd:39]{\#39})
\begin{verbatim}
SD FX_Priority [#01..#FE]
SC #45
WC
\end{verbatim}
После закачки каждому эффекту выставляется по умолчанию приоритет \#80. Эффекты, проигрываемые в модулях, имеют приоритет \#40.
Установка параметра Seek First для текущего эффекта. (См. Команду~\hyperref[cmd:39]{\#39})
\begin{verbatim}
SD FX_SeekFirst
SC #46
WC
\end{verbatim}
В параметре FX\_SeekFirst используются 4 младших бита, номера каналов GS располагаются по возрастающей.
\begin{verbatim}
бит 0 - канал 0
бит 1 - канал 1
бит 2 - канал 2
бит 3 - канал 3
\end{verbatim}
Например, байт 00001010 включит первый и третий каналы General Sound.
Установка параметра Seek Last для текущего эффекта. (См. команду~\hyperref[cmd:39]{\#39})
\begin{verbatim}
SD FX_SeekLast
SC #47
WC
\end{verbatim}
Формат FX\_SeekLast соответствует формату FX\_SeekFirst (См. команду~\hyperref[cmd:46]{\#46})
Установка начала цикла для текущего эффекта.
\begin{verbatim}
SD LEN.L
SC #48
WC
SD LEN.M
WD
SD LEN.H
WD
\end{verbatim}
При равенстве LEN.H --- \#FF зацикливание не производится
Установка конца цикла для текущего эффекта.
\begin{verbatim}
SD LEN.L
SC #49
WC
SD LEN.M
WD
SD LEN.H
WD
\end{verbatim}
Получение значения переменной Song\_Position в текущем модуле.
\begin{verbatim}
SC #60
WC
GD Song_Position [#00..#FF]
\end{verbatim}
Можно интерпретировать как количество проигранных паттернов модуля. После старта модуля принимает значение 0 и увеличивается на единицу после проигрывания очередного паттерна. Эта переменная может использоваться для синхронизирования процессов в Спектруме с проигрыванием модуля. Для этого можно, например, в начале процедуры обработки прерывания сделать SC \#60, затем выполнить процедуры различных операций с экраном, скроллинга строчек и т.п. (т.е. чтобы была достаточная для выполнения команды задержка), а затем прочитать значение порта 179 (GD Song\_Position), и сравнить его с требуемым и, в случае равенства, перейти на следующую часть демы, т.е.
\begin{verbatim}
if (Song_Position==My_Position)
then goto Next_Part_Of_Demo
\end{verbatim}
Получение значения переменной Pattern\_Position в текущем модуле.
\begin{verbatim}
SC #61
WC
GD Pattern_Position [#00..#3F]
\end{verbatim}
Получить значение смещения в паттерне (текущий ROW), использование --- аналогично предыдущей команде, однако требуется заметить, что эта величина изменяется довольно быстро, и поэтому
\begin{verbatim}
if (Pattern_Position>=My_Position)
then goto Next_Part_Of_Demo
\end{verbatim}
Получить значение Pattern\_Position, немного смешанной с Song\_Position.
\begin{verbatim}
SC #62
WC
GD Mixed_Position
\end{verbatim}
Mixed\_Position: (по битам)
\begin{verbatim}
7-Song_Position.1
6-Song_Position.0
5-Pattern_Position.5
4-Pattern_Position.4
3-Pattern_Position.3
2-Pattern_Position.2
1-Pattern_Position.1
0-Pattern_Position.0
\end{verbatim}
Т.е. если получить Mixed\_Position и сделать с ним \texttt{AND} \#3F, то получится вылитый Pattern\_Position,
а если после получения его немного \texttt{RLCA}, \texttt{RLCA}, \texttt{AND} \#02 --- то это будут младшие два бита Song\_Position. См. примечания к командам \#60 и \#61.
Получить ноты всех каналов модуля.
\begin{verbatim}
SC #63
WC
GD Note_of_channel_0
WN
GD Note_of_channel_1
WN
GD Note_of_channel_2
WN
GD Note_of_channel_3
\end{verbatim}
Если в каком-либо канале значение ноты изменилось с последнего исполнения команды~\hyperref[cmd:63]{\#63},
то бит 7 полученного значения Note\_of\_channel\_N будет в нуле, если же это значение то же самое,
что и было раньше, то этот бит будет в единице. Младшие семь битов и есть собственно нота от 0 до 95.
Если это значение равно 127, то это означает, что никакие сэмплы в канале не играют.
Данная команда предназначена в основном для построения на ее основе различных анализаторов.
Получить громкости всех каналов модуля.
\begin{verbatim}
SC #64
WC
GD Volume_of_channel_0
WN
GD Volume_of_channel_1
WN
GD Volume_of_channel_2
WN
GD Volume_of_channel_3
\end{verbatim}
См. описание команды \#63
Делает переход на заданную позицию.
\begin{verbatim}
SD Position
SC #65
WC
\end{verbatim}
Установка скорости в пределах \#01~---~\#1F. При значениях \#20~---~\#FF устанавливается темп проигрывания. Значения темпа соответствуют оригинальным при скорости равной \#06.
\begin{verbatim}
SD Speed/Tempo
SC #66
WC
\end{verbatim}
Чтение текущей скорости.
\begin{verbatim}
SC #67
WC
GD Speed
WD
\end{verbatim}
Чтение текущего темпа.
\begin{verbatim}
SC #68
WC
GD Tempo
WD
\end{verbatim}
Переход на следующий кварк (или тик) в процессе проигрывания звука. Может, в частности, использоваться для синхронизации с выводом звука. Для этого нужно установить флаг занятости (Busy On — что вызовет остановку звука), а затем с нужной периодичностью выдавать команду \#69 для дальнейшего проигрывания.
\begin{verbatim}
SC #69
WC
\end{verbatim}
Проигрывание сэмпла в заданном канале.
\begin{verbatim}
SD Sample_Number
SC #80..#83 (Младшие биты определяют непосредственно номер канала, в
котором требуется играть сэмпл)
WC
\end{verbatim}
Проигрывание сэмпла в заданном канале с заданной нотой.
\begin{verbatim}
SD Sample_Number
SC #88..#8B (Младшие биты определяют непосредственно номер канала, в
котором требуется играть сэмпл)
WC
SD Note [0..95]
WD
\end{verbatim}
Проигрывание сэмпла в заданном канале с заданной громкостью.
\begin{verbatim}
SD Sample_Number
SC #90..#93 (Младшие биты определяют непосредственно номер канала, в
котором требуется играть сэмпл)
WC
SD Volume [#00..#40]
WD
\end{verbatim}
Проигрывание сэмпла в заданном канале с заданной нотой и громкостью.
\begin{verbatim}
SD Sample_Number
SC #98..#9B (Младшие биты определяют непосредственно номер канала, в
котором требуется играть сэмпл)
WC
SD Note [0..95]
WD
SD Volume [#00..#40]
WD
\end{verbatim}
Смена текущей ноты в заданном канале. Производится «на лету».
\begin{verbatim}
SD Note
SC #A0..#A3
WC
\end{verbatim}
Подобно предыдущей команде «на лету» меняет громкость канала.
\begin{verbatim}
SD Volume
SC #A8..#AB
WC
\end{verbatim}
Предыдущие две команды работают вне зависимости от того, что проигрывается в данном канале --- семпл или модуль. Появлении в канале нового звука — от модуля либо семпла вернет все в исходное состояние — то есть громкость либо нота будут те, которые указаны вновь поступившего звука. В силу данной темперированности для получения требуемого эффекта данные команды следует вызывать с некоей периодичностью (устанавливается экспериментально).
Сбрасывает полностью GS, но пропускает этапы определения количества страниц памяти и их проверки, что очень сильно ускоряет процесс инициализации.
\begin{verbatim}
SC #F3
WC
\end{verbatim}
Полный перезапуск GS со всеми проверками. По сути, \texttt{JP \#0000}.
\begin{verbatim}
SC #F4
WC
\end{verbatim}
Устанавливает флаг занятости в \#FF
\begin{verbatim}
SC #F5
WC
\end{verbatim}
Устанавливает флаг занятости в \#00
\begin{verbatim}
SC #F6
WC
\end{verbatim}
Изначально Busy = \#00. Исполнение всех команд в GS выполняется в главном цикле командного интерпретатора. Этот цикл в условном виде можно представить так:
\begin{verbatim}
1 if Command bit=0 then go to 1
2 Execute Command
3 if Command bit=1 then go to 2
4 if Playing=0 then go to 1
5 if Busy=#FF then go to 1
6 Process Sound
7 go to 1
\end{verbatim}
Используя команды Busy можно например инициировать проигрывание сэмплов во всех каналах, потом скажем изменить параметры проигрывания в каналах, а потом запустить это все одновременно. Если же их не использовать, то возможна такая ситуация: инициируется первый (сэмпл станет проигрываться, а только потом инициируется второй сэмпл и т.д.).
Получить содержимое регистра HX (GS).
HX участвует в обработке флага Busy (bit 7 0/1 – Busy On/Off).
\begin{verbatim}
SC #F7
WC
GD HX
WN
\end{verbatim}
Вывод нуля в нулевой (конфигурационный) порт GS. Делает приостановку звучания музыки до следующего чтения из к.л. порта.
\begin{verbatim}
SC #FA
WC
\end{verbatim}
Специальная команда для тестирования General Sound: 250 (\#FA).
Сначала в регистр команд кидается команда \#FA, после чего General Sound переходит в режим тестовых команд.
Далее в тот же регистр команд кидаем следующие команды:\\
\begin{tabu}{|l|X|}
2 --- 5 & запись в громкость 63 и потом в звук попеременно 0 и 255, до тех пор, пока не подана новая команда \\ \hline
6 & пишет в регистр данных то 0, то 255 (для проверки считывания данных со стороны спектрума)\\ \hline
7 --- 10 & пишет 255 в звук, затем 0 и 255 в громкость\\ \hline
11 --- 14 & ставит максимальную громкость, пищит в канал 0 и 255, и громкость уменьшает\\ \hline
15 & во всех каналах ставит максимальные громкости и выдает пилу на звук\\ \hline
16 & забирает данные из регистра данных и сбрасывает флаг поступления команды\\ \hline
17 & забирает данные и не сбрасывает флаг поступления команды\\ \hline
18 --- 21 & при максимальной громкости пишет в звук то 0, то байт из регистра данных\\
\end{tabu}
\begin{enumerate}
\item Команды, отмеченные как~\Cyrax, являются недокументированными и в полной мере относятся только к версии 1.04.
На работоспособность этих команд в последующих версиях автор описания (\hyperref[Cyrax]{2}) ответственности не несет.
\item Напоминаю, что регистры (их имена), упомянутые в этом описании, относятся только и только к внутренним регистрам GS и никакого отношения к регистрам основного процессора не имеют.
\item Данная редакция (006) является наиболее полной и точной на данный момент --- 18.04.2002. Так же она является последней и несет скорее познавательный характер (по крайней мере автор не видит иного применения данной сводки команд, кроме как для высокоуровневой эмуляции GS).
На дополнение сего текста меня сподвиг тот факт, что после порядка 4х лет я узрел свой труд в сети, да еще в составе тех описаний карточки. Поэтому мне хотелось бы, чтобы дока была по возможности наиболее полной и точной\ldots
\end{enumerate}
\subsection{Соответствие нумерации каналов Amiga и General Sound\label{sec:chn}}
Несмотря на то, что раскладка каналов по стерео в General Sound выполнена аналогично Amiga Protracker, нумерация каналов, принятая в трекерах, не соответствует нумерации каналов в командах управления General Sound.
Раскладка каналов по стерео в Amiga Protracker:
\begin{description}
\item[AM1] --- левый канал
\item[AM2] --- правый канал
\item[AM3] --- правый канал
\item[AM4] --- левый канал
\end{description}
Раскладка каналов по стерео в General Sound:
\begin{description}
\item[GS0] --- левый канал
\item[GS1] --- левый канал
\item[GS2] --- правый канал
\item[GS3] --- правый канал
\end{description}
Таким образом, между собой каналы соответствуют друг-другу следующим образом:
\begin{verbatim}
GS0->AM1
GS1->AM4
GS2->AM2
GS3->AM3
\end{verbatim}
Эта информация полезна при выставлении SeekFirst/SeekLast параметров для совмещения проигрывания модулей и эффектов.
Если перевести нумерацию каналов параметра SeekFirst из GS в Amiga нумерацию, то раскладка каналов по битам SeekFirst/SeekLast будет следующей:
\begin{verbatim}
00001111
||||
3241
\end{verbatim}
Например, если в Mod-трекере видно, что у модуля свободны каналы 1 и 3, то в большинстве случаев было бы разумно сначала попытаться проиграть сэмпл в этих каналах, а потом уже в любых других. Для этого в SeekFirst параметр следует выставить
\%00001001, задействовав GS-каналы 0 и 3, которым соответствуют каналы трекера 1 и 3.
Eму пpинaдлeжит идeя сoздaния GS'a, aппapaтнaя peaлизaция oнoгo, нeкoтopыe пoжeлaния oтнoситeльнo Soft'a GS'a, a тaкжe Amiga~1200, нa кoтopoй мнoю пpoизвoдились всячeскиe экспepимeнты. Oн eдинстeнный и нeпoвтopимый пpoизвoдитeль General Sound'a и имeннo oн зaвeдуeт пpoизвoдствoм и пpoдaжeй GS.
Этo я, aвтop сeгo oпусa и пo сoвмeститeльству душa и сepдцe GeneralSound'a.
Я являюсь paзpaбoтчикoм всeгo встpoeннoгo Soft'a в GS'e и пpeдпoлaгaю и дaльшe зaнимaться сим дeйствoм.
(Дa, я тaкжe являюсь aвтopoм нeкoтopыx xитpыx нaвopoтoв в aппapaтнoй чaсти GS'a, и был бы aвтopoм eщe мнoгиx,
eсли бы нe был всe вpeмя сдepживaeм Cлaвoй, пoстoяннo oзaбoчeнным пpoблeмaми пoнижeния цeны.)
Haписaв oкoлo 20 кб кoдa зa пoл-гoдa, пpизнaться, я нeмнoгo устaл, нo имeю дoвoльнo бoльшиe
плaны oтнoситeльнo слeдующиx вepсий GS'a, кaк-тo:
\begin{itemize}
\item Wave 4 Sound Generators вoспpoизвoдящиx всe oктaвы
\item Ускopeниe зa счeт oныx пpoцeнтoв нa 30~---~40 гeнepaции звукa
\item Oчeнь xoтeлoсь бы пpoигpывaниe STM'oв oт PC
\item Paзвитaя систeмa кoмaнд
\item Paзличныe спeцэффeкты нaд сэмплaми
\item Xpaнeниe пaттepнoв в зaкoмпpeссoвaннoм видe (oстaeтся oкoлo 15\% oт изнaчaльнoгo oбьeмa)
\end{itemize}
Bсe пpoгpaммнoe oбeспeчeниe дoлжнo paбoтaть и нa пoслeдующиx вepсияx пpoшивки,
eсли oнo нaписaнo в сooтвeтствии с мoими вышeизлoжeнными пoжeлaниями и тpeбoвaниями.
Кpoмe oписaнныx кoмaнд в GS'e сущeствуeт eщe бoльшoe кoличeствo кoмaнд, кoтopыe нe дoкумeнтиpoвaны,
и я oстaвляю зa сoбoй пpaвo измeнять иx кaким-угoднo oбpaзoм и тoлькo oтнoситeльнo
дoкумeнтиpoвaныx кoмaнд пpиeмлю зaкoнныe пpeтeнзии типa:
``B дoкумeнтaции нaписaнo тaк, a в пpoшивкe этo paбoтaeт пo дpугoму...''.
Я плaниpую знaчитeльнoe paсшиpeниe систeмы кoмaнд, и буду paд кoнстpуктивным (жeлaтeльнo кoнкpeтным) пpeдлoжeниям.
Taк чтo, eсли вы oзвучивaeтe игpушку или пишeтe музыкaльный peдaктop для GS и oбнapуживaeтe,
чтo вaм oчeнь нe xвaтaeт кaкoй-либo кoмaнды, тo звoнитe мнe и выскaзывaйтe пpeдлoжeния.
(Teлeфoн, я думaю, oсoбoгo тpудa узнaть нe сoстaвит ;)
Димa (X-Trade)
SParker (XLD)
\copyright~Stinger, 1997,
bugfixed by psb \& Evgeny Muchkin, 2007.
В данной версии прошивки исправлены глюки версии 1.04 Beta.
\begin{enumerate}
\item Глюк с модулями, в которых >= 63 паттерна (klisje.mod, tranceillusion.mod).
\item Глюк со скоростью проигрывания ПОСЛЕДНЕЙ ноты модуля, её скорость выставлялась стандартной,
во многих модулях при зацикливании была заметна задержка\\
(напр., technostyle(z).mod).
Более того, при зацикливании не на 1ю позицию, скорость все равно выставлялась стандартной!
\item Пофиксена неправильная скорость проигрывания сэмплов. На некоторых модулях было заметно,
что сэмплы играли немного быстрее чем надо (например, EightMayDay.mod).
\item При начале проигрывания модуля GS сообщал, что играет какая-то нота, даже если в канале ничего не играло
(команда~\hyperref[cmd:64]{\#64} возвращала не 127).
\item Добавлена команда для плееров: \#6A --- Set player mode.
После этой команды GS перестанет обращать внимание на команду останова в модуле (ком. F00).
Полезно для некоторых модулей (bst.mod).\label{cmd:6A}
\end{enumerate}
Формат команды:
\begin{verbatim}
SD #01 ;#01 - On, #00 — Off
SC #6A
WC
\end{verbatim}
\begin{enumerate}[resume]
\item Встроен релупер для модулей. Раньше, если в модуле играл сэмпл,
длина лупа которого была слишком маленькой (десятки-сотни байт), GS тормозил или зависал.
После этой команды сэмплы в загружаемом модуле фиксятся и GS не тормозит.\label{cmd:6B}
\end{enumerate}
Формат команды:
\begin{verbatim}
SD MinLoopLen_Low
SC #6B
WC
SD MinLoopLen_High
\end{verbatim}
Параметр MinLoopLen задается в СЛОВАХ и может быть в диапазоне от 0 до 16384
(0 --- релупер выключен).
Возможен короткий формат команды:
\begin{verbatim}
SC #6B
WC
SC ... ;следующая команда GS
\end{verbatim}
В этом случае длина поумолчанию будет 512 слов.
\textbf{ВНИМАНИЕ!} Настройки команд~\hyperref[cmd:6A]{\#6A} и \hyperref[cmd:6B]{\#6B}
сбрасываются только аппаратным RESET или командой~\hyperref[cmd:F4]{\#F4}
(командой~\hyperref[cmd:F3]{\#F3} не сбрасываются!).
\textbf{P.S.} В прошивке по смещению \#0004 находится номер версии в BCD формате;
по смещению \#0100 находятся оригинальные копирайты (3 строки по 24 символа);
по смещению \#0080 находится информация о патче, строка заканчивается 0.
\textbf{P.P.S.} Для работы старых плееров в новых режимах (п.\ref{cmd:6A} и \ref{cmd:6B}),
достаточно перед их запуском дать из бейсика команды:
\begin{verbatim}
OUT 179,1
OUT 187,106
OUT 187,107
\end{verbatim}
\textbf{P.P.P.S.} Хочется выразить особую благодарность следующим людям:
\begin{description}
\item[Stinger:] за прошивку и доступные исходники
\item[Aprisobal:] без SjASMPlus не было бы ничего этого
\item[Evgeny Muchkin:] за всяческое содействие при создании патча
\item[Caro:] за IDA и моральную поддержку
\item[SMT \& Alone Coder:] за UnrealSpeccy (и за исправление глюков в нем!;)
\item[Half Elf:] за плагины к FAR'у
\item[n1k-o \& Manwe:] за консультации по mod'ам
\end{description}
24.02.2012: thx Evgeny Muchkin\\
добавлен пункт~\ref{cmd:FA}~\hyperref[cmd:FA]{\#FA} включение тестового режима.
18.01.2011: thx moroz1999\\
дополнено описание~\ref{cmd:45}~\hyperref[cmd:45]{\#45} Set FX Sample Priority;\\
дополнено описание~\ref{cmd:46}~\hyperref[cmd:46]{\#46} Set FX Sample Seek First parameter;\\
дополнено описание~\ref{cmd:47}~\hyperref[cmd:47]{\#47} Set FX Sample Seek Last parameter;\\
добавлен пункт~\ref{sec:chn} Соответствие нумерации каналов Amiga и General Sound.
19.07.2009: базовая версия.
\end{document}