Files

1884 lines
118 KiB
C
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.
/**
* @file wc_api.h
* @brief Полное API Wild Commander (WC) для SDCC Z80
* @version 5.0
*
* Самодостаточная библиотека: зависит только от types.h.
* Реализация: asm/wc_api.s (ASM-обёртки вокруг CALL 0x6006).
*
* ══════════════════════════════════════════════════════════════════════
* МЕХАНИЗМ ВЫЗОВА
* ══════════════════════════════════════════════════════════════════════
*
* Единственная точка входа ко всем функциям WC:
*
* CALL 0x6006 ; A = номер функции, остальные регистры — аргументы
*
* WC минимально гробит: AF, BC, DE, HL, IX (при каждом вызове!)
* WC сохраняет: IY, AF', BC', DE', HL', SP
*
*
* ══════════════════════════════════════════════════════════════════════
* СОГЛАШЕНИЕ ВЫЗОВА (sdcccall(1), SDCC 4.x)
* ══════════════════════════════════════════════════════════════════════
*
* Первый аргумент: uint8 → A
* uint16 / ptr → HL (ВАЖНО: именно HL, не DE!)
* Второй аргумент: uint16 / ptr → DE
* Остальные: на стек, крайний правый аргумент первым (push)
* callee обязан очистить стек сам (callee-cleans)
* Возврат: uint8 → A; uint16 → HL
*
* ══════════════════════════════════════════════════════════════════════
* СТАРТОВЫЕ УСЛОВИЯ ПЛАГИНА
* ══════════════════════════════════════════════════════════════════════
*
* WC вызывает плагин как CALL #8000. На входе:
*
* A = тип вызова:
* 0x00 — по расширению файла
* 0x01 — при загрузке плагина (по ENTER)
* 0x02 — по таймеру скринсейвера / из меню F10
* 0x03 — из меню запуска плагинов (F10)
* 0x04 — из меню выбора просмотрщика (Shift+F3 / Shift+Enter)
* 0x12 — по F2
* 0x14 — по F4
* [DE,HL] = размер файла (DE = старший, HL = младший; 32-бит)
* BC = адрес имени файла: name(1−255), 0x00 (только для 0x00/0x04/0x14)
* IX = адрес структуры активной панели (нужен для некоторых функций!)
*
* Дополнительно для 0x00/0x04/0x12/0x14:
* A' — номер расширения (0–31), по которому файл передан плагину
* DE' — количество файлов/каталогов в активной панели (включая "..")
* HL' — номер объекта из панели (от 1)
* если HL' == DE', переход на следующий файл не имеет смысла
*
* Значение A при выходе из плагина (записывать в wc_exit_code):
* 0 — выход (ESC)
* 1 — файл не распознан, передать другому просмотрщику/плагину
* (только для типа вызова 0x00)
* 2 — перейти к следующему файлу в каталоге (только 0x00)
* 3 — перечитать каталог (не для типов 0x01, 0x02)
* 4 — перейти к предыдущему файлу в каталоге (только 0x00)
*
* ══════════════════════════════════════════════════════════════════════
* КАРТА АДРЕСНОГО ПРОСТРАНСТВА CPU (4 окна × 16 КБ)
* ══════════════════════════════════════════════════════════════════════
*
* TSConfig даёт CPU 64 КБ через 4 переключаемых окна, каждое может
* отображать любую из 256 физических страниц (по 16 КБ = 4 МБ всего).
* Текущие номера страниц читаются из #6000–#6003 (WC_SYS_PG0–PGC).
*
* Адреса CPU Окно Назначение при работе плагина
* ─────────────── ────── ──────────────────────────────────────────
* #0000 – #3FFF Win 0 ★ Плагин может переключать через mng0_pl()
* По умолчанию: TXT-буфер / системная стр.
* WC API: сохраняет и восстанавливает при
* каждом CALL #6006 (P8000 dispatch)
* ⚠ GEDPL затирает область #1000–#1447!
* ⚠ Нельзя хранить здесь параметры для WC API
* (SOW-структуры, строки, буферы файлов),
* т.к. WC ремапит это окно внутри вызовов.
* ✓ Можно использовать для своих данных МЕЖДУ
* вызовами WC (например, VGM-буферы).
*
* #4000 – #7FFF Win 1 ✖ СИСТЕМНАЯ ОБЛАСТЬ — НЕ ТРОГАТЬ!
* Содержит: FAT engine (#4000+), стек WC,
* ядро WC, точка входа API (#6006).
* Плагин НЕ имеет права ремапить это окно.
*
* #8000 – #BFFF Win 2 ★ КОД И ДАННЫЕ ПЛАГИНА
* WC загружает сюда .WMF-файл при запуске.
* Точка входа: CALL #8000.
* WC API: сохраняет/восстанавливает страницу
* при каждом вызове P8000.
* ✓ Структуры SOW, строки для UI-функций,
* буферы файловых операций — всё здесь.
* Рекомендуемая раскладка SDCC:
* #8000–#B7FF CODE (до 14 КБ)
* #B800–#BFFF DATA (до 2 КБ)
*
* #C000 – #FFFF Win 3 ★ Плагин может переключать через mngc_pl()
* ⚠ WC API СБРАСЫВАЕТ это окно на TXT-стр.
* (#00) при КАЖДОМ вызове P8000 и
* НЕ ВОССТАНАВЛИВАЕТ после возврата!
* → После любого вызова UI-функции (gedpl,
* prwow, txtpr, rresb...) плагин обязан
* заново вызвать mngc_pl() если ему нужны
* свои данные в #C000–#FFFF.
* ✓ Удобно для загрузки данных:
* mngcvpl(N) → load512(0xC000, 32)
* загружает 16 КБ в VPL-страницу N.
* ✓ Через mng0_pl() потом можно прочитать
* ту же страницу в #0000–#3FFF.
*
* ══════════════════════════════════════════════════════════════════════
* МЕХАНИЗМ P8000 DISPATCH (внутренний CALL #6006)
* ══════════════════════════════════════════════════════════════════════
*
* При каждом вызове WC API (CALL #6006) происходит:
*
* 1. Сохранение текущей страницы Win 0 → переменная PG0BU
* 2. Сохранение IX → переменная IXBU
* 3. Установка Win 3 (#C000) = TXT-страница (#00 / MTXPG)
* 4. Выполнение запрошенной функции WC
* 5. Восстановление Win 0 из PG0BU
* 6. Восстановление IX из IXBU
* 7. RET к плагину
*
* Важные следствия:
* • Win 0: после вызова — та же страница, что была до вызова ✓
* • Win 3: после вызова — ВСЕГДА TXT-страница (#00) ✗
* • IX: сохраняется через вызов (но A, BC, DE, HL — портятся!)
*
* ══════════════════════════════════════════════════════════════════════
* БУФЕР СОХРАНЕНИЯ ОКОН (RESB — страница #1D)
* ══════════════════════════════════════════════════════════════════════
*
* WC использует страницу #1D (WC_PAGE_RESB) как стек для сохранения
* фона под окнами. Размер — 16 КБ, что хватает для нескольких окон.
*
* Поле buf_addr в структуре SOW (wc_window_t):
*
* 0x0000 → "Автоматическое выделение" — WC при вызове prwow()
* сохранит фон в RESB-стек и запишет реальный адрес.
* При rresb() — фон будет корректно восстановлен.
* ЭТО РЕКОМЕНДУЕМОЕ ЗНАЧЕНИЕ. Все штатные плагины
* WC (gsplayer, mounter, clock и т.д.) используют 0.
*
* 0xFFFF → "Не сохранять фон" — prwow() нарисует окно поверх,
* но rresb() не сможет восстановить содержимое.
* Используется только если фон не нужен (fullscreen).
*
* иное → ручной адрес буфера (не рекомендуется).
*
* ══════════════════════════════════════════════════════════════════════
* GEDPL — ПОВРЕЖДЕНИЕ ОБЛАСТИ #1000–#1447
* ══════════════════════════════════════════════════════════════════════
*
* Функция wc_gedpl() (восстановление дисплея WC) при работе
* затирает область #1000–#1447 (1096 байт) в окне 0.
* Это используется для внутренних буферов восстановления палитры
* и атрибутов текстового экрана.
*
* Последствия для плагина:
* • Нельзя хранить критичные данные по адресам #1000–#1447
* • Если плагин использует #0000–#3FFF через mng0_pl(),
* данные в этом диапазоне будут повреждены после gedpl()
* • Безопасные области в Win 0: #0000–#0FFF и #1448–#3FFF
* (но только МЕЖДУ вызовами WC API, не во время!)
*
* ══════════════════════════════════════════════════════════════════════
* АДРЕСНЫЕ ОГРАНИЧЕНИЯ ПАРАМЕТРОВ WC API
* ══════════════════════════════════════════════════════════════════════
*
* Структуры и буферы, передаваемые в функции WC, ОБЯЗАНЫ находиться
* в определённых адресных диапазонах:
*
* Файловые функции (load512, save512, fentry, mkfile, findnext...):
* Буферы и имена файлов: #8000–#FFFF
* (WC ремапит Win 0 и Win 1 для FAT — #0000–#7FFF недоступен!)
*
* UI функции (prwow, txtpr, rresb, scrlwow, mezz...):
* Структура SOW: #8000–#BFFF (предпочтительно)
* Строки текста: #8000–#BFFF
* (Win 3 сброшен на TXT — #C000+ тоже допустим, но это TXT-стр.)
*
* Общее правило: размещайте ВСЕ данные WC API в #8000–#BFFF.
* Это единственный диапазон, гарантированно стабильный во время
* и после любых вызовов WC API.
*
* ══════════════════════════════════════════════════════════════════════
* МНОГОСТРАНИЧНЫЕ ПЛАГИНЫ (до 64 страниц)
* ══════════════════════════════════════════════════════════════════════
*
* Плагин может занимать до 64 страниц (#61–#A0) в пуле WC.
* Для переключения между страницами используются резиденты:
*
* Резидент перехода (#6020, PLRESAD):
* A=номер страницы (0=первая), HL=адрес → JP (HL)
* Точка невозврата: управление передаётся навсегда.
*
* Резидент вызова (#6028, PLRESCALL):
* A=номер страницы, HL=адрес → CALL (HL), затем возврат
* Сохраняет все регистры кроме A и HL.
*
* Функции mng0_pl(page) и mngc_pl(page) принимают ЛОГИЧЕСКИЙ
* номер страницы (от 0), WC добавляет базу PLGPG (#61) автоматически.
*
* Типичная архитектура многостраничного плагина:
* Стр. 0: код + данные (постоянно в #8000–#BFFF)
* Стр. 1+: данные, загружаемые в #0000 через mng0_pl()
* или в #C000 через mngc_pl() / mngcvpl()
*
* ══════════════════════════════════════════════════════════════════════
* РЕКОМЕНДУЕМАЯ РАСКЛАДКА ПАМЯТИ SDCC-ПЛАГИНА
* ══════════════════════════════════════════════════════════════════════
*
* Адреса Содержимое
* ─────────────── ───────────────────────────────────────────────
* #0000 – #3FFF VGM/данные через mng0_pl() (только между
* вызовами WC! GEDPL затирает #1000–#1447)
* #4000 – #7FFF ✖ WC SYSTEM — не трогать
* #8000 – #B7FF _CODE: код плагина (до 14 КБ)
* #B800 – #BFFF _DATA: переменные, SOW, строки (до 2 КБ)
* #C000 – #FFFF Загрузка данных через mngcvpl()/mngc_pl()
* (сбрасывается WC после каждого API-вызова!)
*
* build.bat: sdcc --code-loc 0x8000 --data-loc 0xB800
*
* ══════════════════════════════════════════════════════════════════════
* СИСТЕМНЫЕ ПЕРЕМЕННЫЕ WC (читаются напрямую по фиксированным адресам)
* ══════════════════════════════════════════════════════════════════════
*
* #6000 [4] — номера страниц в окнах #0000, #4000, #8000, #C000
* #6004 [1] — ABT: 1 = был нажат ESC
* #6005 [1] — ENT: 1 = был нажат ENTER
* #6009 [2] — TMN: таймер INT (uint16_t, инкрементируется каждым INT)
* #600D [1] — CNFv: тип ЦАП/видео:
* 0=ШИМ/IDE, 1=3-bit VDAC, 2=4-bit VDAC,
* 3=5-bit VDAC, 7=VDAC2 (FT812)
* #600E [1] — HEI: высота текстового экрана в строках (25 / 30 / 36)
*
* ══════════════════════════════════════════════════════════════════════
* СТРУКТУРА SOW — Structure Of Window (16 байт)
* ══════════════════════════════════════════════════════════════════════
*
* Поле type (+0):
* [7] = 1 → тень вокруг окна
* [6] = 1 → курсор во всю ширину окна
* [5] = зарезервировано
* [4] = 1 → шапка-заголовок (инверсная строка вверху)
* [3:0] = стиль рамки:
* 0 — без рамки (стандартное окно, рамка 1-го типа)
* 1 — с заголовками и текстом (рамка 1-го типа, без курсора)
* 2 — с заголовками и текстом + курсор (рамка 1-го типа)
* 3 — стандартное окно, рамка 2-го типа
* 4 — с заголовками и текстом (рамка 2-го типа, без курсора)
* 5 — с заголовками и текстом + курсор (рамка 2-го типа)
* 6 — стандартное окно, рамка 3-го типа
* 7 — с заголовками и текстом (рамка 3-го типа, без курсора)
* 8 — с заголовками и текстом + курсор (рамка 3-го типа)
*
* Примечание: WC заменяет x=255→реальное значение при первом рисовании;
* аналогично y=255, width=0, height=0.
*
* Поле buf_addr (+8): — см. раздел «БУФЕР СОХРАНЕНИЯ ОКОН (RESB)»
* 0x0000 — авто-выделение в RESB-стеке (РЕКОМЕНДУЕТСЯ!)
* 0xFFFF — не сохранять фон (нельзя восстановить через rresb)
* иное — ручной адрес буфера (не рекомендуется)
*
* Линии-разделители (+10, +11):
* Задают Y-смещение от нижнего края окна.
* Если != 0, WC рисует горизонтальную линию внутри рамки.
*
* Поля +12–+15 зависят от стиля (type[3:0]):
* Стиль 0/3/6 (стандартное окно):
* +12 — позиция курсора (от 1)
* +13 — нижний ограничитель курсора
* +14 — цвет курсора (накладывается по маске cur_mask)
* +15 — цвет окна под курсором
* Стиль 1/4/7 (заголовочное окно без курсора):
* +12,+13 — адрес строки верхнего заголовка (0 = не выводить)
* +14,+15 — адрес строки нижнего заголовка (0 = не выводить)
*
* Адресные ограничения: структура SOW должна находиться в
* #8000–#BFFF или #0000–#3FFF (недоступна из #C000!).
*
* ══════════════════════════════════════════════════════════════════════
* ТЕКСТОВАЯ РАЗМЕТКА (для TXTPR, MEZZ, PRWOW)
* ══════════════════════════════════════════════════════════════════════
*
* Строки могут содержать управляющие коды:
*
* 0x00 — конец строки и абзаца
* 0x00, 0x00 — разделитель сообщений (только для MEZZ)
* 0xFE, lo, hi — ссылка на строку по адресу lo|hi<<8
* (абзацы связывать бесполезно — выводит только первую строку)
* 0x01–0x08 — цвет символов (INK): 1=синий ... 7=белый, 8=чёрный
* 0x01–0x08, 0x01–0x08— цвет PAPER+INK (первый=фон, второй=символ)
* если оба одинаковы → ярко-фоновый INK
* 0x09 — инверсия цвета (PAPER ↔ INK)
* 0x0B, XX — сдвиг вправо на XX символов
* 0x0C, YY — сдвиг вниз на YY строк (X и цвет не трогает)
* 0x0D — перенос строки, INK → фоновый цвет
* 0x0E — центрирование строки (X сбрасывается)
* не работает если строка >= ширины окна
* или если есть ссылка (#FE)
*
* ══════════════════════════════════════════════════════════════════════
* АТРИБУТ ЦВЕТА (EGA-формат, 1 байт)
* ══════════════════════════════════════════════════════════════════════
*
* [7:4] = BACKGROUND (фон): 16 цветов (0–15)
* [3:0] = FOREGROUND (текст): 16 цветов (0–15)
*
* Кодировка YGRB (Bright, Green, Red, Blue):
* 0=чёрный, 1=синий, 2=красный, 3=пурпурный,
* 4=зелёный, 5=циановый, 6=жёлтый, 7=белый,
* 8=тёмно-серый, 9..15 = яркие варианты 1..7
*
* Используй макрос WC_COLOR(bg, fg) и константы WC_BLACK..WC_BRIGHT_WHITE
*
* ══════════════════════════════════════════════════════════════════════
* ПРИМЕР ИСПОЛЬЗОВАНИЯ
* ══════════════════════════════════════════════════════════════════════
*
* #include "wc_api.h"
*
* static wc_window_t win;
*
* void show_message(void) {
* win.type = WC_WIN_SINGLE; // однолинейная рамка
* win.cur_mask = 0x07;
* win.x = 255; win.y = 255; // по центру
* win.width = 40; win.height = 5;
* win.color = WC_COLOR(WC_BLACK, WC_BRIGHT_WHITE);
* win.buf_addr = 0xFFFF; // без сохранения фона
*
* wc_gedpl(); // восстановить дисплей WC
* wc_prwow(&win); // нарисовать окно
* wc_txtpr(&win, "Hello!", 1, 1); // вывести текст
* wc_key_wait_any(); // ждать любую клавишу
* wc_rresb(&win); // убрать окно
* }
*
* void load_data(void) {
* uint8_t rc;
* wc_mngcvpl(0); // VPL-страница 0 → #C000
* rc = wc_load512(0xC000, 32); // загрузить 16384 байт
* if (rc && rc != WC_EOF) { } // error
* }
*/
#ifndef WC_API_H
#define WC_API_H
#include "types.h"
/* ══════════════════════════════════════════════════════════════════════
* Константы
* ══════════════════════════════════════════════════════════════════════ */
#define WC_ENTRY 0x6006 /* адрес точки входа WC */
/* Стили рамки окна (type[3:0]): стандартные окна с курсором */
#define WC_WIN_NO_BORDER 0x00 /* без рамки, стандартное с курсором */
#define WC_WIN_SINGLE 0x01 /* однолинейная рамка, без курсора */
#define WC_WIN_DOUBLE 0x02 /* двойная рамка + курсор */
#define WC_WIN_TYPE3 0x03 /* рамка 2-го типа, с курсором */
#define WC_WIN_TYPE4 0x04 /* рамка 2-го типа, с заголовком/текстом, без курсора */
#define WC_WIN_TYPE5 0x05 /* рамка 2-го типа + курсор */
#define WC_WIN_TYPE6 0x06 /* рамка 3-го типа, с курсором */
#define WC_WIN_TYPE7 0x07 /* рамка 3-го типа, с заголовком/текстом, без курсора */
#define WC_WIN_TYPE8 0x08 /* рамка 3-го типа + курсор */
/* Флаги type[7:4] */
#define WC_WIN_HEADER 0x10 /* инверсная строка-заголовок вверху */
#define WC_WIN_WIDE_CURSOR 0x40 /* курсор на всю ширину окна */
#define WC_WIN_SHADOW 0x80 /* тень вокруг окна */
/* wc_yn() mode */
#define WC_YN_OK_CANCEL 0x01
#define WC_YN_YES_NO 0x02
#define WC_YN_POLL 0x00 /* опросить без ожидания */
#define WC_YN_EXIT 0xFF /* закрыть меню YN */
/* wc_kbscn() mode */
#define WC_KBSCN_NORMAL 0x00 /* учитывать SHIFT / CL / Lang */
#define WC_KBSCN_RAW 0x01 /* сырой скан-код */
/* wc_mngv_pl() mode */
#define WC_VIDEO_TXT 0x00 /* текстовый режим WC */
#define WC_VIDEO_BUF1 0x01 /* видеобуфер 1 */
#define WC_VIDEO_BUF2 0x02
#define WC_VIDEO_BUF3 0x03
#define WC_VIDEO_BUF4 0x04
/* wc_adir() mode */
#define WC_ADIR_SEEK_START 0x00 /* установить позицию на начало */
#define WC_ADIR_RESET_NEXT 0x01 /* сброс FindNext */
/* wc_stream() mode */
#define WC_STREAM_ROOT 0xFF /* корневой поток */
#define WC_STREAM_CLONE 0xFE /* клонировать текущий поток */
#define WC_STREAM_WCDIR 0xFD /* каталог WC */
/* 0x00, 0x01 = номер потока 0 или 1 */
/* wc_int_pl() mode */
#define WC_INT_DISABLE_ALL 0x00 /* отключить все прерывания WC */
#define WC_INT_NO_TIME 0x01 /* отключить таймерное INT WC */
#define WC_INT_NO_PS2 0x02 /* отключить PS/2 INT WC */
#define WC_INT_PLUGIN 0xFF /* установить обработчик плагина */
/* wc_turbopl() mode */
#define WC_TURBO_CPU 0x00 /* B=0: настройка CPU; C=частота */
#define WC_TURBO_AY 0x01 /* B=1: AY частота */
#define WC_TURBO_RESTORE 0xFF /* восстановить оригинальный режим */
/* Коды ошибок файловых операций */
#define WC_EOF 0x0F /* конец файла / конец цепочки (LOAD512) — не ошибка! */
/* Флаги типа файла (wc_fentry, wc_mkfile, wc_delfl, wc_rename) */
#define WC_FILE_FLAG 0x00 /* объект является файлом */
#define WC_DIR_FLAG 0x10 /* объект является каталогом */
/* Коды ошибок FAT-операций */
#define WC_ERR_LONG_NAME 1 /* длинное имя некорректно */
#define WC_ERR_SHORT_IDX 2 /* короткое имя требует индекса */
#define WC_ERR_LONG_EXISTS 3 /* длинное имя уже существует */
#define WC_ERR_SHORT_EXISTS 4 /* короткое имя уже существует */
#define WC_ERR_NOT_FOUND 8 /* источник не найден */
#define WC_ERR_NO_SPACE 16 /* нет свободного места */
#define WC_ERR_UNKNOWN 255 /* неизвестная ошибка */
/* Константы прокрутки wc_scrlwow (flags) */
#define WC_SCRL_DOWN 0x00 /* направление — вниз */
#define WC_SCRL_UP 0x01 /* направление — вверх */
/* правка/влево (0x02, 0x03) зарезервированы, не работают */
#define WC_SCRL_WITH_ATTRS 0x80 /* прокручивать атрибуты вместе с символами */
#define WC_SCRL_CLEAR_SRC 0x40 /* очищать исходную позицию после прокрутки */
/* [5:2] = шаг: 0=1 символ, 0xF=16 символов (сдвинуть на N+1) */
/* При step=0 и без флагов [7:2] — автоматически использует DMA */
/* ──────────────────────────────────────────────────────────────────────
* СТРАНИЦЫ ПАМЯТИ WC (физические номера страниц #00–#FF)
* ────────────────────────────────────────────────────────────────────── */
/* Текстовые экраны */
#define WC_PAGE_MTXPG 0x00 /* основной текстовый экран */
#define WC_PAGE_FONT0 0x01 /* шрифт основного TXT + Hrust/MegaLZ*/
#define WC_PAGE_BUFPG 0x08 /* второй текстовый экран / буфер */
#define WC_PAGE_FONT1 0x09 /* шрифт второго TXT */
/* Код WC */
#define WC_PAGE_CODE 0x02 /* основной код WC (#8000–#AFFF) */
#define WC_PAGE_FAT_ENGINE 0x05 /* FAT engine (#4000) + stack + код */
/* Каталоги панелей */
#define WC_PAGE_CTZPG 0x0B /* начало каталога LEFT панели */
#define WC_PAGE_CAT_LEFT 0x0B /* стр. #0B-#13 = 9 стр. */
#define WC_PAGE_CAT_RIGHT 0x14 /* стр. #14-#1C = 9 стр. */
#define WC_CAT_MAX_PAGES 9 /* макс. страниц на каталог */
#define WC_PAGE_RESB 0x1D /* буфер сохранения окон */
#define WC_PAGE_CPPG1 0x1E /* позиции каталога LEFT */
#define WC_PAGE_CPPG2 0x1F /* позиции каталога RIGHT */
/* Megabuffer / видеобуферы */
#define WC_PAGE_TVBPG 0x20 /* начало MEGABUFFER (64 стр.) */
#define WC_PAGE_TVEPG 0x5F /* конец MEGABUFFER */
#define WC_PAGE_GV1 0x20 /* видеобуфер 1 (16 стр.) */
#define WC_PAGE_GV2 0x30 /* видеобуфер 2 (16 стр.) */
#define WC_PAGE_GV3 0x40 /* видеобуфер 3 (16 стр.) */
#define WC_PAGE_GV4 0x50 /* видеобуфер 4 (16 стр.) */
#define WC_VIDEO_PAGES_PER_BUF 16 /* страниц в одном видеобуфере */
/* Плагины */
#define WC_PAGE_PLHPG 0x60 /* заголовки плагинов (96) */
#define WC_PAGE_PLGPG 0x61 /* начало кода плагинов (97) */
#define WC_PAGE_PLEPG 0xA0 /* конец кода плагинов (160) */
#define WC_PLUGIN_MAX_PAGES 64 /* макс. страниц для всех плагинов */
/* TAP */
#define WC_PAGE_TAP_START 0xA1 /* смонтированный TAP-файл */
#define WC_PAGE_TAP_END 0xBF /* конец TAP (31 стр.) */
/* vDOS RAM Disk */
#define WC_PAGE_VDOS_RAM 0xC0 /* начало RAM-диска vDOS */
#define WC_PAGE_VDOS_END 0xE7 /* конец RAM-диска (40 стр. = 640 КБ) */
#define WC_PAGE_VDOS_FREE 0xE8 /* свободные стр. (#E8–#EF) */
/* Системные потоки */
#define WC_PAGE_PNLPG 0xF0 /* поток ЛЕВОЙ панели */
#define WC_PAGE_PNL_RIGHT 0xF1 /* поток ПРАВОЙ панели */
#define WC_PAGE_INIPG 0xF2 /* поток плагина #00 / INI */
#define WC_PAGE_STRPG 0xF3 /* поток плагина #01 */
#define WC_PAGE_DBPG 0xF4 /* буфер SPG-загрузчика */
#define WC_PAGE_D5PG 0xF5 /* загрузочная стр. 5 */
#define WC_PAGE_D2PG 0xF6 /* загрузочная стр. 2 */
#define WC_PAGE_RPPG 0xF7 /* пути панелей + флаг активной */
/* vDOS системные */
#define WC_PAGE_VROM_START 0xF8 /* Virtual ROM (4 стр.) */
#define WC_PAGE_VROM_END 0xFB
#define WC_PAGE_VDOS_TABLES 0xFC /* vDOS Tables (#FC–#FD) */
#define WC_PAGE_VDOS_DRV 0xFE /* vDOS Drivers */
#define WC_PAGE_VDOS_TRAPS 0xFF /* vDOS Traps */
/* ──────────────────────────────────────────────────────────────────────
* СИСТЕМНАЯ ОБЛАСТЬ WC (#6000–#602F)
* ────────────────────────────────────────────────────────────────────── */
#define WC_SYS_PG0 0x6000 /* [1] страница в окне #0000 */
#define WC_SYS_PG4 0x6001 /* [1] страница в окне #4000 */
#define WC_SYS_PG8 0x6002 /* [1] страница в окне #8000 */
#define WC_SYS_PGC 0x6003 /* [1] страница в окне #C000 */
#define WC_SYS_ABT 0x6004 /* [1] ABT: флаг ESC (1=нажат) */
#define WC_SYS_ENT 0x6005 /* [1] ENT: флаг ENTER (1=нажат) */
#define WC_SYS_FUN 0x6006 /* [3] API entry point (JP instr) */
#define WC_SYS_TMN 0x6009 /* [2] таймер INT (uint16, ++/frame) */
#define WC_SYS_FEP 0x600B /* [1] страница FAT engine в окне 0 */
#define WC_SYS_XPP 0x600C /* [1] сохранённая страница */
#define WC_SYS_CNFV 0x600D /* [1] конфиг видео (%000–%111) */
#define WC_SYS_HEI 0x600E /* [1] высота экрана (25/30/36) */
#define WC_SYS_TXTMD 0x600F /* [1] текстовый режим */
#define WC_SYS_INIFLG 0x6010 /* [1] INI загружен */
#define WC_SYS_RSQBC 0x6011 /* [2] RSQ buffer counter */
#define WC_SYS_RSQB2 0x6013 /* [2] RSQ buffer 2 */
#define WC_SYS_SDBSF 0x6015 /* [1] SD1 driver state */
#define WC_SYS_SDBSF2 0x6016 /* [1] SD2 driver state */
#define WC_SYS_F3ENTFL 0x6017 /* [1] F3/ENTER execution flag */
#define WC_SYS_FEP2 0x6018 /* [2] FAT engine page extra */
#define WC_SYS_PLRESAD 0x6020 /* [8] резидент перехода (JP page+HL) */
#define WC_SYS_PLRESCALL 0x6028 /* [8] резидент вызова (CALL page+HL) */
/* ──────────────────────────────────────────────────────────────────────
* КОДЫ УСТРОЙСТВ (для wc_stream, регистр B)
* ────────────────────────────────────────────────────────────────────── */
#define WC_DEV_SD_ZC 0 /* SD (Z-Controller) */
#define WC_DEV_IDE_NEMO_M 1 /* IDE Nemo Master */
#define WC_DEV_IDE_NEMO_S 2 /* IDE Nemo Slave */
#define WC_DEV_SD_NGS 3 /* SD (NeoGS) */
#define WC_DEV_IDE_SMUC_M 4 /* IDE SMUC Master */
#define WC_DEV_IDE_SMUC_S 5 /* IDE SMUC Slave */
#define WC_DEV_SD2_ZC 6 /* SD2 (Z-Controller) */
#define WC_DEV_COUNT 7 /* всего устройств */
/* ──────────────────────────────────────────────────────────────────────
* КОНФИГУРАЦИЯ ВИДЕО (CNFv, #600D)
* ────────────────────────────────────────────────────────────────────── */
#ifndef WC_CNFV_PWM
#define WC_CNFV_PWM 0 /* ШИМ (IDE) */
#endif
#ifndef WC_CNFV_VDAC3
#define WC_CNFV_VDAC3 1 /* 3-bit VDAC */
#endif
#ifndef WC_CNFV_VDAC4
#define WC_CNFV_VDAC4 2 /* 4-bit VDAC */
#endif
#ifndef WC_CNFV_VDAC5
#define WC_CNFV_VDAC5 3 /* 5-bit VDAC */
#endif
#ifndef WC_CNFV_VDAC2
#define WC_CNFV_VDAC2 7 /* VDAC2 (FT812) */
#endif
/* ──────────────────────────────────────────────────────────────────────
* ВИДЕОРЕЖИМЫ TSConfig (для wc_gvmod)
* ────────────────────────────────────────────────────────────────────── */
/* Разрешение [7:6] */
#define WC_RRES_256x192 0x00
#define WC_RRES_320x200 0x40
#define WC_RRES_320x240 0x80
#define WC_RRES_360x288 0xC0
/* Графический режим [1:0] */
#define WC_VMODE_ZX 0x00 /* ZX Spectrum (6912 байт) */
#define WC_VMODE_16C 0x01 /* 16 цветов (4 бит/пиксель) */
#define WC_VMODE_256C 0x02 /* 256 цветов (8 бит/пиксель) */
#define WC_VMODE_TXT 0x03 /* текстовый режим */
/* Прочие биты */
#define WC_VMODE_NOGFX 0x20 /* выключить графику */
#define WC_VMODE_NOTSU 0x10 /* выключить TSU */
/* Размеры текстового экрана */
#define WC_SCREEN_80x25 25
#define WC_SCREEN_80x30 30
#define WC_SCREEN_90x36 36
#define WC_SCREEN_W80 80
#define WC_SCREEN_W90 90
/* ──────────────────────────────────────────────────────────────────────
* АППАРАТНЫЕ ПОРТЫ TSConfig (для __sfr / inline asm)
* ────────────────────────────────────────────────────────────────────── */
#define WC_PORT_PW0 0x10AF /* Page Window 0 */
#define WC_PORT_PW1 0x11AF /* Page Window 1 */
#define WC_PORT_PW2 0x12AF /* Page Window 2 */
#define WC_PORT_PW3 0x13AF /* Page Window 3 */
#define WC_PORT_VMOD 0x00AF /* Video mode */
#define WC_PORT_VPAG 0x01AF /* Video page */
#define WC_PORT_VPAL 0x07AF /* Palette */
#define WC_PORT_BORDER 0x0FAF /* Border color */
#define WC_PORT_SYSCONF 0x20AF /* System config (turbo [1:0]) */
#define WC_PORT_CACHECONF 0x2BAF /* Cache config */
#define WC_PORT_FDDVIRT 0x29AF /* FDD Virtualization (vDOS) */
/* DMA порты */
#define WC_PORT_DMA_SL 0x1AAF /* DMA Source Low */
#define WC_PORT_DMA_SH 0x1BAF /* DMA Source High */
#define WC_PORT_DMA_SX 0x1CAF /* DMA Source Page */
#define WC_PORT_DMA_DL 0x1DAF /* DMA Dest Low */
#define WC_PORT_DMA_DH 0x1EAF /* DMA Dest High */
#define WC_PORT_DMA_DX 0x1FAF /* DMA Dest Page */
#define WC_PORT_DMA_N 0x26AF /* DMA burst size */
#define WC_PORT_DMA_C 0x27AF /* DMA command / status */
#define WC_PORT_DMA_T 0x28AF /* DMA burst count */
/* ──────────────────────────────────────────────────────────────────────
* FAT32 ENTRY (стандартная 32-байтная структура каталога)
* ────────────────────────────────────────────────────────────────────── */
typedef struct {
uint8_t name[11]; /* +0: короткое имя 8.3 (без точки) */
uint8_t attr; /* +11: атрибуты (EFLG) */
uint8_t _res1; /* +12: reserved (NTRes) */
uint8_t crt_time_ms; /* +13: время создания (0.1 сек) */
uint16_t crt_time; /* +14: время создания (FAT format) */
uint16_t crt_date; /* +16: дата создания (FAT format) */
uint16_t acc_date; /* +18: дата последнего доступа */
uint16_t cluster_hi; /* +20: старшее слово кластера (CLSDE) */
uint16_t mod_time; /* +22: время модификации */
uint16_t mod_date; /* +24: дата модификации */
uint16_t cluster_lo; /* +26: младшее слово кластера (CLSHL) */
uint32_t file_size; /* +28: размер файла (SIZIK) */
} wc_fat_entry_t;
/* Атрибуты FAT entry (attr поле) */
#define WC_FAT_ATTR_RDONLY 0x01 /* Read-only */
#define WC_FAT_ATTR_HIDDEN 0x02 /* Hidden */
#define WC_FAT_ATTR_SYSTEM 0x04 /* System */
#define WC_FAT_ATTR_VOLLBL 0x08 /* Volume label */
#define WC_FAT_ATTR_DIR 0x10 /* Directory */
#define WC_FAT_ATTR_ARCHIVE 0x20 /* Archive */
#define WC_FAT_ATTR_LFN 0x0F /* Long File Name entry */
/* Макросы для FAT date/time */
#define WC_FAT_TIME(h,m,s) (uint16_t)(((h)<<11)|((m)<<5)|((s)>>1))
#define WC_FAT_DATE(y,m,d) (uint16_t)((((y)-1980)<<9)|((m)<<5)|(d))
#define WC_FAT_TIME_HOUR(t) (((t) >> 11) & 0x1F)
#define WC_FAT_TIME_MIN(t) (((t) >> 5) & 0x3F)
#define WC_FAT_TIME_SEC(t) (((t) & 0x1F) << 1)
#define WC_FAT_DATE_YEAR(d) ((((d) >> 9) & 0x7F) + 1980)
#define WC_FAT_DATE_MONTH(d) (((d) >> 5) & 0x0F)
#define WC_FAT_DATE_DAY(d) ((d) & 0x1F)
/* ──────────────────────────────────────────────────────────────────────
* FindNext выходной буфер (формат записи)
* ────────────────────────────────────────────────────────────────────── */
/**
* Структура записи, возвращаемой wc_findnext.
* Порядок полей зависит от флагов (size/date/time опциональны).
* При всех включённых флагах ([4:2]=111):
*/
typedef struct {
uint32_t size; /* [2]=1: размер файла */
uint16_t date; /* [3]=1: дата создания (FAT формат) */
uint16_t time; /* [4]=1: время создания (FAT формат) */
uint8_t flag; /* Entry Flag: 0x10=DIR, 0x00=file */
char name[1]; /* имя файла (переменная длина, ZS) */
} wc_findnext_entry_t;
/* Флаги для wc_findnext */
#define WC_FIND_ALL 0x00 /* все записи [1:0]=00 */
#define WC_FIND_FILES 0x01 /* только файлы [1:0]=01 */
#define WC_FIND_DIRS 0x02 /* только каталоги [1:0]=10 */
#define WC_FIND_SHORT_ONLY 0x80 /* только короткие имена [7]=1 */
#define WC_FIND_WITH_SIZE 0x04 /* включить поле size [2]=1 */
#define WC_FIND_WITH_DATE 0x08 /* включить поле date [3]=1 */
#define WC_FIND_WITH_TIME 0x10 /* включить поле time [4]=1 */
#define WC_FIND_FULL (WC_FIND_WITH_SIZE | WC_FIND_WITH_DATE | WC_FIND_WITH_TIME)
/* ──────────────────────────────────────────────────────────────────────
* КАТАЛОЖНАЯ ЗАПИСЬ ПАНЕЛИ WC (внутренний формат)
* ────────────────────────────────────────────────────────────────────── */
/**
* INDEX entry (4 байта): ссылка из индекса на DATA entry.
*/
typedef struct {
uint8_t page; /* +0: логическая страница (0–8) */
uint16_t addr; /* +1: адрес в странице (#C000–#FDFF) */
uint8_t name_len; /* +3: длина имени */
} wc_cat_index_t;
/**
* DATA entry (переменная длина): полная запись файла в каталоге.
*/
typedef struct {
uint8_t mark; /* +0: Mark flags (бит 0: помечен) */
char ext[3]; /* +1: расширение (без точки) */
uint32_t cluster; /* +4: первый кластер FAT32 */
uint32_t size; /* +8: размер файла */
uint16_t date; /* +12: дата (FAT формат) */
uint16_t time; /* +14: время (FAT формат) */
uint8_t flag; /* +16: #10=файл, #00=каталог */
char name[1]; /* +17: имя (переменная длина, ZS) */
} wc_cat_data_t;
/* ──────────────────────────────────────────────────────────────────────
* ЗАГОЛОВОК ПЛАГИНА (+0..+260)
* ────────────────────────────────────────────────────────────────────── */
#define WC_PLUGIN_ID "WildCommanderMDL"
#define WC_PLUGIN_VERSION 0x10 /* текущая версия формата */
/* Типы вызова плагина (поле +197) */
#define WC_PCOND_EXT_ONLY 0x00 /* только по расширению */
#define WC_PCOND_ON_LOAD 0x01 /* при загрузке (ENTER) */
#define WC_PCOND_TIMER 0x02 /* по таймеру / F10 */
#define WC_PCOND_F10 0x03 /* из меню F10 */
#define WC_PCOND_VIEWER 0x04 /* из меню Shift+F3 */
#define WC_PCOND_F10_VIEW 0x05 /* F10 или Shift+F3 */
#define WC_PCOND_F10_F2 0x11 /* F10 или F2 */
#define WC_PCOND_F2 0x12 /* F2 */
#define WC_PCOND_F10_F4 0x13 /* F10 или F4 */
#define WC_PCOND_F4 0x14 /* F4 */
#define WC_PCOND_F10_V_F4 0x15 /* F10, Shift+F3 или F4 */
/* ──────────────────────────────────────────────────────────────────────
* DMA операции (коды для wc_dmapl и прямого доступа)
* ────────────────────────────────────────────────────────────────────── */
/* DMA device codes (биты [2:0] регистра DMA_C) */
#define WC_DMA_RAM_RAM 0x01 /* W=0: RAM→RAM copy */
#define WC_DMA_BLT_RAM 0x01 /* W=1: RAM blit (transparency) */
#define WC_DMA_SPI_RAM_R 0x02 /* W=0: SPI→RAM */
#define WC_DMA_SPI_RAM_W 0x02 /* W=1: RAM→SPI */
#define WC_DMA_IDE_RAM_R 0x03 /* W=0: IDE→RAM */
#define WC_DMA_IDE_RAM_W 0x03 /* W=1: RAM→IDE */
#define WC_DMA_FILL 0x04 /* W=0: FILL RAM */
#define WC_DMA_RAM_CRAM 0x04 /* W=1: RAM→CRAM (палитра) */
#define WC_DMA_RAM_SFILE 0x05 /* W=1: RAM→SFILE (спрайты) */
/* Подфункции dmapl (А' регистр) */
#define WC_DMA_INIT_SD 0x00 /* задать src + dst */
#define WC_DMA_INIT_SRC 0x01 /* задать только src */
#define WC_DMA_INIT_DST 0x02 /* задать только dst */
#define WC_DMA_INIT_SRC_WIN 0x03 /* src из окна WC */
#define WC_DMA_INIT_DST_WIN 0x04 /* dst из окна WC */
#define WC_DMA_SET_T 0x05 /* задать DMA_T */
#define WC_DMA_SET_N 0x06 /* задать DMA_N */
#define WC_DMA_SET_TN 0x07 /* задать DMA_T + DMA_N */
#define WC_DMA_ALIGN_SRC 0x08 /* выравнивание src */
#define WC_DMA_ALIGN_DST 0x09 /* выравнивание dst */
#define WC_DMA_ALIGN_SIZE 0x0A /* размер выравнивания */
#define WC_DMA_ALIGN_ALL 0x0B /* все параметры выравнивания */
#define WC_DMA_RUN_RAM 0xFC /* RAM→RAM с ожиданием */
#define WC_DMA_RUN_BLT 0xFB /* BLT→RAM */
#define WC_DMA_RUN_FILL 0xFA /* FILL */
#define WC_DMA_RUN_CRAM 0xF9 /* RAM→CRAM */
#define WC_DMA_RUN_SFILE 0xF8 /* RAM→SFILE */
#define WC_DMA_RUN_NOWAIT 0xFD /* запустить без ожидания */
#define WC_DMA_RUN_WAIT 0xFE /* запустить с ожиданием */
#define WC_DMA_BUSY_WAIT 0xFF /* ждать готовности DMA */
/* DMA page flags (для subfunc #00–#02: бит 7 регистров B/C) */
#define WC_DMA_PAGE_PLUGIN 0x80 /* страница из блока плагина (0–5) */
/* WC_DMA_PAGE_VIDEO 0x00 страница из видеобуферов (0–63) */
/* ══════════════════════════════════════════════════════════════════════
* SOW — Structure of Window (16 байт)
* ══════════════════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════════════════
* КАРТА ПАМЯТИ TSConfig (4 МБ = 256 страниц × 16 КБ)
* ══════════════════════════════════════════════════════════════════════
*
* Z80 CPU имеет 4 окна по 16 КБ. Каждое окно может адресовать любую
* из 256 страниц (8-бит page register). Управление через порты:
*
* Окно 0: #0000–#3FFF порт PW0 = #10AF (при сбросе = #00)
* Окно 1: #4000–#7FFF порт PW1 = #11AF (при сбросе = #05)
* Окно 2: #8000–#BFFF порт PW2 = #12AF (при сбросе = #02)
* Окно 3: #C000–#FFFF порт PW3 = #13AF (при сбросе = #00)
*
* ┌─────────┬─────────┬─────────────────────────────────────────────┐
* │ Стр. │ Дес. │ Назначение │
* ├─────────┼─────────┼─────────────────────────────────────────────┤
* │ #00 │ 0 │ MTXPG — основной текстовый экран (TXT page) │
* │ #01 │ 1 │ Шрифт (основного TXT) + Hrust/MegaLZ │
* │ │ │ (#FE00+) │
* │ #02 │ 2 │ Основной код WC (#8000–#AFFF) │
* │ #03–04 │ 3–4 │ Не используются │
* │ #05 │ 5 │ FAT Engine (#4000) + Stack (#5BFF) + │
* │ │ │ код WC (#6000–#7FFF) │
* │ #06–07 │ 6–7 │ Не используются │
* │ #08 │ 8 │ BUFPG — второй текст. экран / буфер 16 КБ │
* │ #09 │ 9 │ Шрифт (второго TXT экрана) │
* │ #0A │ 10 │ (зарезервирован) │
* │ #0B–13 │ 11–19 │ CTZPG — каталог ЛЕВОЙ панели (9 стр.) │
* │ #14–1C │ 20–28 │ Каталог ПРАВОЙ панели (9 стр.) │
* │ #1D │ 29 │ RESB — буфер сохранения окон (WOW save) │
* │ #1E │ 30 │ CPPG1 — позиции каталога ЛЕВОЙ панели │
* │ #1F │ 31 │ CPPG2 — позиции каталога ПРАВОЙ панели │
* │ #20–5F │ 32–95 │ TVBPG — MEGABUFFER (1 МБ, 64 стр.) │
* │ #60 │ 96 │ PLHPG — заголовки плагинов (512 б каждый) │
* │ #61–A0 │ 97–160 │ PLGPG — код плагинов (до 1 МБ, 64 стр.) │
* │ #A1–BF │ 161–191 │ Смонтированный TAP-файл (31 стр.) │
* │ #C0–E7 │ 192–231 │ vDOS RAM Disk (40 стр. = 640 КБ) │
* │ #E8–EF │ 232–239 │ Свободно (дополнит. дорожки RAM-диска) │
* │ #F0 │ 240 │ PNLPG — поток ЛЕВОЙ панели │
* │ #F1 │ 241 │ Поток ПРАВОЙ панели │
* │ #F2 │ 242 │ INIPG — поток плагина #00 / INI │
* │ #F3 │ 243 │ STRPG — поток плагина #01 │
* │ #F4 │ 244 │ DBPG — буфер SPG-загрузчика │
* │ #F5 │ 245 │ D5PG — загрузочная страница 5 │
* │ #F6 │ 246 │ D2PG — загрузочная страница 2 │
* │ #F7 │ 247 │ RPPG — пути панелей: │
* │ │ │ LEFT (#1000), RIGHT (#2000), │
* │ │ │ флаг активной панели (#1FFC) │
* │ #F8–FB │ 248–251 │ Virtual ROM (vDOS, системная прошивка) │
* │ #FC–FD │ 252–253 │ vDOS Tables (таблицы TR-DOS) │
* │ #FE │ 254 │ vDOS Drivers (драйверы дисководов) │
* │ #FF │ 255 │ vDOS Traps (перехватчик команд TR-DOS) │
* └─────────┴─────────┴─────────────────────────────────────────────┘
*
* ВИДЕОБУФЕРЫ (4 × 16 страниц = 256 КБ каждый, 1 МБ всего):
* Буфер 1: страницы #20–#2F (GVvPG=32)
* Буфер 2: страницы #30–#3F (GV2PG=48)
* Буфер 3: страницы #40–#4F (GV3PG=64)
* Буфер 4: страницы #50–#5F (GV4PG=80)
* (МEGABUFFER #20–#5F включает все 4 видеобуфера)
*
* Функция MNGCVPL (65): A' = 0x00–0x0F → buf1,
* A' = 0x10–0x1F → buf2,
* A' = 0x20–0x2F → buf3,
* A' = 0x30–0x3F → buf4
*
* ТЕКСТОВЫЙ ЭКРАН WC: страница #00 (MTXPG) занимает 2 страницы:
* стр. #00 = символы + атрибуты (EGA-формат: [7:4]=bg, [3:0]=fg)
* стр. #01 = шрифт 8×8 (используется TSConfig text mode)
* Размеры: 80×25, 80×30 или 90×36 (HEI = 25/30/36)
*
* АДРЕСНОЕ ПРОСТРАНСТВО процессора (runtime плагина):
* #0000–#3FFF Окно 0: обычно FAT engine / буферы (page #05)
* ⚠ #0000–#1FFF гробится FindNext!
* ⚠ #1000–#1447 гробится GEDPL!
* #4000–#5FFF Окно 1: FAT engine (page #05), код #6000–#7FFF
* #6000–#602F Системная область WC (переменные, API entry point)
* #8000–#BFFF Окно 2: код плагина (page из PLGPG)
* #C000–#FFFF Окно 3: переключаемая (TXT/данные/видео)
*
* ══════════════════════════════════════════════════════════════════════
* vDOS (Virtual DOS) — эмуляция TR-DOS на TSConfig
* ══════════════════════════════════════════════════════════════════════
*
* vDOS обеспечивает совместимость с TR-DOS (Beta Disk Interface) на
* аппаратном уровне TSConfig FPGA. Состоит из:
*
* Страница #FF — vDOS Traps:
* Перехватывает обращения Z80 к стандартным адресам TR-DOS ROM
* (#3D13, #3D2F и др.) и эмулирует дисковые операции.
*
* Страница #FE — vDOS Drivers:
* Низкоуровневые драйверы виртуального дисковода:
* чтение/запись секторов, форматирование, позиционирование.
*
* Страницы #FC–#FD — vDOS Tables:
* Таблицы трансляции: соответствие виртуальных дорожек/секторов
* физическим секторам на SD/IDE. Каталоги дисков A–D.
*
* Страницы #F8–#FB — Virtual ROM (4 × 16 КБ = 64 КБ):
* Прошивка: Service ROM, DOS ROM, 128 ROM, 48 ROM.
* Переключение через биты DOS/ROM128 в page register окна 0.
*
* Страницы #C0–#E7 — vDOS RAM Disk (40 × 16 КБ = 640 КБ):
* RAM-диск для эмуляции дисководов. SCL-образы распаковываются
* сюда целиком. TRD-образы монтируются напрямую с FAT32.
* Страницы #E8–#EF — резерв (дополнительные дорожки).
*
* Инициализация vDOS:
* vDOS инициализируется автоматически при старте TSConfig (прошивка).
* Плагин не может и не должен напрямую управлять vDOS — только
* через плагин MOUNTER (монтирование/размонтирование образов).
* Регистр FDDVirt (#29AF) управляет виртуализацией FDD на уровне FPGA.
*
* ══════════════════════════════════════════════════════════════════════
* FAT32 ENGINE — файловая подсистема WC
* ══════════════════════════════════════════════════════════════════════
*
* FAT-движок (KERNEL.ASM + CORE32.ASM) располагается на странице #05
* в адресах #4000–#5BFF (окно 1). Системная переменная FEP (#600B)
* хранит номер страницы FAT engine в окне #0000.
*
* Буферы FAT engine (адреса относительно окна 0, когда FEP=#05):
* #0000–#1FFF GENBU/NXTBU — общий буфер / буфер FindNext
* #3000–#31FF SECBU — секторный буфер (512 б)
* #3200–#33FF LOBU — буфер загрузки (512 б)
* #3400–#35FF LOBU2 — второй буфер загрузки (512 б)
* #3800+ DRVR — область драйвера устройства
*
* Jump Table FAT Engine (ORG #4000):
* +0 SELDEV (заглушка)
* +3 DEVINI — инициализация устройства
* +6 REINI (заглушка)
* +9 HDD — поиск FAT32 раздела
* +12 RDD — чтение секторов с позиционированием
* +15 SDD — запись секторов с позиционированием
* +18 GIPAG — позиционирование на первый кластер
* +21 LOAD512 — потоковое чтение (HL=addr, B=blocks)
* +24 SAVE512 — потоковая запись
* +27 DOS_SWP — выбор устройства + загрузка драйвера
* +30 GLSTCAT — установить активный каталог
* +33 TLSTCAT — получить кластер активного каталога
* +36 SRHFCL — найти свободный кластер
* +42 MKSG — создать FAT-цепочку
* +48 RFRH — обновить FSInfo
* +51 GENTRY — скопировать entry в буфер
* +54 TENTRY — скопировать entry из буфера
* +57 MKFILE — создать файл
* +60 MKDIR — создать каталог
* +63 DELFL — удалить файл/каталог
* +66 RENAME — переименовать
* +69 DLSG — удалить FAT-цепочку
* +72 CHTOSE — цепочка кластер→сектор
* +78 SRHDRN — поиск entry в каталоге
* +81 LOAD256 — чтение 256 байт
* +84 LOADNON — пропуск секторов
* +87 NXTETY — следующий entry (полный)
* +90 NXTETY2 — следующий entry (фильтрованный)
*
* FAT32 Directory Entry (32 байта, стандарт):
* +0 [11] — короткое имя 8.3
* +11 [1] — EFLG: атрибуты (0x10=DIR, 0x20=ARCHIVE, ...)
* +14 [2] — время создания (FAT time format)
* +16 [2] — дата создания (FAT date format)
* +20 [2] — CLSDE: старшее слово кластера
* +22 [2] — дата модификации
* +24 [2] — время модификации
* +26 [2] — CLSHL: младшее слово кластера
* +28 [4] — SIZIK: размер файла (32-бит LE)
*
* ══════════════════════════════════════════════════════════════════════
* ПОТОКОВАЯ ПОДСИСТЕМА (STREAM)
* ══════════════════════════════════════════════════════════════════════
*
* Каждый поток — полный FAT32-контекст (устройство, раздел, каталог).
* WC поддерживает 4 потока:
*
* Страница #F0 (PNLPG) — поток ЛЕВОЙ панели
* Страница #F1 — поток ПРАВОЙ панели
* Страница #F2 (INIPG) — поток плагина #00 / INI-файл
* Страница #F3 (STRPG) — поток плагина #01
*
* Типичный сценарий работы с файлами из плагина:
*
* // 1. Клонировать поток панели → потоки #00 и #01
* wc_stream(WC_STREAM_CLONE); // D=#FE
*
* // 2. Перейти в root
* wc_stream(WC_STREAM_ROOT); // D=#FF
*
* // 3. Найти файл
* static uint8_t buf[] = { WC_FILE_FLAG, 'm','y','f','i','l','e','.','t','x','t', 0 };
* if (wc_fentry((uint16_t)buf)) {
* wc_gfile(); // открыть файл
* wc_load512(0xC000, 32); // прочитать
* }
*
* Устройства (код в регистре B при создании потока):
* 0 — SD (Z-Controller)
* 1 — IDE Nemo Master
* 2 — IDE Nemo Slave
* 3 — SD (NeoGS)
* 4 — IDE SMUC Master
* 5 — IDE SMUC Slave
* 6 — SD2 (Z-Controller)
*
* ⚠ GDIR нельзя вызывать на потоке активной панели!
* ⚠ MKfile/MKdir/LOAD512/SAVE512/FENTRY разрушают кеш FindNext!
* ⚠ GIPAGPL можно использовать со STREAM только после клонирования!
*
* ══════════════════════════════════════════════════════════════════════
* КАТАЛОГ ПАНЕЛИ (CAT) — внутренняя структура WC
* ══════════════════════════════════════════════════════════════════════
*
* Каждая панель хранит каталог до 4095 файлов в 9 страницах RAM:
* Левая: стр. #0B–#13 (CTZPG), позиции: стр. #1E (CPPG1)
* Правая: стр. #14–#1C, позиции: стр. #1F (CPPG2)
*
* INDEX entry (4 байта):
* +0 [1] — Логическая страница (0–8)
* +1 [2] — Адрес в странице (#C000–#FDFF)
* +3 [1] — Длина имени (без заголовка и терминатора)
*
* DATA entry (переменная длина):
* +0 [1] — Mark flags (бит 0: %1=помечен, %0=нет)
* +1 [3] — Расширение (без точки, верхний регистр)
* +4 [4] — Первый кластер FAT32
* +8 [4] — Размер файла
* +12 [2] — Дата (FAT формат)
* +14 [2] — Время (FAT формат)
* +16 [1] — Flag: #10=файл, #00=каталог (инвертировано для сортировки)
* +17 [1–255]— Имя файла ("file.bin" / "megadir")
* +xx [1] — Терминатор #00
*
* ══════════════════════════════════════════════════════════════════════
* ДРАЙВЕРЫ УСТРОЙСТВ
* ══════════════════════════════════════════════════════════════════════
*
* Драйвер загружается в область DRVR (#3800) FAT engine.
* Jump Table драйвера:
* +0 SELDEV — выбор устройства (master/slave)
* +3 DEVINI — инициализация
* +6 REINI — переинициализация
* +9 XPOZI — установить позицию (LBA)
* +12 PROZ — альтернативная позиция
* +15 RDDSE — чтение секторов (HL=addr, A=count)
* +18 SDDSE — запись секторов (HL=addr, A=count)
* +21 RDD256 — чтение 256 байт
*
* SD Card порты (Z-Controller): CONF=#77, DATA=#57
* IDE SMUC порты: CMD=#FFBE, DRV=#FEBE,
* CYLH=#FDBE, CYLL=#FCBE, SECR=#FBBE
*
* ══════════════════════════════════════════════════════════════════════
* АППАРАТНЫЕ РЕГИСТРЫ TSConfig (частые)
* ══════════════════════════════════════════════════════════════════════
*
* #00AF Vmod — видеорежим (RRES[7:6] | NOGFX | NOTSU | TYPE[1:0])
* #01AF Vpag — видеостраница
* #07AF Vpal — индекс палитры
* #0FAF BORDER — цвет бордюра
* #10AF PW0 — страница окна #0000
* #11AF PW1 — страница окна #4000
* #12AF PW2 — страница окна #8000
* #13AF PW3 — страница окна #C000
* #15AF Vfm — режим шрифта
* #16AF Vtm — страница Tile Map
* #17AF Vtl0 — Tile Layer 0
* #18AF Vtl1 — Tile Layer 1
* #19AF Vsgp — Sprite Graphics page
* #1AAF DMASL — DMA Source Low
* #1BAF DMASH — DMA Source High
* #1CAF DMASX — DMA Source Page
* #1DAF DMADL — DMA Dest Low
* #1EAF DMADH — DMA Dest High
* #1FAF DMADX — DMA Dest Page
* #20AF SYC — System Config (turbo bits [1:0])
* #26AF DMA_N — DMA burst size
* #27AF DMA_C — DMA command / status
* #28AF DMA_T — DMA burst count
* #29AF FDDVirt — FDD Virtualization (vDOS)
* #2BAF CYC — Cache Config (per-window enable)
*/
typedef struct {
uint8_t type; /* +0: стиль+флаги (WC_WIN_*) */
uint8_t cur_mask; /* +1: маска цвета курсора */
uint8_t x; /* +2: позиция X в символах (255=центр) */
uint8_t y; /* +3: позиция Y в символах (255=центр) */
uint8_t width; /* +4: ширина (0=весь экран) */
uint8_t height; /* +5: высота (0=весь экран) */
uint8_t color; /* +6: атрибут цвета (EGA: bg[7:4]|fg[3:0]) */
uint8_t _reserved; /* +7: ЗАРЕЗЕРВИРОВАНО (всегда 0!) */
uint16_t buf_addr; /* +8: адрес буфера фона (0xFFFF=нет) */
uint8_t divider1; /* +10: Y-смещение разделит. линии 1 от низа окна (0=нет) */
uint8_t divider2; /* +11: Y-смещение разделит. линии 2 от низа окна (0=нет) */
uint8_t _ext[12]; /* +12–15: расширение (заголовок/курсор) */
} wc_window_t;
/* ══════════════════════════════════════════════════════════════════════
* Вспомогательный макрос инициализации окна
* ══════════════════════════════════════════════════════════════════════
* Пример:
* static wc_window_t my_win = WC_WIN_INIT(1, 0, 5, 5, 30, 10, 0x47, 0xFFFF);
* (border=1, x=5, y=5, w=30, h=10, color=bright white on black, no buf)
*/
/* WC_WIN_INIT: инициализация структуры окна.
* Поля +12..+15 (_ext) = 0 → подходит для типов без курсора (SINGLE, DOUBLE).
* Для типов 0/3/6 (с курсором) нужно вручную инициализировать _ext[0..3].
*/
#define WC_WIN_INIT(style, flags, _x, _y, _w, _h, _color, _buf) \
{ (uint8_t)((style) | (flags)), 0x07, (_x), (_y), (_w), (_h), \
(_color), 0, (_buf), 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0} }
/* ══════════════════════════════════════════════════════════════════════
* INIT / СТРАНИЧНОЕ УПРАВЛЕНИЕ
* ══════════════════════════════════════════════════════════════════════ */
/**
* Переключить страницу плагина на адрес #C000.
* @param page номер страницы плагина:
* 0x00–0x3F — страницы RAM плагина
* 0xFF — страница с шрифтом WC (не использовать #E000–#FFFF!)
* 0xFE — первый текстовый экран WC
* Остальные страницы #8000 и #0000 при этом НЕ изменяются.
*/
void wc_mngc_pl(uint8_t page);
/** Отобразить страницу плагина (page) на адрес #0000 */
void wc_mng0_pl(uint8_t page);
/** Отобразить страницу плагина (page) на адрес #8000 */
void wc_mng8_pl(uint8_t page);
/* ══════════════════════════════════════════════════════════════════════
* UI / ОКНА
* ══════════════════════════════════════════════════════════════════════ */
/**
* Восстановить дисплей WC: палитру, смещения, текстовый режим.
*
* Обязательно вызывать при старте плагина, прежде чем вызывать
* любые функции вывода окон (wc_prwow и т.д.).
*
* ⚠ ВНИМАНИЕ: ГРОБИТ область #1000–#1447 на странице, отображённой
* на #0000! Сохранить данные в этом диапазоне до вызова!
*
* НЕ переключает страницу в окне #0000–#3FFF.
* НЕ переключает страницу в окне #C000.
*/
void wc_gedpl(void);
/**
* Нарисовать окно win на экране (сохранить фон в buf_addr).
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата. Вызывать при 3.5 МГц!
*
* Структура win и строки заголовков должны находиться в #8000–#BFFF
* или #0000–#3FFF (не в #C000–#FFFF!).
*
* После первого вызова WC записывает реальный адрес буфера в win->buf_addr
* (если был 0). При buf_addr=0xFFFF фон не сохраняется.
*/
void wc_prwow(wc_window_t *win);
/**
* Удалить окно win с экрана (восстановить фон из buf_addr).
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата. Вызывать при 3.5 МГц!
*/
void wc_rresb(wc_window_t *win);
/**
* Напечатать строку str в окне win.
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата.
*
* @param y строка внутри окна (1-based от верха содержимого)
* @param x столбец внутри окна (1-based)
* @param len длина строки в символах
*
* Адрес win и строки str должны быть в #8000–#BFFF или #0000–#3FFF.
*/
void wc_prsrw(wc_window_t *win, const char *str, uint8_t y, uint8_t x, uint16_t len);
/**
* Напечатать строку str в окне win c атрибутом цвета attr (EGA-формат: bg[7:4] | fg[3:0]).
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата.
*
* @param y строка внутри окна (1-based от верха содержимого)
* @param x столбец внутри окна (1-based)
* @param len длина строки в символах
* @param attr EGA-атрибут: bg[7:4] | fg[3:0]
*
* Адрес win и строки str должны быть в #8000–#BFFF или #0000–#3FFF.
*/
void wc_prsrw_attr(wc_window_t *win, const char *str, uint8_t y, uint8_t x, uint16_t len, uint8_t attr);
/**
* Вернуть физический адрес ячейки символа (y, x) внутри окна win.
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата.
*
* @param y строка (1-based)
* @param x столбец (1-based)
* @return адрес байта символа в видеобуфере
*/
uint16_t wc_gadrw(wc_window_t *win, uint8_t y, uint8_t x);
/**
* Нарисовать курсор в текущей позиции окна win.
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата.
*/
void wc_cursor(wc_window_t *win);
/**
* Стереть курсор (восстановить исходный символ/атрибут).
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата.
*/
void wc_curser(wc_window_t *win);
/**
* Меню выбора Да/Нет или ОК/Отмена.
*
* Работа в цикле (вызывать раз в кадр):
* 1. wc_yn(WC_YN_OK_CANCEL) — инициализация меню "ОК / Отмена"
* 2. wc_yn(WC_YN_YES_NO) — инициализация меню "Да / Нет"
* 3. В цикле: wc_yn(WC_YN_POLL) — опросить и перерисовать кнопки
* (вызывать каждый кадр, пока не будет нажата клавиша)
* 4. wc_yn(WC_YN_EXIT) — закрыть меню (скрыть кнопки)
*
* После WC_YN_POLL:
* Z=1 → нажато ОК / Да (A = 0)
* Z=0 → нажато Отмена / Нет (A != 0)
* Если клавиша ещё не нажата — никакого результата нет,
* нужно продолжать вызывать WC_YN_POLL в следующих кадрах.
*
* @param mode WC_YN_OK_CANCEL (0x01), WC_YN_YES_NO (0x02),
* WC_YN_POLL (0x00), WC_YN_EXIT (0xFF)
* @return 0 = ОК/Да нажато или ещё не нажато (Z=1),
* ненулевое = Отмена/Нет (Z=0)
*/
uint8_t wc_yn(uint8_t mode);
/**
* Встроенный редактор строки ввода.
*
* Имеет несколько режимов работы (передаются как mode):
*
* 0xFF — инициализировать тип 0: редактировать экранный буфер.
* win=SOW, DE=CURMAX<<8|CURNOW (макс.длина и текущая позиция).
* Рисует курсор. Дальнейшие вызовы: 0x00 (опрос).
*
* 0x00 — опросить клавиатуру. Вызывать раз в кадр.
* Возврат: A=0 — продолжает ввод, A!=0 — завершён.
* HL = текущий адрес курсора на экране.
*
* 0x01 — завершить ввод: стереть курсор.
* HL = адрес курсора на экране.
*
* 0xFD — инициализировать тип 2: редактировать буфер в памяти.
* HL=addr буфера, HL'=размер буфера,
* D=Y, E=X (позиция на экране),
* BC=CURMAX<<8|CURNOW.
*
* 0x02 — перерисовать блок редактора (обновить экран из буфера).
*
* 0xFE — инициализировать индикаторы (CAPS/INS/Lang/OVR).
* H=0: нижний заголовок, H=1: верхний.
*
* 0x03 — обновить индикаторы (вызывать раз в кадр).
*
* 0xFC — опросить состояние Caps/Ins/Ctrl+Shift.
* Возврат A: [7]=OVR, [6]=CAPS, [1:0]=язык.
*
* ⚠ НЕЛЬЗЯ одновременно вызывать 0x00 и 0xFC в одном кадре!
*
* @param win SOW-структура окна
* @param mode режим (0xFF, 0x00, 0x01, 0xFD, 0x02, 0xFE, 0x03, 0xFC)
*/
void wc_istr(wc_window_t *win, uint8_t mode);
/**
* Записать байт val в виде 2 шестнадцатеричных символов в экранный буфер.
* @param addr адрес в видеопамяти (в текущем экранном буфере)
* @param val значение, которое нужно отобразить как HEX (00–FF)
*
* Используется, например, для вывода шестнадцатеричного редактора.
*/
void wc_nork(uint16_t addr, uint8_t val);
/**
* Напечатать форматированный текст str в окне win начиная с (y, x).
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата. Вызывать при 3.5 МГц!
*
* Текст может содержать управляющие коды (см. раздел «Текстовая разметка»
* в заголовке файла): #0D (перенос строки), #0E (центрирование),
* #0B,XX (сдвиг вправо), #0C,YY (сдвиг вниз), #0x01-08 (цвета),
* #09 (инверсия), #FE,addr (ссылка).
*
* Структура win и строка str должны быть в #8000–#BFFF или #0000–#3FFF.
*
* @param y начальная строка в окне (1-based)
* @param x начальный столбец в окне (1-based)
* @return номер строки, следующей за последней напечатанной
*/
uint8_t wc_txtpr(wc_window_t *win, const char *str, uint8_t y, uint8_t x);
/**
* Напечатать n-е сообщение из блока текста str.
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата. Вызывать при 3.5 МГц!
*
* Блок состоит из абзацев, разделённых двойным нулём (0x00 0x00).
* msg_num задаёт, который по счёту абзац использовать (0-based).
*
* @param msg_num номер абзаца (0-based) в блоке текста
* @param str адрес начала текстового блока
* @param y начальная строка (1-based)
* @param x начальный столбец (1-based)
* @return номер строки после последней напечатанной
*/
uint8_t wc_mezz(wc_window_t *win, uint8_t msg_num, const char *str, uint8_t y, uint8_t x);
/**
* Прокрутить прямоугольную область внутри окна.
*
* ⚠ Включает основной текстовый экран на #C000 и оставляет его
* включённым после возврата. Вызывать при 3.5 МГц!
*
* Если step=0 и флаги [7:2] = 0 — автоматически используется DMA.
*
* @param y верхняя строка области (1-based)
* @param x левый столбец области (1-based)
* @param h высота области в строках
* @param w ширина области в символах
* @param flags битовые флаги:
* [7] = 1 → прокручивать атрибуты вместе с символами
* [6] = 1 → очистить исходную позицию после прокрутки
* [5:2] = шаг прокрутки (0=1 символ, 0xF=16 символов)
* [1:0] = направление: 00=вниз, 01=вверх, 10/11=зарезерв.
* При [7:2]=0 и step=0 DMA задействуется автоматически.
*/
void wc_scrlwow(wc_window_t *win, uint8_t y, uint8_t x, uint8_t h, uint8_t w, uint8_t flags);
/* ══════════════════════════════════════════════════════════════════════
* КЛАВИАТУРА
* Все функции возвращают 0 = не нажато, ненулевое = нажато (или код клавиши).
* Автоповтор: у Space, стрелок, Tab, Enter, Bspc, F1-F10, Pgup/Pgdn, Home, End.
* Без автоповтора: Alt, Shift, Ctrl.
* ══════════════════════════════════════════════════════════════════════ */
uint8_t wc_key_space(void); /* Space / Пробел */
uint8_t wc_key_up(void); /* Стрелка вверх */
uint8_t wc_key_down(void); /* Стрелка вниз */
uint8_t wc_key_left(void); /* Стрелка влево */
uint8_t wc_key_right(void); /* Стрелка вправо */
uint8_t wc_key_tab(void); /* Tab */
uint8_t wc_key_enter(void); /* Enter */
uint8_t wc_key_esc(void); /* Escape */
uint8_t wc_key_bspc(void); /* Backspace */
uint8_t wc_key_pgup(void); /* Page Up */
uint8_t wc_key_pgdn(void); /* Page Down */
uint8_t wc_key_home(void); /* Home */
uint8_t wc_key_end(void); /* End */
uint8_t wc_key_f1(void); /* F1 */
uint8_t wc_key_f2(void);
uint8_t wc_key_f3(void);
uint8_t wc_key_f4(void);
uint8_t wc_key_f5(void);
uint8_t wc_key_f6(void);
uint8_t wc_key_f7(void);
uint8_t wc_key_f8(void);
uint8_t wc_key_f9(void);
uint8_t wc_key_f10(void); /* F10 */
uint8_t wc_key_alt(void); /* Alt (без автоповтора) */
uint8_t wc_key_shift(void); /* Shift (без автоповтора) */
uint8_t wc_key_ctrl(void); /* Ctrl (без автоповтора) */
/**
* Сканировать клавиатуру, вернуть код нажатой клавиши.
* @param mode WC_KBSCN_NORMAL (с учётом SHIFT/CL/Lang) или WC_KBSCN_RAW
* @return код клавиши, 0 = ничего не нажато
*/
uint8_t wc_kbscn(uint8_t mode);
uint8_t wc_key_del(void); /* Delete */
uint8_t wc_key_caps(void); /* Caps Lock */
uint8_t wc_key_any(void); /* Любая клавиша */
void wc_key_wait_release(void); /* Ждать отпускания всех клавиш */
uint8_t wc_key_wait_any(void); /* Ждать нажатия любой клавиши */
uint8_t wc_key_ins(void); /* Insert */
/* ══════════════════════════════════════════════════════════════════════
* ФАЙЛОВЫЕ ОПЕРАЦИИ
* ══════════════════════════════════════════════════════════════════════ */
/**
* Загрузить blocks блоков по 512 байт из текущего потока в dest.
*
* @param dest адрес буфера назначения
* @param blocks количество блоков (blocks * 512 = байт)
* @return A = 0 — ОК, продолжение
* A = WC_EOF (0x0F) — нормальный конец цепочки FAT
* (на последней секторной загрузке!)
* A = другое — реальная ошибка чтения
*
* HL при возврате = новое значение dest (указывает на следующий байт
* после загруженных данных).
*
* Пример проверки ошибок:
* rc = wc_load512(addr, n);
* if (rc && rc != WC_EOF) { } // I/O error
*/
uint8_t wc_load512(uint16_t dest, uint8_t blocks);
/**
* Сохранить blocks блоков по 512 байт из src в текущий поток.
*
* HL при возврате = новое значение src (указывает на следующий байт
* после сохранённых данных).
*
* @param src адрес буфера источника
* @param blocks количество блоков
*/
void wc_save512(uint16_t src, uint8_t blocks);
/**
* Перейти к началу файла плагина (позиционировать поток на первый блок).
* Используется после открытия файла WC для повторного чтения.
*/
void wc_gipagpl(void);
/** Прочитать ENTRY (32 байта) файла в буфер по адресу addr */
void wc_tentry(uint16_t addr);
/**
* Обойти цепочку секторов, записать в buf список секторов.
* @param buf адрес буфера назначения
* @param bufend адрес конца буфера
*/
void wc_chtosep(uint16_t buf, uint16_t bufend);
/**
* Получить заголовок помеченного файла.
* @param panel IX = структура панели WC
* @param filenum номер файла в каталоге
* @param namebuf буфер для имени файла
*/
void wc_tmrkdfl(uint16_t panel, uint16_t filenum, uint16_t namebuf);
/**
* Управление директорией / позиционирование.
* @param mode WC_ADIR_SEEK_START или WC_ADIR_RESET_NEXT
*/
void wc_adir(uint8_t mode);
/**
* Открыть/переключить файловый поток.
*
* Параметр mode задаёт тип потока (передаётся внутри в регистр D):
* WC_STREAM_ROOT (0xFF) — корневой поток FAT
* WC_STREAM_CLONE (0xFE) — клонировать текущий поток
* WC_STREAM_WCDIR (0xFD) — каталог WC
* 0x00 / 0x01 — номер потока 0 или 1
*
* Если BC = 0xFFFF --- только переключить поток без пересоздания.
* Если BC ≠ 0xFFFF --- создать или пересоздать поток.
* (BC сейчас захардкожено в wc_api.s; уточните, если нужен другой режим.)
*
* @param mode один из WC_STREAM_* или 0/1
*/
void wc_stream(uint8_t mode);
/**
* Найти следующий файл/каталог в текущей директории.
*
* ⚠ ВНИМАНИЕ: ГРОБИТ область #0000–#1FFF на странице с драйвером!
* Не вызывать, если в этом диапазоне хранятся важные данные!
*
* @param entry_buf адрес буфера для заголовка файла (32+ байт)
* @param flags фильтр:
* [7] = 1 → только короткие имена
* [4] = 1 → загрузить время создания
* [3] = 1 → загрузить дату создания
* [2] = 1 → загрузить размер файла
* [1:0] = 00 → все объекты; 01 → только файлы;
* 10 → только каталоги
* @return 0 (Z=1) = конец директории;
* ненулевое = найден файл/каталог
*/
uint8_t wc_findnext(uint16_t entry_buf, uint8_t flags);
/**
* Найти файл по имени в текущей директории.
* @param name_with_flag адрес строки вида: [1 байт флагов][имя][0]
* @return 0 = не найден, ненулевое = найден; после вызова DE=entry, HL=size
*/
uint8_t wc_fentry(uint16_t name_with_flag);
/** Пропустить n секторов по 512 байт в текущем потоке */
void wc_loadnone(uint8_t sectors);
/**
* Открыть файл, найденный последним wc_fentry().
* Позиционирует поток чтения на найденный файл.
*/
void wc_gfile(void);
/**
* Войти в каталог, найденный последним wc_fentry().
*/
void wc_gdir(void);
/**
* Создать файл.
* @param name_with_flag [1 байт флагов][длина][имя][0]
* @return 0 = OK, ненулевое = ошибка
*/
uint8_t wc_mkfile(uint16_t name_with_flag);
/**
* Создать каталог.
* @param name указатель на имя каталога (ZS-строка)
* @return 0 = OK, ненулевое = ошибка
*/
uint8_t wc_mkdir(uint16_t name);
/**
* Переименовать файл/каталог.
* @param old_name [1 байт флагов][старое имя][0]
* @param new_name [новое имя][0]
* @return 0 = OK (Z), ненулевое = ошибка
*/
uint8_t wc_rename(uint16_t old_name, uint16_t new_name);
/**
* Удалить файл.
* @param name_with_flag [1 байт флагов][имя][0]
* @return 0 = OK (Z), ненулевое = ошибка
*/
uint8_t wc_delfl(uint16_t name_with_flag);
/* ══════════════════════════════════════════════════════════════════════
* ГРАФИКА
* ══════════════════════════════════════════════════════════════════════ */
/**
* Переключить видеорежим.
* @param mode WC_VIDEO_TXT / WC_VIDEO_BUF1..4
*/
void wc_mngv_pl(uint8_t mode);
/**
* Отобразить видеостраницу vpage на адрес #C000.
* @param vpage номер страницы (0x00–0x3F)
*/
void wc_mngcvpl(uint8_t vpage);
/**
* Установить видеорежим TSConfig.
* @param mode байт VConfig (TSCONF_VM_* | TSCONF_RRES_* | ...)
*/
void wc_gvmod(uint8_t mode);
/** Установить Y-смещение прокрутки графики */
void wc_gyoff(uint16_t y);
/** Установить X-смещение прокрутки графики */
void wc_gxoff(uint16_t x);
/** Установить страницу тайловой карты */
void wc_gvtm(uint8_t page);
/**
* Установить страницу графики тайловой плоскости.
* @param plane 0 = плоскость 0, 1 = плоскость 1
* @param page номер страницы
*/
void wc_gvtl(uint8_t plane, uint8_t page);
/** Установить страницу графики спрайтов */
void wc_gvsgp(uint8_t page);
/** Отобразить видеостраницу vpage на адрес #0000 */
void wc_mng0vpl(uint8_t vpage);
/** Отобразить видеостраницу vpage на адрес #8000 */
void wc_mng8vpl(uint8_t vpage);
/* ══════════════════════════════════════════════════════════════════════
* DMA
* ══════════════════════════════════════════════════════════════════════ */
/**
* Настройка и запуск DMA TSConfig.
*
* Подфункции DMA (передаются как subfunc):
*
* Конфигурация:
* 0x00 — задать src+dst: B=src_page, HL=src_addr, C=dst_page, DE=dst_addr
* 0x01 — задать только src
* 0x02 — задать только dst
* 0x03 — src из координат окна (адрес → окно WC)
* 0x04 — dst из координат окна
* 0x05 — задать DMA_T (тип тайла/спрайта)
* 0x06 — задать DMA_N (число объектов)
* 0x07 — задать T + N
* 0x08 — выравнивание источника
* 0x09 — выравнивание назначения
* 0x0A — выравнивание размера
* 0x0B — задать все параметры
*
* Запуск:
* 0xFC — RAM→RAM (с ожиданием завершения)
* 0xFB — BLT→RAM
* 0xFA — FILL (заполнение)
* 0xF9 — RAM→CRAM (палитра)
* 0xF8 — RAM→SFILE (спрайты)
* 0xFD — запустить без ожидания (следует вызвать 0xFF для синхронизации)
* 0xFE — запустить с ожиданием
* 0xFF — ждать готовности DMA (busy-wait)
*
* Обычная последовательность:
* // Настройка src/dst выполняется перед вызовом через регистры SDCC
* wc_dmapl(0xFC); // запустить и подождать
*/
void wc_dmapl(uint8_t subfunc);
/* ══════════════════════════════════════════════════════════════════════
* РАЗНОЕ
* ══════════════════════════════════════════════════════════════════════ */
/**
* Управление тактовой частотой CPU или AY через WC.
*
* WC управляет своими собственными настройками турбо и НЕ влияет
* на отображение экрана. Никаких конфликтов с WC по выбору частоты нет.
* WC запоминает выбранный режим и восстанавливает его при 0xFF.
*
* @param mode WC_TURBO_CPU (0x00) — выбрать частоту процессора:
* param = 0: 3.5 МГц
* param = 1: 7 МГц
* param = 2: 14 МГц ← для быстрых вычислений
* param = 3: 28 МГц
* WC_TURBO_AY (0x01) — выбрать частоту AY:
* param = 0: 1.750 МГц
* param = 1: 1.773 МГц
* param = 2: 3.5 МГц
* param = 3: 3.546 МГц
* WC_TURBO_RESTORE (0xFF) — восстановить режимы CPU+AY
* из настроек WC (игнорирует param)
*
* @param param параметр частоты (0–3); игнорируется при RESTORE
*/
void wc_turbopl(uint8_t mode, uint8_t param);
/**
* Получить параметр из INI-файла плагина.
* @param param_num номер параметра
* @return номер опции (0-based), 0xFF если параметр не задан
*/
uint8_t wc_prm_pl(uint8_t param_num);
/**
* Управление прерываниями WC.
*
* ⚠ По выходу из плагина (возврат из main) WC автоматически
* восстанавливает все параметры INT, установленные этой функцией!
*
* @param mode WC_INT_DISABLE_ALL (0x00) — отключить все прерывания WC
* WC_INT_NO_TIME (0x01) — отключить таймерное INT
* WC_INT_NO_PS2 (0x02) — отключить PS/2 INT
* WC_INT_PLUGIN (0xFF) — установить свой обработчик;
* в этом случае использовать wc_int_pl_handler(addr)
*/
void wc_int_pl(uint8_t mode);
/**
* Установить ASM-обработчик прерываний плагина.
* Вызывать после wc_int_pl(WC_INT_PLUGIN).
*
* Обработчик должен заканчиваться на RET (не RETI).
* Всё, что нужно сохранить — регистры; стек WC НЕ менять.
*
* @param handler_addr адрес ASM-обработчика в памяти плагина.
*/
void wc_int_pl_handler(uint16_t handler_addr);
/* ══════════════════════════════════════════════════════════════════════
* ВСПОМОГАТЕЛЬНЫЕ МАКРОСЫ
* ══════════════════════════════════════════════════════════════════════ */
/**
* Атрибут цвета (EGA-формат): bg[7:4] | fg[3:0]
* @param bg цвет фона (0–15)
* @param fg цвет текста (0–15)
* Пример: WC_COLOR(WC_BLUE, WC_BRIGHT_WHITE) = 0x1F
*/
#define WC_COLOR(bg, fg) ((uint8_t)(((bg) << 4) | (fg)))
/* 16 цветов (YGRB: Bright, Green, Red, Blue) */
#define WC_BLACK 0
#define WC_BLUE 1
#define WC_RED 2
#define WC_MAGENTA 3
#define WC_GREEN 4
#define WC_CYAN 5
#define WC_YELLOW 6
#define WC_WHITE 7
#define WC_DARK_GRAY 8
#define WC_BRIGHT_BLUE 9
#define WC_BRIGHT_RED 10
#define WC_BRIGHT_MAGENTA 11
#define WC_BRIGHT_GREEN 12
#define WC_BRIGHT_CYAN 13
#define WC_BRIGHT_YELLOW 14
#define WC_BRIGHT_WHITE 15
/* ══════════════════════════════════════════════════════════════════════
* Параметры плагина (заполняются crt0.s при вызове плагина WC)
* ══════════════════════════════════════════════════════════════════════
*
* Передаются WC через регистры:
* A' → wc_file_ext (индекс расширения в списке WC)
* BC → wc_file_name (указатель на z-строку с именем файла)
* HL → wc_file_size (младшее слово, 16 бит)
* DE → wc_file_size+2 (старшее слово, 16 бит; для файлов > 64 КБ)
*
* wc_file_size занимает 4 байта в _DATA (32-bit LE):
* lo = *(uint16_t *)&wc_file_size
* hi = *((uint16_t *)&wc_file_size + 1)
*/
extern uint8_t wc_file_ext; /* индекс расширения */
extern char * wc_file_name; /* char *: указатель на имя файла */
extern uint32_t wc_file_size; /* 32-bit LE: [+0]=lo, [+2]=hi */
/* Код возврата плагина: запись в wc_exit_code перед return из main() */
extern uint8_t wc_exit_code;
/** Номер текущего объекта в панели (от 1). Передаётся WC в HL'. */
extern uint16_t wc_file_idx;
/** Количество объектов в панели (включая ".."). Передаётся WC в DE'.
* Если wc_file_idx == wc_file_count — это последний файл. */
extern uint16_t wc_file_count;
#define WC_EXIT_ESC 0 /* закрыть плагин / выйти (стоп) */
#define WC_EXIT_UNRECOGNIZED 1 /* файл не распознан → другому плагину */
#define WC_EXIT_NEXT 2 /* перейти к следующему файлу */
#define WC_EXIT_RELOAD_DIR 3 /* перечитать каталог WC */
#define WC_EXIT_PREV 4 /* перейти к предыдущему файлу */
/* ══════════════════════════════════════════════════════════════════════
* СИСТЕМНЫЕ ПЕРЕМЕННЫЕ — inline чтение через volatile-указатели
* ══════════════════════════════════════════════════════════════════════
*
* Эти макросы читают системную область WC (#6000+) напрямую:
*/
/** Текущая страница в окне 0 (#0000–#3FFF) */
#define wc_get_pg0() (*(volatile uint8_t *)WC_SYS_PG0)
/** Текущая страница в окне 1 (#4000–#7FFF) */
#define wc_get_pg4() (*(volatile uint8_t *)WC_SYS_PG4)
/** Текущая страница в окне 2 (#8000–#BFFF) */
#define wc_get_pg8() (*(volatile uint8_t *)WC_SYS_PG8)
/** Текущая страница в окне 3 (#C000–#FFFF) */
#define wc_get_pgc() (*(volatile uint8_t *)WC_SYS_PGC)
/** Флаг ESC (ABT): 1 = пользователь нажал ESC */
#define wc_get_abt() (*(volatile uint8_t *)WC_SYS_ABT)
/** Флаг ENTER (ENT): 1 = пользователь нажал ENTER */
#define wc_get_ent() (*(volatile uint8_t *)WC_SYS_ENT)
/** Таймер INT (инкрементируется каждым прерыванием) */
#define wc_get_timer() (*(volatile uint16_t *)WC_SYS_TMN)
/** Страница FAT engine в окне 0 */
#define wc_get_fep() (*(volatile uint8_t *)WC_SYS_FEP)
/** Конфигурация видео (CNFv): 0=PWM, 1=3bit, ..., 7=VDAC2 */
#define wc_get_cnfv() (*(volatile uint8_t *)WC_SYS_CNFV)
/** Высота экрана в строках (25, 30 или 36) */
#define wc_get_height() (*(volatile uint8_t *)WC_SYS_HEI)
/** Текстовый режим (TXTmd) */
#define wc_get_txtmode() (*(volatile uint8_t *)WC_SYS_TXTMD)
/** SD1 driver state */
#define wc_get_sd1_state() (*(volatile uint8_t *)WC_SYS_SDBSF)
/** SD2 driver state */
#define wc_get_sd2_state() (*(volatile uint8_t *)WC_SYS_SDBSF2)
/* ══════════════════════════════════════════════════════════════════════
* ВСПОМОГАТЕЛЬНЫЕ МАКРОСЫ ДЛЯ FAT
* ══════════════════════════════════════════════════════════════════════ */
/**
* Собрать 32-битный кластер из двух 16-битных полей FAT entry.
* @param hi cluster_hi (CLSDE, +20)
* @param lo cluster_lo (CLSHL, +26)
*/
#define WC_FAT_CLUSTER(hi, lo) ((uint32_t)(hi) << 16 | (uint32_t)(lo))
/**
* Построить буфер с флагом для wc_fentry / wc_mkfile / wc_delfl.
* Пример:
* static uint8_t buf[] = WC_FAT_NAME(WC_FILE_FLAG, "readme.txt");
* Раскрывается в: { 0x00, 'r','e','a','d','m','e','.','t','x','t', 0 }
*/
#define WC_FAT_NAME(flag, name) { (flag), name "\0" }
/**
* Построить буфер для wc_mkfile (flag + size + name).
* Пример:
* static uint8_t buf[32];
* wc_mkfile_buf(buf, 1024, "output.bin");
*/
#define wc_mkfile_buf(buf, sz, nm) do { \
(buf)[0] = WC_FILE_FLAG; \
*(uint32_t*)&(buf)[1] = (uint32_t)(sz); \
uint8_t _i = 0; \
while ((nm)[_i]) { (buf)[5 + _i] = (nm)[_i]; _i++; } \
(buf)[5 + _i] = 0; \
} while(0)
/* ══════════════════════════════════════════════════════════════════════
* РЕЗИДЕНТНЫЙ ПЕРЕХОД / ВЫЗОВ (для многостраничных плагинов)
* ══════════════════════════════════════════════════════════════════════
*
* Для плагинов, занимающих несколько страниц в #8000–#BFFF:
*
* Резидент перехода (#6020):
* A = номер страницы плагина (от 0)
* HL = адрес перехода (#8000–#BFFF)
* Выполняет: переключение страницы в окне 2, JP (HL)
*
* Резидент вызова (#6028):
* A = номер страницы плагина (от 0)
* HL = адрес вызова (#8000–#BFFF)
* Выполняет: переключение, CALL (HL), возврат обратно
* Сохраняет все регистры кроме A и HL!
*
* Пример:
* // Вызвать функцию load_data на странице 2 плагина
* wc_resident_call(2, (uint16_t)&load_data);
*/
/** Перейти (JP) в код на другой странице плагина (без возврата!) */
void wc_resident_jump(uint8_t page, uint16_t addr);
/** Вызвать (CALL) код на другой странице плагина (с возвратом) */
void wc_resident_call(uint8_t page, uint16_t addr);
/* ══════════════════════════════════════════════════════════════════════
* LOAD256 / LOADNONE (дополнительные файловые функции)
* ══════════════════════════════════════════════════════════════════════ */
/**
* Загрузить blocks блоков по 256 байт (используется видео плеером).
* Параметры и возврат аналогичны wc_load512.
*/
uint8_t wc_load256(uint16_t dest, uint8_t blocks);
/* ══════════════════════════════════════════════════════════════════════
* MNG0VPL / MNG8VPL (видеостраницы в другие окна)
* ══════════════════════════════════════════════════════════════════════
*
* ⚠ MNG0VPL (Win 0, #0000–#3FFF):
* — WC API сохраняет/восстанавливает Win 0 при каждом CALL #6006
* — Но GEDPL затирает #1000–#1447!
* — Нельзя передавать через #0000–#3FFF параметры WC-функциям
* (SOW, строки, буферы), т.к. WC ремапит окно внутри вызова
* — Безопасно для данных плагина МЕЖДУ вызовами WC API
*
* ⚠ MNGCVPL (Win 3, #C000–#FFFF):
* — WC API сбрасывает Win 3 на TXT-страницу (#00) при выходе
* из КАЖДОГО вызова P8000 и НЕ восстанавливает!
* — После любого CALL #6006 нужно заново вызвать mngc_pl()
* — Удобно для загрузки: mngcvpl(N) → load512(0xC000, 32)
*
* См. разделы «КАРТА АДРЕСНОГО ПРОСТРАНСТВА» и «P8000 DISPATCH».
*/
void wc_strset(char *dsr, uint16_t len, char c);
#endif /* WC_API_H */