Files
VGMPLAY.WMF/doc/06_Example_Plugin.md

476 lines
13 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.
# Пример простого плагина для WC
## Минимальный плагин "Hello World"
Этот пример показывает базовую структуру плагина для Wild Commander.
```asm
;---------------------------------------
; Hello World Plugin
; Простейший пример плагина для WC
;---------------------------------------
ORG #6000
; Константы WC API
WLD EQU #6006 ; Точка вызова API
ABT EQU #6004 ; Флаг ESC
ENT EQU #6005 ; Флаг ENTER
;---------------------------------------
; ЗАГОЛОВОК ПЛАГИНА (512 байт)
;---------------------------------------
; +0: Резерв (16 байт)
DS 16
; +16: Идентификатор (16 байт)
DB "WildCommanderMDL"
; +32: Версия формата
DB #10
; +33: Резерв
DB 0
; +34: Количество страниц
DB 1 ; Плагин занимает 1 страницу
; +35: Стартовая страница
DB 0 ; Запускаться будет со страницы 0
; +36: Блоки данных (6 x 2 байта)
DB 0,1 ; Блок 0: код, 1x512 байт
DB 0,0
DB 0,0
DB 0,0
DB 0,0
DB 0,0
; +48: Резерв (15 байт)
DS 15
; +63: Флаги расширений
DB 0 ; Стандартный режим
; +64: Блок расширений (32 x 3 байта)
DS 32*3 ; Не используем расширения
; +160: Конец блока расширений
DB 0
; +161: Максимальный размер файла (4 байта)
DW #FFFF,#FFFF ; Без ограничений
; +165: Имя плагина (32 байта)
DB "Hello World Plugin "
; +197: Тип условий вызова
DB #03 ; Из меню (F10)
; +198: Текст для F2/F4 (6 байт)
DS 6
; +204: Текст для меню вьювера (24 байта)
DS 24
; +228: Резерв (32 байта)
DS 32
; +260: Параметры INI (252 байта)
DB 0 ; Нет параметров
DS 251
;---------------------------------------
; КОД ПЛАГИНА
;---------------------------------------
ORG #8000,#6200
;---------------------------------------
; Точка входа плагина
;---------------------------------------
PLUGIN:
; Сохранить входные параметры
LD (SAVE_IX),IX
LD (SAVE_HL),HL
LD (SAVE_DE),DE
LD (SAVE_BC),BC
LD (SAVE_A),A
; Восстановить текстовый режим
CALL GEDPL
; Показать окно
LD IX,WINDOW
CALL PRWOW
; Вывести текст
LD HL,TEXT
LD DE,#0102 ; Y=1, X=2
LD BC,12 ; Длина
CALL PRSRW
; Применить цвет
EXA
LD A,%00001111 ; Синий на белом
CALL PRIAT
EXA
; Главный цикл
MAIN_LOOP:
EI
HALT ; Ждем прерывание
; Проверка ESC
CALL ESC_KEY
JR Z,MAIN_LOOP
; Выход
LD IX,WINDOW
CALL RRESB ; Стереть окно
XOR A ; A=0: просто выход
RET
;---------------------------------------
; WC API функции
;---------------------------------------
PRWOW: ; Вывод окна
LD A,1
JP WLD
RRESB: ; Стирание окна
LD A,2
JP WLD
PRSRW: ; Печать строки
LD A,3
JP WLD
PRIAT: ; Установка цвета
EXA
LD A,4
JP WLD
ESC_KEY: ; Проверка ESC
LD A,#17
JP WLD
GEDPL: ; Восстановление TXT режима
LD A,#0F
JP WLD
;---------------------------------------
; Данные
;---------------------------------------
; Структура окна
WINDOW:
DB 0 ; Флаги
DB 0 ; Маска курсора
DB 255 ; X (центр)
DB 255 ; Y (центр)
DB 30 ; Ширина
DB 5 ; Высота
DB %01111111 ; Цвет (белый на синем)
DB 0 ; Резерв
DW #0000 ; Буфер
DB 0,0 ; Разделители
; Текст
TEXT:
DB "Hello World!"
; Сохраненные регистры
SAVE_A: DB 0
SAVE_BC: DW 0
SAVE_DE: DW 0
SAVE_HL: DW 0
SAVE_IX: DW 0
END
```
## Компиляция
Используйте sjasm для компиляции:
```batch
sjasm hello.asm hello.wmf
```
## Пояснения к коду
### Структура
1. **Заголовок (#6000-#61FF)** - 512 байт метаданных
2. **Код (#8000+)** - исполняемый код плагина
### Ключевые моменты
1. **GEDPL обязателен!** Вызывайте его в начале, чтобы восстановить текстовый режим.
2. **Структура окна** должна быть в памяти плагина (#8000-#BFFF).
3. **Главный цикл** должен включать:
- `EI` - разрешить прерывания
- `HALT` - ждать прерывание (экономит энергию)
- Проверку клавиш
4. **Выход** через `RET` с кодом в A.
## Пример с файлом
```asm
;---------------------------------------
; Text Viewer Plugin - просмотр текстовых файлов
;---------------------------------------
ORG #6000
WLD EQU #6006
; ... Заголовок плагина ...
; +64: Расширения
DB "TXT" ; .txt файлы
DB "DOC" ; .doc файлы
DS 30*3
; +197: Тип вызова
DB #00 ; По расширению
ORG #8000,#6200
;---------------------------------------
; Точка входа
;---------------------------------------
PLUGIN:
; Проверить тип вызова
CP #00 ; По расширению?
JR Z,VIEW_FILE
; Другой тип вызова - не наш файл
LD A,1 ; Файл не опознан
RET
VIEW_FILE:
; Сохранить параметры
LD (FILESIZE),HL
LD (FILESIZE+2),DE
LD (FILENAME),BC
LD (PANEL_IX),IX
; Восстановить TXT режим
CALL GEDPL
; Загрузить начало файла
LD HL,BUFFER
LD B,16 ; 16 блоков = 8KB
CALL LOAD512
; Показать окно
LD IX,WINDOW
CALL PRWOW
; Вывести текст
LD HL,BUFFER
LD DE,#0101
LD BC,1000
CALL PRSRW
; Цикл просмотра
VIEW_LOOP:
EI
HALT
CALL ESC_KEY
JR Z,VIEW_LOOP
; Выход
LD IX,WINDOW
CALL RRESB
XOR A
RET
;---------------------------------------
; WC API
;---------------------------------------
LOAD512: LD A,#30
JP WLD
PRWOW: LD A,1
JP WLD
RRESB: LD A,2
JP WLD
PRSRW: LD A,3
JP WLD
ESC_KEY: LD A,#17
JP WLD
GEDPL: LD A,#0F
JP WLD
;---------------------------------------
; Данные
;---------------------------------------
WINDOW:
DB 0
DB 0
DB 0 ; X=0
DB 0 ; Y=0
DB 80 ; На весь экран
DB 25
DB %01111111
DB 0
DW #0000
DB 0,0
BUFFER: DS 8192 ; Буфер для файла
FILENAME: DW 0
FILESIZE: DS 4
PANEL_IX: DW 0
END
```
## Следующий шаг
Изучите примеры в каталоге PlugRef/:
- **wild_player/** - полноценный музыкальный плеер
- **gsplayer/** - плеер для GS
- **txt_editor/** - редактор текста
---
## Минимальный плагин на SDCC C
> Компилятор: SDCC 4.x, ключи `--opt-code-size -mz80 --sdcccall 1`
> Источник соглашений: `src_sdcc/inc/wc_api.h`
```c
/*
* hello_plugin.c — минимальный WC-плагин на SDCC C
*
* Сборка:
* sdcc --opt-code-size -mz80 --sdcccall 1 --code-loc 0x8000 \
* --data-loc 0xB800 -I inc hello_plugin.c wc_api.s crt0.s
* makebin -s 16384 hello_plugin.ihx hello_plugin.bin
* pack hello_plugin.bin HELLO.WMF # добавить заголовок WMF
*/
#include "inc/wc_api.h"
#include "inc/types.h"
/* ─────────────────────────────────────────────────
* DATA: размещается в #B800+ (--data-loc 0xB800)
* ──────────────────────────────────────────────── */
/* Окно в секции DATA (адрес в #8000–#BFFF — обязательно!) */
static wc_window_t g_win;
/* Буфер вывода */
static char g_buf[64];
/*
* wc_exit_code — специальная переменная: WC читает A при
* выходе из main(). Значения: 0=ESC, 2=след. файл, 3=reload dir.
*/
uint8_t wc_exit_code;
/* ─────────────────────────────────────────────────
* CODE: размещается в #8000+ (--code-loc 0x8000)
* ──────────────────────────────────────────────── */
int main(void) {
/* 1. Восстановить дисплей WC (TXT-режим, панели)
* ⚠ Затирает #1000–#1447 в окне 0 */
wc_gedpl();
/* 2. Настроить окно */
g_win.type = WC_WIN_SINGLE; /* однолинейная рамка */
g_win.cur_mask = 0x07;
g_win.x = 255; /* 255 = по центру */
g_win.y = 255;
g_win.width = 44;
g_win.height = 6;
g_win.color = WC_COLOR(WC_BLACK, WC_BRIGHT_WHITE);
g_win.buf_addr = 0x0000; /* 0 = автосохранение фона в RESB */
/* 3. Нарисовать окно */
wc_prwow(&g_win);
/* 4. Вывести текст (разметка WC: 0x0E = центрирование) */
wc_txtpr(&g_win, "\x0eHello from C plugin!", 1, 1);
wc_txtpr(&g_win, "\x0e\x03Press ESC to exit", 3, 1);
/* 5. Главный цикл */
while (1) {
if (wc_key_esc()) break;
if (wc_key_enter()) break;
/* пример: цикл ожидания клавиши без блокировки */
/* uint8_t k = wc_kbscn(WC_KBSCN_NORMAL); */
}
/* 6. Убрать окно и восстановить дисплей */
wc_rresb(&g_win);
wc_gedpl();
/* 7. Код выхода */
wc_exit_code = 0; /* 0 = нормальный выход */
return 0;
}
```
### Полезные паттерны
**Загрузка файла в мегабуфер:**
```c
void load_file_to_megabuf(void) {
uint8_t page = 0;
uint8_t rc;
wc_stream(WC_STREAM_ROOT); // открыть файловый поток
// wc_fentry() уже вызван WC перед стартом плагина
do {
/* ⚠ После любого wc_* Win3 сбрасывается — переключаем снова */
wc_mngcvpl(page);
rc = wc_load512(0xC000, 32); // читать 16 КБ
page++;
} while (rc == 0 && page < 64);
/* rc == WC_EOF (0x0F) = конец файла, rc != 0 && != 0x0F = ошибка */
}
```
**Установка собственного ISR:**
```c
extern void my_isr(void); // ISR написан на ASM в asm/isr.s
void setup_isr(void) {
wc_int_pl(WC_INT_PLUGIN); // режим плагина
wc_int_pl_handler((uint16_t)my_isr); // задать адрес
/* Теперь my_isr() вызывается ~1367 раз/сек */
}
void restore_isr(void) {
wc_int_pl(WC_INT_DISABLE_ALL); // вернуть управление WC
}
```
**Переключение CPU на 14 МГц:**
```c
wc_turbopl(WC_TURBO_CPU, 0x02); // 14 МГц
// ...работа...
wc_turbopl(WC_TURBO_RESTORE, 0); // восстановить
```