Files
VGMPLAY.WMF/doc/06_Example_Plugin.md
T

13 KiB
Raw Blame History

Пример простого плагина для WC

Минимальный плагин "Hello World"

Этот пример показывает базовую структуру плагина для Wild Commander.

;---------------------------------------
; 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 для компиляции:

sjasm hello.asm hello.wmf

Пояснения к коду

Структура

  1. Заголовок (#6000-#61FF) - 512 байт метаданных
  2. Код (#8000+) - исполняемый код плагина

Ключевые моменты

  1. GEDPL обязателен! Вызывайте его в начале, чтобы восстановить текстовый режим.

  2. Структура окна должна быть в памяти плагина (#8000-#BFFF).

  3. Главный цикл должен включать:

    • EI - разрешить прерывания
    • HALT - ждать прерывание (экономит энергию)
    • Проверку клавиш
  4. Выход через RET с кодом в A.

Пример с файлом

;---------------------------------------
; 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

/*
 * 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;
}

Полезные паттерны

Загрузка файла в мегабуфер:

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:

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 МГц:

wc_turbopl(WC_TURBO_CPU, 0x02);    // 14 МГц
// ...работа...
wc_turbopl(WC_TURBO_RESTORE, 0);   // восстановить