Think #29.5
24 июля 1999
  Звук  

Железо - программирование General Sound.

               ╔═════════╗
               ║  ЖЕЛЕЗО ║
               ╚═════════╝

                               (с) STINGER

   ╔═════════════════════════════════╗
   ║ Руководство по программированию ║░
   ║          General Sound.         ║░
   ║                                 ║░
   ║   Версия v1.03. Редакция 003.   ║░
   ╚═════════════════════════════════╝░
    ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░


 1. Краткие технические характеристики GS.
    --------------------------------------


Процессор: Z80, 12MHz, без циклов wait
ROM      : 32k, 27256
RAM      : Static Ram 128k всего,112k дос-
           тупно  для  модулей и сэмплов в
           базовой версии
INT      : 37.5 KHz
Каналы   : 4 независимых 8-и битных  кана-
           ла, каждый с 6-и битным контро-
           лем громкости.


  2. Краткое описание 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'а, за исключением двух:

 Е01 Filter On   Амига-специфичная  коман-
                 да, включает фильтр высо-
                 ких частот.

 EFX Invert Loop  Я еще не  видел плейера,
который  бы  поддерживал эту команду. Воз-
можно, она поддерживается на каких-то ста-
рых плейерах.

  GS  представляет  из себя, по-сути, мик-
ропроцессорный комплекс со своим процессо-
ром,  ПЗУ,  ОЗУ  и портами, и абсолютно не
зависит  от главного процессора Спектрума,
что  позволяет,  например,  загрузить свой
любимый  модуль, сбросить Спектрум, загру-
зить ассемблер и творить под любимую музы-
ку. Soft внутри GS полностью берет на себя
задачи  проигрывания  звука, интерпретации
модуля и т.д.  Программирование GS'а  сво-
дится  к  передаче  байт  за байтом модуля
и/или  сэмплов,  а  затем требуется только
подавать   команды   типа:  запустить  мо-
дуль, установить глобальную громкость про-
игрывания  модуля,  запустить  сэмпл #09 в
канале #02 и т.д.

  Если   предполагается  загрузить  модуль
вместе с сэмплами, то ОБЯЗАТЕЛЬHО требует-
ся загружать вначале модуль, а затем сэмп-
лы.

  При  загрузке модуля очень рекомендуется
оставить свободными 2к памяти, т.е. загру-
жать  модули длиной максимум 110K. Это ус-
ловие  не является необходимым, но его ис-
полнение  очень желательно в целях совмес-
тимости с последующими версиями.

  Аналогично очень рекомендуется оставлять
по  80  байт для каждого сэмпла, например,
если  требуется загрузить 63-х килобайтный
модуль и 18 сэмплов, то имеем:

 Total_Sample_Length = 112*1024-63*1024-2*
*1024-18*80=46688 байт

  Это суммарная длина сэмплов, которые при
таком  положении вещей могут быть загруже-
ны.

  Если  же, например, требуется вычислить,
сколько поместится в память GS'а 2-х кило-
байтных сэмплов, то это вычисляется следу-
ющим образом:

 112*1024/(2048+80)=53 сэмпла.

  В GS'е  имеются 4 физических канала, ко-
торые и проигрывают звук.

  Каналы 0 и 1 - левые, а 2 и 3 - правые.


 3. Интерфейс со Спектрумом.
    ------------------------


  Hа  мир  GS смотрит при помощи 4 регист-
ров:

1. Command register - регистр команд, дос-
тупный  для  записи  порт по адресу 187. В
этот регистр записываются команды.

2. Status register  -  регистр  состояния,
доступный для чтения порт по адресу 187.

  Биты регистра:

   7 - Data bit, флаг данных
   6 - Hеопределен
   5 - Hеопределен
   4 - Hеопределен
   3 - Hеопределен
   2 - Hеопределен
   1 - Hеопределен
   0 - Command bit, флаг команд

  Этот регистр позволяет определить состо-
яние  GS, в частности  можно  ли прочитать
или  записать  очередной  байт данных, или
подать очередную команду, и т.п.

