Исторический контекст и место в эволюции ZX Spectrum

Происхождение и создание

Звуковой сопроцессор AY-3-8910 был разработан американской компанией General Instrument в конце 1970-х годов как часть семейства программируемых звуковых генераторов (PSG). Его более простая версия, AY-3-8912, отличалась только уменьшенным количеством линий ввода-вывода. Интересно, что изначально чип создавался для аркадных автоматов и бытовых компьютеров, но настоящую популярность обрел именно в клонах ZX Spectrum.

Внедрение в мир Spectrum

Первым компьютером Spectrum с аппаратной поддержкой AY стал Sinclair ZX Spectrum 128K (1985 год), разработанный совместно с испанской компанией Investrónica. Примечательно, что в оригинальном дизайне Клайва Синклера не предусматривался отдельный звуковой чип — всем звуком управлял сам Z80 через простой бипер. AY стал компромиссным решением для улучшения звуковых возможностей.

В СССР и странах Восточной Европы AY получил особенное распространение благодаря клонам Spectrum, таким как:

  • Pentagon — использовал AY-3-8910/8912
  • Scorpion — часто имел два чипа AY для стереозвука
  • ATM Turbo — советский клон с расширенными возможностями
  • Profi — семейство российских компьютеров, разработанных в СП «Крамис»

Аппаратные характеристики и архитектура

Технические спецификации

  • Тактовая частота: 1.773445 МГц для Spectrum (CLK = CPU clock / 2)
  • Разрядность: 12-битные таймеры тона, 5-битный генератор шума
  • Каналы: 3 независимых звуковых канала (A, B, C)
  • Возможности каждого канала:
    • Программируемая частота (12 бит, ~ 0-110840 Гц)
    • 16 уровней громкости (4 бита)
    • Возможность использования огибающей (envelope)
    • Индивидуальное включение тона/шума
  • Генератор огибающей: 16 предустановленных форм + программная
  • Порты ввода-вывода: 2 программируемых 8-битных порта (только в AY-3-8910)

Схема подключения в Spectrum 128K

В оригинальном Spectrum 128K чип AY подключен через декодер адресов:
(весь пример кода и технические детали будут на ассемблере Z80)
Assembler Z80:
; Порты AY в Spectrum 128K
AY_REG_SELECT   equ $FFFD   ; Выбор регистра AY
AY_REG_WRITE    equ $BFFD   ; Запись значения в выбранный регистр
AY_REG_READ     equ $BFFD   ; Чтение значения из выбранного регистра

Адресное пространство и конфликты

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

Регистры AY: полный разбор

Структура регистров (16 регистров, R0-R15)

Регистры тона (R0-R5):
Код:
R0/R1: Канал A тон (младший/старший байт)
R2/R3: Канал B тон
R4/R5: Канал C тон

Частота рассчитывается по формуле:
F = CPU_CLOCK / 16 / (256 * R_high + R_low)

Регистр 6: Генератор шума (5 бит)
Код:
Биты 0-4: период шума (N = значение * 2)

Регистры 7-9: Включение тона/шума
Код:
Регистр 7: Управление смешиванием
Бит 0: Выключить тон канала A (1=выкл)
Бит 1: Выключить тон канала B
Бит 2: Выключить тон канала C
Бит 3: Выключить шум канала A
Бит 4: Выключить шум канала B
Бит 5: Выключить шум канала C
Биты 6-7: Не используются

Регистры 8-10: Громкость каналов
Код:
4 бита на канал (0-15)
Если бит 4 = 1, используется огибающая вместо фиксированной громкости

Регистры 11/12: Огибающая период
Код:
Аналогично тону: 16-битное значение периода огибающей

Регистр 13: Форма огибающей
Код:
Биты 0-3: Номер формы (0-15)
Основные формы:
  0: \___ однонаправленное затухание
  1: /___ нарастание
  4: \\\\ пилообразная
  8: \___\___ повторяющаяся
  10: \\__\\__ прямоугольная

Регистры 14/15: Порты ввода-вывода
Код:
Только для AY-3-8910, в Spectrum обычно не используются

Программирование AY на ассемблере Z80

Базовые процедуры работы

Assembler Z80:
; Инициализация AY
INIT_AY:
    ld bc, AY_REG_SELECT
    ld e, 7            ; Регистр 7 - управление
    out (c), e
    ld bc, AY_REG_WRITE
    ld a, %00111111    ; Выключить все тона и шумы
    out (c), a
    ret

; Установка значения в регистр AY
; Вход: A = номер регистра, E = значение
SET_AY_REG:
    ld bc, AY_REG_SELECT
    out (c), a         ; Выбираем регистр
    ld bc, AY_REG_WRITE
    out (c), e         ; Записываем значение
    ret

