381 lines
21 KiB
Markdown
381 lines
21 KiB
Markdown
# 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
|