# 10. Звук

## 10.1 Общие сведения

> Документ описывает звуковую подсистему платы **Sp2000** и более новых моделей.
> Информация о Sprinter-97 не рассматривается.

Звуковая подсистема Sprinter Sp2000 включает три компонента:

| Компонент | Тип | Описание |
|-----------|-----|----------|
| GM80C760 (AY-3-8910 совм.) | Музыкальный сопроцессор | 3 голоса + шум + огибающая, эмуляция в ППЛМ |
| COVOX | 8-битный ЦАП | Прямой вывод сэмплов через порт `#FB`/`#4F` |
| CBL (COVOX-Blaster) | COVOX с буфером | 256 байт буфера, прерывания, разные режимы |

Весь звук выводится через **16-битный ЦАП TDA1543** (двухканальный, стерео).
Реально используется 10 бит для одновременного смешивания 8-bit COVOX и AY
[IvanMak.txt:632–634].

**На Sp2000 все три компонента присутствуют в основной прошивке ППЛМ.**
CBL включается через порт `#4E` (бит 7 = 1). При выключенном CBL (бит 7 = 0)
работает обычный 8-bit COVOX [IvanMak.txt:662–666].

## 10.2 GM80C760 (AY-3-8910)

### 10.2.1 Реализация

AY-функция реализована на микросхеме **GM80C760** (ППЛМ ALTERA EP1K30),
AY-3-8910/8912 совместимая. Это не отдельная дискретная микросхема
[IvanMak.txt:636–637].

На EP1K30 (Sp2000) реализована **3-я версия** AY. Включает три голоса,
шум и генератор огибающей (envelope). В формирователе огибающей обнаружена
небольшая ошибка, детали не документированы [IvanMak.txt:224–229],
[12-bugs.md §11.2].

### 10.2.2 Порты

| Порт | Назначение |
|------|-----------|
| `#BFFD` | AY-8910 — запись номера регистра (address) |
| `#FFFD` | AY-8910 — чтение/запись данных регистра (data) |

Внутренние номера в карте дешифратора ППЛМ: `90h` (`#BFFD`), `91h` (`#FFFD`)
[IvanMak.txt:1298–1299].

### 10.2.3 Программирование

Программирование — **стандартное для AY-3-8910/8912**
[IvanMak.txt:636–637]:

```asm
; Установить регистр AY
LD   BC, #BFFD
OUT  (C), A           ; A = номер регистра (0..15)
LD   BC, #FFFD
OUT  (C), A           ; A = значение

; Прочитать регистр AY (3-я версия)
LD   BC, #BFFD
OUT  (C), A           ; A = номер регистра
LD   BC, #FFFD
IN   A, (C)           ; читать данные
```

**Регистры AY-3-8910** (стандартные):

| № | Назначение |
|:-:|-----------|
| 0 | Частота канала A (младший байт) |
| 1 | Частота канала A (старшие 4 бита) |
| 2 | Частота канала B (младший байт) |
| 3 | Частота канала B (старшие 4 бита) |
| 4 | Частота канала C (младший байт) |
| 5 | Частота канала C (старшие 4 бита) |
| 6 | Период шума |
| 7 | Микшер (включение каналов и шума) |
| 8 | Громкость A |
| 9 | Громкость B |
| 10 | Громкость C |
| 11 | Период огибающей (младший байт) |
| 12 | Период огибающей (старшие 8 бит) |
| 13 | Форма огибающей |
| 14 | Порт I/O A (не используется на Sprinter) |
| 15 | Порт I/O B (не используется на Sprinter) |

**Стерео:** AY имеет два выходных канала, но в стандартной конфигурации
Sprinter оба канала получают монофонический сигнал.

---

## 10.3 COVOX (8-бит)

### 10.3.1 Порты

| Порт | Назначение |
|------|-----------|
| `#FB` | COVOX data (он же порт включения КЭШ-ОЗУ по IN) |
| `#4F` | COVOX data (альтернативный адрес) |

[IvanMak.txt:642, 1347]

### 10.3.2 Программирование

Простейший вывод сэмплов — последовательная запись байтов в порт `#FB`
или `#4F`:

```asm
; Цикл вывода сэмплов (8 бит, моно)
LD   HL, sample_data
LD   BC, sample_length
loop:
LD   A, (HL)
OUT  (#FB), A
INC  HL
DEC  BC
LD   A, B
OR   C
JR   NZ, loop
```

### 10.3.3 Конфликт с Fast RAM (КЭШ-ОЗУ)

Порт `#FB` используется **для двух целей**:
- `OUT (#FB), A` — запись в COVOX
- `IN A, (#FB)` — включение КЭШ-ОЗУ

Если КЭШ-ОЗУ включено, запись в `#FB` уходит в кэш, а не в COVOX.
Перед работой с COVOX кэш нужно отключать: `IN A, (#7B)`
[IvanMak.txt:850–851], [12-bugs.md §11.4].

---

## 10.4 COVOX-Blaster (CBL)

### 10.4.1 Общее описание

