# 6. Акселератор

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

Акселератор — внутреннее устройство, реализованное в ППЛМ. Присутствует почти
во всех конфигурациях Sprinter (Sprinter-2 и выше). Предназначен для ускорения
пересылки блоков данных между RAM и VRAM (до физического предела скорости ОЗУ).
Не поддерживает ПЗУ и Fast RAM [accelerator_doc.txt:5].

**Возможности:**

- Быстрая заливка горизонтальной или вертикальной линии экрана (1..256 пикселей)
  одним цветом; в режиме 640×256 — линия 1..512 пикселей.
- Быстрое копирование горизонтальной или вертикальной линии (1..256 пикселей).
- Логические операции AND, OR, XOR с блоками данных.

**Внутренняя память:** 256 байт в ППЛМ. Данные загружаются в эту память,
затем копируются в выбранный участок RAM/VRAM. Операция копирования может
повторяться многократно — это позволяет заполнять экран текстурой без
перезагрузки данных.

**Автоматический инкремент адреса:**

- **Горизонтальные** операции (`LD L,L` — копирование, `LD C,C` — заливка):
  акселератор автоматически инкрементирует регистр HL (адрес приёмника) на
  каждом шаге. Для копирования источник (DE) также инкрементируется.
- **Вертикальные** операции (`LD A,A` — копирование, `LD E,E` — заливка):
  акселератор автоматически меняет порт `RGADR` (`#89`) — координату Y.
  После завершения операции `RGADR` содержит новое значение
  (Y + размер_блока, если не было переполнения).
- **Комбинированный** режим (горизонтальный источник + вертикальный приёмник):
  сначала загружается байт из последовательных данных, затем выполняется
  вертикальная запись. `RGADR` инкрементируется; регистр-источник (DE)
  тоже инкрементируется — можно последовательно читать спрайт и рисовать
   его вертикальными строками (§6.3, пример «Спрайт»).

## 6.2 Команды

Акселератор управляется специальными инструкциями Z80, которые выполняются
как NOP обычным процессором, но распознаются ППЛМ [IvanMak.txt:810–848].

| Инструкция | Код | Действие |
|-----------|:---:|----------|
| `LD B,B` | `#40` | **Выключить** акселератор |
| `LD D,D` | `#52` | **Включить** + режим «задать размер блока» |
| `LD A,size` | `#3E size` | Установить размер блока (сразу после `LD D,D`) |
| `LD C,C` | `#4F` | **Горизонтальная заливка** одним байтом |
| `LD E,E` | `#5B` | **Вертикальная заливка** одним байтом |
| `LD L,L` | `#6D` | **Горизонтальное копирование** блока |
| `LD A,A` | `#7F` | **Вертикальное копирование** блока |
| `LD H,H` | `#64` | Зарезервировано |

**Логические операции** (AND, OR, XOR) выполняются командами
`AND (HL)`, `OR (HL)`, `XOR (HL)` после загрузки блока в память акселератора.

## 6.3 Примеры

### Копирование экрана (страница 0 → страница 1)

```asm
; страница VRAM уже открыта по адресу #C000
LD   HL, #C000       ; адрес начала линии первого экрана
LD   DE, #C140       ; адрес начала линии второго экрана
LD   BC, #140        ; длина строки по горизонтали (320)
DI                   ; запретить прерывания
LD   D,D             ; включить акселератор: режим размера блока
LD   A, 0            ; размер блока = 256 байт (0 = 256)
LD   A,A             ; вертикальное копирование
LDIR                 ; копировать
LD   B,B             ; выключить акселератор
EI                   ; разрешить прерывания
```

Время исполнения: ~1.2 прерывания (~24–26 мс на полный экран)
[IvanMak.txt:822–823], [accelerator_doc.txt:37].

### Заливка блока одним цветом

```asm
LD   DE, #C000 + X   ; X — смещение по горизонтали
LD   A, Y
OUT  (#89), A        ; RGADR = Y
DI
LD   D,D             ; режим размера блока
LD   A, width        ; ширина блока
LD   B,B             ; стоп
LD   A, colour       ; цвет заливки
LD   C,C             ; горизонтальная заливка
LD   (DE), A         ; залить
LD   B,B             ; стоп
EI
```

### XOR блока данных

```asm
LD   HL, addr_1
LD   DE, xor_data
DI
LD   D,D
LD   A, 0            ; 256 байт
LD   L,L             ; горизонтальное копирование
LD   A, (DE)         ; загрузить блок в память акселератора
XOR  (HL)            ; XOR с данными в RAM
LD   (HL), A         ; сохранить результат
LD   B,B             ; стоп
EI
```

