Add OPL4 playback and analyzer support
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
# Пример простого плагина для 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); // восстановить
|
||||
```
|
||||
Reference in New Issue
Block a user