**CBL (COVOX-Blaster)** — COVOX с буферным ОЗУ на **256 байт**. Позволяет
выводить звук поблочно, освобождая процессор для другой работы
[IvanMak.txt:647], [Parinov.txt:688].

### 10.4.2 Порт управления `#4E`

**Порт `#4E` — 16-битный! Только через `OUT (C),A`** (с `LD BC,004Eh`)
[Parinov.txt:692–693].

> **Примечание:** IvanMak.txt упоминает его как 8-битный, но Parinov.txt явно
> указывает 16-битный доступ. Современный код использует `OUT (C),A`.

**Биты порта `#4E`** [Parinov.txt:695–698]:

| Бит | Мнемоника | Назначение |
|-----|-----------|-----------|
| 7 | **CBL_ON** | 1 = CBL включён, 0 = обычный COVOX |
| 6 | **STEREO** | 1 = стерео, 0 = моно |
| 5 | **16BIT** | 1 = 16-битные сэмплы, 0 = 8-битные |
| 4 | **IRQ_EN** | 1 = включить прерывания от CBL |
| 3–0 | **FREQ** | Частота дискретизации (см. таблицу) |

### 10.4.3 Частоты дискретизации

[Parinov.txt:700–718]:

| Код | Частота | Примечание |
|:---:|---------|-----------|
| 0 | 16 kHz | старые режимы, **НЕ ИСПОЛЬЗОВАТЬ** |
| 1 | 22 kHz | старые режимы, **НЕ ИСПОЛЬЗОВАТЬ** |
| 2–7 | — | зарезервировано |
| **8** | **7.8125 kHz** | mono/stereo, 8/16-bit |
| **9** | **10.9375 kHz** | mono/stereo, 8/16-bit |
| **A** | **15.625 kHz** | mono/stereo, 8/16-bit |
| **B** | **21.875 kHz** | mono/stereo, 8/16-bit |
| **C** | **31.25 kHz** | mono/stereo, 8/16-bit |
| **D** | **43.75 kHz** | mono/stereo, 8/16-bit |
| **E** | **54.6875 kHz** | mono/stereo, 8/16-bit |
| **F** | **109.375 kHz** | mono/stereo, 8/16-bit |

> **Важно:** коды 0 и 1 — старые режимы из первых прошивок, несовместимы с
> новыми режимами (8–F). Использовать только коды 8–F.

### 10.4.4 Режимы и формат данных

[Parinov.txt:767–783]:

| Режим | Нулевой уровень | Размер блока | Формат данных |
|-------|:---------------:|:------------:|--------------|
| mono 8-bit | `80h` | 128 байт | `DB 80h, 81h, 7Fh, ...` |
| mono 16-bit | `0000h` | 256 байт | `DW 0, 1000, -1000, ...` |
| stereo 8-bit | `80h,80h` | 128 байт | `DB 80h,80h, 81h,80h, ...` (L, R чередуются) |
| stereo 16-bit | `0000h,0000h` | 256 байт | `DW 0,0, 1000,0, ...` (L, R чередуются) |

Формат данных выбран так, чтобы WAV-файлы можно было пересылать в CBL
простой `OTIR` [Parinov.txt:791].

### 10.4.5 Буфер CBL и механизм банок

CBL имеет буферное ОЗУ на **256 байт**, организованное как кольцевой буфер
[Parinov.txt:758–760]:

- Счётчик CBL считает **«назад»** — из-за особенности команды `OUTI`, где `B`
  уменьшается и попадает на A15..A8.
- Для 8-битных режимов буфер условно разбит на **две банки по 128 байт**.
- Для 16-битного режима блок = **256 байт** (весь буфер).

**Механизм работы:**

1. Процессор записывает блок данных (128 байт для 8-бит, 256 байт для 16-бит)
   в буфер CBL.
2. Аппаратный счётчик CBL автоматически считывает данные из буфера и
   отправляет в ЦАП на заданной частоте.
3. Бит 7 порта `#FE` указывает, какая банка сейчас выводится в ЦАП.
4. Когда банка переключается — CBL запрашивает следующую порцию данных
   (через прерывание или polling).
5. Запрос данных возникает для **каждых 128 байт (256 в 16-битном режиме)**.

### 10.4.6 Порт статуса `#FE`

**Бит 7 порта `#FE`** — запрос данных для CBL [Parinov.txt:720–724]:

| Бит | Назначение |
|-----|-----------|
| 7 | **CBL data request** — 1 = CBL запрашивает новую порцию данных |
| 5 | **Frame sync** — кадровый импульс (4 мс длиной, 20 мс период) |

> **Примечание:** В MAME при выключенном CBL бит 7 порта #FE всегда = 1.
> Это следует учитывать при разработке обработчика прерываний
> [`im2_isr_design.md:26–31`].

**Противоречие** [08-io.md §8.3]:
- IvanMak.txt описывает бит 7 как старший бит счётчика CBL для polling
  (определение, какая половина буфера выводится).
- Parinov.txt описывает его как запрос прерывания.
- Оба описывают одну физическую линию, но в разных режимах (polling vs interrupt).

### 10.4.7 Прерывания и IM2