; Воспроизведение ноты на канале A
; Вход: HL = частота (12 бит)
PLAY_NOTE_A:
    ld a, 0
    ld e, l
    call SET_AY_REG    ; Младший байт тона
    ld a, 1
    ld e, h
    call SET_AY_REG    ; Старший байт тона
    ; Включаем тон канала A
    ld a, 7
    ld e, %00111110    ; Оставляем только тон A
    call SET_AY_REG
    ; Устанавливаем громкость
    ld a, 8
    ld e, 15           ; Максимальная громкость
    call SET_AY_REG
    ret

Продвинутые техники: программная огибающая

Реальные трекеры часто реализуют собственную огибающую для большего контроля:
Assembler Z80:
; Структура канала в памяти
CHANNEL_STRUCT:
    .freq      dw 0    ; Текущая частота
    .volume    db 0    ; Текущая громкость (0-15)
    .env_pos   db 0    ; Позиция в огибающей
    .env_speed db 0    ; Скорость изменения
    .env_table dw 0    ; Адрес таблицы огибающей

; Таблица огибающей атака-сустейн
ENV_ATTACK:
    db 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
    db 15,15,15,15,15,15,15,15  ; Сустейн
    db 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0  ; Релиз

; Процедура обработки огибающей (вызывается в прерывании)
PROCESS_ENVELOPE:
    ld ix, CHANNEL_STRUCT
    ld a, (ix + CHANNEL_STRUCT.env_pos)
    inc a
    cp 48              ; Длина огибающей
    jr nz, .save_pos
    xor a              ; Зациклить
.save_pos:
    ld (ix + CHANNEL_STRUCT.env_pos), a
    ld hl, ENV_ATTACK
    ld d, 0
    ld e, a
    add hl, de
    ld a, (hl)         ; Новое значение громкости
    ld (ix + CHANNEL_STRUCT.volume), a
    ; Применяем к AY
    ld a, 8            ; Регистр громкости канала A
    ld e, (ix + CHANNEL_STRUCT.volume)
    call SET_AY_REG
    ret

Генерация шумовых эффектов

Assembler Z80:
; Установка параметров шума
SET_NOISE:
    ; Период шума
    ld a, 6
    ld e, 16           ; Среднее значение
    call SET_AY_REG
    ; Включаем шум на каналах
    ld a, 7
    ld e, %00110111    ; Тон A,B,C выкл, шум вкл на всех
    call SET_AY_REG
    ; Громкости
    ld a, 8
    ld e, 12
    call SET_AY_REG
    ld a, 9
    ld e, 12
    call SET_AY_REG
    ld a, 10
    ld e, 12
    call SET_AY_REG
    ret

; Эффект выстрела
SHOT_EFFECT:
    ld hl, 50          ; Высокая частота
    call PLAY_NOTE_A
    ld a, 6
    ld e, 1            ; Максимальная частота шума
    call SET_AY_REG
    ; Плавное уменьшение громкости
    ld b, 15
.loop:
    push bc
    ld a, 8
    ld e, b
    call SET_AY_REG
    ld bc, 1000        ; Задержка
.delay:
    dec bc
    ld a, b
    or c
    jr nz, .delay
    pop bc
    djnz .loop
    ret

Программирование на BASIC 128K

Хотя BASIC не оптимален для работы со звуком, в 128K режиме есть специальные команды:
Бейсик ZX:
10 REM Инициализация AY
20 BORDER 0: PAPER 0: INK 7: CLS
30 REM Воспроизведение ноты
40 SOUND 1, 100, 10: REM Канал A, частота 100, длительность 10
50 REM Аккорд
60 SOUND 1, 100, 20: SOUND 2, 125, 20: SOUND 4, 150, 20
70 REM Шум
80 SOUND 8, 10, 30: REM Шум на канале A
90 REM Использование огибающей
100 ENV 1, 5, 1000, 100, 5, 0, 5, 1000, 100
110 SOUND 1, 10, 100, 0, 1

Оптимизации и хитрости программирования

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

Assembler Z80:
SETUP_MUSIC_IRQ:
    di
    ld hl, MUSIC_INT
    ld ($FE00), hl     ; Вектор прерывания IM2
    ld a, $FE
    ld i, a
    im 2
    ei
    ret

MUSIC_INT:
    push af
    push bc
    push de
    push hl
    push ix
    exx
    ex af, af'
    push af
    push bc
    push de
    push hl
  
    ; Обработка одного тика музыки
    call MUSIC_TICK
  
    pop hl
    pop de
    pop bc
    pop af
    ex af, af'
    exx
    pop ix
    pop hl
    pop de
    pop bc
    pop af
    ei
    ret

Полифоническая музыка с тремя каналами

Сложность в том, что нужно распределять ноты между каналами:
Assembler Z80:
; Структура для трекера
MUSIC_TRACKER:
    .pattern_ptr   dw PATTERN_DATA
    .position      db 0
    .speed_counter db 0
    .speed         db 6
    .channels:
    .chA_note      db 0
    .chA_instr     db 0
    .chA_volume    db 0
    .chB_note      db 0
    .chB_instr     db 0
    .chB_volume    db 0
    .chC_note      db 0
    .chC_instr     db 0
    .chC_volume    db 0