3. Data register - регистр данных, доступ-
ный  для записи порт по адресу 179. В этот
регистр Спектрум записывает данные, напри-
мер, это могут быть аргументы команд.

4. Output register - регистр  вывода, дос-
тупный  для  чтения порт по адресу 179. Из
этого  регистра  Спектрум  читает  данные,
идущие от  GS.

  Command  bit  в регистре состояний уста-
навливается аппаратно после записи команды
в  регистр команд. Сбрасываться в 0 он мо-
жет только из GS, что сигнализирует об оп-
ределенном этапе исполнения команды.

  Data bit в регистре состояний может быть
установлен  или  сброшен  как  по  желанию
Спектрума, так и по желанию GS: при записи
Спектрумом  в  регистр данных он аппаратно
устанавливается в 1, а после чтения  GS'ом
из  этого  регистра  сбрасывается в 0. При
записи  GS в регистр вывода он (все тот же
Data bit) аппаратно устанавливается в 1, а
после  чтения  из  этого  порта Спектрумом
сбрасывается аппаратно в 0.

  Несмотря на то, что регистр данных и ре-
гистр  вывода  расположены  в пространстве
адресов  портов по одному и тому же адресу
и  воздействуют  на один и тот же бит дан-
ных, они  являются  двумя независимыми ре-
гистрами. Значение, один  раз записанное в
один  из этих регистров, остается неизмен-
ным в нем до новой записи.

  Состояние  бита данных очень часто неоп-
ределено,  и если в спецификации команд не
определены значения этого бита на опреден-
ных этапах исполнения команды, недопустимо
делать  какие-либо  предположения  относи-
тельно значения этого бита.


 4. Система команд GS.
    ------------------


  Вначале позволю себе небольшое отступле-
ние от собственно системы команд.  GS, как
известно, предназначен в основном для про-
игрывания модулей и сэмплов. В данной вер-
сии (1.03) GS ROM  допукается загрузка од-
ного модуля и/или до 32 сэмплов.

  Каждый  сэмпл  при загрузке его в память
получает  свой  уникальный  идентификатор,
который  однозначно определяет обращение к
данному сэмплу в командах, которые требуют
номер  сэмпла.  Самый  первый  загруженный
сэмпл получает номер (handle) = 1, следую-
щий - номер 2, и т.д.

  То  же  самое  применимо  и к модулям, и
этот единственный загруженный модуль будет
иметь handle=1 после загрузки.

  Особенностью данной версии является так-
же то, что вначале требуется загружать мо-
дуль, а затем уже сэмплы.

  Особенности описания команд:

  Команды описываются следующим образом:

 1. Hex код команды
 2. Hазвание команды
 3. Выполняемые  действия  при  исполнении
     команды
 4. Формат команды
 5. Комментарии к команде

  Формат команды описывается следующим об-
разом:

 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


               Команды GS:

#00 Reset flags -
    Сбрасывает флаги Data bit и Command
    bit.

    SC #00
    WC

    (Data bit=0, Command bit=0)

#0E Go in Covox mode
    Переходит в режим Ковокс, напрямую
    копирует регистр данных в ЦАПы двух
    (правого и левого) каналов.
    Выход из этого режима - запись #00
    в регистр команд.

    SC #0E
    WC

    SD  
    SD   
    ...  / Это вывод в ЦАПы
    SD  /

    SC #00
    WC

#F3 Warm restart
    Сбрасывает полностью GS, но пропускает
    этапы определения количества страниц
    памяти и их провеки, что очень сильно
    ускоряет процесс инициализации.

    SC #F3
    WC

#F4 Cold restart
    Полный перезапуск GS со всеми провер-
    ками. По сути, JP #0000.

    SC #F4
    WC

#20 Get total RAM
    Получить общий объем доступной памяти
    на  GS. ( В базовой версии это 112к)

    SC #20
    WC
    GD RAM.L(Младшая часть)
    WN
    GD RAM.M(Средняя часть)
    WN
    GD RAM.H(Старшая часть)