- При включённых прерываниях CBL (бит 4 порта `#4E` = 1) необходим **режим IM 2**.
- Если остаться на IM1, DOS-процедура прерываний будет вызываться слишком часто,
  что вызовет тормоза на высоких частотах [Parinov.txt:762–764].
- **Вектор прерывания CBL — `#FF`** (как и у клавиатуры, и у кадрового).
  Отличать — по биту 7 порта `#FE` [`sprinterIntLib.asm:24–26`].

**Обработчик прерываний на IM2:**

```asm
; Проверка источника
IN   A, (#FE)
RLCA                   ; бит 7 → CF
JR   NC, not_cbl       ; не CBL — на другой обработчик

; CBL запрашивает данные
LD   BC, 804Fh         ; B = 128 (счётчик), C = 4Fh (порт CBL data)
LD   HL, next_block
OTIR                   ; вывести 128 байт в буфер CBL
...
```

### 10.4.8 Запись данных в CBL

**Способ 1: OTIR/OUTI через порт `#4F`**

```asm
LD   BC, 804Fh         ; B = 128 (счётчик), C = #4F (CBL data)
LD   HL, data_block
OTIR                   ; вывести 128 байт
```

**Способ 2: через страницу ОЗУ `#FD` с акселератором**

Страница `#FD`, отображённая в окно `#C000`, аппаратно связана с буфером CBL.
Для 8-битного режима — записать 128 байт; для 16-битного — 256 байт
[Parinov.txt:727–731].

```asm
; Запись блока данных в CBL через страницу #FD с акселератором
IN   A, (#E2)           ; сохранить текущую страницу W3
LD   (.save_page), A
LD   A, #FD
OUT  (#E2), A           ; W3 = страница CBL

LD   DE, #C000          ; приёмник (CBL buffer)
LD   B, 0               ; B = 0 → 256 байт (или 128 для 8-bit)
LD   D,D                ; ACC_ON: включить акселератор
LD   L,L                ; ACC_CopyBlock_Horizontal
LD   A, (HL)            ; загрузить байт в буфер акселератора
LD   (DE), A            ; записать в CBL
LD   B,B                ; ACC_OFF: выключить акселератор

LD   A, (.save_page)
OUT  (#E2), A           ; восстановить страницу
```

### 10.4.9 Полный пример

Из `docs/samples/Пример для CBL.asm` [sprinterIntLib.asm]:

```asm
; Инициализация IM2
LD   A, #BE
LD   I, A              ; таблица векторов по #BE00
IM   2

; Вектор прерывания CBL = #BEFF
; (Interrupt_Vector OR 0FFh)

; Включение CBL
LD   BC, #4E
LD   A, %10011000b     ; bit7=1 CBL on, bit4=1 IRQ en, bits3-0=1000 = 7.8125 kHz
OUT  (C), A

; В ISR:
; IN   A, (#FE)
; RLCA
; JR   NC, .NoSound
; ... копировать 128 байт через страницу #FD с акселератором

; После проигрывания:
; Установить NumberPage=2, подождать HALT

; Выключение CBL:
LD   BC, #4E
LD   A, 0
OUT  (C), A
```

### 10.4.10 Известные проблемы

1. **CBL vs Fast RAM:** порт `#FB` используется и для COVOX/CBL, и для
   включения КЭШ-ОЗУ (см. §10.3.3).
2. **Щелчок перед первым звуком:** известная проблема при старте CBL
   [`TODO.md:263`].
3. **Помехи при движении мыши:** часть тиков прерываний, обслуживающих
   CBL-насос, уходит на обслуживание мыши [`TODO.md:270–277`].
4. **Порт #4E — 8-бит vs 16-бит:** IvanMak подразумевает 8-битный,
   Parinov требует 16-битный `OUT (C),A` (см. §10.4.2).
5. **MAME: бит 7 порта #FE = 1 при выключенном CBL:** может вызвать
   ложные срабатывания обработчика (см. §10.4.6).

---

## 10.5 Бипер

Стандартный Spectrum-бипер — бит 3 (в некоторых конфигурациях бит 5)
порта `#FE`. Выведен через ту же схему TDA1543, что COVOX и AY
[IvanMak.txt:639–640].

```asm
; Включить бипер
LD   A, %00001000       ; бит 3 = 1
OUT  (#FE), A

; Выключить бипер
LD   A, %00000000       ; бит 3 = 0
OUT  (#FE), A
```

---

## 10.6 SSC (Sprinter Sound Card)

Разрабатывалась, но **не реализована** ни в одной прошивке Sp2000
из-за большого объёма ПЛМ [IvanMak.txt:763–772].

Параметры проекта:
- 2 канала
- 16 восьмибитных голосов
- 256-байтные Wave-таблицы для каждого голоса
- 8-битный регулятор амплитуды

---

## 10.7 Кросс-ссылки

- Порты звука: `11-ports.md`
- Баги AY и CBL: `12-bugs.md`
- Fast RAM и конфликт с COVOX: `04-memory.md §4.6`
- 08-io.md (остальной ввод-вывод): `08-io.md`
