Shaos
22.05.2021, 08:13
Передаю в общественное достояние графическую программу просмотра спектрумовской графики в форматах SCR (6912 и 6144 байта), ATR (768 байт) и IMG (Gigascreen 13824 байта), написанную мной в марте-апреле прямо на Спринтере Sp2000 для компиляции в SOLiD-C - вот батник для сборки:
set PROG=zxview
set PREFIX=C:\SOLID\EXAMPLES
REM SolidC EXE files must be in PATH
cls 2
del %PROG%.exe
cc1 -m %PREFIX%\%PROG%
cc2 %PREFIX%\%PROG%
as %PREFIX%\%PROG%
REM LD filename length is limited to 12 characters!
%PREFIX%\ld /R6DF0 /P6E00 %PROG%,clib/l/gXMAIN /x
REM %PREFIX%\ld %PROG%,clib/l/gXMAIN /x
REM del %PROG%.tmc
REM del %PROG%.rel
REM del %PROG%.asm
Исходник программы:
/* ZXVIEW.C - Alexander "Shaos" Shabarshin <[email protected]>
25-MAR-2021 - initial version for Solid-C that shows SCR and IMG
26-MAR-2021 - v1.0 optimized and linked from 0x7000 to avoid screen snowing
27-MAR-2021 - v1.1 implemented gamma correction as per sRGB (gamma=2.2)
23-APR-2021 - v1.2 implemented incomplete palette filling and B/W SCR
25-APR-2021 - v1.3 fixed incomplete palette and also ATR support
09-MAY-2021 - v1.4 rolled back pallette function update
This code is PUBLIC DOMAIN - use it on your own RISK! */
#include <malloc.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <mem.h>
#define SCRSIZE 6912
#define ATRSIZE 768
#define BWSCRSZ 6144
union REGS inregs,outregs;
int oldmode,oldpage;
char *scr;
FILE *f;
#define WINDOW3 0xE2 /* Port for memory page 0xC000...0xFFFF */
#define PORT_Y 0x89 /* Port to set Y for videomemory */
/*
Color number Binary value BRIGHT 0 (RGB) BRIGHT 1 (RGB) Color name
---------------------------------------------------------------------------
0 000 #000000 #000000 black
1 001 #0000CD #0000FF blue
2 010 #CD0000 #FF0000 red
3 011 #CD00CD #FF00FF magenta
4 100 #00CD00 #00FF00 green
5 101 #00CDCD #00FFFF cyan
6 110 #CDCD00 #FFFF00 yellow
7 111 #CDCDCD #FFFFFF white
*/
setpal()
{
/* sRGB scale (gamma=2.2): 0% ---- 20% 40% 60% 80% 100% */
static char scale[] = { 0x00, 0x0E, 0x7C, 0xAA, 0xCB, 0xE7, 0xFF };
static char *po = (char*)(0xC000+992); /* address to apply palette */
static int i,c1,c2;
for(i=0;i<256;i++)
{
outp(PORT_Y,(char)i);
/* palette_index = (first_color<<4)|second_color */
c1 = (i&0x80)?3:2;
c2 = (i&0x08)?3:2;
po[2] = scale[((i&0x10)?c1:0)+((i&0x01)?c2:0)];
po[0] = scale[((i&0x20)?c1:0)+((i&0x02)?c2:0)];
po[1] = scale[((i&0x40)?c1:0)+((i&0x04)?c2:0)];
}
}
int getsize()
{
static f_point *fp;
fseek(f,0,0,SEEK_END);
fp = ftell(f);
fseek(f,0,0,SEEK_SET);
return fp->low;
}
int fread1(buf,sz,f)
char* buf;
int sz;
FILE *f;
{
static int i,o,r;
r = 0;
for(i=0;i<sz;i++)
{
o = fgetc(f);
if(o < 0) break;
buf[i] = o;
r++;
}
return r;
}
main(argc,argv)
int argc;
char** argv;
{
int sz;
static char c1,c2,*po;
static int i,j,k,l,l1,l2;
static int atr1,atr2,pla1,pla2;
printf("ZXVIEW v1.4 - viewer for SCR, ATR and IMG (Gigascreen) graphics\n");
printf("This code is PUBLIC DOMAIN - use it on your own RISK!\n");
printf("Created by Shaos in March-April 2021 for SOLiD-C examples\n");
printf("http://sprinter.nedopc.org\n");
if(argc<2)
{
printf("\nUsage: zxview path-to-file\n\n");
exit(-1);
}
scr = (char*)malloc(SCRSIZE*2);
if(scr==NULL) exit(-2);
printf("Screen buffer 0x%X...0x%X\n",scr,scr+SCRSIZE*2-1);
l = (int)(&inregs);
printf("Data start: 0x%X\n",l);
f = fopen(argv[1],"rb");
if(f==NULL) exit(-3);
sz = getsize();
j = -1;
i = fread1(scr,sz,f);
if(sz==ATRSIZE)
{
memcpy(&scr[BWSCRSZ],scr,ATRSIZE);
memcpy(&scr[BWSCRSZ+SCRSIZE],scr,ATRSIZE);
for(l=0;l<12;l++)
{
l1 = l<<9;
l2 = l1 + 256;
memset(&scr[l1],0x55,256);
memset(&scr[l2],0xAA,256);
memset(&scr[l1+SCRSIZE],0x55,256);
memset(&scr[l2+SCRSIZE],0xAA,256);
}
}
else if(sz<=SCRSIZE)
{
fseek(f,0,0,SEEK_SET);
j = fread1(&scr[SCRSIZE],sz,f);
}
fclose(f);
printf("File %s (%d) - read %d %d\n",argv[1],sz,i,j);
if(i==BWSCRSZ) /* B/W SCR */
{
for(j=BWSCRSZ;j<SCRSIZE;j++)
{
scr[j] = 0x07;
scr[j+SCRSIZE] = 0x07;
}
}
inregs.h.c = 0x51;
intdos(&inregs,&outregs);
oldmode = (outregs.x.bc & 0xFF00) | outregs.h.a;
printf("Old mode %04X\n",oldmode);
inregs.h.c = 0x50;
inregs.h.b = 0;
inregs.h.a = 0x81;
intdos(&inregs,&outregs);
oldpage = inp(WINDOW3);
outp(WINDOW3,0x50);
setpal();
for(j=0;j<192;j++)
{
// if(kbhit()) break;
po = (char*)0xC020; /* it's not a constant - it's incremented below! */
outp(PORT_Y,(char)(j+32));
l1 = SCRSIZE-768+((j&0xF8)<<2);
l2 = ((j&0xC0)<<5)+((j&0x07)<<8)+((j&0x38)<<2);
for(i=0;i<32;i++)
{
l = l1 + i;
atr1 = scr[l];
atr2 = scr[l+SCRSIZE];
l = l2 + i;
pla1 = scr[l];
pla2 = scr[l+SCRSIZE];
l = 0x80;
for(k=0;k<8;k++)
{
if(pla1 & l)
c1 = atr1&7;
else
c1 = (atr1>>3)&7;
if(pla2 & l)
c2 = atr2&7;
else
c2 = (atr2>>3)&7;
if(atr1 & 0x40) c1 += 8;
if(atr2 & 0x40) c2 += 8;
if(c1 >= c2)
*po++ = (c1<<4)|c2;
else
*po++ = (c2<<4)|c1;
l >>= 1;
}
}
}
getch();
free(scr);
outp(WINDOW3,oldpage);
inregs.x.bc = oldmode;
inregs.h.a = oldmode & 255;
inregs.h.c = 0x50;
intdos(&inregs,&outregs);
}
- - - Добавлено - - -
Кое какие пояснения по коду - в данный момент fread на солиде глючит, поэтому я написал свою заглушку int fread1(buf,sz,f) работающую через fgetc
Сохранение текущего графического режима с видеостраницей:
inregs.h.c = 0x51;
intdos(&inregs,&outregs);
oldmode = (outregs.x.bc & 0xFF00) | outregs.h.a;
printf("Old mode %04X\n",oldmode);
Установка графического режима 320x256 256 цветов:
inregs.h.c = 0x50;
inregs.h.b = 0;
inregs.h.a = 0x81;
intdos(&inregs,&outregs);
Сохранение страницы памяти в последнем окне и установка туда графической страницы #50:
oldpage = inp(WINDOW3);
outp(WINDOW3,0x50);
Хитрая 256-цветная палитра создаётся процедурно и записывается в видеопамять по смещению 992 (палитра №0) в функции setpal():
setpal()
{
/* sRGB scale (gamma=2.2): 0% ---- 20% 40% 60% 80% 100% */
static char scale[] = { 0x00, 0x0E, 0x7C, 0xAA, 0xCB, 0xE7, 0xFF };
static char *po = (char*)(0xC000+992); /* address to apply palette */
static int i,c1,c2;
for(i=0;i<256;i++)
{
outp(PORT_Y,(char)i);
/* palette_index = (first_color<<4)|second_color */
c1 = (i&0x80)?3:2;
c2 = (i&0x08)?3:2;
po[2] = scale[((i&0x10)?c1:0)+((i&0x01)?c2:0)];
po[0] = scale[((i&0x20)?c1:0)+((i&0x02)?c2:0)];
po[1] = scale[((i&0x40)?c1:0)+((i&0x04)?c2:0)];
}
}
Хитрость заключается в том, что взяв 2 спектрумовских 4-битных цвета c1 и c2 можно легко получить их смешение (причём с учётом гаммы sRGB) как цвет в этой 256-цветной палитре по индексу (c1<<4)|c2
Далее засылаем координату Y в порт #89 outp(PORT_Y,y) и пишем нужный цвет по смещению 0xC000+x (используя координату X для отступа от начала последнего окна памяти) - всё просто :)
- - - Добавлено - - -
Ну и в конце код возврата старой страницы памяти в последнее окно и возвращение старого графического режима со старой видеостраницей:
outp(WINDOW3,oldpage);
inregs.x.bc = oldmode;
inregs.h.a = oldmode & 255;
inregs.h.c = 0x50;
intdos(&inregs,&outregs);
- - - Добавлено - - -
Для сравнения вот как просмотрщик показывает гигаскриновскую картинку в эмуляторе (нижняя картинка) рядом с картинкой из ZXArt.ee (верхняя картинка):
https://zx-pk.ru/attachment.php?attachmentid=75446 https://zx-pk.ru/attachment.php?attachmentid=75447 https://zx-pk.ru/attachment.php?attachmentid=75448
IMG-файлы взяты по следующим ссылкам:
https://zxart.ee/rus/avtory/t/tutty/farewell-mister-arthur-fleck/
https://zxart.ee/rus/avtory/t/tmk/save-the-animals/
https://zxart.ee/rus/avtory/r/riskej/shepherds-outstanding-single/
- - - Добавлено - - -
P.S. Если кто-то придерживается мнения, что гигаскрин обязан мерцать, а у смотрящего из глаз должна сочиться кровь, лучше сюда ничего не писать :)
P.P.S. Отдаю исходник в PUBLIC DOMAIN соответственно публика может делать с ним что захочет, а я в бижайшем будущем хочу добавить просмотр мультиколорных картинок и SXG (поэтому желательно релизить под другим названием и версионностью, если кто-то что-то поменяет в нём)
set PROG=zxview
set PREFIX=C:\SOLID\EXAMPLES
REM SolidC EXE files must be in PATH
cls 2
del %PROG%.exe
cc1 -m %PREFIX%\%PROG%
cc2 %PREFIX%\%PROG%
as %PREFIX%\%PROG%
REM LD filename length is limited to 12 characters!
%PREFIX%\ld /R6DF0 /P6E00 %PROG%,clib/l/gXMAIN /x
REM %PREFIX%\ld %PROG%,clib/l/gXMAIN /x
REM del %PROG%.tmc
REM del %PROG%.rel
REM del %PROG%.asm
Исходник программы:
/* ZXVIEW.C - Alexander "Shaos" Shabarshin <[email protected]>
25-MAR-2021 - initial version for Solid-C that shows SCR and IMG
26-MAR-2021 - v1.0 optimized and linked from 0x7000 to avoid screen snowing
27-MAR-2021 - v1.1 implemented gamma correction as per sRGB (gamma=2.2)
23-APR-2021 - v1.2 implemented incomplete palette filling and B/W SCR
25-APR-2021 - v1.3 fixed incomplete palette and also ATR support
09-MAY-2021 - v1.4 rolled back pallette function update
This code is PUBLIC DOMAIN - use it on your own RISK! */
#include <malloc.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <mem.h>
#define SCRSIZE 6912
#define ATRSIZE 768
#define BWSCRSZ 6144
union REGS inregs,outregs;
int oldmode,oldpage;
char *scr;
FILE *f;
#define WINDOW3 0xE2 /* Port for memory page 0xC000...0xFFFF */
#define PORT_Y 0x89 /* Port to set Y for videomemory */
/*
Color number Binary value BRIGHT 0 (RGB) BRIGHT 1 (RGB) Color name
---------------------------------------------------------------------------
0 000 #000000 #000000 black
1 001 #0000CD #0000FF blue
2 010 #CD0000 #FF0000 red
3 011 #CD00CD #FF00FF magenta
4 100 #00CD00 #00FF00 green
5 101 #00CDCD #00FFFF cyan
6 110 #CDCD00 #FFFF00 yellow
7 111 #CDCDCD #FFFFFF white
*/
setpal()
{
/* sRGB scale (gamma=2.2): 0% ---- 20% 40% 60% 80% 100% */
static char scale[] = { 0x00, 0x0E, 0x7C, 0xAA, 0xCB, 0xE7, 0xFF };
static char *po = (char*)(0xC000+992); /* address to apply palette */
static int i,c1,c2;
for(i=0;i<256;i++)
{
outp(PORT_Y,(char)i);
/* palette_index = (first_color<<4)|second_color */
c1 = (i&0x80)?3:2;
c2 = (i&0x08)?3:2;
po[2] = scale[((i&0x10)?c1:0)+((i&0x01)?c2:0)];
po[0] = scale[((i&0x20)?c1:0)+((i&0x02)?c2:0)];
po[1] = scale[((i&0x40)?c1:0)+((i&0x04)?c2:0)];
}
}
int getsize()
{
static f_point *fp;
fseek(f,0,0,SEEK_END);
fp = ftell(f);
fseek(f,0,0,SEEK_SET);
return fp->low;
}
int fread1(buf,sz,f)
char* buf;
int sz;
FILE *f;
{
static int i,o,r;
r = 0;
for(i=0;i<sz;i++)
{
o = fgetc(f);
if(o < 0) break;
buf[i] = o;
r++;
}
return r;
}
main(argc,argv)
int argc;
char** argv;
{
int sz;
static char c1,c2,*po;
static int i,j,k,l,l1,l2;
static int atr1,atr2,pla1,pla2;
printf("ZXVIEW v1.4 - viewer for SCR, ATR and IMG (Gigascreen) graphics\n");
printf("This code is PUBLIC DOMAIN - use it on your own RISK!\n");
printf("Created by Shaos in March-April 2021 for SOLiD-C examples\n");
printf("http://sprinter.nedopc.org\n");
if(argc<2)
{
printf("\nUsage: zxview path-to-file\n\n");
exit(-1);
}
scr = (char*)malloc(SCRSIZE*2);
if(scr==NULL) exit(-2);
printf("Screen buffer 0x%X...0x%X\n",scr,scr+SCRSIZE*2-1);
l = (int)(&inregs);
printf("Data start: 0x%X\n",l);
f = fopen(argv[1],"rb");
if(f==NULL) exit(-3);
sz = getsize();
j = -1;
i = fread1(scr,sz,f);
if(sz==ATRSIZE)
{
memcpy(&scr[BWSCRSZ],scr,ATRSIZE);
memcpy(&scr[BWSCRSZ+SCRSIZE],scr,ATRSIZE);
for(l=0;l<12;l++)
{
l1 = l<<9;
l2 = l1 + 256;
memset(&scr[l1],0x55,256);
memset(&scr[l2],0xAA,256);
memset(&scr[l1+SCRSIZE],0x55,256);
memset(&scr[l2+SCRSIZE],0xAA,256);
}
}
else if(sz<=SCRSIZE)
{
fseek(f,0,0,SEEK_SET);
j = fread1(&scr[SCRSIZE],sz,f);
}
fclose(f);
printf("File %s (%d) - read %d %d\n",argv[1],sz,i,j);
if(i==BWSCRSZ) /* B/W SCR */
{
for(j=BWSCRSZ;j<SCRSIZE;j++)
{
scr[j] = 0x07;
scr[j+SCRSIZE] = 0x07;
}
}
inregs.h.c = 0x51;
intdos(&inregs,&outregs);
oldmode = (outregs.x.bc & 0xFF00) | outregs.h.a;
printf("Old mode %04X\n",oldmode);
inregs.h.c = 0x50;
inregs.h.b = 0;
inregs.h.a = 0x81;
intdos(&inregs,&outregs);
oldpage = inp(WINDOW3);
outp(WINDOW3,0x50);
setpal();
for(j=0;j<192;j++)
{
// if(kbhit()) break;
po = (char*)0xC020; /* it's not a constant - it's incremented below! */
outp(PORT_Y,(char)(j+32));
l1 = SCRSIZE-768+((j&0xF8)<<2);
l2 = ((j&0xC0)<<5)+((j&0x07)<<8)+((j&0x38)<<2);
for(i=0;i<32;i++)
{
l = l1 + i;
atr1 = scr[l];
atr2 = scr[l+SCRSIZE];
l = l2 + i;
pla1 = scr[l];
pla2 = scr[l+SCRSIZE];
l = 0x80;
for(k=0;k<8;k++)
{
if(pla1 & l)
c1 = atr1&7;
else
c1 = (atr1>>3)&7;
if(pla2 & l)
c2 = atr2&7;
else
c2 = (atr2>>3)&7;
if(atr1 & 0x40) c1 += 8;
if(atr2 & 0x40) c2 += 8;
if(c1 >= c2)
*po++ = (c1<<4)|c2;
else
*po++ = (c2<<4)|c1;
l >>= 1;
}
}
}
getch();
free(scr);
outp(WINDOW3,oldpage);
inregs.x.bc = oldmode;
inregs.h.a = oldmode & 255;
inregs.h.c = 0x50;
intdos(&inregs,&outregs);
}
- - - Добавлено - - -
Кое какие пояснения по коду - в данный момент fread на солиде глючит, поэтому я написал свою заглушку int fread1(buf,sz,f) работающую через fgetc
Сохранение текущего графического режима с видеостраницей:
inregs.h.c = 0x51;
intdos(&inregs,&outregs);
oldmode = (outregs.x.bc & 0xFF00) | outregs.h.a;
printf("Old mode %04X\n",oldmode);
Установка графического режима 320x256 256 цветов:
inregs.h.c = 0x50;
inregs.h.b = 0;
inregs.h.a = 0x81;
intdos(&inregs,&outregs);
Сохранение страницы памяти в последнем окне и установка туда графической страницы #50:
oldpage = inp(WINDOW3);
outp(WINDOW3,0x50);
Хитрая 256-цветная палитра создаётся процедурно и записывается в видеопамять по смещению 992 (палитра №0) в функции setpal():
setpal()
{
/* sRGB scale (gamma=2.2): 0% ---- 20% 40% 60% 80% 100% */
static char scale[] = { 0x00, 0x0E, 0x7C, 0xAA, 0xCB, 0xE7, 0xFF };
static char *po = (char*)(0xC000+992); /* address to apply palette */
static int i,c1,c2;
for(i=0;i<256;i++)
{
outp(PORT_Y,(char)i);
/* palette_index = (first_color<<4)|second_color */
c1 = (i&0x80)?3:2;
c2 = (i&0x08)?3:2;
po[2] = scale[((i&0x10)?c1:0)+((i&0x01)?c2:0)];
po[0] = scale[((i&0x20)?c1:0)+((i&0x02)?c2:0)];
po[1] = scale[((i&0x40)?c1:0)+((i&0x04)?c2:0)];
}
}
Хитрость заключается в том, что взяв 2 спектрумовских 4-битных цвета c1 и c2 можно легко получить их смешение (причём с учётом гаммы sRGB) как цвет в этой 256-цветной палитре по индексу (c1<<4)|c2
Далее засылаем координату Y в порт #89 outp(PORT_Y,y) и пишем нужный цвет по смещению 0xC000+x (используя координату X для отступа от начала последнего окна памяти) - всё просто :)
- - - Добавлено - - -
Ну и в конце код возврата старой страницы памяти в последнее окно и возвращение старого графического режима со старой видеостраницей:
outp(WINDOW3,oldpage);
inregs.x.bc = oldmode;
inregs.h.a = oldmode & 255;
inregs.h.c = 0x50;
intdos(&inregs,&outregs);
- - - Добавлено - - -
Для сравнения вот как просмотрщик показывает гигаскриновскую картинку в эмуляторе (нижняя картинка) рядом с картинкой из ZXArt.ee (верхняя картинка):
https://zx-pk.ru/attachment.php?attachmentid=75446 https://zx-pk.ru/attachment.php?attachmentid=75447 https://zx-pk.ru/attachment.php?attachmentid=75448
IMG-файлы взяты по следующим ссылкам:
https://zxart.ee/rus/avtory/t/tutty/farewell-mister-arthur-fleck/
https://zxart.ee/rus/avtory/t/tmk/save-the-animals/
https://zxart.ee/rus/avtory/r/riskej/shepherds-outstanding-single/
- - - Добавлено - - -
P.S. Если кто-то придерживается мнения, что гигаскрин обязан мерцать, а у смотрящего из глаз должна сочиться кровь, лучше сюда ничего не писать :)
P.P.S. Отдаю исходник в PUBLIC DOMAIN соответственно публика может делать с ним что захочет, а я в бижайшем будущем хочу добавить просмотр мультиколорных картинок и SXG (поэтому желательно релизить под другим названием и версионностью, если кто-то что-то поменяет в нём)