Total RAM=65536*RAM.H+256*RAM.M+RAM.L

#21 Get free RAM
    Получить общий объем свободной памяти
    на  GS.

    SC #20
    WC
    GD RAM.L(Младшая часть)
    WN
    GD RAM.M(Средняя часть)
    WN
    GD RAM.H(Старшая часть)

 Free_RAM=65536*RAM.H+256*RAM.M+RAM.L

#23 Get number of RAM Pages
    Получить число страниц на  GS.

    SC #23
    WC
    GD Number_RAM_Pages

    В базовой версии 3 страницы.

#2A Set Module Master Volume
    Установить громкость проигрывания
    модулей.

    SD Module_Master_Volume [#00..#40]
    SC #2A
    WC
   [GD Old_Master_Volume] - Старая громк.

    Маленький пример использования данной
    команды:

    (Предполагается, что играется модуль)

           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

  Вышеописанное  плавно  снижает громкость
играющего  модуля, а  затем  останавливает
его.

#2B Set FX Master Volume
    Установить громкость проигрывания
    эффектов.

    SD FX_Master_Volume [#00..#40]
    SC #2B
    WC
   [GD Old_FX_Volume] - Старая громкость

  Аналогично  предыдущей команде, но дейс-
твует на сэмплы.

  С помощью этих двух команд можно регули-
ровать баланс громкостей модуля и сэмплов,
и т.п.

#2E Set Current FX
    Установить   текущий   эффект.  Просто
    присваивает  переменной CURFX это зна-
    чение. Если какая-либо команда требует
    номер сэмпла (sample handle), то можно
    вместо  этого  номера  подать ей #00 и
    интерпретатор  подставит  вместо этого
    нуля  значение  переменной CURFX. (См.
    команды #38, #39, #40- #4F для понима-
    ния вышеизложенного.)

    SD Cur_FX
    SC #2E
    WC

#30 Load Module
    Загрузка модуля в память.

    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

    Пример:
             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) ; Hомер модуля
             OUT (GSDAT),A
             LD A,#31

    SENDCOM: OUT (GSCOM),A
    WAITCOM: IN A,(GSCOM)
             RRCA
             JR C,WAITCOM
             RET

#31 Play module
    Проигрывание модуля.

    SD Module_Handle - номер модуля
    SC #31
    WC

#32 Stop module
    Остановить проигрывание модуля.

    SC #32
    WC

#33 Continue module
    Продолжить проигрывание модуля после
    остановки.

    SC #33
    WC

#38 Load FX
    Загрузка сэмпла эффекта в память.
    Загружает беззнаковые сэмплы
    (PC type)

    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

  При загрузке каждого сэмпла, в памяти GS
создается  для  этого  сэмпла заголовок, в
котором  описываются  различные  параметры
сэмпла. После загрузки эти параметры уста-
навливаются  в  определенные значения, как
то:

  Note=60,  Volume=#40,  FineTune=0, Seek-
First=#0F,  SeekLast=#0F, Priority=#80, No
Loop  и  внутренняя переменная CurFX уста-
навливается равной FX_Handle.

  Затем  командами #40, #41, #42, #45, #46
и #47 можно эти значения по умолчанию сме-
нить на свои. Это требуется потому что ко-
манда #39 для инициации проигрывания сэмп-
ла использует значения параметров из заго-
ловка сэмпла.

  В  своем естественном виде сэмплы обычно
плохо  пакуются  компрессорами, но сжимае-
мость обычно можно поднять, если перевести
сэмпл  в  Delta-вид, т.е. хранить не абсо-
лютные  значения  сэмпла,  а относительное
смещение  относительно  предыдущего байта.
Примерно вот так вот можно перевести сэмпл
в Delta-вид:

            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

    А вот как можно закачать сэмпл:

             LD IX,Parameters
             LD HL,Sample_adress
             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  ; Hота
             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

#39 Play FX
    Проигрывание эффекта.

    SD FX_Handle - номер сэмпла
    SC #39
    WC

  При  исполнении  этой команды происходит
следующее:  смотрятся  каналы, указанные в
SeekFirst  параметре нашего сэмпла, и если
хотя-бы один из них свободен, в нем и про-
игрывается сэмпл, в противном случае смот-
рятся каналы, указанные в  SeekLast и если
один  из  них  свободен,  в нем и играется
сэмпл, если свободных нет, то просматрива-
ются  все  каналы,  указанные SeekLast, из
них  выбирается канал с наименьшим приори-
тетом  и сравнивается с приоритетом нашего
сэмпла  (имеется  в виду сэмпл, который мы
хотим  проиграть), если у этого сэмпла бу-
дет  больший  приоритет, чем у сэмпла, уже
играющего  в  канале, то играющий в канале
сэмпл  будет остановлен, а наш сэмпл будет
запущен в этом канале вместо старого сэмп-
ла. Вот такая вот приоритетная схема ...

  Когда сэмпл запускается в канале, то его
нота, громкость и т.п. параметры записыва-
ются  в область данных канала из заголовка
сэмпла.

  В общем случае, что-бы проиграть сэмпл с
нужными  параметрами, вы можете установить
эти параметры после загрузки сэмпла и сме-
ло использовать команду #39. Если же пара-
метры  должны меняться, то можно поступать
следующим  образом:  командой  #2E сделать
текущим требуемый сэмпл, командами #4x из-
менить  его  параметры, а затем уже запус-
кать его командой #39.

  Альтернативный   метод  запуска  сэмплов
предоставляют команды #80..#9F, при испол-
нении  этих команд вы прямо в коде команды
указываете,  в  каком канале требуется за-
пустить  сэмпл,  и  кроме этого, вы можете
также  указать  с  какой нотой и/или гром-
костью требуется запустить сэмпл.

#3A Stop FX in channels
    Остановка проигрывания эффектов в
    заданных каналах, которые указываются
    в маске каналов (Channel Mask).
    В ней единица в n-ном бите
    указывает на то, что эффект в n-ном
    канале требуется остановить

    SD Channel_Mask
    SC #3A
    WC

  Описанное  выше  есть  идеальный вариант
работы  данной  команды, но к сожалению не
все  так просто в этом мире, и эта команда
действует не так, а именно: единица в бите
7  останавливает сэмпл в нулевом канале, и
т.п. В следующих версиях это будет исправ-
лено,  а пока я могу порекомендовать оста-
навливать вообще все сэмплы маской #FF.

#3E Load FX (Extended version)
    Загрузка сэмпла эффекта в память.
    Позволяет загружать сэмплы со знаком.
    (Amiga type)

    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

#40 Set FX Sample Playing Note
    Установка ноты по умолчанию для
    текущего эффекта.

    SD Note [0..95]
    SC #40
    WC

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

  В данной версии Sound Generators Wave 2,
3 могут воспроизвести октавы 3, 4 и 5, по-
этому  допустимым значением параметра Note
является диапазон от 36 до 71.

#41 Set FX Sample Volume
    Установка громкости по умолчанию для
    текущего эффекта.

    SD FX_Volume [#00..#40]
    SC #41
    WC

#45 Set FX Sample Priority
    Установка приоритета для
    текущего эффекта.
    (См. команду #39)

    SD FX_Priority [#01..#fe]
    SC #45
    WC

#46 Set FX Sample Seek First parameter
    Установка параметра Seek First для
    текущего эффекта.
    (См. команду #39)

    SD FX_SeekFirst
    SC #46
    WC

#47 Set FX Sample Seek Last parameter
    Установка параметра Seek Last для
    текущего эффекта.
    (См. команду #39)

    SD FX_SeekLast
    SC #47
    WC

#60 Get Song Position
    Получение значения переменной
    Song_Position в текущем модуле.

    SC #60
    WC
    GD Song_Position  [#00..#FF]

  Можно  интерпретировать  как  количество
проигранных паттернов модуля. После старта
модуля принимает значение 0 и увеличивает-
ся на единицу после проигрывания очередно-
го  паттерна. Эта переменная может исполь-
зоваться для синхронизирования процессов в
Спектруме с проигрыванием модуля. Для это-
го можно, например, в начале процедуры об-
работки  прерывания  сделать SC #60, затем
выполнить  процедуры  различных операций с
экраном,  скруллинга  строчек и т.п. (т.е.
чтобы  была достаточная для выполнения ко-
манды  задержка), а затем прочитать значе-
ние  порта 179 (GD Song_Position), и срав-
нить  его  с требуемым и, в случае равенс-
тва, перейти на следующую часть демы, т.е.

  if (Song_Position==My_Position) then goto Next_Part_Of_Demo

#61 Get Pattern Position
    Получение значения переменной
    Pattern_Position в текущем модуле.

    SC #61
    WC
    GD Pattern_Position  [#00..#3F]

  Получить  значение  смещения  в паттерне
(текущий  ROW), использование - аналогично
предыдущей команде, однако требуется заме-
тить, что эта величина изменяется довольно
быстро, и поэтому

 if (Pattern_Position>=My_Position) then
goto Next_Part_Of_Demo

#62 Get Mixed Position
    Получить значение Pattern_Position,
    немного смешанной с Song_Position.

    SC #62
    WC
    GD Mixed_Position


    Mixed_Position: (по битам)

    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

  Т.е если получить Mixed_Position  и сде-
лать  с  ним AND #3F, то получится вылитый
Pattern_Position,  а  если после получения
его  немного  RLCA, RLCA, AND #02 - то это
будут младшие два бита  Song_Position. См.
примечания к командам #60 и #61.

#63 Get Channel Notes
    Получить ноты всех каналов модуля.

    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

  Если  в  каком-либо канале значение ноты
изменилось с последнего исполнения команды
#63, то бит 7 полученного заначения

            Note_of_channel_N

будет  в  нуле, если же это значение то же
самое,  что и было раньше, то этот бит бу-
дет  в  единице. Младшие семь битов и есть
собственно  нота от 0 до 95; если это зна-
чение  равно 127, то это означает, что ни-
какие  сэмплы  в  канале не играют. Данная
команда предназначена в основном для пост-
роения  на  ее основе различных анализато-
ров.

#64 Get Channel Volumes
    Получить громкости всех каналов
    модуля.

    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

    См. описание команды #63

#80 Direct Play FX Sample (#80..#83)
    Проигрывание сэмпла в заданном канале.

    SD Sample_Number
    SC #80..#83 (Младшие  биты  определяют
                 непосредственно номер ка-
                 нала, в котором требуется
                 играть сэмпл)
    WC

#88 Direct Play FX Sample (#88..#8B)
    Проигрывание  сэмпла в заданном канале
    с заданной нотой.

    SD Sample_Number
    SC #88..#8B (Младшие  биты  определяют
                 непосредственно номер ка-
                 нала, в котором требуется
                 играть сэмпл)
    WC
    SD Note [0..95]
    WD

#90 Direct Play FX Sample (#90..#93)
    Проигрывание  сэмпла в заданном канале
    с заданной громкостью.

    SD Sample_Number
    SC #90..#93 (Младшие биты определяют
                 непосредственно номер
                 канала, в котором требу
                 ется играть сэмпл)
    WC
    SD Volume [#00..#40]
    WD

#98 Direct Play FX Sample (#98..#9B)
    Проигрывание сэмпла в заданном кана-
    ле с заданной нотой и громкостью.

    SD Sample_Number
    SC #88..#8B (Младшие биты определяют
                 непосредственно номер
                 канала, в котором требу
                 ется играть сэмпл)
    WC
    SD Note [0..95]
    WD
    SD Volume [#00..#40]
    WD
        __________________________
           ────────────────────
               === Cut ===


           





Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
ATM-Turbo and others ZX Spectrum clones , history of Profi computer
Приложение - Doxycon'99 - Invitation intro.
Guide - Формат модуля Pro Tracker v3.x

В этот день...   19 ноября