; Таблица частот нот (октава 3)
NOTE_FREQ_TABLE:
    dw 3822, 3607, 3405, 3214, 3034, 2863  ; C, C#, D, D#, E, F
    dw 2702, 2551, 2407, 2272, 2145, 2024  ; F#, G, G#, A, A#, B

Особенности разных моделей Spectrum

Spectrum 128K (оригинальный)

  • Один чип AY-3-8912
  • Монофонический выход
  • Конфликты при чтении портов

Pentagon 128K/512K

  • Часто два чипа AY для стерео
  • Возможность независимого программирования
  • Порт $FFDF для выбора чипа

Scorpion ZS 256

  • Поддержка стерео через один чип
  • Дополнительные режимы смешивания
  • Программируемый усилитель

Советские клоны (ATM Turbo, Profi)

  • Часто нестандартные порты
  • Дополнительные аппаратные возможности
  • Проблемы с совместимостью

Создание музыки: практические советы

Ограничения и обходные пути

  1. Только 3 канала → Используйте быстрое переключение нот (арпеджио)
  2. Нет SAMPLES → Генерация шумом ударных
  3. Ограниченная огибающая → Программная реализация

Оптимальные частоты дискретизации

Для музыки: 50 Гц (частота прерываний)
Для эффектов: до 100 Гц с чередованием каналов

Стерео-эффекты на моно AY

Хотя оригинальный Spectrum 128K имеет моно выход, можно эмулировать стерео:
Assembler Z80:
; Псевдо-стерео чередованием каналов
PSEUDO_STEREO:
    ld a, (tick_counter)
    and 1
    jr z, .left
.right:
    ld e, %00000101    ; Только каналы B и C
    jr .apply
.left:
    ld e, %00001010    ; Только каналы A и C
.apply:
    ld a, 7
    call SET_AY_REG
    ret

Инструменты и разработка

Современные средства

  1. AY-трекеры: Vortex Tracker, Arkos Tracker
  2. Эмуляторы: FUSE, UnrealSpeccy, ZXSpin
  3. Компиляторы: SJASM, Pasmo, Z80asm

Оптимизация кода

Assembler Z80:
; Быстрая установка регистров AY (экономия тактов)
FAST_AY_OUT:
    ld bc, AY_REG_SELECT
    out (c), a         ; A = номер регистра
    inc b              ; BC = AY_REG_WRITE
    out (c), e         ; E = значение
    ret

; Массовая инициализация регистров
INIT_AY_BULK:
    ld hl, AY_DEFAULT_VALUES
    ld b, 16           ; 16 регистров
    ld c, 0            ; Начинаем с регистра 0
.loop:
    ld a, c
    ld e, (hl)
    call FAST_AY_OUT
    inc hl
    inc c
    djnz .loop
    ret

AY_DEFAULT_VALUES:
    db 0,0,0,0,0,0,0,%00111111,0,0,0,0,0,0,0,0

Пример полноценной музыкальной дорожки

Assembler Z80:
MUSIC_DATA:
    ; Паттерн 0
PATTERN_0:
    db $24, $01, $0F, $00, $00, $00, $28, $01, $0F  ; Нота, инструмент, громкость
    db $00, $00, $00, $2B, $01, $0F, $00, $00, $00
    db $30, $01, $0F, $00, $00, $00, $00, $00, $00
    db $00, $00, $00, $00, $00, $00, $00, $00, $00

INSTRUMENT_TABLE:
    ; Инструмент 0: пианино
    db %00000001       ; Использовать огибающую
    db 0, 100          ; Атака
    db 15, 50          ; Сустейн
    db 0, 100          ; Релиз
    db 0               ; Нет вибрато

PLAY_MUSIC:
    ld ix, MUSIC_TRACKER
    ld a, (ix + MUSIC_TRACKER.speed_counter)
    inc a
    cp (ix + MUSIC_TRACKER.speed)
    jr nz, .no_new_note
  
    ; Обработка новой ноты
    call GET_NEXT_NOTE
    call APPLY_NOTE_TO_AY
  
.no_new_note:
    ld (ix + MUSIC_TRACKER.speed_counter), a
    ret

Заключение

AY-3-8910, несмотря на свои ограничения, стал легендарным звуковым чипом благодаря простоте программирования и характерному "спектрумовскому" звучанию. Его изучение — это не только погружение в историю, но и отличный способ понять основы синтеза звука и низкоуровневого программирования периферии.

Ключевые принципы для запоминания:

  1. AY — это программируемый генератор тона и шума
  2. 3 канала с независимыми частотами и громкостями
  3. Огибающая позволяет создавать динамическое звучание
  4. Оптимизация через прерывания и быстрые процедуры записи
  5. Совместимость между разными клонами требует тестирования
Мастерское владение AY позволяло создателям демо и игр для Spectrum выдавать впечатляющие для своего времени результаты, многие из которых до сих пор восхищают своей изобретательностью в условиях жестких аппаратных ограничений.