Initial commit: FT812 Viewer (reworked - no .XM/.XMZ)

Wild Commander plugin for JPG/PNG/DLS/DXP/AVI via the FT812 (EVE) chip.
XM/XMZ support (and the ESP32 audio-playback code it needed) has been
removed here so this plugin can be installed alongside xmplay
(https://git.alexkorochinskiy.ru/alex/xmplayer.git) without both
registering for the same extensions - see README.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 12:44:32 +03:00
co-authored by Claude Sonnet 5
commit 15f0d752f6
11 changed files with 1263 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
# Build output (SDCC/Z80 toolchain — regenerated by build.bat)
obj/
bin/
build/
# SDCC-generated files if they ever end up outside obj/
*.rel
*.lst
*.map
*.noi
*.sym
*.lk
*.ihx
*.adb
*.cdb
*.omf
# Editor/OS cruft
.vscode/
*.swp
Thumbs.db
.DS_Store
BIN
View File
Binary file not shown.
+62
View File
@@ -0,0 +1,62 @@
# FT812 Viewer
Плагин для Wild Commander (ZX Evolution / TS-Conf), показывающий изображения
и видео через графический чип FT812 (EVE) платы.
> ⚠️ **Это переработанная версия.** Стандартный (оригинальный) `ftview`
> дополнительно сам регистрируется на расширения `.XM` и `.XMZ` и
> проигрывает их через ESP32-сопроцессор. Из этой версии поддержка `.XM` и
> `.XMZ` убрана: воспроизведение модулей вынесено в отдельный плагин
> [xmplay](https://git.alexkorochinskiy.ru/alex/xmplayer.git), а у Wild
> Commander таблица "расширение → плагин" статическая и зашивается в
> заголовок при сборке — если поставить `xmplay` вместе со стандартным
> (не переработанным) `ftview`, оба будут претендовать на одни и те же
> расширения и конфликтовать. Ставить эту версию `ftview` вместе с `xmplay`,
> не стандартную.
## Требования
- ZX Evolution / TS-Conf с графическим чипом FT812 (EVE)
- Wild Commander
## Возможности
- `.JPG`, `.PNG` — просмотр изображений
- `.DLS` — воспроизведение "сырого" display list FT812 (уже скомпилированная
команда отрисовки, без разбора формата)
- `.DXP` — свой формат растрового изображения (заголовок с сигнатурой,
типом и размерами + данные)
- `.AVI` — воспроизведение видео через встроенный видеодекодер FT812
(`MediaFifo`, полноэкранно, со звуком)
## Управление
| Клавиша | Действие |
|---------|-------------------|
| PgDn | Следующий файл |
| PgUp | Предыдущий файл |
| Esc | Выйти |
## Установка
Скопировать `ftview.wmf` в каталог плагинов Wild Commander. Плагин сам
регистрируется на `.JPG`, `.PNG`, `.DLS`, `.DXP` и `.AVI`.
## Сборка
Нужен [SDCC](http://sdcc.sourceforge.net/) версии 4.0.0 (зафиксирована в
`build.bat`) и `hex2bin` — оба есть в тулчейне SDK ZX Evolution. Версия
SDCC важна: собранные другой версией `lib/ft812`/`lib/tsconf`/`lib/sdk`
дают несовместимый ABI (разная конвенция `sdcccall`) без явной ошибки
сборки — плагин просто виснет или ведёт себя не так на реальном железе.
```
build.bat
```
Результат — `ftview.wmf` в корне проекта.
## Статус
Проверено на реальном железе: просмотр JPG/PNG, воспроизведение DLS/AVI,
навигация по файлам.
+55
View File
@@ -0,0 +1,55 @@
set SDCC=E:\SDCC\4.0.0
set path=%SDCC%\bin;E:\zx-evo-master\pentevo\tools\hex2bin;%path%
if not exist obj md obj
del /q /s obj >nul
sdasz80 -o obj/crt0.rel src/crt0.s
if errorlevel 1 pause & exit
sdcc -Isrc -I../lib/sdk -I../lib/ft812 -I../lib/tsconf -mz80 --std-sdcc11 --opt-code-speed --no-std-crt0 obj/crt0.rel --code-loc 0x8003 --data-loc 0xA800 src/main.c ../lib/ft812/ft812.lib ../lib/tsconf/ts.lib ../lib/sdk/sdk.lib -o obj/out.hex
if errorlevel 1 pause & exit
if exist obj/out.hex hex2bin obj/out.hex >nul
for %%i in (obj/out.bin) do set sz=%%~zi
set /a sz=(%sz% - 1) / 512 + 1
REM A WC plugin page is exactly 16KB = 32 512-byte blocks (WALK in WCVW.ASM
REM loads N_BLK0 blocks straight off disk into the #C000-#FFFF window as
REM one contiguous chunk - anything past 32 blocks overflows that window).
REM --data-loc 0xAE00 (the value ftview shipped with) left so much unused
REM gap between code and data that the total came to 33 blocks - one over
REM the limit. That silently broke WC's boot-time [PLUGINS] autoload
REM (wc.ini parsing never reached [LPANEL]/[RPANEL]) while F3/open-by-
REM extension still worked, since by then the plugin was already resident
REM and didn't need a fresh disk load. Confirmed on real hardware
REM 2026-08-28 by comparing against the original shipped ftview.wmf (which
REM never got anywhere near 32 blocks) and a from-GitHub unmodified
REM rebuild (which hit the exact same 33-block overflow). 0xA800 leaves
REM enough headroom above current code size without wasting most of a page.
if %sz% GTR 32 (
echo ERROR: build needs %sz% x 512-byte blocks, but a single WC plugin page holds only 32 - shrink the code or lower --data-loc's headroom above it.
pause
exit
)
REM Pad obj/out.bin up to a whole number of 512-byte blocks (sz*512 bytes).
REM hex2bin only emits the bytes actually present in the hex file, which
REM generally is NOT block-aligned, while N_BLK0 below always rounds up -
REM so the unpadded file was technically short of what the header promised
REM (not the cause of the autoload bug above - the original shipped binary
REM has the same kind of shortfall and works fine - but still worth being
REM exact about).
set /a padded=%sz% * 512
powershell -NoProfile -Command "$fs=[System.IO.File]::Open('obj\out.bin',[System.IO.FileMode]::Open,[System.IO.FileAccess]::Write); $fs.SetLength(%padded%); $fs.Close()"
sdcc -DN_BLK0=%sz% -Isrc -I../lib/sdk -I../lib/ft812 -I../lib/tsconf --no-std-crt0 -mz80 --std-sdcc11 --opt-code-speed src/wc_h.c -o obj/hdr.hex
if errorlevel 1 pause & exit
if exist obj/hdr.hex hex2bin obj/hdr.hex >nul
copy /b obj\hdr.bin + obj\out.bin ftview.wmf >nul
REM imgcpy.exe ftview.wmf unreal\wc.img=c:\wc\ftview.wmf
REM if errorlevel 1 pause & exit
REM unreal\unreal
+24
View File
@@ -0,0 +1,24 @@
.module crt0
.globl _main
.area _HEADER (ABS)
.org 0x8000
jp _main
;; Ordering of segments for the linker.
.area _HOME
.area _CODE
.area _INITIALIZER
.area _GSINIT
.area _GSFINAL
.area _DATA
.area _INITIALIZED
.area _BSEG
.area _BSS
.area _HEAP
.area _GSINIT
.area _GSFINAL
+456
View File
@@ -0,0 +1,456 @@
#include <stdio.h>
#include <string.h>
#include <defs.h>
#include <sdklib.h>
#include <ft812.h>
#include <ft812lib.h>
#include <tslib.h>
#include <wc_api.h>
#include "tsconf.h"
#include <wc_api.c>
typedef struct
{
u8 sig[3];
u8 type;
u16 xs;
u16 ys;
} DXP;
typedef struct
{
u16 sig;
u16 len;
u8 prec;
u8 ys[2];
u8 xs[2];
} SOF;
typedef struct
{
u32 sig;
u8 xs[4];
u8 ys[4];
u8 bits;
u8 ctype;
} IHDR;
typedef struct
{
u32 len;
u32 sig;
} tRNS;
// const is to get rid of vars runtime init - const located in RAM anyway
const u8 state_ft = 0;
#define FS_BUF_SIZE 0x1000
u8 fs_buf[FS_BUF_SIZE]; // at 0xAE00 (_PAGE2 to get page)
u8 cmdl[0x180];
u16 ret_sp;
u32 filesize;
u8 file_ext;
u8 call_type;
// --- Extensions ---------------------------
enum
{
EXT_JPG,
EXT_PNG,
EXT_DLS,
EXT_DXP,
EXT_AVI,
};
// --- Windows ------------------------------
const WC_TX_WINDOW err_no_ft =
{
/* window with header and text */ WC_WIND_HDR_TXT,
/* cursor color mask */ 0,
/* X,Y (position) */ 24, 10,
/* W,H (size) */ 32, 5,
/* paper/ink (window color) */ 0x2F,
/* -reserved- */ 0,
/* window restore buffer address */ 0,
/* separators */ 0, 0,
/* header text */ "\x0E" " Error! ",
/* footer text */ "",
/* window text */ "\x0E\x0C\x01" "No VDAC2 detected!",
};
const WC_TX_WINDOW win_about =
{
/* window with header and text */ WC_WIND_HDR_TXT,
/* cursor color mask */ 0,
/* X,Y (position) */ 22, 10,
/* W,H (size) */ 36, 5,
/* paper/ink (window color) */ 0x4F,
/* -reserved- */ 0,
/* window restore buffer address */ 0,
/* separators */ 0, 0,
/* header text */ "\x0E" " About ",
/* footer text */ "",
/* window text */ "\x0E\x0C\x01" "FT812 Viewer v1.2, (c)2024 TSL"
};
// --- FT812 --------------------------------
bool vdac2_init()
{
if ((ts_rreg(TS_STATUS) & 7) != 7)
return false;
ft_init(FT_MODE_1024_768_59);
return ft_rreg8(FT_REG_ID) == 0x7C;
}
void show_jpg_png(u8 is_jpg)
{
u32 sz = filesize;
u16 xs = 1024; u16 ys = 768;
u8 fmt = FT_RGB565;
u16 ofs = 0;
wc_vmode(0x87);
wc_load512(fs_buf, FS_BUF_SIZE >> 9);
if (is_jpg) // JPEG
{
int i;
for (i = 0; i < (FS_BUF_SIZE - sizeof(SOF)); i++)
{
SOF *sof = (SOF*)&fs_buf[i];
if ((sof->sig & 0xF0FF) == 0xC0FF) // SOFn
{
xs = ((u16)sof->xs[0] << 8) + sof->xs[1];
ys = ((u16)sof->ys[0] << 8) + sof->ys[1];
break;
}
}
}
else // PNG
{
int i;
for (i = 0; i < (FS_BUF_SIZE - sizeof(IHDR)); i++)
{
IHDR *ihdr = (IHDR*)&fs_buf[i];
if (ihdr->sig == 0x52444849)
{
xs = ((u16)ihdr->xs[2] << 8) + ihdr->xs[3];
ys = ((u16)ihdr->ys[2] << 8) + ihdr->ys[3];
switch (ihdr->ctype)
{
case 0:
fmt = FT_L8;
break;
case 3:
fmt = FT_PALETTED565;
ofs = 512;
for (i = 0; i < (FS_BUF_SIZE - sizeof(tRNS)); i++)
{
tRNS *trns = (tRNS*)&fs_buf[i];
if ((trns->len == 0x01000000) && (trns->sig == 0x534E5274))
{
fmt = FT_PALETTED4444;
break;
}
}
break;
case 6:
fmt = FT_ARGB4;
break;
}
break;
}
}
}
ft_ccmd_start(cmdl);
ft_Dlstart();
ft_Clear(1, 1, 1);
if (ofs) ft_PaletteSource(FT_RAM_G);
ft_SetBitmap(FT_RAM_G + ofs, fmt, xs, ys);
ft_Begin(FT_BITMAPS);
ft_Vertex2ii((1024 - min(1024, xs)) >> 1, (768 - min(768, ys)) >> 1, 0, 0);
ft_Display();
ft_ccmd(FT_CCMD_SWAP);
ft_LoadImage(FT_RAM_G, FT_OPT_NODL);
ft_ccmd_write();
while (sz)
{
u16 s;
s = min(sz, FS_BUF_SIZE);
if (!ft_load_cfifo(fs_buf, s)) return;
sz -= s;
if (sz) wc_load512(fs_buf, FS_BUF_SIZE >> 9);
}
ft_cp_wait();
}
void show_dxp()
{
u32 sz, o;
u16 xo, yo;
DXP *d;
wc_vmode(0x87);
wc_load512(fs_buf, FS_BUF_SIZE >> 9);
sz = filesize - sizeof(DXP);
d = (DXP*)fs_buf;
o = (u32)d->xs * ((u32)d->ys >> 2);
xo = (1024 - d->xs) >> 1;
yo = (768 - d->ys) >> 1;
ft_ccmd_start(cmdl);
ft_Dlstart();
ft_Clear(1, 1, 1);
// ft_ccmd(ft_SaveContext());
ft_BitmapHandle(0);
ft_SetBitmap(FT_RAM_G + o, FT_L2, d->xs, d->ys);
ft_BitmapHandle(1);
ft_SetBitmap(FT_RAM_G, FT_RGB565, d->xs >> 2, d->ys >> 2);
ft_BitmapSize(FT_NEAREST, FT_BORDER, FT_BORDER, d->xs, d->ys);
ft_Begin(FT_BITMAPS);
ft_ColorA(255);
ft_BlendFunc(FT_ONE, FT_ZERO);
ft_Vertex2ii(xo, yo, 0, 0);
ft_ColorMask(1, 1, 1, 0);
ft_BitmapTransformA(64);
ft_BitmapTransformE(64);
ft_BlendFunc(FT_ONE_MINUS_DST_ALPHA, FT_ZERO);
ft_Vertex2ii(xo, yo, 1, 0);
ft_BlendFunc(FT_DST_ALPHA, FT_ONE);
ft_Vertex2ii(xo, yo, 1, 1);
// ft_RestoreContext();
// ft_SetBase(10);
// ft_Number(0, 0, 18, 0, filesize);
ft_Display();
ft_ccmd(FT_CCMD_SWAP);
ft_ccmd_write();
ft_cp_wait();
{
u8 *bptr = fs_buf + sizeof(DXP);
u16 bsz = FS_BUF_SIZE - sizeof(DXP);
if (d->type == 0)
{
u32 gptr = FT_RAM_G;
while (sz)
{
u16 s = min(sz, bsz);
ft_write(bptr, gptr, s);
gptr += s, sz -= s, bptr = fs_buf, bsz = FS_BUF_SIZE;
if (sz) wc_load512(fs_buf, FS_BUF_SIZE >> 9);
}
}
else
{
ft_ccmd_start(cmdl);
ft_Inflate(FT_RAM_G);
ft_ccmd_write();
while (sz)
{
u16 s = min(sz, bsz);
if (!ft_load_cfifo(bptr, s)) return;
sz -= s, bptr = fs_buf, bsz = FS_BUF_SIZE;
if (sz) wc_load512(fs_buf, FS_BUF_SIZE >> 9);
}
ft_cp_wait();
}
}
}
void show_dls()
{
wc_vmode(0x87);
wc_load512(fs_buf, FS_BUF_SIZE >> 9);
ft_write_dl(fs_buf, filesize >> 2);
ft_wreg8(FT_REG_DLSWAP, FT_DLSWAP_FRAME);
}
void show_avi()
{
u32 sz = filesize;
wc_vmode(0x87);
ft_wreg32(FT_REG_FREQUENCY, 64000000UL);
ft_ccmd_start(cmdl);
ft_Dlstart();
ft_ClearColorRGB(128, 128, 160);
ft_Clear(1, 1, 1);
ft_Display();
ft_ccmd(FT_CCMD_SWAP);
ft_ccmd_write();
ft_cp_wait();
// delay(50000);
#define CHUNK_SIZE 4096
#define FIFO_ADDR 0x080000
#define FIFO_SIZE 0x080000
ft_MediaFifo(FIFO_ADDR, FIFO_SIZE);
ft_ccmd_write();
ft_cp_wait();
u32 waddr = FIFO_ADDR;
while (waddr < (FIFO_ADDR + FIFO_SIZE - CHUNK_SIZE))
{
wc_load512(fs_buf, CHUNK_SIZE >> 9);
ft_load_ram_dma(fs_buf, *(u8*)_PAGE2, waddr, CHUNK_SIZE);
waddr += CHUNK_SIZE;
}
ft_PlayVideo(OPT_FULLSCREEN | OPT_SOUND | OPT_MEDIAFIFO | OPT_NOTEAR);
ft_ccmd_write();
while (sz)
{
ft_wreg32(REG_MEDIAFIFO_WRITE, waddr);
wc_load512(fs_buf, CHUNK_SIZE >> 9);
while (1)
{
u32 raddr = ft_rreg32(REG_MEDIAFIFO_READ);
u32 free = FIFO_SIZE - ((waddr >= raddr) ? (waddr - raddr) : (FIFO_SIZE - (raddr - waddr)));
if (free >= CHUNK_SIZE) break;
}
ft_load_ram_dma(fs_buf, *(u8*)_PAGE2, waddr, CHUNK_SIZE);
waddr += CHUNK_SIZE;
if (waddr >= (FIFO_ADDR + FIFO_SIZE)) waddr = FIFO_ADDR;
}
// ft_cp_wait_free(CHUNK_SIZE);
// u16 s = min(sz, CHUNK_SIZE);
// wc_load512(fs_buf, CHUNK_SIZE >> 9);
// ft_load_ram_dma(fs_buf, *(u8*)_PAGE2, s);
// sz -= s;
}
// --- Aux functions ------------------------
void wait_esc_key()
{
while (1)
{
__asm
halt
__endasm;
if (wc_api__bool(_ESC))
break;
}
}
void about()
{
wc_api_u16(_PRWOW, (u16)&win_about);
wait_esc_key();
wc_api_u16(_RRESB, (u16)&win_about);
wc_exit(WC_EXIT);
}
void error_ft()
{
wc_api_u16(_PRWOW, (u16)&err_no_ft);
wait_esc_key();
wc_api_u16(_RRESB, (u16)&err_no_ft);
wc_exit(WC_EXIT);
}
void main_start()
{
if (!state_ft)
*(u8*)&state_ft = vdac2_init() ? 1 : 2;
if (state_ft != 1)
error_ft();
else switch (file_ext)
{
case EXT_JPG:
case EXT_PNG:
show_jpg_png(file_ext == EXT_JPG);
break;
case EXT_DXP:
show_dxp();
break;
case EXT_DLS:
show_dls();
break;
case EXT_AVI:
show_avi();
break;
}
// poll keys
while (1)
{
__asm
halt
__endasm;
if (wc_api__bool(_PGD)) // PgDn - next file
wc_exit(WC_NEXT_FILE);
if (wc_api__bool(_PGU)) // PgUp - previous file
wc_exit(WC_PREV_FILE);
if (wc_api__bool(_ESC)) // Esc - exit
wc_exit(WC_EXIT);
}
}
// ------------------------------------------
void main()
{
__asm
ld (_ret_sp), sp
ld (_call_type), a
ex af, af
ld (_file_ext), a
ld (_filesize), hl
ld (_filesize + 2), de
__endasm;
switch (call_type)
{
case WC_CALL_EXT:
main_start();
break;
case WC_CALL_MENU:
about();
break;
default:
wc_exit(WC_EXIT);
}
}
+195
View File
@@ -0,0 +1,195 @@
#pragma once
__sfr __at 0x57 SPI_DATA;
__sfr __at 0x77 SPI_CTRL;
__sfr __at 0xFE BORDER;
__sfr __banked __at 0x00AF TS_VCONFIG;
__sfr __banked __at 0x00AF TS_STATUS;
__sfr __banked __at 0x01AF TS_VPAGE;
__sfr __banked __at 0x02AF TS_GXOFFSL;
__sfr __banked __at 0x03AF TS_GXOFFSH;
__sfr __banked __at 0x04AF TS_GYOFFSL;
__sfr __banked __at 0x05AF TS_GYOFFSH;
__sfr __banked __at 0x06AF TS_TSCONFIG;
__sfr __banked __at 0x07AF TS_PALSEL;
__sfr __banked __at 0x0FAF TS_BORDER;
__sfr __banked __at 0x10AF TS_PAGE0;
__sfr __banked __at 0x11AF TS_PAGE1;
__sfr __banked __at 0x12AF TS_PAGE2;
__sfr __banked __at 0x13AF TS_PAGE3;
__sfr __banked __at 0x15AF TS_FMADDR;
__sfr __banked __at 0x16AF TS_TMPAGE;
__sfr __banked __at 0x17AF TS_T0GPAGE;
__sfr __banked __at 0x18AF TS_T1GPAGE;
__sfr __banked __at 0x19AF TS_SGPAGE;
__sfr __banked __at 0x1AAF TS_DMASADDRL;
__sfr __banked __at 0x1BAF TS_DMASADDRH;
__sfr __banked __at 0x1CAF TS_DMASADDRX;
__sfr __banked __at 0x1DAF TS_DMADADDRL;
__sfr __banked __at 0x1EAF TS_DMADADDRH;
__sfr __banked __at 0x1FAF TS_DMADADDRX;
__sfr __banked __at 0x20AF TS_SYSCONFIG;
__sfr __banked __at 0x21AF TS_MEMCONFIG;
__sfr __banked __at 0x22AF TS_HSINT;
__sfr __banked __at 0x23AF TS_VSINTL;
__sfr __banked __at 0x24AF TS_VSINTH;
__sfr __banked __at 0x25AF TS_DMAWPD;
__sfr __banked __at 0x26AF TS_DMALEN;
__sfr __banked __at 0x27AF TS_DMACTR;
__sfr __banked __at 0x27AF TS_DMASTATUS;
__sfr __banked __at 0x28AF TS_DMANUM;
__sfr __banked __at 0x29AF TS_FDDVIRT;
__sfr __banked __at 0x2AAF TS_INTMASK;
__sfr __banked __at 0x2BAF TS_CACHECONF;
__sfr __banked __at 0x2CAF TS_DMANUMH;
__sfr __banked __at 0x2DAF TS_DMAWPA;
__sfr __banked __at 0x40AF TS_T0XOFFSL;
__sfr __banked __at 0x41AF TS_T0XOFFSH;
__sfr __banked __at 0x42AF TS_T0YOFFSL;
__sfr __banked __at 0x43AF TS_T0YOFFSH;
__sfr __banked __at 0x44AF TS_T1XOFFSL;
__sfr __banked __at 0x45AF TS_T1XOFFSH;
__sfr __banked __at 0x46AF TS_T1YOFFSL;
__sfr __banked __at 0x47AF TS_T1YOFFSH;
// TS parameters
#define TS_FM_EN 0x10
// FPGA arrays
#define TS_FM_CRAM 0x0000
#define TS_FM_SFILE 0x0200
// VIDEO
#define TS_VID_256X192 0x00
#define TS_VID_320X200 0x40
#define TS_VID_320X240 0x80
#define TS_VID_360X288 0xC0
#define TS_VID_RASTER_BS 6
#define TS_VID_ZX 0x00
#define TS_VID_16C 0x01
#define TS_VID_256C 0x02
#define TS_VID_TEXT 0x03
#define TS_VID_FT812 0x04
#define TS_VID_NOGFX 0x20
#define TS_VID_MODE_BS 0
// PALSEL
#define TS_PAL_GPAL_MASK 0x0F
#define TS_PAL_GPAL_BS 0
#define TS_PAL_T0PAL_MASK 0x30
#define TS_PAL_T0PAL_BS 4
#define TS_PAL_T1PAL_MASK 0xC0
#define TS_PAL_T1PAL_BS 6
// TSU
#define TS_TSU_T0ZEN 0x04
#define TS_TSU_T1ZEN 0x08
#define TS_TSU_T0EN 0x20
#define TS_TSU_T1EN 0x40
#define TS_TSU_SEN 0x80
// SYSTEM
#define TS_SYS_ZCLK3_5 0x00
#define TS_SYS_ZCLK7 0x01
#define TS_SYS_ZCLK14 0x02
#define TS_SYS_ZCLK_BS 0
#define TS_SYS_CACHEEN 0x04
// MEMORY
#define TS_MEM_ROM128 0x01
#define TS_MEM_W0WE 0x02
#define TS_MEM_W0MAP_N 0x04
#define TS_MEM_W0RAM 0x08
#define TS_MEM_LCK512 0x00
#define TS_MEM_LCK128 0x40
#define TS_MEM_LCKAUTO 0x80
#define TS_MEM_LCK1024 0xC0
#define TS_MEM_LCK_BS 6
// INT
#define TS_INT_VEC_FRAME 0xFF
#define TS_INT_VEC_LINE 0xFD
#define TS_INT_VEC_DMA 0xFB
#define TS_INT_VEC_WTP 0xF9
#define TS_INT_MSK_FRAME 0x01
#define TS_INT_MSK_LINE 0x02
#define TS_INT_MSK_DMA 0x04
#define TS_INT_MSK_WTP 0x08
// DMA
#define TS_DMA_WNR 0x80
#define TS_DMA_SALGN 0x20
#define TS_DMA_DALGN 0x10
#define TS_DMA_ASZ 0x08
#define TS_DMA_RAM 0x01
#define TS_DMA_BLT 0x81
#define TS_DMA_FILL 0x04
#define TS_DMA_SPI_RAM 0x02
#define TS_DMA_WTP_RAM 0x07
#define TS_DMA_RAM_SPI 0x82
#define TS_DMA_IDE_RAM 0x03
#define TS_DMA_RAM_IDE 0x83
#define TS_DMA_RAM_CRAM 0x84
#define TS_DMA_RAM_SFILE 0x85
#define TS_WPD_GLU 0
#define TS_WPD_COM 1
// SPRITES
#define TS_SP_XF 0x80
#define TS_SP_YF 0x80
#define TS_SP_LEAP 0x40
#define TS_SP_ACT 0x20
#define TS_SP_SIZE8 0x00
#define TS_SP_SIZE16 0x02
#define TS_SP_SIZE24 0x04
#define TS_SP_SIZE32 0x06
#define TS_SP_SIZE40 0x08
#define TS_SP_SIZE48 0x0A
#define TS_SP_SIZE56 0x0C
#define TS_SP_SIZE64 0x0E
#define TS_SP_SIZE_BS 1
#define TS_SP_PAL_MASK 0xF0
#define TS_SP_XF_W 0x8000
#define TS_SP_YF_W 0x8000
#define TS_SP_LEAP_W 0x4000
#define TS_SP_ACT_W 0x2000
#define TS_SP_SIZE8_W 0x0000
#define TS_SP_SIZE16_W 0x0200
#define TS_SP_SIZE24_W 0x0400
#define TS_SP_SIZE32_W 0x0600
#define TS_SP_SIZE40_W 0x0800
#define TS_SP_SIZE48_W 0x0A00
#define TS_SP_SIZE56_W 0x0C00
#define TS_SP_SIZE64_W 0x0E00
#define TS_SP_SIZE_BS_W 9
#define TS_SP_X_MASK_W 0x01FF
#define TS_SP_Y_MASK_W 0x01FF
#define TS_SP_TNUM_MASK_W 0x0FFF
#define TS_SP_PAL_MASK_W 0xF000
// TILES
#define TS_TL_XF 0x40
#define TS_TL_YF 0x80
#define TS_TL_PAL_MASK 0x30
#define TS_TL_PAL_BS 4
#define TS_TL_XF_W 0x4000
#define TS_TL_YF_W 0x8000
#define TS_TL_TNUM_MASK_W 0x0FFF
#define TS_TL_PAL_MASK_W 0x3000
#define TS_TL_PAL_BS_W 12
+76
View File
@@ -0,0 +1,76 @@
bool wc_api__bool(u8 a) __naked
{
a; // to avoid SDCC warning
__asm
ld hl, #2
add hl, sp
ld a, (hl)
call _WCAPI
ld l, #0
ret z
inc l
ret
__endasm;
}
void wc_api_u16(u8 a, u16 b) __naked
{
a; // to avoid SDCC warning
b; // to avoid SDCC warning
__asm
ld hl, #2
add hl, sp
ld a, (hl)
inc hl
ld c, (hl)
inc hl
ld b, (hl)
push bc
pop ix
jp _WCAPI
__endasm;
}
bool wc_vmode(u8 a) __naked
{
a; // to avoid SDCC warning
__asm
ld hl, #2
add hl, sp
ld a, (hl)
ex af, af
ld a, #_GVmod
jp _WCAPI
__endasm;
}
u16 wc_load512(u8 *a, u8 b) __naked
{
a; // to avoid SDCC warning
b; // to avoid SDCC warning
__asm
ld hl, #2
add hl, sp
ld e, (hl)
inc hl
ld d, (hl)
inc hl
ld b, (hl)
ex de, hl
ld a, #_LOAD512
jp _WCAPI
__endasm;
}
void wc_exit(u8 rc)
{
rc; // to avoid SDCC warning
__asm
ld hl, #2
add hl, sp
ld a, (hl)
ld sp, (_ret_sp)
ret
__endasm;
}
+309
View File
@@ -0,0 +1,309 @@
// WildCommander API Header
// Refer to plugins/READ_ME.TXT
#pragma once
//---------------------------------------
// Addresses
#define _WCINT 0x5BFF // адрес обработки int wild commander
#define _PAGE0 0x6000 // номер страницы подключенной с адреса 0x0000-0x3fff
#define _PAGE1 0x6001 // номер страницы подключенной с адреса 0x4000-0x7fff
#define _PAGE2 0x6002 // номер страницы подключенной с адреса 0x8000-0xdfff
#define _PAGE3 0x6003 // номер страницы подключенной с адреса 0xc000-0xffff
#define _ABT 0x6004 // флаг выставляется, если был нажат ESC
#define _ENT 0x6005 // флаг выставляется, если был нажат ENTER
#define _WCAPI 0x6006 // точка доступа к функциям
#define _TMN 0x6009 // синхра. переменная-таймер, инкрементится по инту
//---------------------------------------
// API functions
#define _MNGC_PL 0x00 // включение страницы на 0xC000 (из выделенного блока)
// нумерация совпадает с использующейся в +36
// i:A' - номер страницы (от 0)
// 0xFF - страница с фонтом (1го текстового экрана)
// 0xFE - первый текстовый экран (в нём панели)
#define _MNG0_PL 0x78
#define _MNG8_PL 0x79
#define _PRWOW 0x01 // вывод окна на экран
// i:IX - адрес по которому лежит структура окна (SOW)
#define _RRESB 0x02 // cтирание окна (восстановление информации)
// i:IX - SOW
#define _PRSRW 0x03 // печать строки в окне
// i:IX - SOW
// HL - Text addres (должен быть в 0x8000-0xBFFF!)
// D - Y
// E - X
// BC - Length
#define _PRIAT 0x04 // выставление цвета (вызывается сразу после PRSRW)
// i:PRSRW - выставленные координаты и длина
// A' - цвет
#define _GADRW 0x05 // получение адреса в окне
// i:IX - SOW
// D - Y
// E - X
// o:HL - Address
#define _CURSOR 0x06 // печать курсора
// i:IX - SOW
#define _CURSER 0x07 // стирание курсора (восстановление цвета)
// i:IX - SOW
#define _YN 0x08 // меню ok/cancel
// i:A'
// 0x01 - инициализация (вычисляет координаты)
// 0x00 - обработка нажатий (вызывать раз в фрейм)
// 0xFF - выход
// o:NZ - выбран CANCEL
// Z - выбран OK
#define _ISTR 0x09 // редактирование строки
// i:A'
// 0xFF - инициализация (рисует курсор)
// i:HL - адрес строки
// DE - CURMAX+CURNOW (длина строки + начальная позиция курсора в ней)
// 0x00 - опрос клавиатуры
// >опрашивает LF,RG,BackSpace
// >собственно редактируется строка
// >нужно вызывать каждый фрейм
// 0x01 - выход (стирает курсор)
#define _NORK 0x0A // перевод байта в HEX (текстовый формат)
// i:HL - Text Address
// A - Value
#define _DMAPL 0x0D // работа с DMA
// i: A' - тип операции
// 0x00 - инит S и D (BHL - Source, CDE - Destination)
// 0x01 - инит S (BHL)
// 0x02 - инит D (CDE)
// 0x03 - инит S с пагой из окна (HL, B - 0-3 [номер окна])
// 0x04 - инит D с пагой из окна (HL, B - 0-3 [номер окна])
// 0x05 - выставление DMA_T (B - кол-во бёрстов)
// 0x06 - выставление DMA_N (B - размер бёрста)
//
// 0xFD - запуск без ожидания завершения (o:NZ - DMA занята)
// 0xFE - запуск с ожиданием завершения (o:NZ - DMA занята)
// 0xFF - ожидание готовности дма
//
// в функциях 0x00-0x02 формат B/C следующий:
// [7]:%1 - выбор страницы из блока выделенного плагину (0-5)
// %0 - выбор страницы из видео буферов (0-31)
// [6-0]:номер страницы
#define _TURBOPL 0x0E // i:B - выбор Z80/AY
// 0x00 - меняется частота Z80
// i:C - %00 - 3.5 MHz
// %01 - 7 MHz
// %10 - 14 MHz
// %11 - 28 MHz (в данный момент 14MHz)
// 0x01 - меняется частота AY
// i:C - %00 - 1.75 MHz
// %01 - 1.7733 MHz
// %10 - 3.5 MHz
// %11 - 3.546 MHz
#define _GEDPL 0x0F // восстановление палитры, всех офсетов и txt режима
// ! обязательно вызывать при запуске плагина!
// (включает основной txt экран)
// i:none
// клавиатура: NZ - клавиша нажата
//--------------------------------------------------
#define _SPKE 0x10 // (SPACE)
#define _UPPP 0x11 // (UP Arrow)
#define _DWWW 0x12 // (Down Arrow)
#define _LFFF 0x13 // (Left Arrow)
#define _RGGG 0x14 // (Right Arrow)
#define _TABK 0x15 // (Tab)
#define _ENKE 0x16 // (Enter)
#define _ESC 0x17 // (Escape)
#define _BSPC 0x18 // (Backspace)
#define _PGU 0x19 // (pgUP)
#define _PGD 0x1A // (pgDN)
#define _HOME 0x1B
#define _END 0x1C
#define _F1 0x1D
#define _F2 0x1E
#define _F3 0x1F
#define _F4 0x20
#define _F5 0x21
#define _F6 0x22
#define _F7 0x23
#define _F8 0x24
#define _F9 0x25
#define _F10 0x26
#define _ALT 0x27
#define _SHIFT 0x28
#define _CTRL 0x29
#define _KBSCN 0x2A // опрос клавиш
// i:A' - обработчик
// 0x00 - учитывает SHIFT (TAI1/TAI2)
// 0x01 - всегда выдает код из TAI1
// o: NZ: A - TAI1/TAI2 (see PS2P.ASM)
// Z: A=0x00 - unknown key
// A=0xFF - buffer end
#define _CAPS 0x2C // (Caps Lock)
#define _ANYK 0x2D // any key
#define _USPO 0x2E // pause for keyboard ready (recomended for NUSP)
#define _NUSP 0x2F // waiting for any key
// работа с файлами:
//---------------------------------------
#define _LOAD512 0x30 // потоковая загрузка файла
// i:HL - Address
// B - Blocks (512b)
// o:HL - New Value
#define _SAVE512 0x31 // потоковая запись файла
// i:HL - Address
// B - Blocks (512b)
// o:HL - New Value
#define _GIPAGPL 0x32 // позиционировать на начало файла
// (сразу после запуска плагина — уже вызвана)
#define _TENTRY 0x33 // получить ENTRY(32) из коммандера
// (структура как в каталоге FAT32)
// i:DE - Address
// o:DE(32) - ENTRY
#define _CHTOSEP 0x34 // разложение цепочки активного файла в сектора
// i:DE - BUFFER (куда кидать номера секторов)
// BC - BUFFER END (=BUFFER+BUFFERlenght)
#define _TENTRYN 0x35 // reserved ???
#define _TMRKDFL 0x36 // получить заголовок маркированного файла
// i:HL - File number (1-2047)
// DE - Address (32byte buffer) [0x8000-0xBFFF!]
// (if HL=0// o:BC - count of marked files)
// o:NZ - File not found or other error
// Z - Buffer updated
// >так же делается позиционированиена на начало этого файла!!!
// >соотв. функции LOAD512/SAVE512 будут читать/писать этот файл от начала.
#define _TMRKNXT 0x37 // reserved ???
#define _STREAM 0x39 // работа с потоками
// i:D - номер потока (0/1)
// B - устройство: 0-SD(ZC)
// 1-Nemo IDE Master
// 2-Nemo IDE Slave
// C - раздел (не учитывается)
// BC=0xFFFF: включает поток из D (не возвращает флагов)
// иначе создает/пересоздает поток.
// o:NZ - устройство или раздел не найдены
// Z - можно начинать работать с потоком
#define _MKFILE 0x3A // создание файла в активном каталоге
// i:DE(12) - name(8)+ext(3)+flag(1)
// HL(4) - File Size
// o:NZ - Operation failed
// A - type of error (in next versions!)
// Z - File created
// ENTRY(32) [use TENTRY]
#define _FENTRY 0x3B // поиск файла/каталога в активной директории
// i:HL - flag(1),name(1-12),0x00
// flag:0x00 - file
// 0x10 - dir
// name:"NAME.TXT","DIR"...
// o: Z - entry not found
// NZ - CALL GFILE/GDIR for activating file/dir
// [DE,HL] - file length
#define _LOAD256 0x3C // reserved ???
#define _LOADNONE 0x3D // reserved ???
#define _GFILE 0x3E // выставить указатель на начало найденного файла
// (вызывается после FENTRY!)
#define _GDIR 0x3F // сделать найденный каталог активным
// (вызывается после FENTRY!)
// работа с режимами графики
//---------------------------------------
#define _MNGV_PL 0x40 // включение видео страницы
// i:A' - номер видео страницы
// 0x00 - основной экран (тхт)
// >палитра выставляется автоматом
// >как и все режимы и смещения
// 0x01 - 1й видео буфер (16 страниц)
// 0x02 - 2й видео буфер (16 страниц)
#define _MNGCVPL 0x41 // включение страницы из видео буферов
// i:A' - номер страницы
// 0x00-0x0F - страницы из 1го видео буфера
// 0x10-0x1F - страницы из 2го видео буфера
#define _GVmod 0x42 // включение видео режима (разрешение+тип)
// i:A' - видео режим
// [7-6]: %00 - 256x192
// %01 - 320x200
// %10 - 320x240
// %11 - 360x288
// [5-2]: %0000
// [1-0]: %00 - ZX
// %01 - 16c
// %10 - 256c
// %11 - txt
#define _GYoff 0x43 // выставление смещения экрана по Y
// i:HL - Y (0-511)
#define _GXoff 0x44 // выставление смещения экрана по X
// i:HL - X (0-511)
#define _GVtm 0x69 // выставление страницы для TileMap
#define _GVtl 0x70 // выставление страницы для TileGraphics
#define _GVsgp 0x71 // выставление страницы для SpriteGraphics
typedef enum
{
WC_WIND_HDR_TXT = 1,
} WC_WIND_TYPE;
typedef struct
{
WC_WIND_TYPE type;
u8 curs_col_mask;
u8 x_pos;
u8 y_pos;
u8 x_size;
u8 y_size;
u8 attr;
u8 res0;
u16 restore_addr;
u8 div0;
u8 div1;
u16 *hdr_txt;
u16 *ftr_txt;
u16 *wnd_txt;
} WC_TX_WINDOW;
typedef enum
{
WC_CALL_EXT = 0x00,
WC_CALL_ONLOAD = 0x01,
WC_CALL_TIMER = 0x02,
WC_CALL_MENU = 0x03,
WC_CALL_EDIT = 0x14,
} WC_CALL_TYPE;
typedef enum
{
WC_EXIT = 0,
WC_TYPE_UNK = 1,
WC_NEXT_FILE = 2,
WC_REREAD_DIR = 3,
WC_PREV_FILE = 4,
} WC_EXIT_SIGNAL;
+28
View File
@@ -0,0 +1,28 @@
#include <defs.h>
#include <wc_h.h>
static const WC_HDR hdr =
{
/* +00 -reserved- */ "",
/* +16 Magic string */ {'W', 'i', 'l', 'd', 'C', 'o', 'm', 'm', 'a', 'n', 'd', 'e', 'r', 'M', 'D', 'L'},
/* +32 Format version */ 7,
/* +33 -reserved- */ "",
/* +34 Number of pages */ 1,
/* +35 Page at 0x8000 */ 0,
/* +36 Page definitions */ {
/* +0 Page, +1 NBlocks */ {0, N_BLK0}
/* */ },
/* +48 -reserved- */ "",
/* +64 Extensions */ {
/* */ {'J', 'P', 'G'},
/* */ {'P', 'N', 'G'},
/* */ {'D', 'L', 'S'},
/* */ {'D', 'X', 'P'},
/* */ {'A', 'V', 'I'},
/* */ },
/* +160 0x00 */ "",
/* +161 Max file size */ 0xFFFFFFFF,
/* +165 Plugin name */ "FT812 Viewer",
/* +197 Starting condition */ SC_MENU // also run by extension
};
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <defs.h>
typedef enum
{
SC_EXT,
SC_ONLOAD,
SC_TIMER,
SC_MENU,
} SCOND;
typedef struct
{
u8 page;
u8 size;
} WC_HDR_BLK;
typedef struct
{
u8 res0[16]; /* +00 reserved */
char mdl[16]; /* +16 Magic string */
u8 ver; /* +32 Format version */
u8 res1[1]; /* +33 reserved */
u8 num_pages; /* +34 Number of pages */
u8 page2; /* +35 Page at 0x8000 */
WC_HDR_BLK blk[6]; /* +36 Page definitions */
u8 res2[16]; /* +48 -reserved- */
char ext[32][3]; /* +64 Extensions */
u8 res3[1]; /* +160 0x00 */
u32 file_max_size; /* +161 Max file size */
char name[32]; /* +165 Plugin name */
SCOND cond; /* +197 Starting condition */
u8 res4[314]; /* +198 -reserved- */
} WC_HDR;