### Копирование вертикальной линии с разделением load/write

Загрузка байта в буфер акселератора и запись из буфера в VRAM разнесены —
между ними можно выполнять произвольный код (например, сменить Y).

Буфер акселератора и размер блока **сохраняются** между включениями и
выключениями (`LD B,B`), что позволяет гибко разделять чтение
и запись.

```asm
LD   HL, #C000 + X1     ; источник
LD   DE, #C000 + X2     ; приёмник

DI
LD   D,D                ; акселератор вкл, режим размера
LD   A, size            ; размер блока
LD   B,B                ; выключить (размер запомнен)

LD   A, Y1
OUT  (#89), A           ; RGADR = Y1

LD   A,A                ; вертикальное копирование
LD   A, (HL)            ; загрузить байт из (HL) в буфер акселератора
LD   B,B                ; выключить (буфер запомнен)

LD   A, Y2
OUT  (#89), A           ; RGADR = Y2 (смена Y между load и write)

LD   A,A                ; вертикальное копирование
LD   (DE), A            ; записать буфер в (DE) — акселератор пишет
                        ; вертикальную линию size пикселей
LD   B,B                ; выключить
EI
```

### Спрайт вертикальными строками из последовательных данных

Спрайт хранится как последовательность байт вертикальных линий. Для каждой
колонки спрайта: загружается байт из последовательных данных, затем
акселератор пишет его вертикально (автоинкремент `RGADR`). После завершения
колонки — сдвиг по X (`INC HL`) и повтор.

```asm
LD   DE, sprite_data      ; DE → последовательные данные спрайта
LD   HL, #C000 + X_COORD  ; HL → начальная позиция на экране
LD   B, sprite_width      ; ширина спрайта в колонках

LD   D,D                  ; акселератор вкл, режим размера
LD   A, sprite_height     ; высота одной вертикальной линии
LD   B,B                  ; акселератор выключили (размер акселератор запомнил)

row:
LD   A, Y_COORD
OUT  (#89), A             ; RGADR = стартовая Y
DI
LD   L,L                  ; режим «загрузить из последовательных данных»
LD   A, (DE)              ; загрузить байт спрайта в буфер акселератора
LD   A,A                  ; вертикальное копирование
LD   (HL), A              ; записать — акселератор копирует байт
                          ; вертикально, RGADR++ на каждом шаге
LD   B,B                  ; выключить акселератор
EI
INC  HL                   ; следующая колонка по X
DJNZ row
```

## 6.4 Скорость

Время работы акселератора ограничено только физической скоростью ОЗУ
[IvanMak.txt:840–843]:

```
Время ≈ время команды без акселератора + число_байт / 7 000 000 (сек)
```

## 6.5 Комбинированный режим: общая схема

Общий паттерн для работы с акселератором в комбинированном режиме
(горизонтальный источник + вертикальный приёмник):

```
LD   D,D            ; 1. Включить акселератор, режим размера
LD   A, size        ; 2. Задать размер блока (1..256)
LD   L,L            ; 3. Режим «горизонтальный источник»
<чтение байта>      ; 4. Загрузить байт в буфер акселератора (LD A,(DE) и т.п.)
LD   A,A            ; 5. Режим «вертикальный приёмник»
<запись байта>      ; 6. Записать — акселератор делает вертикальное копирование
                    ;    с автоинкрементом RGADR и приёмника
LD   B,B            ; 7. Выключить акселератор
```

После шага 6 `RGADR` содержит Y + size, HL — X + 1 (если был `INC HL`).

## 6.6 Прерывания

**Старая прошивка:** DI обязателен на время работы акселератора — система
команд Z80 сильно меняется, и ISR не сможет корректно выполниться
[IvanMak.txt:844–846].

**Новая прошивка:** акселератор может работать с EI. По приходу прерывания
он отключается, а по `RETI` включается обратно [IvanMak.txt:846–848].
Использовать этот режим следует с осторожностью — без проверки на реальном
железе неизвестно, какая версия прошивки установлена.

---

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

- Баги акселератора: `12-bugs.md`
- Использование акселератора в libbgi: `sprite-api-design.md`
- Ограничение скорости: `accel-fill-budget.md`
- Fast RAM и конфликт с портом CBL: `04-memory.md §4.6`
