Files
VGMPLAY.WMF/doc/01_WC_Plugin_Overview.md

381 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wild Commander — Обзор Системы Плагинов
> Версия документа: 2.0 — дополнена по результатам анализа исходников
> Источники: `inc/wc_api.h`, `asm/wc_api.s`, `asm/crt0.s`, `DiskRef/Help.txt`, `DiskRef/WC_History.txt`
## Введение
Wild Commander (WC) — файловый менеджер с системой плагинов для платформы
**TS-Configuration** (ZX Spectrum Evo). Выполняет роль основной оболочки и
загрузчика для работы с FAT32-дисками, SD-картами и IDE.
WC запускается из BootROM / vDOS и предоставляет:
- Двухпанельный интерфейс (80 × 25/30/36 TXT-режим)
- FAT32 (длинные и короткие имена до 255 символов)
- Систему плагинов (.WMF) с горячей загрузкой/выгрузкой
- Поддержку устройств: SD (Z-Controller), IDE NEMO, IDE SMUC, NeoGS SD
- Встроенный просмотрщик (F3): TXT, HEX, TGA, SCR, Gigascreen, MGS — до 1 МБ
- Мультиязычность (ENG / RUS, переключение CTRL+SHIFT)
### Версии
| Компонент | Версия | Примечание |
|-----------|--------|------------|
| WC | 1.10 (03.08.2025) | Последняя из `WC_History.txt` |
| Plugin System | **#10** | Формат заголовка `.WMF` |
| Идентификатор | `WildCommanderMDL` | 16 байт, offset +16 в заголовке |
## Общая концепция
Плагины WC (.WMF файлы) — модульные расширения, выполняемые в адресном
пространстве Z80. Поддерживают до **64 страниц** (1 МБ) кода/данных.
Типичные области применения:
- Просмотр файлов различных форматов (BMP, GIF, ILBM, SCR)
- Воспроизведение музыки и видео (VGM, MOD, WAV, MP3/GS)
- Редактирование файлов (TXT Editor с подсветкой синтаксиса)
- Работа с ROM/Flash (ROM_PROG)
- Монтирование образов дисков (MOUNTER: TRD, TAP, FDI)
- Утилиты (часы, скринсейвер, компилятор, дамп памяти)
## Типы запуска плагинов
При запуске плагина, в регистр **A** передается тип вызова:
| Код | Константа (C) | Описание |
|-----|---------------|----------|
| #00 | `WC_PCOND_EXT_ONLY` | По расширению файла (ENTER / F3) |
| #01 | `WC_PCOND_ON_LOAD` | При первой загрузке плагина (ENTER) |
| #02 | `WC_PCOND_TIMER` | По таймеру скринсейвера + меню F10 |
| #03 | `WC_PCOND_F10` | Из меню запуска плагинов (F10) |
| #04 | `WC_PCOND_VIEWER` | Из меню вьювера (SHIFT+F3 / SHIFT+ENTER) |
| #05 | `WC_PCOND_F10_VIEW` | F10 + SHIFT+F3 |
| #11 | `WC_PCOND_F10_F2` | F10 + F2 |
| #12 | `WC_PCOND_F2` | По нажатию F2 |
| #13 | `WC_PCOND_F10_F4` | F10 + F4 |
| #14 | `WC_PCOND_F4` | По нажатию F4 |
| #15 | `WC_PCOND_F10_V_F4` | F10 + SHIFT+F3 + F4 |
> **Примечание:** Все плагины также вызываются по расширению, если расширения
> указаны в блоке расширений заголовка (offset +64). ENTER и F3 разделены
> начиная с WC v1.00 — флаг +63 определяет, какой из них запускает плагин.
## Входные параметры при запуске
WC делает `CALL #8000`. В `crt0.s` (SDCC) входные регистры сохраняются
в глобальные переменные автоматически.
```asm
; Входные параметры:
A - тип вызова (см. таблицу выше)
[DE,HL] - длина файла в байтах (DE=hi16, HL=lo16)
BC - адрес строки с именем файла (для типов #00, #04, #14)
формат: name(1-255), 0 (длинное FAT имя)
IX - адрес структуры активной панели
; Альтернативные регистры (только для #00,#04,#12,#14):
A' - номер расширения (0-31) для типа #00
DE' - количество файлов/каталогов в панели (включая "..")
HL' - номер объекта из панели (от 1)
```
В SDCC-плагине (`crt0.s` автоматически):
```c
extern uint8_t wc_file_ext; // ← A' (номер расширения)
extern char * wc_file_name; // ← BC (указатель на имя)
extern uint32_t wc_file_size; // ← [DE,HL] (32-бит размер)
```
## Коды возврата из плагина
При выходе из плагина регистр **A** определяет действие WC:
| A | Константа (C) | Действие |
|---|---------------|----------|
| 0 | `WC_EXIT_ESC` | Просто выход (ESC) |
| 1 | `WC_EXIT_UNRECOGNIZED` | Файл не опознан → передать другому плагину (тип #00) |
| 2 | `WC_EXIT_NEXT` | Перейти на следующий файл (тип #00) |
| 3 | `WC_EXIT_RELOAD_DIR` | Перечитать каталог |
| 4 | `WC_EXIT_PREV` | Перейти на предыдущий файл (тип #00) |
В SDCC-плагине: `wc_exit_code = 0; return 0;` — `crt0.s` считывает
`wc_exit_code` в A перед `RET` обратно в WC.
> **Код 2/4 (NEXT/PREV):** WC вызывает тот же плагин повторно с другим
> файлом. `crt0.s` заново обнуляет BSS (`_DATA`) и копирует инициализаторы
> (`_INITIALIZER → _INITIALIZED`), поэтому все `static` переменные сбросятся.
## Системные адреса
> Источник: `src_sdcc/inc/wc_api.h`, раздел «СИСТЕМНЫЕ ПЕРЕМЕННЫЕ WC»
| Адрес | Константа | Размер | Описание |
|---------------|--------------|--------|---------------------------------------------------|
| `#6000` | `WC_SYS_PG0` | 1 | Текущая страница в окне #0000 |
| `#6001` | `WC_SYS_PG4` | 1 | Текущая страница в окне #4000 |
| `#6002` | `WC_SYS_PG8` | 1 | Текущая страница в окне #8000 |
| `#6003` | `WC_SYS_PGC` | 1 | Текущая страница в окне #C000 |
| `#6004` | `WC_SYS_ABT` | 1 | 1 = был нажат ESC |
| `#6005` | `WC_SYS_ENT` | 1 | 1 = был нажат ENTER |
| `#6006` | `WC_SYS_FUN` | 3 | **Точка входа WC API** (JP к диспетчеру) |
| `#6009` | `WC_SYS_TMN` | 2 | Таймер INT (uint16_t, ++/кадр ≈50 Гц) |
| `#600B` | `WC_SYS_FEP` | 1 | Страница FAT-движка в окне 0 |
| `#600C` | `WC_SYS_XPP` | 1 | Сохранённая страница |
| `#600D` | `WC_SYS_CNFV`| 1 | ЦАП: 0=PWM, 1=3bit, 2=4bit, 3=5bit, 7=VDAC2/FT812 |
| `#600E` | `WC_SYS_HEI` | 1 | Высота TXT-экрана: 25 / 30 / 36 |
| `#600F` | `WC_SYS_TXTMD`| 1 | Текущий видеорежим WC (0=TXT) |
| `#6010` | `WC_SYS_INIFLG`| 1 | Флаг загрузки INI-конфигурации |
| `#6011` | `WC_SYS_RSQBC`| 2 | RSQ буфер-счётчик |
| `#6013` | `WC_SYS_RSQB2`| 2 | RSQ буфер 2 |
| `#6015` | `WC_SYS_SDBSF`| 1 | Состояние SD1 драйвера |
| `#6016` | `WC_SYS_SDBSF2`| 1 | Состояние SD2 драйвера |
| `#6017` | `WC_SYS_F3ENTFL`| 1 | Флаг F3/ENTER для файловых операций |
| `#6018` | `WC_SYS_FEP2`| 2 | FAT engine page extra |
| `#6020` | `WC_SYS_PLRESAD`| 8 | Резидент перехода: A=страница, HL=адрес → JP(HL) |
| `#6028` | `WC_SYS_PLRESCALL`| 8 | Резидент вызова: A=страница, HL=адрес → CALL(HL) |
| `#8000` | — | — | **Точка входа плагина** (WC делает CALL #8000) |
Чтение из C:
```c
#define WC_SYS_ABT (*(volatile uint8_t*)0x6004)
#define WC_SYS_TMN (*(volatile uint16_t*)0x6009)
#define WC_SYS_HEI (*(volatile uint8_t*)0x600E)
// Или через макросы wc_api.h:
uint8_t h = wc_get_height(); // 25/30/36
uint16_t t = wc_get_timer(); // кадровый счётчик
uint8_t esc = wc_get_abt(); // 1 = ESC нажат
```
## Вызов API функций
Все функции WC вызываются через точку доступа #6006:
```asm
WLD EQU #6006
; Пример вызова функции PRWOW (вывод окна)
PRWOW LD A,1
JP WLD
; Пример вызова ESC (проверка нажатия ESC)
ESC LD A,#17
JP WLD
```
**Регистровые соглашения WC API:**
- Вход: **A** = номер функции, остальные параметры в регистрах (см. справочник)
- Некоторые функции используют **A'** (альтернативный A через `EX AF,AF'`)
- **Портит**: AF, BC, DE, HL, IX
- **Сохраняет**: IY, AF', BC', DE', HL', SP
> ⚠️ **IX сохраняется** диспетчером P8000, но **AF, BC, DE, HL портятся**
> при каждом вызове `CALL #6006`.
---
## Карта памяти (физические страницы)
> Источник: `wc_api.h` — полная таблица `WC_PAGE_*`
| Страницы | Константа | Назначение |
|----------|-----------|------------|
| `#00` | `WC_PAGE_MTXPG` | Основной текстовый экран (TXT) |
| `#01` | `WC_PAGE_FONT0` | Шрифт основного TXT + Hrust/MegaLZ |
| `#02` | `WC_PAGE_CODE` | Код WC (ядро) |
| `#05` | `WC_PAGE_FAT_ENGINE` | FAT движок + стек + данные |
| `#08` | `WC_PAGE_BUFPG` | 2-й текстовый экран / буфер |
| `#09` | `WC_PAGE_FONT1` | Шрифт для 2-го TXT |
| `#0B–#13` | `WC_PAGE_CAT_LEFT` | Каталог левой панели (9 стр.) |
| `#14–#1C` | `WC_PAGE_CAT_RIGHT` | Каталог правой панели (9 стр.) |
| `#1D` | `WC_PAGE_RESB` | Буфер сохранения окон (RESB) |
| `#1E` | `WC_PAGE_CPPG1` | Позиции левой панели |
| `#1F` | `WC_PAGE_CPPG2` | Позиции правой панели |
| **#20–#5F** | **`WC_PAGE_TVBPG`** | **Мегабуфер (64 стр. = 1 МБ)**, он же видео: |
| `#20–#2F` | `WC_PAGE_GV1` | — видеобуфер 1 (16 стр.) |
| `#30–#3F` | `WC_PAGE_GV2` | — видеобуфер 2 |
| `#40–#4F` | `WC_PAGE_GV3` | — видеобуфер 3 |
| `#50–#5F` | `WC_PAGE_GV4` | — видеобуфер 4 |
| `#60` | `WC_PAGE_PLHPG` | Заголовки плагинов |
| **#61–#A0** | **`WC_PAGE_PLGPG`** | **Код/данные плагинов (до 64 стр.)** |
| `#A1–#BF` | `WC_PAGE_TAP_START` | TAP-область (31 стр. = 496 КБ) |
| `#C0–#E7` | `WC_PAGE_VDOS_RAM` | vDOS RAM-диск (40 стр. = 640 КБ) |
| `#E8–#EF` | `WC_PAGE_VDOS_FREE` | Свободные страницы |
| `#F0` | `WC_PAGE_PNLPG` | Поток левой панели |
| `#F1` | `WC_PAGE_PNL_RIGHT` | Поток правой панели |
| `#F2` | `WC_PAGE_INIPG` | INI-поток (плагин #00) |
| `#F3` | `WC_PAGE_STRPG` | Поток плагина #01 |
| `#F4–#F7` | — | SPG loader, boot pages (5,2), panel paths |
| `#F8–#FB` | `WC_PAGE_VROM_START` | Виртуальный ROM (4 стр.) |
| `#FC–#FF` | — | vDOS: таблицы, драйверы, трапы |
> **Плагин видит:** свои заказанные страницы (0..N) через `wc_mngc_pl()` /
> `wc_mng0_pl()` / `wc_mng8_pl()`. Физическая нумерация скрыта — WC
> маппит логическую страницу N плагина в `WC_PAGE_PLGPG + N`.
---
## Поддерживаемые устройства хранения
| Код | Константа | Устройство |
|-----|-----------|------------|
| 0 | `WC_DEV_SD_ZC` | SD-карта (Z-Controller) |
| 1 | `WC_DEV_IDE_NEMO_M` | IDE Nemo — Master |
| 2 | `WC_DEV_IDE_NEMO_S` | IDE Nemo — Slave |
| 3 | `WC_DEV_SD_NGS` | SD-карта (NeoGS) |
| 4 | `WC_DEV_IDE_SMUC_M` | IDE SMUC — Master |
| 5 | `WC_DEV_IDE_SMUC_S` | IDE SMUC — Slave |
| 6 | `WC_DEV_SD2_ZC` | Вторая SD (Z-Controller SD2) |
---
## Работа со страницами памяти
### Логическая нумерация
Плагин запрашивает N страниц в заголовке (offset +34). WC выделяет их
из пула `#61–#A0`. Плагин обращается по **логическому** номеру 0..N−1.
### Окна CPU и функции маппинга
| Окно | Адреса | Функция ASM | Функция C | Примечание |
|------|--------|-------------|-----------|------------|
| Win 0 | `#0000–#3FFF` | `MNG0_PL` (#4E) | `wc_mng0_pl(page)` | ⚠ Данные для WC API нельзя класть сюда |
| Win 2 | `#8000–#BFFF` | `MNG8_PL` (#4F) | `wc_mng8_pl(page)` | Код плагина — обычно стартовая страница |
| Win 3 | `#C000–#FFFF` | `MNGC_PL` (#00) | `wc_mngc_pl(page)` | ⚠ Сбрасывается после КАЖДОГО вызова WC API |
Специальные значения для `MNGC_PL`:
- `#FF` — шрифтовая страница (нельзя писать в `#E000–#FFFF`!)
- `#FE` — первый текстовый экран (с панелями WC)
### Резидент перехода (#6020)
Переключает страницу плагина и делает **безвозвратный** JP:
```c
// C: никогда не вернётся в вызывающий код
void wc_resident_jump(uint8_t page, uint16_t addr);
```
```asm
; ASM: A = страница (0..N-1), HL = адрес (#8000–#BFFF)
LD A,1
LD HL,#8000
CALL #6020 ; → WC переключает Win2 на стр.1, JP (HL)
```
### Резидент вызова (#6028)
Вызывает код в другой странице плагина **с возвратом**:
```c
void wc_resident_call(uint8_t page, uint16_t addr);
```
```asm
; ASM: A = страница, HL = адрес. Сохраняет всё кроме A,HL.
LD A,2
LD HL,#A000
CALL #6028 ; → WC переключает, CALL (HL), возвращает стр. назад
```
> Добавлено в WC v1.09 (31.07.2024). До этого многостраничные плагины
> требовали прямой OUT в порт Page2 (#12AF).
---
## Важные замечания
1. **При входе в плагин может быть любой экран!** Обязательно вызывайте
`GEDPL` (#0F) / `wc_gedpl()` для переключения на текстовый режим.
2. **Вызывать GEDPL лучше после анализа файла**, когда убедились что файл
подходит для плагина (и вернули `wc_exit_code = 1` если нет).
3. **Обязательно сохраняйте IX** при входе в плагин, если планируете
использовать файловые функции.
4. **⚠ Win3 (#C000) сбрасывается после каждого вызова WC API!**
Внутри `CALL #6006` WC переключает окно `#C000` на текстовую страницу
и **не восстанавливает** его. Если плагин работал через `mngcvpl()` /
`mngc_pl()`, он **обязан** повторить маппинг после любого UI-вызова
(`gedpl`, `prwow`, `txtpr`, `rresb`, `load512` и т.д.).
5. **GEDPL затирает #1000–#1447 в окне 0** — нельзя хранить данные в этой области.
6. **Буферы и строки для файловых функций** (`load512`, `fentry`, `findnext`,
и т.д.) **обязаны** быть в `#8000–#FFFF`. WC ремапит `#0000–#7FFF`
под FAT-движок внутри каждого файлового вызова.
7. **FindNext портит #0000–#1FFF** на странице драйвера — не хранить
данные там при итерации по каталогу.
8. **WC API портит регистры:** AF, BC, DE, HL, IX. Сохраняет: IY, AF',
BC', DE', HL', SP. Это верно для **всех** функций через `CALL #6006`.
9. **Стек не настраивается** — плагин использует стек WC (в Win 1, `#4000–#7FFF`).
Стек WC достаточно глубокий для обычных плагинов, но рекурсия требует осторожности.
---
## Горячие клавиши WC (для пользователя)
> Источник: `DiskRef/Help.txt`
| Клавиша | Действие |
|---------|----------|
| ENTER | Войти в каталог / запустить .WMF / просмотреть файл |
| F3 | Встроенный просмотрщик (TXT/HEX/TGA/SCR/MGS, до 1 МБ) |
| SHIFT+F3/SHIFT+ENTER | Меню выбора вьювера (плагин с `WC_PCOND_VIEWER`) |
| F2 | Пользовательское меню (из `wc.mnu`) |
| F4 | Редактирование / выбор текстового редактора |
| F5 | Копирование отмеченных файлов в другую панель |
| F6 | Переименование файла/каталога |
| F7 | Создание каталога |
| F8 / DEL | Удаление отмеченных файлов |
| F9 | Настройки INI (авто-создаёт каталог WC на загрузочном устройстве) |
| F10 | Меню запуска/удаления плагинов |
| TAB | Переключение между панелями |
| SPACE / INSERT | Отметить файл/каталог |
| * | Инвертировать выделение |
| ALT+F1/F2 | Выбор диска для левой/правой панели |
| CTRL+F3 | Сортировка по имени |
| CTRL+F4 | Сортировка по расширению |
| CTRL+F6 | Сортировка по размеру |
| CTRL+R | Обновить каталог |
| CTRL+A | Выделить все объекты |
| CTRL+SHIFT | Переключение ENG/RUS |
---
## Примеры плагинов
В каталоге `DiskRef/WC/` находятся скомпилированные плагины WC:
| Файл | Назначение |
|------|------------|
| `GSPLAYER.WMF` | Плеер для GS (MP3, MOD, WAV) |
| `WPLAYER.WMF` | Музыкальный плеер с плейлистами |
| `VGMPLAY.WMF` | VGM плеер (OPL3/AY/SAA) — этот проект |
| `TXTEDIT.WMF` | Текстовый редактор с подсветкой синтаксиса |
| `BMPV.WMF` | Просмотрщик BMP |
| `GIFVIEW.WMF` | Просмотрщик GIF |
| `ILBMVIEW.WMF` | Просмотрщик IFF/ILBM |
| `FTVIEW.WMF` | Просмотрщик FT-шрифтов |
| `MOUNTER.WMF` | Монтирование TRD/TAP/FDI образов |
| `ROM_PROG.WMF` | Программатор ROM/Flash |
| `SCRSAVER.WMF` | Скринсейвер (тип вызова `WC_PCOND_TIMER`) |
| `CLOCK.WMF` | Часы |
| `SETIME.WMF` | Установка времени |
| `COMPILER.WMF` | Компилятор |
| `RE.WMF` | Ассемблер/отладчик |
---
## Ссылки на другие разделы документации
- [02_Plugin_Header_Structure.md](02_Plugin_Header_Structure.md) — формат 512-байтного заголовка
- [03_WC_API_Reference.md](03_WC_API_Reference.md) — полный справочник функций
- [04_TSConfig_Platform.md](04_TSConfig_Platform.md) — аппаратная платформа, порты, регистры
- [06_Example_Plugin.md](06_Example_Plugin.md) — примеры плагинов на ASM и SDCC C