mirror of https://github.com/zxdos/zxuno.git
moved from old repository
This commit is contained in:
parent
9f9327bee2
commit
fa046b7b47
Binary file not shown.
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
Compilar con:
|
||||
sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node 10000
|
||||
--nostdlib --nostdinc --no-std-crt0 --code-loc 8192 joyconf.c
|
||||
*/
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
|
||||
enum {IBLACK=0, IBLUE, IRED, IMAG, IGREEN, ICYAN, IYELLOW, IWHITE};
|
||||
enum {PBLACK=0, PBLUE=8, PRED=16, PMAG=24, PGREEN=32, PCYAN=40, PYELLOW=48, PWHITE=56};
|
||||
#define BRIGHT 64
|
||||
#define FLASH 128
|
||||
|
||||
__sfr __at (0xfe) ULA;
|
||||
__sfr __at (0xff) ATTR;
|
||||
__sfr __at (0x1f) KEMPSTONADDR;
|
||||
__sfr __at (0x7f) FULLERADDR;
|
||||
|
||||
__sfr __banked __at (0xf7fe) SEMIFILA1;
|
||||
__sfr __banked __at (0xeffe) SEMIFILA2;
|
||||
__sfr __banked __at (0xfbfe) SEMIFILA3;
|
||||
__sfr __banked __at (0xdffe) SEMIFILA4;
|
||||
__sfr __banked __at (0xfdfe) SEMIFILA5;
|
||||
__sfr __banked __at (0xbffe) SEMIFILA6;
|
||||
__sfr __banked __at (0xfefe) SEMIFILA7;
|
||||
__sfr __banked __at (0x7ffe) SEMIFILA8;
|
||||
|
||||
#define ATTRP 23693
|
||||
#define ATTRT 23695
|
||||
#define BORDR 23624
|
||||
#define LASTK 23560
|
||||
|
||||
#define WAIT_VRETRACE __asm halt __endasm
|
||||
#define WAIT_HRETRACE while(ATTR!=0xff)
|
||||
#define SETCOLOR(x) *(BYTE *)(ATTRP)=(x)
|
||||
#define LASTKEY *(BYTE *)(LASTK)
|
||||
#define ATTRPERMANENT *((BYTE *)(ATTRP))
|
||||
#define ATTRTEMPORARY *((BYTE *)(ATTRT))
|
||||
#define BORDERCOLOR *((BYTE *)(BORDR))
|
||||
|
||||
__sfr __banked __at (0xfc3b) ZXUNOADDR;
|
||||
__sfr __banked __at (0xfd3b) ZXUNODATA;
|
||||
#define MASTERCONF 0
|
||||
#define SCANCODE 4
|
||||
#define KEYBSTAT 5
|
||||
#define JOYCONF 6
|
||||
#define COREID 255
|
||||
|
||||
void __sdcc_enter_ix (void) __naked;
|
||||
void cls (BYTE);
|
||||
void border (BYTE);
|
||||
void locate (BYTE, BYTE);
|
||||
|
||||
void puts (BYTE *);
|
||||
void putdec (WORD);
|
||||
void u16todec (WORD, char *);
|
||||
void u16tohex (WORD, char *);
|
||||
void u8tohex (BYTE, char *);
|
||||
|
||||
void memset (BYTE *, BYTE, WORD);
|
||||
void memcpy (BYTE *, BYTE *, WORD);
|
||||
|
||||
int abs (int);
|
||||
signed char sgn (int);
|
||||
|
||||
long frames (void) __naked;
|
||||
void pause (BYTE);
|
||||
|
||||
void wait_key (void);
|
||||
BYTE inkey (BYTE, BYTE);
|
||||
|
||||
void beep (WORD, BYTE) __critical;
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
BYTE printconf (void);
|
||||
void printstatictext (void);
|
||||
void getcoreid(BYTE *s);
|
||||
void interactivemode (void);
|
||||
void usage (void);
|
||||
void commandlinemode (char *p);
|
||||
|
||||
BYTE main (char *p);
|
||||
|
||||
void init (void) __naked
|
||||
{
|
||||
__asm
|
||||
push hl
|
||||
call _main
|
||||
pop af
|
||||
or a ;reset carry: clean exit
|
||||
ld b,h
|
||||
ld c,l
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
BYTE main (char *p)
|
||||
{
|
||||
if (!p)
|
||||
interactivemode();
|
||||
else
|
||||
commandlinemode(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void commandlinemode (char *p)
|
||||
{
|
||||
BYTE kbdconf, db9conf;
|
||||
|
||||
ZXUNOADDR = JOYCONF;
|
||||
kbdconf = ZXUNODATA & 0xf;
|
||||
db9conf = (ZXUNODATA>>4) & 0xf;
|
||||
while (*p!=0 && *p!=0xd && *p!=':')
|
||||
{
|
||||
if (*p==' ')
|
||||
{
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
if (*p=='-')
|
||||
{
|
||||
p++;
|
||||
if (*p=='k')
|
||||
{
|
||||
p++;
|
||||
kbdconf &= 0x8;
|
||||
switch (*p)
|
||||
{
|
||||
case 'd': kbdconf |= 0; break;
|
||||
case 'k': kbdconf |= 1; break;
|
||||
case '1': kbdconf |= 2; break;
|
||||
case '2': kbdconf |= 3; break;
|
||||
case 'c': kbdconf |= 4; break;
|
||||
case 'f': kbdconf |= 5; break;
|
||||
default: usage(); return;
|
||||
}
|
||||
p++;
|
||||
if (*p=='1')
|
||||
{
|
||||
kbdconf |= 0x8;
|
||||
p++;
|
||||
}
|
||||
else if (*p=='0')
|
||||
{
|
||||
kbdconf &= 0x7;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
else if (*p=='j')
|
||||
{
|
||||
p++;
|
||||
db9conf &= 0x8;
|
||||
switch (*p)
|
||||
{
|
||||
case 'd': db9conf |= 0; break;
|
||||
case 'k': db9conf |= 1; break;
|
||||
case '1': db9conf |= 2; break;
|
||||
case '2': db9conf |= 3; break;
|
||||
case 'c': db9conf |= 4; break;
|
||||
case 'f': db9conf |= 5; break;
|
||||
case 'o': db9conf |= 6; break;
|
||||
default: usage(); return;
|
||||
}
|
||||
p++;
|
||||
if (*p=='1')
|
||||
{
|
||||
db9conf |= 0x8;
|
||||
p++;
|
||||
}
|
||||
else if (*p=='0')
|
||||
{
|
||||
db9conf &= 0x7;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ZXUNODATA = db9conf<<4 | kbdconf;
|
||||
}
|
||||
|
||||
void usage (void)
|
||||
{
|
||||
// 01234567890123456789012345678901
|
||||
puts ("Configures/tests protocols for\xd"
|
||||
"keyb joystick and DB9 joystick\xd\xd"
|
||||
"Usage: JOYCONF [-kAx] [-jBx]\xd"
|
||||
" where A,B can be:\xd"
|
||||
" d: Disabled\xd"
|
||||
" k: Kempston\xd"
|
||||
" 1: Sinclair port 1\xd"
|
||||
" 2: Sinclair port 2\xd"
|
||||
" c: Cursor/Protek/AGF\xd"
|
||||
" f: Fuller\xd"
|
||||
" o: OPQA-SPACE-M (DB9 only)\xd"
|
||||
" where x can be:\xd"
|
||||
" 0: disable autofire\xd"
|
||||
" 1: enable autofire\xd"
|
||||
" other/none: keep setting\xd"
|
||||
" No arguments: interactive mode\xd\xd"
|
||||
"Example: .joyconf -kc0 -jk1\xd"
|
||||
"Sets Cursor, no autofire for the"
|
||||
"keyboard joystick, and Kempston\xd"
|
||||
"w/autofire for the DB9 joystick.\xd");
|
||||
}
|
||||
|
||||
void interactivemode (void)
|
||||
{
|
||||
BYTE bkbor, bkattr;
|
||||
|
||||
bkbor = BORDERCOLOR;
|
||||
bkattr = ATTRPERMANENT;
|
||||
|
||||
border(IBLACK);
|
||||
cls(BRIGHT|PBLACK|IWHITE);
|
||||
printstatictext();
|
||||
while(!printconf());
|
||||
|
||||
border(bkbor);
|
||||
cls(bkattr);
|
||||
}
|
||||
|
||||
|
||||
void printstatictext (void)
|
||||
{
|
||||
char coreid[32];
|
||||
|
||||
locate(0,0);
|
||||
puts ("\x4\x78JOYSTICK CONFIGURATION AND TEST ");
|
||||
|
||||
locate(2,0);
|
||||
puts ("KBD joystick: ");
|
||||
|
||||
locate(3,0);
|
||||
puts ("DB9 joystick: ");
|
||||
|
||||
locate(5,0);
|
||||
puts("\x4\x45T/G to change KBD/DB9 protocol");
|
||||
|
||||
locate(6,0);
|
||||
puts("\x4\x45W/S to change KBD/DB9 autofire");
|
||||
|
||||
locate(20,0);
|
||||
puts("\x4\x47ZX-UNO Core ID: ");
|
||||
coreid[0]=0x4;
|
||||
coreid[1]=0x46;
|
||||
getcoreid(coreid+2);
|
||||
if (coreid[2]==0)
|
||||
puts("\x4\x46NOT AVAILABLE");
|
||||
else
|
||||
puts(coreid);
|
||||
|
||||
locate(21,0);
|
||||
puts("\x4\x70 Press 'E' to exit ");
|
||||
}
|
||||
|
||||
void getcoreid(BYTE *s)
|
||||
{
|
||||
BYTE cont;
|
||||
volatile BYTE letra;
|
||||
|
||||
s[0]='\0';
|
||||
ZXUNOADDR = COREID;
|
||||
cont=0;
|
||||
while (ZXUNODATA!=0 && cont<32) cont++;
|
||||
if (cont==32)
|
||||
return;
|
||||
cont=0;
|
||||
do
|
||||
{
|
||||
letra = ZXUNODATA;
|
||||
cont++;
|
||||
}
|
||||
while (letra==0 && cont<32);
|
||||
if (cont==32)
|
||||
return;
|
||||
*(s++) = letra;
|
||||
do
|
||||
{
|
||||
letra = ZXUNODATA;
|
||||
*(s++) = letra;
|
||||
}
|
||||
while (letra!=0);
|
||||
}
|
||||
|
||||
void printjoystat (char *proto, BYTE joy) // FUDLR
|
||||
{
|
||||
puts (proto);
|
||||
if (joy&0x8)
|
||||
puts ("\x4\x78 U ");
|
||||
else
|
||||
puts (" U ");
|
||||
if (joy&0x4)
|
||||
puts ("\x4\x78 D ");
|
||||
else
|
||||
puts (" D ");
|
||||
if (joy&0x2)
|
||||
puts ("\x4\x78 L ");
|
||||
else
|
||||
puts (" L ");
|
||||
if (joy&0x1)
|
||||
puts ("\x4\x78 R ");
|
||||
else
|
||||
puts (" R ");
|
||||
if (joy&0x10)
|
||||
puts ("\x4\x78 F ");
|
||||
else
|
||||
puts (" F ");
|
||||
if (joy&0x20)
|
||||
puts ("\x4\x78 B2 ");
|
||||
else
|
||||
puts (" B2 ");
|
||||
}
|
||||
|
||||
BYTE printconf (void)
|
||||
{
|
||||
BYTE kbconf, db9conf, kbdis=0, db9dis=0;
|
||||
BYTE joy, joy1, joy2, joyb2, joyOP, joyQ, joyA, joySPCM;
|
||||
|
||||
WAIT_VRETRACE;
|
||||
ZXUNOADDR = JOYCONF;
|
||||
kbconf = ZXUNODATA & 0x0f;
|
||||
db9conf = (ZXUNODATA >> 4) & 0x0f;
|
||||
locate(2,14);
|
||||
switch (kbconf&0x7)
|
||||
{
|
||||
case 1: puts("\x4\x46""KEMPSTON"); break;
|
||||
case 2: puts("\x4\x46""SINCL P1"); break;
|
||||
case 3: puts("\x4\x46""SINCL P2"); break;
|
||||
case 4: puts("\x4\x46""CURSOR "); break;
|
||||
case 5: puts("\x4\x46""FULLER "); break;
|
||||
default: puts("\x4\x46""DISABLED"); kbdis=1; break;
|
||||
}
|
||||
if (!kbdis && kbconf&0x8)
|
||||
puts("\x4\x45 AUTOFIRE");
|
||||
else
|
||||
puts(" ");
|
||||
|
||||
locate(3,14);
|
||||
switch (db9conf&0x7)
|
||||
{
|
||||
case 1: puts("\x4\x46""KEMPSTON"); break;
|
||||
case 2: puts("\x4\x46""SINCL P1"); break;
|
||||
case 3: puts("\x4\x46""SINCL P2"); break;
|
||||
case 4: puts("\x4\x46""CURSOR "); break;
|
||||
case 5: puts("\x4\x46""FULLER "); break;
|
||||
case 6: puts("\x4\x46""OPQASPCM"); break;
|
||||
default: puts("\x4\x46""DISABLED"); db9dis=1; break;
|
||||
}
|
||||
if (!db9dis && db9conf&0x8)
|
||||
puts("\x4\x45 AUTOFIRE");
|
||||
else
|
||||
puts(" ");
|
||||
|
||||
if (LASTKEY == 't' || LASTKEY == 't')
|
||||
{
|
||||
kbconf = kbconf&0x8 | (((kbconf&7)+1==6)? 0 : (kbconf&7)+1);
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 'g' || LASTKEY == 'G')
|
||||
{
|
||||
db9conf = db9conf&0x8 | (((db9conf&7)+1==7)? 0 : (db9conf&7)+1); //q
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 'w' || LASTKEY == 'W')
|
||||
{
|
||||
kbconf ^= 0x8;
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 's' || LASTKEY == 'S')
|
||||
{
|
||||
db9conf ^= 0x8;
|
||||
LASTKEY = 0;
|
||||
}
|
||||
ZXUNODATA = db9conf<<4 | kbconf;
|
||||
|
||||
joy = KEMPSTONADDR;
|
||||
locate(8,0); printjoystat("\x4\x7Kempston : ", joy);
|
||||
|
||||
joyb2 = ~SEMIFILA7; // -----2-- a --2FUDLR
|
||||
joy = ~SEMIFILA2; // LRDUF a --2FUDLR
|
||||
joy = (joyb2&4)<<3 | (joy&1)<<4 | (joy&2)<<2 | (joy&4) | (joy&0x10)>>3 | (joy&8)>>3;
|
||||
locate(10,0); printjoystat("\x4\x7Sinclair 1: ", joy);
|
||||
|
||||
joyb2 = ~SEMIFILA7; // ------2- a --2FUDLR
|
||||
joy = ~SEMIFILA1; // FUDRL a --2FUDLR
|
||||
joy = (joyb2&2)<<4 | (joy&0x1c) | (joy&2)>>1 | (joy&1)<<1;
|
||||
locate(12,0); printjoystat("\x4\x7Sinclair 2: ", joy);
|
||||
|
||||
joy1 = ~SEMIFILA2; // DUR2F a --2FUDLR
|
||||
joy2 = ~SEMIFILA1; // L---- a --2FUDLR
|
||||
joy = (joy1&2)<<4 | (joy1&1)<<4 | (joy1&8) | (joy1&0x10)>>2 | (joy2&0x10)>>3 | (joy1&4)>>2;
|
||||
locate(14,0); printjoystat("\x4\x7""Cursor : ", joy);
|
||||
|
||||
joy = ~FULLERADDR; // F2--RLDU a --2FUDLR
|
||||
joy = (joy&0x40)>>1 | (joy&0x80)>>3 | (joy&1)<<3 | (joy&2)<<1 | (joy&4)>>1 | (joy&8)>>3;
|
||||
locate(16,0); printjoystat("\x4\x7""Fuller : ", joy);
|
||||
|
||||
//Nuevo modo joy OPQASPCM
|
||||
joyOP = ~SEMIFILA4; // ------LR a --2FUDLR
|
||||
joyQ = ~SEMIFILA3; // -------U a --2FUDLR
|
||||
joyA = ~SEMIFILA5; // -------D a --2FUDLR
|
||||
joySPCM = ~SEMIFILA8; // -----2-F a --2FUDLR
|
||||
joy = (joySPCM&4)<<3 | (joySPCM&1)<<4 | (joyQ&1)<<3 | (joyA&1)<<2 | (joyOP&2) | (joyOP&1);
|
||||
locate(18,0); printjoystat("\x4\x7""OPQASPCM : ", joy);
|
||||
|
||||
//if (LASTKEY==' ')
|
||||
if (LASTKEY == 'e' || LASTKEY == 'E')
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
#pragma disable_warning 85
|
||||
int abs (int n)
|
||||
{
|
||||
return (n>=0)?n:-n;
|
||||
}
|
||||
|
||||
signed char sgn (int n)
|
||||
{
|
||||
return (n>=0)?1:-1;
|
||||
}
|
||||
|
||||
long frames (void) __naked
|
||||
{
|
||||
__asm
|
||||
ld a,(#23672)
|
||||
ld l,a
|
||||
ld a,(#23673)
|
||||
ld h,a
|
||||
ld a,(#23674)
|
||||
ld e,a
|
||||
ld d,#0
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void pause (BYTE frames)
|
||||
{
|
||||
BYTE i;
|
||||
|
||||
for (i=0;i!=frames;i++)
|
||||
{
|
||||
WAIT_VRETRACE;
|
||||
}
|
||||
}
|
||||
|
||||
void memset (BYTE *dir, BYTE val, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
ld a,6(ix)
|
||||
ld c,7(ix)
|
||||
ld b,8(ix)
|
||||
ld d,h
|
||||
ld e,l
|
||||
inc de
|
||||
dec bc
|
||||
ld (hl),a
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void memcpy (BYTE *dst, BYTE *fue, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld e,4(ix)
|
||||
ld d,5(ix)
|
||||
ld l,6(ix)
|
||||
ld h,7(ix)
|
||||
ld c,8(ix)
|
||||
ld b,9(ix)
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void cls (BYTE attr)
|
||||
{
|
||||
memset((BYTE *)16384,0,6144);
|
||||
memset((BYTE *)22528,attr,768);
|
||||
SETCOLOR(attr);
|
||||
}
|
||||
|
||||
void border (BYTE b)
|
||||
{
|
||||
ULA=(b>>3)&0x7;
|
||||
*((BYTE *)BORDR)=b;
|
||||
}
|
||||
|
||||
void puts (BYTE *str)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,(#ATTRT)
|
||||
push af
|
||||
ld a,(#ATTRP)
|
||||
ld (#ATTRT),a
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
buc_print:
|
||||
ld a,(hl)
|
||||
or a
|
||||
jp z,fin_print
|
||||
cp #4
|
||||
jr nz,no_attr
|
||||
inc hl
|
||||
ld a,(hl)
|
||||
ld (#ATTRT),a
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_attr:
|
||||
rst #16
|
||||
inc hl
|
||||
jp buc_print
|
||||
|
||||
fin_print:
|
||||
pop af
|
||||
ld (#ATTRT),a
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void locate (BYTE row, BYTE col)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,#22
|
||||
rst #16
|
||||
ld a,4(ix)
|
||||
rst #16
|
||||
ld a,5(ix)
|
||||
rst #16
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
// void putdec (WORD n)
|
||||
// {
|
||||
// BYTE num[6];
|
||||
//
|
||||
// u16todec (n,num);
|
||||
// puts (num);
|
||||
// }
|
||||
//
|
||||
// void u16todec (WORD n, char *s)
|
||||
// {
|
||||
// BYTE i=4;
|
||||
// WORD divisor=10, resto;
|
||||
//
|
||||
// memset(s,' ',5);
|
||||
// do
|
||||
// {
|
||||
// resto=n%divisor;
|
||||
// n/=divisor;
|
||||
// s[i--]=resto+'0';
|
||||
// }
|
||||
// while (n);
|
||||
// s[5]='\0';
|
||||
// }
|
||||
|
||||
void u16tohex (WORD n, char *s)
|
||||
{
|
||||
u8tohex((n>>8)&0xFF,s);
|
||||
u8tohex(n&0xFF,s+2);
|
||||
}
|
||||
|
||||
void u8tohex (BYTE n, char *s)
|
||||
{
|
||||
BYTE i=1;
|
||||
BYTE resto;
|
||||
|
||||
resto=n&0xF;
|
||||
s[1]=(resto>9)?resto+55:resto+48;
|
||||
resto=n>>4;
|
||||
s[0]=(resto>9)?resto+55:resto+48;
|
||||
s[2]='\0';
|
||||
}
|
||||
|
||||
void wait_key (void)
|
||||
{
|
||||
while ((ULA&0x1f)==0x1f);
|
||||
}
|
||||
|
||||
BYTE inkey (BYTE semif, BYTE pos)
|
||||
{
|
||||
BYTE teclas;
|
||||
BYTE posbit;
|
||||
|
||||
switch (semif)
|
||||
{
|
||||
case 1: teclas=SEMIFILA1; break;
|
||||
case 2: teclas=SEMIFILA2; break;
|
||||
case 3: teclas=SEMIFILA3; break;
|
||||
case 4: teclas=SEMIFILA4; break;
|
||||
case 5: teclas=SEMIFILA5; break;
|
||||
case 6: teclas=SEMIFILA6; break;
|
||||
case 7: teclas=SEMIFILA7; break;
|
||||
case 8: teclas=SEMIFILA8; break;
|
||||
default: teclas=ULA; break;
|
||||
}
|
||||
posbit=1<<(pos-1);
|
||||
return (teclas&posbit)?0:1;
|
||||
}
|
||||
|
||||
void beep (WORD durmili, BYTE freq) __critical
|
||||
{
|
||||
volatile BYTE cborde;
|
||||
|
||||
cborde=(*(BYTE *)(BORDR));
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
|
||||
ld l,6(ix) ;se desplaza dos byte por ser "critical".
|
||||
ld h,7(ix)
|
||||
ld d,-1(ix)
|
||||
|
||||
ld b,8(ix)
|
||||
xor a
|
||||
sub b
|
||||
ld b,a
|
||||
ld c,a
|
||||
|
||||
bucbeep:
|
||||
ld a,d
|
||||
xor #0x18
|
||||
ld d,a
|
||||
out (#254),a
|
||||
|
||||
ld b,c
|
||||
bucperiodobeep:
|
||||
djnz bucperiodobeep
|
||||
|
||||
dec hl
|
||||
ld a,h
|
||||
or l
|
||||
jr nz,bucbeep
|
||||
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void __sdcc_enter_ix (void) __naked
|
||||
{
|
||||
__asm
|
||||
pop hl ; return address
|
||||
push ix ; save frame pointer
|
||||
ld ix,#0
|
||||
add ix,sp ; set ix to the stack frame
|
||||
jp (hl) ; and return
|
||||
__endasm;
|
||||
}
|
||||
|
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
Compilar con:
|
||||
sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node X
|
||||
--alow-unsafe-reads --nostdlib --nostdinc --no-std-crt0 --out-fmt-s19
|
||||
--port-mode=z80 --code-loc 8192 --data-loc 0 --stack-loc 65535 joyconf.sdcc
|
||||
*/
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
|
||||
enum {IBLACK=0, IBLUE, IRED, IMAG, IGREEN, ICYAN, IYELLOW, IWHITE};
|
||||
enum {PBLACK=0, PBLUE=8, PRED=16, PMAG=24, PGREEN=32, PCYAN=40, PYELLOW=48, PWHITE=56};
|
||||
#define BRIGHT 64
|
||||
#define FLASH 128
|
||||
|
||||
__sfr __at (0xfe) ULA;
|
||||
__sfr __at (0xff) ATTR;
|
||||
__sfr __at (0x1f) KEMPSTONADDR;
|
||||
|
||||
__sfr __banked __at (0xf7fe) SEMIFILA1;
|
||||
__sfr __banked __at (0xeffe) SEMIFILA2;
|
||||
__sfr __banked __at (0xfbfe) SEMIFILA3;
|
||||
__sfr __banked __at (0xdffe) SEMIFILA4;
|
||||
__sfr __banked __at (0xfdfe) SEMIFILA5;
|
||||
__sfr __banked __at (0xbffe) SEMIFILA6;
|
||||
__sfr __banked __at (0xfefe) SEMIFILA7;
|
||||
__sfr __banked __at (0x7ffe) SEMIFILA8;
|
||||
|
||||
#define COORDS 23296
|
||||
#define SPOSN 23298
|
||||
#define ATTRP 23300
|
||||
#define BORDR 23301
|
||||
#define CHARS 23606
|
||||
#define LASTK 23560
|
||||
|
||||
#define COLUMN (SPOSN)
|
||||
#define ROW (SPOSN+1)
|
||||
|
||||
#define WAIT_VRETRACE __asm halt __endasm
|
||||
#define WAIT_HRETRACE while(ATTR!=0xff)
|
||||
#define SETCOLOR(x) *(BYTE *)(ATTRP)=(x)
|
||||
#define LASTKEY *(BYTE *)(LASTK)
|
||||
|
||||
__sfr __banked __at (0xfc3b) ZXUNOADDR;
|
||||
__sfr __banked __at (0xfd3b) ZXUNODATA;
|
||||
#define MASTERCONF 0
|
||||
#define SCANCODE 4
|
||||
#define KEYBSTAT 5
|
||||
#define JOYCONF 6
|
||||
#define COREID 255
|
||||
|
||||
void __sdcc_enter_ix (void) __naked;
|
||||
void cls (BYTE);
|
||||
void border (BYTE);
|
||||
void locate (BYTE, BYTE);
|
||||
|
||||
void puts (BYTE *);
|
||||
void putdec (WORD);
|
||||
void u16todec (WORD, char *);
|
||||
void u16tohex (WORD, char *);
|
||||
void u8tohex (BYTE, char *);
|
||||
|
||||
void memset (BYTE *, BYTE, WORD);
|
||||
void memcpy (BYTE *, BYTE *, WORD);
|
||||
|
||||
int abs (int);
|
||||
signed char sgn (int);
|
||||
|
||||
long frames (void) __naked;
|
||||
void pause (BYTE);
|
||||
|
||||
void wait_key (void);
|
||||
BYTE inkey (BYTE, BYTE);
|
||||
|
||||
void beep (WORD, BYTE) __critical;
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
BYTE printconf (void);
|
||||
void printstatictext (void);
|
||||
void getcoreid(BYTE *s);
|
||||
|
||||
void main (void)
|
||||
{
|
||||
border(IBLACK);
|
||||
cls(BRIGHT|PBLACK|IWHITE);
|
||||
printstatictext();
|
||||
while(!printconf());
|
||||
|
||||
border(*(BYTE *)(23624));
|
||||
cls(*(BYTE *)(23693));
|
||||
}
|
||||
|
||||
void printstatictext (void)
|
||||
{
|
||||
char coreid[32];
|
||||
|
||||
locate(0,0);
|
||||
puts ("\x4\x78\x6JOYSTICK CONFIGURATION AND TEST ");
|
||||
|
||||
locate(2,0);
|
||||
puts ("KBD joystick: ");
|
||||
|
||||
locate(3,0);
|
||||
puts ("DB9 joystick: ");
|
||||
|
||||
locate(5,0);
|
||||
puts("\x4\x45Q/A to change KBD/DB9 protocol");
|
||||
|
||||
locate(6,0);
|
||||
puts("\x4\x45W/S to change KBD/DB9 autofire");
|
||||
|
||||
locate(20,0);
|
||||
puts("\x4\x47ZX-UNO Core ID: ");
|
||||
coreid[0]=0x4;
|
||||
coreid[1]=0x46;
|
||||
getcoreid(coreid+2);
|
||||
if (coreid[2]==0)
|
||||
puts("\x4\x46NOT AVAILABLE");
|
||||
else
|
||||
puts(coreid);
|
||||
|
||||
locate(22,0);
|
||||
puts("\x4\x70 Press SPACE to exit ");
|
||||
}
|
||||
|
||||
void getcoreid(BYTE *s)
|
||||
{
|
||||
BYTE cont;
|
||||
volatile BYTE letra;
|
||||
|
||||
s[0]='\0';
|
||||
ZXUNOADDR = COREID;
|
||||
cont=0;
|
||||
while (ZXUNODATA!=0 && cont<32) cont++;
|
||||
if (cont==32)
|
||||
return;
|
||||
cont=0;
|
||||
do
|
||||
{
|
||||
letra = ZXUNODATA;
|
||||
cont++;
|
||||
}
|
||||
while (letra==0 && cont<32);
|
||||
if (cont==32)
|
||||
return;
|
||||
*(s++) = letra;
|
||||
do
|
||||
{
|
||||
letra = ZXUNODATA;
|
||||
*(s++) = letra;
|
||||
}
|
||||
while (letra!=0);
|
||||
}
|
||||
|
||||
void printjoystat (char *proto, BYTE joy) // FUDLR
|
||||
{
|
||||
puts (proto);
|
||||
if (joy&0x8)
|
||||
puts ("\x4\x78 U ");
|
||||
else
|
||||
puts (" U ");
|
||||
if (joy&0x4)
|
||||
puts ("\x4\x78 D ");
|
||||
else
|
||||
puts (" D ");
|
||||
if (joy&0x2)
|
||||
puts ("\x4\x78 L ");
|
||||
else
|
||||
puts (" L ");
|
||||
if (joy&0x1)
|
||||
puts ("\x4\x78 R ");
|
||||
else
|
||||
puts (" R ");
|
||||
if (joy&0x10)
|
||||
puts ("\x4\x78 F ");
|
||||
else
|
||||
puts (" F ");
|
||||
}
|
||||
|
||||
BYTE printconf (void)
|
||||
{
|
||||
BYTE kbconf, db9conf, kbdis=0, db9dis=0;
|
||||
BYTE joy, joy1, joy2;
|
||||
|
||||
WAIT_VRETRACE;
|
||||
ZXUNOADDR = JOYCONF;
|
||||
kbconf = ZXUNODATA & 0x0f;
|
||||
db9conf = (ZXUNODATA >> 4) & 0x0f;
|
||||
locate(2,14);
|
||||
switch (kbconf&0x7)
|
||||
{
|
||||
case 1: puts("\x4\x46""KEMPSTON"); break;
|
||||
case 2: puts("\x4\x46""SINCL P1"); break;
|
||||
case 3: puts("\x4\x46""SINCL P2"); break;
|
||||
case 4: puts("\x4\x46""CURSOR "); break;
|
||||
default: puts("\x4\x46""DISABLED"); kbdis=1; break;
|
||||
}
|
||||
if (!kbdis && kbconf&0x8)
|
||||
puts("\x4\x45 AUTOFIRE");
|
||||
else
|
||||
puts(" ");
|
||||
|
||||
locate(3,14);
|
||||
switch (db9conf&0x7)
|
||||
{
|
||||
case 1: puts("\x4\x46""KEMPSTON"); break;
|
||||
case 2: puts("\x4\x46""SINCL P1"); break;
|
||||
case 3: puts("\x4\x46""SINCL P2"); break;
|
||||
case 4: puts("\x4\x46""CURSOR "); break;
|
||||
default: puts("\x4\x46""DISABLED"); db9dis=1; break;
|
||||
}
|
||||
if (!db9dis && db9conf&0x8)
|
||||
puts("\x4\x45 AUTOFIRE");
|
||||
else
|
||||
puts(" ");
|
||||
|
||||
if (LASTKEY == 'q' || LASTKEY == 'Q')
|
||||
{
|
||||
kbconf = kbconf&0x8 | (((kbconf&7)+1==5)? 0 : (kbconf&7)+1);
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 'a' || LASTKEY == 'A')
|
||||
{
|
||||
db9conf = db9conf&0x8 | (((db9conf&7)+1==5)? 0 : (db9conf&7)+1);
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 'w' || LASTKEY == 'W')
|
||||
{
|
||||
kbconf ^= 0x8;
|
||||
LASTKEY = 0;
|
||||
}
|
||||
else if (LASTKEY == 's' || LASTKEY == 's')
|
||||
{
|
||||
db9conf ^= 0x8;
|
||||
LASTKEY = 0;
|
||||
}
|
||||
ZXUNODATA = db9conf<<4 | kbconf;
|
||||
|
||||
joy = KEMPSTONADDR;
|
||||
locate(9,0); printjoystat("\x4\x7Kempston : ", joy);
|
||||
|
||||
joy = ~SEMIFILA2; // LRDUF a FUDLR
|
||||
joy = (joy&1)<<4 | (joy&2)<<2 | (joy&4) | (joy&0x10)>>3 | (joy&8)>>3;
|
||||
locate(11,0); printjoystat("\x4\x7Sinclair 1: ", joy);
|
||||
|
||||
joy = ~SEMIFILA1; // FUDRL a FUDLR
|
||||
joy = (joy&0x1c) | (joy&2)>>1 | (joy&1)<<1;
|
||||
locate(13,0); printjoystat("\x4\x7Sinclair 2: ", joy);
|
||||
|
||||
joy1 = ~SEMIFILA2; // DUR-F a FUDLR
|
||||
joy2 = ~SEMIFILA1; // L---- a FUDLR
|
||||
joy = (joy1&1)<<4 | (joy1&8) | (joy1&0x10)>>2 | (joy2&0x10)>>3 | (joy1&4)>>2;
|
||||
locate(15,0); printjoystat("\x4\x7""Cursor : ", joy);
|
||||
|
||||
if (LASTKEY==' ')
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
#pragma disable_warning 85
|
||||
int abs (int n)
|
||||
{
|
||||
return (n>=0)?n:-n;
|
||||
}
|
||||
|
||||
signed char sgn (int n)
|
||||
{
|
||||
return (n>=0)?1:-1;
|
||||
}
|
||||
|
||||
long frames (void) __naked
|
||||
{
|
||||
__asm
|
||||
ld a,(#23672)
|
||||
ld l,a
|
||||
ld a,(#23673)
|
||||
ld h,a
|
||||
ld a,(#23674)
|
||||
ld e,a
|
||||
ld d,#0
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void pause (BYTE frames)
|
||||
{
|
||||
BYTE i;
|
||||
|
||||
for (i=0;i!=frames;i++)
|
||||
{
|
||||
WAIT_VRETRACE;
|
||||
}
|
||||
}
|
||||
|
||||
void memset (BYTE *dir, BYTE val, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
ld a,6(ix)
|
||||
ld c,7(ix)
|
||||
ld b,8(ix)
|
||||
ld d,h
|
||||
ld e,l
|
||||
inc de
|
||||
dec bc
|
||||
ld (hl),a
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void memcpy (BYTE *dst, BYTE *fue, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld e,4(ix)
|
||||
ld d,5(ix)
|
||||
ld l,6(ix)
|
||||
ld h,7(ix)
|
||||
ld c,8(ix)
|
||||
ld b,9(ix)
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void cls (BYTE attr)
|
||||
{
|
||||
#ifdef USEROM
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,4(ix)
|
||||
ld (#ATTRP),a
|
||||
call #0x0d6b
|
||||
ld a,#0xfe
|
||||
call #0x1601
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
#else
|
||||
memset((BYTE *)16384,0,6144);
|
||||
memset((BYTE *)22528,attr,768);
|
||||
SETCOLOR(attr);
|
||||
*((WORD *)SPOSN)=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void border (BYTE b)
|
||||
{
|
||||
ULA=(b>>3)&0x7;
|
||||
*((BYTE *)BORDR)=b;
|
||||
}
|
||||
|
||||
void puts (BYTE *str)
|
||||
{
|
||||
volatile BYTE over=0;
|
||||
volatile BYTE bold=0;
|
||||
volatile BYTE backup_attrp = *(BYTE *)(ATTRP);
|
||||
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
buc_print:
|
||||
ld a,(hl)
|
||||
or a
|
||||
jr nz,no_fin_print
|
||||
jp fin_print
|
||||
no_fin_print:
|
||||
cp #22
|
||||
jr nz,no_at
|
||||
inc hl
|
||||
ld a,(hl)
|
||||
ld (#ROW),a
|
||||
inc hl
|
||||
ld a,(hl)
|
||||
ld (#COLUMN),a
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_at:
|
||||
cp #13
|
||||
jr nz,no_cr
|
||||
xor a
|
||||
ld (#COLUMN),a
|
||||
ld a,(#ROW)
|
||||
inc a
|
||||
ld (#ROW),a
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_cr:
|
||||
cp #4
|
||||
jr nz,no_attr
|
||||
inc hl
|
||||
ld a,(hl)
|
||||
ld (#ATTRP),a
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_attr:
|
||||
cp #5
|
||||
jr nz,no_pr_over
|
||||
ld -1(ix),#0xff
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_pr_over:
|
||||
cp #6
|
||||
jr nz,no_pr_bold
|
||||
ld -2(ix),#0xff
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_pr_bold:
|
||||
cp #32
|
||||
jr nc,imprimible
|
||||
ld a,#32
|
||||
imprimible:
|
||||
push hl
|
||||
ld hl,(#COLUMN)
|
||||
push hl
|
||||
push af
|
||||
ld de,#16384
|
||||
add hl,de
|
||||
ld a,h
|
||||
and #7
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
or l
|
||||
ld l,a
|
||||
ld a,#248
|
||||
and h
|
||||
ld h,a
|
||||
pop af
|
||||
push hl
|
||||
ld de,(#CHARS)
|
||||
ld l,a
|
||||
ld h,#0
|
||||
add hl,hl
|
||||
add hl,hl
|
||||
add hl,hl
|
||||
add hl,de
|
||||
pop de
|
||||
|
||||
ld b,#8
|
||||
print_car:
|
||||
ld a,(hl)
|
||||
sra a
|
||||
and -2(ix)
|
||||
or (hl)
|
||||
ld c,a
|
||||
ld a,(de)
|
||||
and -1(ix)
|
||||
xor c
|
||||
ld (de),a
|
||||
inc hl
|
||||
inc d
|
||||
djnz print_car
|
||||
|
||||
pop hl
|
||||
push hl
|
||||
ld de,#22528
|
||||
ld b,h
|
||||
ld h,#0
|
||||
add hl,de
|
||||
xor a
|
||||
or b
|
||||
jr z,fin_ca_attr
|
||||
ld de,#32
|
||||
calc_dirat:
|
||||
add hl,de
|
||||
djnz calc_dirat
|
||||
fin_ca_attr:
|
||||
ld a,(#ATTRP)
|
||||
ld (hl),a
|
||||
pop hl
|
||||
inc l
|
||||
bit 5,l
|
||||
jr z,no_inc_fila
|
||||
res 5,l
|
||||
inc h
|
||||
no_inc_fila:
|
||||
ld (#COLUMN),hl
|
||||
pop hl
|
||||
inc hl
|
||||
jp buc_print
|
||||
|
||||
fin_print:
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
|
||||
SETCOLOR(backup_attrp);
|
||||
}
|
||||
|
||||
void locate (BYTE row, BYTE col)
|
||||
{
|
||||
*((BYTE *)ROW)=row;
|
||||
*((BYTE *)COLUMN)=col;
|
||||
}
|
||||
|
||||
// void putdec (WORD n)
|
||||
// {
|
||||
// BYTE num[6];
|
||||
//
|
||||
// u16todec (n,num);
|
||||
// puts (num);
|
||||
// }
|
||||
//
|
||||
// void u16todec (WORD n, char *s)
|
||||
// {
|
||||
// BYTE i=4;
|
||||
// WORD divisor=10, resto;
|
||||
//
|
||||
// memset(s,' ',5);
|
||||
// do
|
||||
// {
|
||||
// resto=n%divisor;
|
||||
// n/=divisor;
|
||||
// s[i--]=resto+'0';
|
||||
// }
|
||||
// while (n);
|
||||
// s[5]='\0';
|
||||
// }
|
||||
|
||||
void u16tohex (WORD n, char *s)
|
||||
{
|
||||
u8tohex((n>>8)&0xFF,s);
|
||||
u8tohex(n&0xFF,s+2);
|
||||
}
|
||||
|
||||
void u8tohex (BYTE n, char *s)
|
||||
{
|
||||
BYTE i=1;
|
||||
BYTE resto;
|
||||
|
||||
resto=n&0xF;
|
||||
s[1]=(resto>9)?resto+55:resto+48;
|
||||
resto=n>>4;
|
||||
s[0]=(resto>9)?resto+55:resto+48;
|
||||
s[2]='\0';
|
||||
}
|
||||
|
||||
void wait_key (void)
|
||||
{
|
||||
while ((ULA&0x1f)==0x1f);
|
||||
}
|
||||
|
||||
BYTE inkey (BYTE semif, BYTE pos)
|
||||
{
|
||||
BYTE teclas;
|
||||
BYTE posbit;
|
||||
|
||||
switch (semif)
|
||||
{
|
||||
case 1: teclas=SEMIFILA1; break;
|
||||
case 2: teclas=SEMIFILA2; break;
|
||||
case 3: teclas=SEMIFILA3; break;
|
||||
case 4: teclas=SEMIFILA4; break;
|
||||
case 5: teclas=SEMIFILA5; break;
|
||||
case 6: teclas=SEMIFILA6; break;
|
||||
case 7: teclas=SEMIFILA7; break;
|
||||
case 8: teclas=SEMIFILA8; break;
|
||||
default: teclas=ULA; break;
|
||||
}
|
||||
posbit=1<<(pos-1);
|
||||
return (teclas&posbit)?0:1;
|
||||
}
|
||||
|
||||
void beep (WORD durmili, BYTE freq) __critical
|
||||
{
|
||||
volatile BYTE cborde;
|
||||
|
||||
cborde=(*(BYTE *)(BORDR));
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
|
||||
ld l,6(ix) ;se desplaza dos byte por ser "critical".
|
||||
ld h,7(ix)
|
||||
ld d,-1(ix)
|
||||
|
||||
ld b,8(ix)
|
||||
xor a
|
||||
sub b
|
||||
ld b,a
|
||||
ld c,a
|
||||
|
||||
bucbeep:
|
||||
ld a,d
|
||||
xor #0x18
|
||||
ld d,a
|
||||
out (#254),a
|
||||
|
||||
ld b,c
|
||||
bucperiodobeep:
|
||||
djnz bucperiodobeep
|
||||
|
||||
dec hl
|
||||
ld a,h
|
||||
or l
|
||||
jr nz,bucbeep
|
||||
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void __sdcc_enter_ix (void) __naked
|
||||
{
|
||||
__asm
|
||||
pop hl ; return address
|
||||
push ix ; save frame pointer
|
||||
ld ix,#0
|
||||
add ix,sp ; set ix to the stack frame
|
||||
jp (hl) ; and return
|
||||
__endasm;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
@echo off
|
||||
rem sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node 30000 ^
|
||||
rem --nostdlib --nostdinc --no-std-crt0 --out-fmt-s19 ^
|
||||
rem --code-loc 32768 --data-loc 0 --stack-loc 65535 joyconf.c
|
||||
rem s19tozx -i joyconf.s37 -o joyconf.tap
|
||||
rem cgleches joyconf.tap joyconf.wav
|
||||
|
||||
sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node 10000 ^
|
||||
--nostdlib --nostdinc --no-std-crt0 --code-loc 8192 joyconf.c
|
||||
|
||||
makebin -p joyconf.ihx joyconf.bin
|
||||
dd if=joyconf.bin of=JOYCONF bs=1 skip=8192 status=noxfer
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
|
||||
Utilidad de conversión de ficheros S-Record de Motorola, creados con SDCC, a TAP de Spectrum
|
||||
(C)2007-2008 Miguel Angel Rodriguez Jodar (McLeod/IdeaFix). Dept. Arquitectura y Tecnología de Computadores. Universidad de Sevilla. rodriguj@atc.us.es
|
||||
Licencia: GPL
|
||||
Se puede compilar con cualquier compilador para Windows/UNIX (gcc, mingw32, etc...)
|
||||
|
||||
Uso:
|
||||
s19tozx -i archivo.s19 -o archivo.tap
|
||||
|
||||
El archivo S19 se habrá generado con SDCC, que debe haberse usado con los siguientes parámetros:
|
||||
|
||||
sdcc -mz80 --opt-code-speed --nostdlib --nostdinc --no-std-crt0 --out-fmt-s19 --code-loc XXXX --data-loc 0 fichero1.c fichero2.c ...
|
||||
|
||||
Donde XXXX es la dirección de memoria donde comenzará nuestro código.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define hextodec(x) ((x)<='9'?(x)-'0':(x)-'A'+10)
|
||||
#define D(x) if (verbose) x
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int address;
|
||||
unsigned int lbytes;
|
||||
unsigned char bytes[65536];
|
||||
} Tbloque;
|
||||
|
||||
Tbloque ElBloque;
|
||||
int verbose=0;
|
||||
|
||||
void SalvarBloqueTAP (FILE *fs, unsigned short int address, unsigned short int lbytes, unsigned char *bytes)
|
||||
{
|
||||
unsigned char cabecera[19];
|
||||
unsigned short int dummy;
|
||||
int i;
|
||||
unsigned char chk;
|
||||
|
||||
cabecera[0]=0; // Flag 0 para indicar cabecera estándar
|
||||
cabecera[1]=3; // Bytes:
|
||||
sprintf(cabecera+2,"datos-%4.4X",address);
|
||||
cabecera[12]=lbytes&0xFF;
|
||||
cabecera[13]=(lbytes>>8)&0xFF;
|
||||
cabecera[14]=address&0xFF;
|
||||
cabecera[15]=(address>>8)&0xFF;
|
||||
cabecera[16]=0;
|
||||
cabecera[17]=0x80;
|
||||
cabecera[18]=0;
|
||||
|
||||
for (i=0;i<=17;i++)
|
||||
cabecera[18]^=cabecera[i];
|
||||
|
||||
dummy=19;
|
||||
fwrite(&dummy,1,2,fs);
|
||||
fwrite(cabecera,1,19,fs);
|
||||
|
||||
dummy=lbytes+2;
|
||||
fwrite(&dummy,1,2,fs);
|
||||
fputc(0xFF,fs); // FF para indicar datos
|
||||
fwrite(bytes,1,lbytes,fs);
|
||||
|
||||
chk=0xFF;
|
||||
for (i=0;i<lbytes;i++)
|
||||
chk^=bytes[i];
|
||||
fputc(chk,fs);
|
||||
}
|
||||
|
||||
void SalvarLoaderTAP (FILE *fs, unsigned short int address)
|
||||
{
|
||||
/* Este cargador corresponde a: (32 columnas por linea)
|
||||
|
||||
10 CLEAR 0: LOAD ""CODE : PRIN
|
||||
T AT 11,0;" Pulsa una tecla para
|
||||
ejecutar ": PAUSE 0: RANDOMIZE
|
||||
USR 0
|
||||
|
||||
*/
|
||||
unsigned char loader[]={19,0,0,0,108,111,97,100,101,114,32,32,32,32,91,0,1,
|
||||
0,91,0,16,93,0,255,0,10,87,0,253,48,14,0,0,0,0,0,58,
|
||||
239,34,34,175,58,245,172,49,49,14,0,0,11,0,0,44,48,
|
||||
14,0,0,0,0,0,59,34,32,80,117,108,115,97,32,117,110,
|
||||
97,32,116,101,99,108,97,32,112,97,114,97,32,101,106,
|
||||
101,99,117,116,97,114,32,32,34,58,242,48,14,0,0,0,0,0,
|
||||
58,249,192,48,14,0,0,0,0,0,13,0};
|
||||
|
||||
int offpdata=23;
|
||||
int offclear=33;
|
||||
int offrand=111;
|
||||
int i,chks;
|
||||
|
||||
if (address>=25000)
|
||||
{
|
||||
loader[offclear]=(address-1)&0xFF;
|
||||
loader[offclear+1]=((address-1)>>8)&0xFF;
|
||||
}
|
||||
|
||||
loader[offrand]=address&0xFF;
|
||||
loader[offrand+1]=(address>>8)&0xFF;
|
||||
|
||||
for (chks=0,i=offpdata;i<115;i++)
|
||||
chks^=loader[i];
|
||||
loader[i]=chks;
|
||||
|
||||
fwrite (loader, 1, 116, fs);
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
char *fsalida=NULL;
|
||||
char *fentrada=NULL;
|
||||
char *opc;
|
||||
char linea[10001];
|
||||
int llinea,indact;
|
||||
unsigned char dato;
|
||||
unsigned short int dir;
|
||||
int i,p;
|
||||
FILE *fe, *fs;
|
||||
|
||||
for (i=1;i<argc;i++)
|
||||
{
|
||||
opc=argv[i];
|
||||
if (opc[0]=='-' && opc[1]=='o')
|
||||
{
|
||||
fsalida=argv[i+1];
|
||||
i++;
|
||||
}
|
||||
else if (opc[0]=='-' && opc[1]=='i')
|
||||
{
|
||||
fentrada=argv[i+1];
|
||||
i++;
|
||||
}
|
||||
else if (opc[0]=='-' && opc[1]=='v')
|
||||
verbose=1;
|
||||
}
|
||||
|
||||
if (!fentrada)
|
||||
fe=stdin;
|
||||
else
|
||||
fe=fopen(fentrada,"rt");
|
||||
|
||||
if (!fsalida)
|
||||
fs=stdout;
|
||||
else
|
||||
fs=fopen(fsalida,"wb");
|
||||
|
||||
if (!fs || !fe)
|
||||
{
|
||||
printf ("No se pudo abrir alguno de los ficheros especificados\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fgets (linea, 10000, fe);
|
||||
indact=0;
|
||||
ElBloque.lbytes=0;
|
||||
|
||||
while (!feof(fe))
|
||||
{
|
||||
if (linea[0]!='S')
|
||||
continue;
|
||||
if (linea[1]=='9')
|
||||
break;
|
||||
if (linea[1]!='1')
|
||||
continue;
|
||||
|
||||
D(printf("%s\n",linea));
|
||||
|
||||
llinea=((hextodec(linea[2])<<4) | hextodec(linea[3])) - 3;
|
||||
dir=(hextodec(linea[4])<<12) |
|
||||
(hextodec(linea[5])<<8) |
|
||||
(hextodec(linea[6])<<4) |
|
||||
(hextodec(linea[7]));
|
||||
|
||||
if (indact==0)
|
||||
ElBloque.address=dir;
|
||||
|
||||
if (dir!=ElBloque.address+ElBloque.lbytes)
|
||||
{
|
||||
SalvarLoaderTAP(fs,ElBloque.address);
|
||||
SalvarBloqueTAP(fs,ElBloque.address,ElBloque.lbytes,ElBloque.bytes);
|
||||
indact=0;
|
||||
ElBloque.address=dir;
|
||||
ElBloque.lbytes=0;
|
||||
}
|
||||
|
||||
D(printf("dir: %4.4X indact: %d llinea: %d\n",dir,indact,llinea));
|
||||
|
||||
for (i=0,p=8;i<llinea;i++,p+=2)
|
||||
{
|
||||
dato=(hextodec(linea[p])<<4) | hextodec(linea[p+1]);
|
||||
ElBloque.bytes[indact++]=dato;
|
||||
ElBloque.lbytes++;
|
||||
}
|
||||
|
||||
fgets (linea,10000,fe);
|
||||
}
|
||||
|
||||
if (indact)
|
||||
{
|
||||
SalvarLoaderTAP(fs,ElBloque.address);
|
||||
SalvarBloqueTAP(fs,ElBloque.address,ElBloque.lbytes,ElBloque.bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,120 @@
|
|||
; API de ESXDOS.
|
||||
include "esxdos.inc"
|
||||
include "errors.inc"
|
||||
|
||||
; KEYMAP. Una utilidad para cargar un mapa de teclado en el ZX-Uno
|
||||
; Necesita sólamente un nombre de fichero de mapa, que debe estar
|
||||
; guardado en /SYS/KEYMAPS dentro de la tarjeta SD donde esté ESXDOS.
|
||||
|
||||
;Para ensamblar con PASMO como archivo binario (no TAP)
|
||||
|
||||
ZXUNOADDR equ 0fc3bh
|
||||
ZXUNODATA equ 0fd3bh
|
||||
|
||||
org 2000h ;comienzo de la ejecución de los comandos ESXDOS.
|
||||
|
||||
Main proc
|
||||
ld a,h
|
||||
or l
|
||||
jr z,PrintUso ;si no se ha especificado nombre de fichero, imprimir uso
|
||||
call RecogerNFile
|
||||
|
||||
call ReadMap
|
||||
ret
|
||||
|
||||
PrintUso ld hl,Uso
|
||||
BucPrintMsg ld a,(hl)
|
||||
or a
|
||||
ret z
|
||||
rst 10h
|
||||
inc hl
|
||||
jr BucPrintMsg
|
||||
endp
|
||||
|
||||
|
||||
RecogerNFile proc ;HL apunta a los argumentos (nombre del fichero)
|
||||
ld de,BufferNFich
|
||||
CheckCaracter ld a,(hl)
|
||||
or a
|
||||
jr z,FinRecoger
|
||||
cp " "
|
||||
jr z,FinRecoger
|
||||
cp ":"
|
||||
jr z,FinRecoger
|
||||
cp 13
|
||||
jr z,FinRecoger
|
||||
ldi
|
||||
jr CheckCaracter
|
||||
FinRecoger xor a
|
||||
ld (de),a
|
||||
inc de ;DE queda apuntando al buffer este que se necesita en OPEN, no sé pa qué.
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
ReadMap proc
|
||||
xor a
|
||||
rst 08h
|
||||
db M_GETSETDRV ;A = unidad actual
|
||||
ld b,FA_READ ;B = modo de apertura
|
||||
ld hl,MapFile ;HL = Puntero al nombre del fichero (ASCIIZ)
|
||||
rst 08h
|
||||
db F_OPEN
|
||||
ret c ;Volver si hay error
|
||||
ld (FHandle),a
|
||||
|
||||
ld bc,ZXUNOADDR
|
||||
ld a,7
|
||||
out (c),a ;select KEYMAP register
|
||||
|
||||
ld b,4 ;4 chunks of 4096 bytes each to load
|
||||
BucReadMapFromFile push bc
|
||||
|
||||
ld bc,4096
|
||||
ld hl,Buffer
|
||||
ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_READ
|
||||
jr c,PrematureEnd ;si error, fin de lectura
|
||||
|
||||
ld hl,Buffer
|
||||
ld bc,ZXUNODATA
|
||||
ld de,4096
|
||||
BucWriteMapToFPGA ld a,(hl)
|
||||
out (c),a
|
||||
inc hl
|
||||
dec de
|
||||
ld a,d
|
||||
or e
|
||||
jr nz,BucWriteMapToFPGA
|
||||
|
||||
pop bc
|
||||
djnz BucReadMapFromFile
|
||||
|
||||
jr FinReadMap
|
||||
|
||||
PrematureEnd pop bc
|
||||
push af
|
||||
ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_CLOSE
|
||||
pop af
|
||||
ret
|
||||
|
||||
FinReadMap ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_CLOSE
|
||||
or a ;Volver sin errores a ESXDOS
|
||||
ret
|
||||
endp
|
||||
|
||||
; 01234567890123456789012345678901
|
||||
Uso db " KEYMAP file",13,13
|
||||
db "Loads the specified keymap from",13
|
||||
db "/SYS/KEYMAPS and enables it.",13,0
|
||||
|
||||
FHandle db 0
|
||||
|
||||
Buffer ds 4096 ;4KB para buffer de lectura
|
||||
MapFile db "/SYS/KEYMAPS/"
|
||||
BufferNFich equ $ ;resto de la RAM para el nombre del fichero
|
||||
Binary file not shown.
|
|
@ -0,0 +1,725 @@
|
|||
/*
|
||||
Compilar con:
|
||||
sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node 10000
|
||||
--nostdlib --nostdinc --no-std-crt0 --code-loc 8192 loadpzx.c
|
||||
*/
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
|
||||
enum {IBLACK=0, IBLUE, IRED, IMAG, IGREEN, ICYAN, IYELLOW, IWHITE};
|
||||
enum {PBLACK=0, PBLUE=8, PRED=16, PMAG=24, PGREEN=32, PCYAN=40, PYELLOW=48, PWHITE=56};
|
||||
#define BRIGHT 64
|
||||
#define FLASH 128
|
||||
|
||||
__sfr __at (0xfe) ULA;
|
||||
__sfr __at (0xff) ATTR;
|
||||
__sfr __at (0x1f) KEMPSTONADDR;
|
||||
__sfr __at (0x7f) FULLERADDR;
|
||||
|
||||
__sfr __banked __at (0xf7fe) SEMIFILA1;
|
||||
__sfr __banked __at (0xeffe) SEMIFILA2;
|
||||
__sfr __banked __at (0xfbfe) SEMIFILA3;
|
||||
__sfr __banked __at (0xdffe) SEMIFILA4;
|
||||
__sfr __banked __at (0xfdfe) SEMIFILA5;
|
||||
__sfr __banked __at (0xbffe) SEMIFILA6;
|
||||
__sfr __banked __at (0xfefe) SEMIFILA7;
|
||||
__sfr __banked __at (0x7ffe) SEMIFILA8;
|
||||
|
||||
#define ATTRP 23693
|
||||
#define ATTRT 23695
|
||||
#define BORDR 23624
|
||||
#define LASTK 23560
|
||||
|
||||
#define WAIT_VRETRACE __asm halt __endasm
|
||||
#define WAIT_HRETRACE while(ATTR!=0xff)
|
||||
#define SETCOLOR(x) *(BYTE *)(ATTRP)=(x)
|
||||
#define LASTKEY *(BYTE *)(LASTK)
|
||||
#define ATTRPERMANENT *((BYTE *)(ATTRP))
|
||||
#define ATTRTEMPORARY *((BYTE *)(ATTRT))
|
||||
#define BORDERCOLOR *((BYTE *)(BORDR))
|
||||
|
||||
#define MAKEWORD(d,h,l) { ((BYTE *)&(d))[0] = (l) ; ((BYTE *)&(d))[1] = (h); }
|
||||
|
||||
__sfr __banked __at (0xfc3b) ZXUNOADDR;
|
||||
__sfr __banked __at (0xfd3b) ZXUNODATA;
|
||||
|
||||
#define MASTERCONF 0
|
||||
#define SCANCODE 4
|
||||
#define KEYBSTAT 5
|
||||
#define JOYCONF 6
|
||||
#define SRAMDATA 0xf2
|
||||
#define SRAMADDRINC 0xf1
|
||||
#define SRAMADDR 0xf0
|
||||
#define COREID 0xff
|
||||
|
||||
/* Some ESXDOS system calls */
|
||||
#define HOOK_BASE 128
|
||||
#define MISC_BASE (HOOK_BASE+8)
|
||||
#define FSYS_BASE (MISC_BASE+16)
|
||||
#define M_GETSETDRV (MISC_BASE+1)
|
||||
#define F_OPEN (FSYS_BASE+2)
|
||||
#define F_CLOSE (FSYS_BASE+3)
|
||||
#define F_READ (FSYS_BASE+5)
|
||||
#define F_WRITE (FSYS_BASE+6)
|
||||
#define F_SEEK (FSYS_BASE+7)
|
||||
#define F_GETPOS (FSYS_BASE+8)
|
||||
#define M_TAPEIN (MISC_BASE+3)
|
||||
#define M_AUTOLOAD (MISC_BASE+8)
|
||||
|
||||
#define FMODE_READ 0x1 // Read access
|
||||
#define FMODE_WRITE 0x2 // Write access
|
||||
#define FMODE_OPEN_EX 0x0 // Open if exists, else error
|
||||
#define FMODE_OPEN_AL 0x8 // Open if exists, if not create
|
||||
#define FMODE_CREATE_NEW 0x4 // Create if not exists, if exists error
|
||||
#define FMODE_CREATE_AL 0xc // Create if not exists, else open and truncate
|
||||
|
||||
#define SEEK_START 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_BKCUR 2
|
||||
|
||||
#define BUFSIZE 2048
|
||||
BYTE errno;
|
||||
char buffer[BUFSIZE];
|
||||
|
||||
void __sdcc_enter_ix (void) __naked;
|
||||
void cls (BYTE);
|
||||
|
||||
void puts (BYTE *);
|
||||
void u16tohex (WORD n, char *s);
|
||||
void u8tohex (BYTE n, char *s);
|
||||
void print8bhex (BYTE n);
|
||||
void print16bhex (WORD n);
|
||||
|
||||
void memset (BYTE *, BYTE, WORD);
|
||||
void memcpy (BYTE *, BYTE *, WORD);
|
||||
|
||||
BYTE open (char *filename, BYTE mode);
|
||||
void close (BYTE handle);
|
||||
WORD read (BYTE handle, BYTE *buffer, WORD nbytes);
|
||||
WORD write (BYTE handle, BYTE *buffer, WORD nbytes);
|
||||
void seek (BYTE handle, WORD hioff, WORD looff, BYTE from);
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
BYTE main (char *p);
|
||||
void getcoreid(BYTE *s);
|
||||
void usage (void);
|
||||
BYTE commandlinemode (char *p);
|
||||
void getfilename (char *p, char *fname);
|
||||
BYTE printheader (BYTE handle);
|
||||
void readpzx (BYTE handle);
|
||||
WORD readblocktag (BYTE handle);
|
||||
WORD readword (BYTE handle);
|
||||
void convertpaus2puls (BYTE handle);
|
||||
void skipblock (BYTE handle, WORD hiskip, WORD loskip);
|
||||
void copyblock (BYTE handle, WORD hicopy, WORD locopy);
|
||||
void rewindsram (void);
|
||||
void writesram (BYTE v);
|
||||
void incaddrsram (void);
|
||||
void copysram (BYTE *p, WORD l);
|
||||
|
||||
void init (void) __naked
|
||||
{
|
||||
__asm
|
||||
xor a
|
||||
ld (#_errno),a
|
||||
push hl
|
||||
call _main
|
||||
inc sp
|
||||
inc sp
|
||||
ld a,l
|
||||
or a
|
||||
jr z,preparaload
|
||||
cp #255
|
||||
jr z,noload
|
||||
scf
|
||||
ret
|
||||
noload:
|
||||
or a
|
||||
ret
|
||||
preparaload:
|
||||
;Cierra TAPE.IN
|
||||
ld b,#1
|
||||
rst #8
|
||||
.db #M_TAPEIN
|
||||
|
||||
;Auto LOAD ""
|
||||
xor a
|
||||
rst #8
|
||||
.db #M_AUTOLOAD
|
||||
|
||||
or a
|
||||
ret
|
||||
|
||||
;Codigo antiguo para hacer LOAD ""
|
||||
; ld bc,#3
|
||||
; ld hl,(#23641)
|
||||
; push hl
|
||||
; rst #0x18
|
||||
; .dw 0x1655
|
||||
; ld hl,#comando_load
|
||||
; pop de
|
||||
; ld bc,#3
|
||||
; ldir
|
||||
; ld hl,#0x12cf
|
||||
; .db 0xc3, 0xfb, 0x1f
|
||||
|
||||
;comando_load:
|
||||
; .db 239,34,34
|
||||
__endasm;
|
||||
}
|
||||
|
||||
BYTE main (char *p)
|
||||
{
|
||||
if (!p)
|
||||
{
|
||||
usage();
|
||||
return 255;
|
||||
}
|
||||
else
|
||||
return commandlinemode(p);
|
||||
}
|
||||
|
||||
BYTE commandlinemode (char *p)
|
||||
{
|
||||
char fname[32];
|
||||
BYTE handle, res;
|
||||
BYTE noautoload;
|
||||
|
||||
noautoload = 0;
|
||||
while (*p==' ')
|
||||
p++;
|
||||
if (*p=='-')
|
||||
{
|
||||
p++;
|
||||
if (*p=='n')
|
||||
{
|
||||
noautoload = 255;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
while (*p==' ')
|
||||
p++;
|
||||
|
||||
getfilename (p, fname);
|
||||
handle = open (fname, FMODE_READ);
|
||||
if (handle==0xff)
|
||||
return errno;
|
||||
|
||||
res = printheader(handle);
|
||||
if (res)
|
||||
readpzx(handle);
|
||||
|
||||
close (handle);
|
||||
|
||||
if (noautoload == 255)
|
||||
puts ("\xd\xdType LOAD \"\" and press PLAY\xd");
|
||||
else
|
||||
{
|
||||
ZXUNOADDR = 0xf3;
|
||||
ZXUNODATA = 0x1; // software assisted PLAY press
|
||||
ZXUNODATA = 0x0;
|
||||
}
|
||||
|
||||
return noautoload;
|
||||
}
|
||||
|
||||
void usage (void)
|
||||
{
|
||||
// 01234567890123456789012345678901
|
||||
puts (" LOADPZX [-n] file.pzx\xd\xd"
|
||||
"Loads a PZX file into the PZX\xd"
|
||||
"player.\xd"
|
||||
"-n : do not autoplay.\xd");
|
||||
}
|
||||
|
||||
void getfilename (char *p, char *fname)
|
||||
{
|
||||
while (*p!=':' && *p!=0xd && *p!=' ')
|
||||
*fname++ = *p++;
|
||||
*fname = '\0';
|
||||
}
|
||||
|
||||
BYTE printheader (BYTE handle)
|
||||
{
|
||||
WORD lblock;
|
||||
|
||||
readblocktag (handle);
|
||||
if (buffer[0]!='P' ||
|
||||
buffer[1]!='Z' ||
|
||||
buffer[2]!='X' ||
|
||||
buffer[3]!='T')
|
||||
{
|
||||
puts ("This is not a PZX valid file\xd");
|
||||
return 0;
|
||||
}
|
||||
|
||||
MAKEWORD(lblock,buffer[5],buffer[4]);
|
||||
read (handle, buffer, lblock);
|
||||
if (buffer[0]!=1 || buffer[1]!=0)
|
||||
{
|
||||
puts ("PZX format not supported\xd");
|
||||
return 0;
|
||||
}
|
||||
if (lblock>3)
|
||||
{
|
||||
puts ("Loading: ");
|
||||
puts(buffer+2);
|
||||
puts ("\xd");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void readpzx (BYTE handle)
|
||||
{
|
||||
WORD hi,lo,res;
|
||||
BYTE scandblctrl;
|
||||
|
||||
ZXUNOADDR = 0xb;
|
||||
scandblctrl = ZXUNODATA;
|
||||
ZXUNODATA = scandblctrl | 0xc0;
|
||||
|
||||
rewindsram();
|
||||
while(1)
|
||||
{
|
||||
*((BYTE *)(23692)) = 0xff; // para evitar el mensaje de scroll
|
||||
res = readblocktag (handle);
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
if (buffer[0]=='P' &&
|
||||
buffer[1]=='U' &&
|
||||
buffer[2]=='L' &&
|
||||
buffer[3]=='S')
|
||||
{
|
||||
puts ("P");
|
||||
MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
writesram(0x2);
|
||||
incaddrsram();
|
||||
copysram(&buffer[4],4);
|
||||
copyblock (handle,hi,lo);
|
||||
}
|
||||
else if (buffer[0]=='D' &&
|
||||
buffer[1]=='A' &&
|
||||
buffer[2]=='T' &&
|
||||
buffer[3]=='A')
|
||||
{
|
||||
puts ("D");
|
||||
MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
writesram(0x3);
|
||||
incaddrsram();
|
||||
copysram(&buffer[4],4);
|
||||
copyblock (handle,hi,lo);
|
||||
}
|
||||
else if (buffer[0]=='P' &&
|
||||
buffer[1]=='A' &&
|
||||
buffer[2]=='U' &&
|
||||
buffer[3]=='S')
|
||||
{
|
||||
puts ("A");
|
||||
MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
convertpaus2puls (handle);
|
||||
//writesram(0x4);
|
||||
//incaddrsram();
|
||||
//copysram(&buffer[4],4);
|
||||
//copyblock (handle,hi,lo);
|
||||
}
|
||||
else if (buffer[0]=='S' &&
|
||||
buffer[1]=='T' &&
|
||||
buffer[2]=='O' &&
|
||||
buffer[3]=='P')
|
||||
{
|
||||
puts ("S");
|
||||
res = readword (handle);
|
||||
//MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
//MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
writesram(0x1);
|
||||
incaddrsram();
|
||||
copysram (&res, 2);
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
//skipblock(handle,hi,lo);
|
||||
}
|
||||
else if (buffer[0]=='B' &&
|
||||
buffer[1]=='R' &&
|
||||
buffer[2]=='W' &&
|
||||
buffer[3]=='S')
|
||||
{
|
||||
puts ("B");
|
||||
writesram(0x4);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
skipblock(handle,hi,lo);
|
||||
}
|
||||
else // skip unsupported block
|
||||
{
|
||||
puts ("x");
|
||||
MAKEWORD(lo,buffer[5],buffer[4]);
|
||||
MAKEWORD(hi,buffer[7],buffer[6]);
|
||||
skipblock(handle,hi,lo);
|
||||
}
|
||||
}
|
||||
// pequeña pausa de 20ms para evitar errores de carga
|
||||
// a causa de la conmutación brusca de la señal de EAR
|
||||
// del player virtual a la entrada real
|
||||
|
||||
// Add full stop mark to the tape
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
writesram(0);
|
||||
incaddrsram();
|
||||
|
||||
ZXUNOADDR = 0xb;
|
||||
ZXUNODATA = scandblctrl;
|
||||
}
|
||||
|
||||
WORD readblocktag (BYTE handle)
|
||||
{
|
||||
return read (handle, buffer, 8);
|
||||
}
|
||||
|
||||
WORD readword (BYTE handle)
|
||||
{
|
||||
WORD res;
|
||||
read (handle, &res, 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
void skipblock (BYTE handle, WORD hiskip, WORD loskip)
|
||||
{
|
||||
seek (handle, hiskip, loskip, SEEK_CUR);
|
||||
}
|
||||
|
||||
void copyblock (BYTE handle, WORD hicopy, WORD locopy)
|
||||
{
|
||||
//print16bhex(hicopy);
|
||||
//print16bhex(locopy); puts("\xd");
|
||||
while (hicopy!=0 || locopy>=BUFSIZE)
|
||||
{
|
||||
read (handle, buffer, BUFSIZE);
|
||||
copysram (buffer, BUFSIZE);
|
||||
if ((locopy-BUFSIZE)>locopy)
|
||||
hicopy--;
|
||||
locopy -= BUFSIZE;
|
||||
//print16bhex(hicopy);
|
||||
//print16bhex(locopy); puts("\xd");
|
||||
}
|
||||
if (locopy>0)
|
||||
{
|
||||
//print16bhex(hicopy);
|
||||
//print16bhex(locopy); puts("\xd");
|
||||
read (handle, buffer, locopy);
|
||||
copysram (buffer, locopy);
|
||||
}
|
||||
}
|
||||
|
||||
void convertpaus2puls (BYTE handle)
|
||||
{
|
||||
BYTE pausa[10] = {0x6,0,0,0,0x1,0x80,0,0,0,0};
|
||||
|
||||
writesram(0x2);
|
||||
incaddrsram();
|
||||
read (handle, buffer, 4);
|
||||
pausa[6] = buffer[2];
|
||||
pausa[7] = buffer[3] | 0x80;
|
||||
pausa[8] = buffer[0];
|
||||
pausa[9] = buffer[1];
|
||||
copysram (pausa,10);
|
||||
}
|
||||
|
||||
void rewindsram (void)
|
||||
{
|
||||
ZXUNOADDR = SRAMADDR;
|
||||
ZXUNODATA = 0;
|
||||
ZXUNODATA = 0;
|
||||
ZXUNODATA = 0;
|
||||
}
|
||||
|
||||
void writesram (BYTE v)
|
||||
{
|
||||
ZXUNOADDR = SRAMDATA;
|
||||
ZXUNODATA = v;
|
||||
}
|
||||
|
||||
void incaddrsram (void)
|
||||
{
|
||||
ZXUNOADDR = SRAMADDRINC;
|
||||
ZXUNODATA = 0;
|
||||
}
|
||||
|
||||
void copysram (BYTE *p, WORD l)
|
||||
{
|
||||
while (l--)
|
||||
{
|
||||
ZXUNOADDR = SRAMDATA;
|
||||
ZXUNODATA = *p++;
|
||||
ZXUNOADDR = SRAMADDRINC;
|
||||
ZXUNODATA = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void getcoreid(BYTE *s)
|
||||
{
|
||||
BYTE cont;
|
||||
volatile BYTE letra;
|
||||
|
||||
ZXUNOADDR = COREID;
|
||||
cont=0;
|
||||
|
||||
do
|
||||
{
|
||||
letra = ZXUNODATA;
|
||||
*(s++) = letra;
|
||||
cont++;
|
||||
}
|
||||
while (letra!=0 && cont<32);
|
||||
*s='\0';
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
/* --------------------------------------------------------------------------------- */
|
||||
#pragma disable_warning 85
|
||||
#pragma disable_warning 59
|
||||
void memset (BYTE *dir, BYTE val, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
ld a,6(ix)
|
||||
ld c,7(ix)
|
||||
ld b,8(ix)
|
||||
ld d,h
|
||||
ld e,l
|
||||
inc de
|
||||
dec bc
|
||||
ld (hl),a
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void memcpy (BYTE *dst, BYTE *fue, WORD nby)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld e,4(ix)
|
||||
ld d,5(ix)
|
||||
ld l,6(ix)
|
||||
ld h,7(ix)
|
||||
ld c,8(ix)
|
||||
ld b,9(ix)
|
||||
ldir
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void puts (BYTE *str)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,(#ATTRT)
|
||||
push af
|
||||
ld a,(#ATTRP)
|
||||
ld (#ATTRT),a
|
||||
ld l,4(ix)
|
||||
ld h,5(ix)
|
||||
buc_print:
|
||||
ld a,(hl)
|
||||
or a
|
||||
jp z,fin_print
|
||||
cp #4
|
||||
jr nz,no_attr
|
||||
inc hl
|
||||
ld a,(hl)
|
||||
ld (#ATTRT),a
|
||||
inc hl
|
||||
jr buc_print
|
||||
no_attr:
|
||||
rst #16
|
||||
inc hl
|
||||
jp buc_print
|
||||
|
||||
fin_print:
|
||||
pop af
|
||||
ld (#ATTRT),a
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
// void u16tohex (WORD n, char *s)
|
||||
// {
|
||||
// u8tohex((n>>8)&0xFF,s);
|
||||
// u8tohex(n&0xFF,s+2);
|
||||
// }
|
||||
//
|
||||
// void u8tohex (BYTE n, char *s)
|
||||
// {
|
||||
// BYTE i=1;
|
||||
// BYTE resto;
|
||||
//
|
||||
// resto=n&0xF;
|
||||
// s[1]=(resto>9)?resto+55:resto+48;
|
||||
// resto=n>>4;
|
||||
// s[0]=(resto>9)?resto+55:resto+48;
|
||||
// s[2]='\0';
|
||||
// }
|
||||
//
|
||||
// void print8bhex (BYTE n)
|
||||
// {
|
||||
// char s[3];
|
||||
//
|
||||
// u8tohex(n,s);
|
||||
// puts(s);
|
||||
// }
|
||||
//
|
||||
// void print16bhex (WORD n)
|
||||
// {
|
||||
// char s[5];
|
||||
//
|
||||
// u16tohex(n,s);
|
||||
// puts(s);
|
||||
// }
|
||||
|
||||
|
||||
void __sdcc_enter_ix (void) __naked
|
||||
{
|
||||
__asm
|
||||
pop hl ; return address
|
||||
push ix ; save frame pointer
|
||||
ld ix,#0
|
||||
add ix,sp ; set ix to the stack frame
|
||||
jp (hl) ; and return
|
||||
__endasm;
|
||||
}
|
||||
|
||||
BYTE open (char *filename, BYTE mode)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
xor a
|
||||
rst #8
|
||||
.db #M_GETSETDRV ;Default drive in A
|
||||
ld l,4(ix) ;Filename pointer
|
||||
ld h,5(ix) ;in HL
|
||||
ld b,6(ix) ;Open mode in B
|
||||
rst #8
|
||||
.db #F_OPEN
|
||||
jr nc,open_ok
|
||||
ld (#_errno),a
|
||||
ld a,#0xff
|
||||
open_ok:
|
||||
ld l,a
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void close (BYTE handle)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,4(ix) ;Handle
|
||||
rst #8
|
||||
.db #F_CLOSE
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
WORD read (BYTE handle, BYTE *buffer, WORD nbytes)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,4(ix) ;File handle in A
|
||||
ld l,5(ix) ;Buffer address
|
||||
ld h,6(ix) ;in HL
|
||||
ld c,7(ix)
|
||||
ld b,8(ix) ;Buffer length in BC
|
||||
rst #8
|
||||
.db #F_READ
|
||||
jr nc,read_ok
|
||||
ld (#_errno),a
|
||||
ld bc,#65535
|
||||
read_ok:
|
||||
ld h,b
|
||||
ld l,c
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
WORD write (BYTE handle, BYTE *buffer, WORD nbytes)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,4(ix) ;File handle in A
|
||||
ld l,5(ix) ;Buffer address
|
||||
ld h,6(ix) ;in HL
|
||||
ld c,7(ix)
|
||||
ld b,8(ix) ;Buffer length in BC
|
||||
rst #8
|
||||
.db #F_WRITE
|
||||
jr nc,write_ok
|
||||
ld (#_errno),a
|
||||
ld bc,#65535
|
||||
write_ok:
|
||||
ld h,b
|
||||
ld l,c
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
void seek (BYTE handle, WORD hioff, WORD looff, BYTE from)
|
||||
{
|
||||
__asm
|
||||
push bc
|
||||
push de
|
||||
ld a,4(ix) ;File handle in A
|
||||
ld c,5(ix) ;Hiword of offset in BC
|
||||
ld b,6(ix)
|
||||
ld e,7(ix) ;Loword of offset in DE
|
||||
ld d,8(ix)
|
||||
ld l,9(ix) ;From where: 0: start, 1:forward current pos, 2: backwards current pos
|
||||
rst #8
|
||||
.db #F_SEEK
|
||||
pop de
|
||||
pop bc
|
||||
__endasm;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@echo off
|
||||
del loadpzx.bin
|
||||
del loadpzx.ihx
|
||||
del LOADPZX
|
||||
|
||||
sdcc -mz80 --reserve-regs-iy --opt-code-size --max-allocs-per-node 10000 ^
|
||||
--nostdlib --nostdinc --no-std-crt0 --code-loc 8192 --data-loc 12288 loadpzx.c
|
||||
|
||||
makebin -p loadpzx.ihx loadpzx.bin
|
||||
dd if=loadpzx.bin of=LOADPZX bs=1 skip=8192
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,499 @@
|
|||
PZX (Perfect ZX Tape) file format version 1.0
|
||||
=============================================
|
||||
|
||||
The PZX file format consists of a sequence of blocks. Each block has the
|
||||
following uniform structure:
|
||||
|
||||
offset type name meaning
|
||||
0 u32 tag unique identifier for the block type.
|
||||
4 u32 size size of the block in bytes, excluding the tag and size fields themselves.
|
||||
8 u8[size] data arbitrary amount of block data.
|
||||
|
||||
The block tags are four ASCII letter identifiers indicating how to interpret the
|
||||
block data. The first letter is stored first in the file (i.e., at
|
||||
offset 0 of the block), the last letter is stored last (i.e., at offset 3).
|
||||
This means the tag may be internally conveniently represented either as 32bit
|
||||
multicharacter constants stored in big endian order or 32bit reversed
|
||||
multicharacter constant stored in little endian order, whichever way an
|
||||
implementation prefers.
|
||||
|
||||
All integral values specified by this format are stored in little endian
|
||||
format, that is, the least significant byte at lower offsets.
|
||||
All values specified are in decimal, unless prefixed with 0x,
|
||||
in which case they are hexadecimal. Any bits specified as unused should be
|
||||
set to zero by encoding implementations and ignored by decoding implementations.
|
||||
|
||||
All strings specified by this format are stored in UTF-8 encoding. However
|
||||
implementations are not required to render Unicode data at all. Any
|
||||
implementation may choose to render only the characters it understands and
|
||||
replace the rest with either a question mark character, or distinctive
|
||||
hexadecimal representation of the character, whatever the implementor prefers.
|
||||
For example an implementation which understands ASCII characters only may
|
||||
render only those ASCII characters in the 32-126 range it understands. Also
|
||||
note that the minimal compliant implementation is not required to render any
|
||||
strings at all.
|
||||
|
||||
The sole purpose of the format is to encode sequence of pulses of certain duration.
|
||||
Pulse level is defined to be either low or high. When loading, low level
|
||||
corresponds to the bit 6 of port 0xFE reset (EAR off) and high level set (EAR on).
|
||||
Similarly, when saving, low level corresponds to the bit 3 of port 0xFE reset
|
||||
(MIC off) and high level to set (MIC on). (Note that O'Hara's ROM Disassembly
|
||||
book got these EAR/MIC on/off terms incorrectly swapped). The block descriptions below
|
||||
define the initial level at start of each block (and would do so even for any future
|
||||
block which might be ever introduced, so an implementation may take this for
|
||||
granted), and assume the level changes after each pulse generated (even if
|
||||
its duration is zero). However note that implementations may as well decide
|
||||
to invert that initial level and invert the level before each pulse, if it
|
||||
makes them easier to implement. All that matters is that the level of the
|
||||
pulses themselves remain the same.
|
||||
|
||||
Any durations are expressed in T cycles of standard 48k Spectrum CPU. This means
|
||||
one T cycle equals 1/3500000 second. It's up to an implementation to convert
|
||||
the durations appropriately if it may not or does not want to use the T cycles directly.
|
||||
However note that this is not necessary in case of 128k emulation. In that
|
||||
case the CPU speed is not substantially different and the ROM I/O routines
|
||||
use the same timing constants anyway, so an implementation may safely use
|
||||
the T cycles directly as well.
|
||||
|
||||
Overall regarding duration precision, note that the pulses generated by the
|
||||
original Spectrum ROM saving routine itself vary by +1, -3, and -1 T cycles
|
||||
in case of first pulse of first bit of leader, regular, and checksum bytes,
|
||||
respectively. And memory contention can increase this by another 6 T cycles.
|
||||
This means that an encoding implementation may choose to consider pulses
|
||||
which do not vary by more than about 2% as reasonably equal for comparison
|
||||
purposes when encoding pulse durations.
|
||||
|
||||
Blocks
|
||||
------
|
||||
|
||||
There are four mandatory block types which any implementation must implement in order
|
||||
to claim PZX compatibility, namely PZXT, PULS, DATA and PAUS blocks. All
|
||||
other block types may be safely ignored, although implementors are welcome
|
||||
to implement them as they see fit. However whenever an implementation
|
||||
encounters a block it doesn't understand, it must skip it and continue as if
|
||||
the block was not there at all.
|
||||
|
||||
In case the specified block size is smaller than the minimum size possible
|
||||
for given block type, it is an error and an application may either ignore the
|
||||
block and try to process the next one or to stop processing the file
|
||||
entirely, whatever an implementor finds more appropriate. Similarly, if some
|
||||
data inside the block indicate that the block should have at least some
|
||||
minimum size but the specified block size is smaller than this, this is an
|
||||
error as well and the above applies as well.
|
||||
|
||||
To allow custom extensions, anyone is free to define his own custom block,
|
||||
however to prevent clashes with the standard, such blocks may use only
|
||||
lowercase tag names, while any standardized blocks use uppercase tag names.
|
||||
|
||||
|
||||
The core blocks which must be supported are:
|
||||
|
||||
PZXT - PZX header block
|
||||
-----------------------
|
||||
|
||||
offset type name meaning
|
||||
0 u8 major major version number (currently 1).
|
||||
1 u8 minor minor version number (currently 0).
|
||||
2 u8[?] info tape info, see below.
|
||||
|
||||
This block distinguishes the PZX files from other files and may provide
|
||||
additional info about the file as well. This block must be always present as
|
||||
the first block of any PZX file.
|
||||
|
||||
Any implementation should check the version number before processing the
|
||||
rest of the PZX file or even the rest of this block itself. Any
|
||||
implementation should accept only files whose major version it implements
|
||||
and reject anything else. However an implementation may report a warning in
|
||||
case it encounters minor greater than it implements for given major, if the
|
||||
implementor finds it convenient to do so.
|
||||
|
||||
Note that this block also allows for simple concatenation of PZX files. Any
|
||||
implementation should thus check the version number not only in case of the
|
||||
first block, but anytime it encounters this block anywhere in the file. This
|
||||
in fact suggests that an implementation might decide not to treat the first
|
||||
block specially in any way, except checking the file starts with this block
|
||||
type, which is usually done because of file type recognition anyway.
|
||||
|
||||
The rest of the block data may provide additional info about the tape. Note
|
||||
that an implementation is not required to process any of this information in
|
||||
any way and may as well safely ignore it.
|
||||
|
||||
The additional information consists of sequence of strings, each terminated
|
||||
either by character 0x00 or end of the block, whichever comes first.
|
||||
This means the last string in the sequence may or may not be terminated.
|
||||
|
||||
The first string (if there is any) in the sequence is always the title of
|
||||
the tape. The following strings (if there are any) form key and value pairs,
|
||||
each value providing particular type of information according to the key.
|
||||
In case the last value is missing, it should be treated as empty string.
|
||||
The following keys are defined (for reference, the value in brackets is the
|
||||
corresponding type byte as specified by the TZX standard):
|
||||
|
||||
Publisher [0x01] - Software house/publisher
|
||||
Author [0x02] - Author(s)
|
||||
Year [0x03] - Year of publication
|
||||
Language [0x04] - Language
|
||||
Type [0x05] - Game/utility type
|
||||
Price [0x06] - Original price
|
||||
Protection [0x07] - Protection scheme/loader
|
||||
Origin [0x08] - Origin
|
||||
Comment [0xFF] - Comment(s)
|
||||
|
||||
Note that some keys (like Author or Comment) may be used more than once.
|
||||
|
||||
Any encoding implementation must use any of the key names as they are listed
|
||||
above, including the case. For any type of information not covered above, it
|
||||
should use either the generic Comment field or invent new sensible key name
|
||||
following the style used above. This allows any decoding implementation to
|
||||
classify and/or localize any of the key names it understands, and use any
|
||||
others verbatim.
|
||||
|
||||
Overall, same rules as for use in TZX files apply, for example it is not
|
||||
necessary to specify the Language field in case all texts are in English.
|
||||
|
||||
PULS - Pulse sequence
|
||||
---------------------
|
||||
|
||||
offset type name meaning
|
||||
0 u16 count bits 0-14 optional repeat count (see bit 15), always greater than zero
|
||||
bit 15 repeat count present: 0 not present 1 present
|
||||
2 u16 duration1 bits 0-14 low/high (see bit 15) pulse duration bits
|
||||
bit 15 duration encoding: 0 duration1 1 ((duration1<<16)+duration2)
|
||||
4 u16 duration2 optional low bits of pulse duration (see bit 15 of duration1)
|
||||
6 ... ... ditto repeated until the end of the block
|
||||
|
||||
This block is used to represent arbitrary sequence of pulses. The sequence
|
||||
consists of pulses of given duration, with each pulse optionally repeated
|
||||
given number of times. The duration may be up to 0x7FFFFFFF T cycles,
|
||||
however longer durations may be achieved by concatenating the pulses by use
|
||||
of zero pulses. The repeat count may be up to 0x7FFF times, however more
|
||||
repetitions may be achieved by simply storing the same pulse again together
|
||||
with another repeat count.
|
||||
|
||||
The optional repeat count is stored first. When present, it is stored as
|
||||
16 bit value with bit 15 set. When not present, the repeat count is considered to be 1.
|
||||
Note that the stored repeat count must be always greater than zero, so when
|
||||
decoding, a value 0x8000 is not a zero repeat count, but prefix indicating the
|
||||
presence of extended duration, see below.
|
||||
|
||||
The pulse duration itself is stored next. When it fits within 15 bits, it is
|
||||
stored as 16 bit value as it is, with bit 15 not set. Otherwise the 15 high
|
||||
bits are stored as 16 bit value with bit 15 set, followed by 16 bit value
|
||||
containing the low 16 bits. Note that in the latter case the repeat count
|
||||
must be present unless the duration fits within 16 bits, otherwise the
|
||||
decoding implementation would treat the high bits as a repeat count.
|
||||
|
||||
The above can be summarized with the following pseudocode for decoding:
|
||||
|
||||
count = 1 ;
|
||||
duration = fetch_u16() ;
|
||||
if ( duration > 0x8000 ) {
|
||||
count = duration & 0x7FFF ;
|
||||
duration = fetch_u16() ;
|
||||
}
|
||||
if ( duration >= 0x8000 ) {
|
||||
duration &= 0x7FFF ;
|
||||
duration <<= 16 ;
|
||||
duration |= fetch_u16() ;
|
||||
}
|
||||
|
||||
The pulse level is low at start of the block by default. However initial
|
||||
pulse of zero duration may be easily used to make it high. Similarly, pulse
|
||||
of zero duration may be used to achieve pulses lasting longer than
|
||||
0x7FFFFFFF T cycles. Note that if the repeat count is present in case of
|
||||
zero pulse for some reason, any decoding implementation must consistently
|
||||
behave as if there was one zero pulse if the repeat count is odd and as if
|
||||
there was no such pulse at all if it is even.
|
||||
|
||||
For example, the standard pilot tone of Spectrum header block (leader < 128)
|
||||
may be represented by following sequence:
|
||||
|
||||
0x8000+8063,2168,667,735
|
||||
|
||||
The standard pilot tone of Spectrum data block (leader >= 128) would be:
|
||||
|
||||
0x8000+3223,2168,667,735
|
||||
|
||||
For the record, the standard ROM save routines create the pilot tone in such
|
||||
a way that the level of the first sync pulse is high and the level of the
|
||||
second sync pulse is low. The bit pulses then follow, each bit starting with
|
||||
high pulse. The creators of the PZX files should use this information to
|
||||
determine if they got the polarity of their files right. Note that although
|
||||
most loaders are not polarity sensitive and would work even if the polarity
|
||||
is inverted, there are some loaders which won't, so it is better to always
|
||||
stick to this scheme.
|
||||
|
||||
DATA - Data block
|
||||
-----------------
|
||||
|
||||
offset type name meaning
|
||||
0 u32 count bits 0-30 number of bits in the data stream
|
||||
bit 31 initial pulse level: 0 low 1 high
|
||||
4 u16 tail duration of extra pulse after last bit of the block
|
||||
6 u8 p0 number of pulses encoding bit equal to 0.
|
||||
7 u8 p1 number of pulses encoding bit equal to 1.
|
||||
8 u16[p0] s0 sequence of pulse durations encoding bit equal to 0.
|
||||
8+2*p0 u16[p1] s1 sequence of pulse durations encoding bit equal to 1.
|
||||
8+2*(p0+p1) u8[ceil(bits/8)] data data stream, see below.
|
||||
|
||||
This block is used to represent binary data using specified sequences of
|
||||
pulses. The data bytes are processed bit by bit, most significant bits first.
|
||||
Each bit of the data is represented by one of the sequences, s0 if its value
|
||||
is 0 and s1 if its value is 1, respectively. Each sequence consists of
|
||||
pulses of specified durations, p0 pulses for sequence s0 and p1 pulses for
|
||||
sequence s1, respectively.
|
||||
|
||||
The initial pulse level is specified by bit 31 of the count field. For data
|
||||
saved with standard ROM routines, it should be always set to high, as
|
||||
mentioned in PULS description above. Also note that pulse of zero duration
|
||||
may be used to invert the pulse level at start and/or the end of the
|
||||
sequence. It would be also possible to use it for pulses longer than 65535 T
|
||||
cycles in the middle of the sequence, if it was ever necessary.
|
||||
|
||||
For example, the sequences for standard ZX Spectrum bit encoding are:
|
||||
(initial pulse level is high):
|
||||
|
||||
bit 0: 855,855
|
||||
bit 1: 1710,1710
|
||||
|
||||
The sequences for ZX81 encoding would be (initial pulse level is high):
|
||||
|
||||
bit 0: 530, 520, 530, 520, 530, 520, 530, 4689
|
||||
bit 1: 530, 520, 530, 520, 530, 520, 530, 520, 530, 520, 530, 520, 530, 520, 530, 520, 530, 4689
|
||||
|
||||
The sequence for direct recording at 44100kHz would be (assuming initial pulse level is low):
|
||||
|
||||
bit 0: 79,0
|
||||
bit 1: 0,79
|
||||
|
||||
The sequence for direct recording resampled to match the common denominator
|
||||
of standard pulse width would be (assuming initial pulse level is low):
|
||||
|
||||
bit 0: 855,0
|
||||
bit 1: 0,855
|
||||
|
||||
After the very last pulse of the last bit of the data stream is output, one
|
||||
last tail pulse of specified duration is output. Non zero duration is
|
||||
usually necessary to terminate the last bit of the block properly, for
|
||||
example for data block saved with standard ROM routine the duration of the
|
||||
tail pulse is 945 T cycles and only then goes the level low again. Of course
|
||||
the specified duration may be zero as well, in which case this pulse has no
|
||||
effect on the output. This is often the case when multiple data blocks are
|
||||
used to represent continuous stream of pulses.
|
||||
|
||||
PAUS - Pause
|
||||
------------
|
||||
|
||||
offset type name meaning
|
||||
0 u32 duration bits 0-30 duration of the pause
|
||||
bit 31 initial pulse level: 0 low 1 high
|
||||
|
||||
This block may be used to produce pauses during which the pulse level is not
|
||||
particularly important. The pause consists of pulse of given duration and
|
||||
given level. However note that some emulators may choose to simulate random
|
||||
noise during this period, occasionally toggling the pulse level. In such case
|
||||
the level must not be toggled during the first 70000 T cycles and then more
|
||||
than once during each 70000 T cycles.
|
||||
|
||||
For example, in case of ZX Spectrum program saved by standard SAVE command
|
||||
there is a low pulse of about one second (49-50 frames) between the header
|
||||
and data blocks. On the other hand, there is usually no pause between the
|
||||
blocks necessary at all, as long as the tail pulse of the preceding block was
|
||||
used properly.
|
||||
|
||||
|
||||
Additional blocks which should be supported:
|
||||
|
||||
BRWS - Browse point
|
||||
-------------------
|
||||
|
||||
offset type name meaning
|
||||
0 u8[?] text text describing this browse point
|
||||
|
||||
This block may be used when it is desirable to mark some point on the tape
|
||||
as target for tape browsing. The text is the single line describing this
|
||||
target.
|
||||
|
||||
If an implementation supports tape browsing, it should implement a mode in
|
||||
which it allows jumping to PZXT or BRWS blocks only. These blocks are the
|
||||
reasonable points to which jumping makes sense, and both provide descriptive
|
||||
name of that point. In case of the PZXT block it is the tape title (or "Tape
|
||||
start" if no name is specified) and in case of BRWS block it is the the
|
||||
description of that browse point. An implementation may choose to also
|
||||
implement a mode in which it allows jumping to arbitrary blocks as well. In
|
||||
such case the blocks may be referred to by their tag names as well, and an
|
||||
implementation may choose to attempt to extract any additional information
|
||||
from the block itself as it sees fit.
|
||||
|
||||
Creators of the PZX files are urged to put the BRWS blocks to wherever it makes
|
||||
sense. They are often not needed, but they should be inserted prior any
|
||||
level data block to which a user might eventually need to fast forward or
|
||||
rewind to, together with appropriately descriptive name.
|
||||
|
||||
STOP - Stop tape command
|
||||
------------------------
|
||||
|
||||
offset type name meaning
|
||||
0 u16 flags when exactly to stop the tape (1 48k only, other always).
|
||||
|
||||
This block is used to instruct an emulator to stop the virtual tape deck.
|
||||
The flags field is used to specify under which conditions this should
|
||||
happen. In case the value is 0, the tape should be always stopped, in case
|
||||
the value is 1, it should be stopped only if 48k Spectrum is being emulated.
|
||||
Other values are not defined yet, however for future compatibility, any
|
||||
implementation should treat any value it doesn't understand as 0.
|
||||
|
||||
Creators of the PZX files are urged to put these blocks to whichever position the program
|
||||
asks the user to stop the tape at, and use the flags field appropriately to
|
||||
the situation as well.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Issues
|
||||
======
|
||||
|
||||
Q: Some pulse sequences are more common than the others. Shall we include
|
||||
some of them in the format definition and provide a way of simply specifying
|
||||
them in the blocks? For example, PULS block of zero size might refer to the
|
||||
standard pilot sequence. Similarly, in DATA block, setting p0 to 0 might
|
||||
indicate that p1 specifies one of the default sets of sequences, say 0 for
|
||||
standard ZX Spectrum and 1 for ZX81. Do we want to do either or both of
|
||||
this or is it just a feature creep?
|
||||
|
||||
A: It is better not to hard code any specific knowledge of this type into an
|
||||
implementation. This way an implementation doesn't have to deal with special
|
||||
cases and it has a better chance of understanding any future revisions of the
|
||||
format. The size savings alone are not worth it.
|
||||
|
||||
|
||||
|
||||
F.A.Q.
|
||||
======
|
||||
|
||||
Q: How do I recognize that a DATA block is suitable for flashloading?
|
||||
|
||||
A: By testing the pulse sequences. What exactly you need to test may depend
|
||||
on what loaders your implementation can flashload. In the common case of the
|
||||
standard loaders you would simply test that each sequence consists of two
|
||||
non-zero pulses, and that the total duration of the sequence s0 is less than
|
||||
the total duration of sequence s1.
|
||||
|
||||
|
||||
|
||||
Mapping of TZX blocks to PZX blocks:
|
||||
====================================
|
||||
|
||||
Here is a brief roadmap how each of the TZX blocks may be mapped to
|
||||
corresponding PZX block(s):
|
||||
|
||||
ID 10 - Standard speed data block
|
||||
|
||||
Trivially encoded with PULS and DATA blocks.
|
||||
|
||||
ID 11 - Turbo speed data block
|
||||
|
||||
Trivially encoded with PULS and DATA blocks.
|
||||
|
||||
ID 12 - Pure tone
|
||||
|
||||
Trivially encoded with PULS block.
|
||||
|
||||
ID 13 - Sequence of pulses of various lengths
|
||||
|
||||
Trivially encoded with PULS block.
|
||||
|
||||
ID 14 - Pure data block
|
||||
|
||||
Trivially encoded with DATA block.
|
||||
|
||||
ID 15 - Direct recording block
|
||||
|
||||
Encoded with DATA block, following the example sequences in DATA block
|
||||
description.
|
||||
|
||||
ID 18 - CSW recording block
|
||||
|
||||
Encoded either as PULS block or DATA block, using the DRB like encoding with
|
||||
appropriate denominator in the latter case. In either case, it would help if
|
||||
the pulse durations are cleaned from the noise caused by interfering sampling frequency.
|
||||
|
||||
ID 19 - Generalized data block
|
||||
|
||||
Encoded as PULS block and DATA block for alphabets with no more than 2
|
||||
symbols. In case of 3+ symbols for the data the pilot would be encoded as PULS
|
||||
block, and for data an encoding same as for CSW above would be used.
|
||||
|
||||
ID 20 - Pause (silence) or 'Stop the tape' command
|
||||
|
||||
Encoded either as PAUS block or STOP block.
|
||||
|
||||
ID 21 - Group start
|
||||
|
||||
Encoded as BRWS block.
|
||||
|
||||
ID 22 - Group end
|
||||
|
||||
Not needed.
|
||||
|
||||
ID 23 - Jump to block
|
||||
ID 24 - Loop start
|
||||
ID 25 - Loop end
|
||||
ID 26 - Call sequence
|
||||
ID 27 - Return from sequence
|
||||
ID 28 - Select block
|
||||
|
||||
All these were dropped as not really needed.
|
||||
|
||||
ID 2A - Stop the tape if in 48K mode
|
||||
|
||||
Encoded as STOP block.
|
||||
|
||||
ID 2B - Set signal level
|
||||
|
||||
Not needed, every block defines pulse levels regardless of other blocks.
|
||||
|
||||
ID 30 - Text description
|
||||
|
||||
Encoded as BRWS block.
|
||||
|
||||
ID 32 - Archive info
|
||||
|
||||
Included as part of PZXT block.
|
||||
|
||||
ID 31 - Message block
|
||||
ID 33 - Hardware type
|
||||
ID 35 - Custom info block
|
||||
|
||||
All these were dropped as not needed, although they might be easily included
|
||||
via custom blocks if anyone really has some use for them.
|
||||
|
||||
ID 5A - "Glue" block (90 dec, ASCII Letter 'Z')
|
||||
|
||||
Not exactly needed, although PZXT block may be used in the same way as well.
|
||||
|
||||
|
||||
|
||||
History
|
||||
=======
|
||||
|
||||
1.0 (28.6.2007)
|
||||
|
||||
* Changed ZXTP tag to PZXT to eliminate any possible problems with draft implementations.
|
||||
|
||||
0.3 draft (20.5.2007)
|
||||
|
||||
* Some values are now intentionally limited to 31 bits to prevent overflow issues while decoding.
|
||||
* Number of pulses of DATA block bit sequences is now stored as 8 bits only.
|
||||
|
||||
0.2 draft (12.5.2007)
|
||||
|
||||
* The ZXTP block now stores the info types verbosely (suggested by Kio and AndyC).
|
||||
* The encoding of PULS block now allows 32 bit durations and optional repeat count.
|
||||
* The DATA block and PAUS blocks now contain explicit initial pulse level.
|
||||
* The PAUS block now consists of single pulse, and specifies how random noise should be used.
|
||||
|
||||
0.1 draft (28.4.2007)
|
||||
|
||||
+ Patrik Rak presents the initial draft release.
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,49 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mem.h>
|
||||
|
||||
#define BW
|
||||
//#define COLOR
|
||||
|
||||
int main ()
|
||||
{
|
||||
char header[256];
|
||||
FILE *fi, *fo;
|
||||
char name[100];
|
||||
char *frame;
|
||||
int fr = 1;
|
||||
|
||||
memcpy (header,"ZXM",3);
|
||||
#ifdef COLOR
|
||||
header[18] = 14;
|
||||
#endif
|
||||
#ifdef BW
|
||||
header[18] = 12;
|
||||
#endif
|
||||
frame = malloc(6912);
|
||||
fo = fopen ("badapple.zxm", "wb");
|
||||
fwrite (header, 1, 256, fo);
|
||||
memset (header, 0, 256);
|
||||
|
||||
while(1)
|
||||
{
|
||||
sprintf (name, "%09.9d.scr", fr++);
|
||||
fi = fopen (name, "rb");
|
||||
if (!fi)
|
||||
break;
|
||||
#ifdef COLOR
|
||||
fread (frame, 1, 6912, fi);
|
||||
fwrite (header, 1, 256, fo);
|
||||
fwrite (frame, 1, 6912, fo);
|
||||
#endif
|
||||
#ifdef BW
|
||||
fread (frame, 1, 6144, fi);
|
||||
fwrite (frame, 1, 6144, fo);
|
||||
#endif
|
||||
printf ("%s\n", name);
|
||||
fclose (fi);
|
||||
}
|
||||
fclose (fo);
|
||||
free(frame);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
; API de ESXDOS.
|
||||
include "esxdos.inc"
|
||||
include "errors.inc"
|
||||
|
||||
; PLAYZXM : un comando para ESXDOS 0.8.5 que permite reproducir videos en formato
|
||||
; ZXM en blanco y negro y color.
|
||||
|
||||
; Video: cabecera + secuencia lineal de frames.
|
||||
; Cabecera (256 bytes):
|
||||
; Offset Contenido
|
||||
; 0 'Z'
|
||||
; 1 'X'
|
||||
; 2 'M'
|
||||
; 16-17 Numero de frames que tiene el video (no usado en esta rutina)
|
||||
; 18 Numero de sectores (de 512 bytes) que ocupa cada frame: 12 para BW, 14 para color
|
||||
; Resto de posiciones: reservado
|
||||
|
||||
; Cada frame: en blanco y negro, 6144 bytes en formato pantalla de Spectrum. Un pixel a 1 se muestra en negro. A 0, en blanco.
|
||||
; en color, 256 bytes de relleno + 6912 bytes en formato pantalla de Spectrum
|
||||
; NOTA: el relleno es simplemente para que cada frame ocupe un número entero de sectores. El relleno se pone a 0
|
||||
; o a lo que se quiera, ya que no se hace nada con él.
|
||||
|
||||
|
||||
;Para ensamblar con PASMO como archivo binario (no TAP)
|
||||
|
||||
BORDERCLR equ 23624
|
||||
PAPERCOLOR equ 23693
|
||||
BANKM equ 7ffdh
|
||||
PILA equ 3deah ;valor sugerido por Miguel Ângelo para poner la pila en el área de comando
|
||||
|
||||
org 2000h ;comienzo de la ejecución de los comandos ESXDOS.
|
||||
|
||||
Main proc
|
||||
ld a,h
|
||||
or l
|
||||
jr z,PrintUso ;si no se ha especificado nombre de fichero, imprimir uso
|
||||
call RecogerNFile
|
||||
|
||||
di
|
||||
ld (BackupSP),sp
|
||||
ld sp,PILA
|
||||
ei
|
||||
call PlayFichero
|
||||
push af
|
||||
call Cls
|
||||
pop af
|
||||
ld sp,(BackupSP)
|
||||
ret
|
||||
|
||||
PrintUso ld hl,Uso
|
||||
BucPrintMsg ld a,(hl)
|
||||
or a
|
||||
ret z
|
||||
rst 10h
|
||||
inc hl
|
||||
jr BucPrintMsg
|
||||
endp
|
||||
|
||||
|
||||
RecogerNFile proc ;HL apunta a los argumentos (nombre del fichero)
|
||||
ld de,BufferNFich
|
||||
CheckCaracter ld a,(hl)
|
||||
or a
|
||||
jr z,FinRecoger
|
||||
cp " "
|
||||
jr z,FinRecoger
|
||||
cp ":"
|
||||
jr z,FinRecoger
|
||||
cp 13
|
||||
jr z,FinRecoger
|
||||
ldi
|
||||
jr CheckCaracter
|
||||
FinRecoger xor a
|
||||
ld (de),a
|
||||
inc de ;DE queda apuntando al buffer este que se necesita en OPEN, no sé pa qué.
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
PlayFichero proc
|
||||
xor a
|
||||
rst 08h
|
||||
db M_GETSETDRV ;A = unidad actual
|
||||
ld b,FA_READ ;B = modo de apertura
|
||||
ld hl,BufferNFich ;HL = Puntero al nombre del fichero (ASCIIZ)
|
||||
rst 08h
|
||||
db F_OPEN
|
||||
ret c ;Volver si hay error
|
||||
ld (FHandle),a
|
||||
|
||||
call SetupVideoMemory
|
||||
call ReadHeader
|
||||
jr c,FinPlay
|
||||
|
||||
BucPlayVideo ld hl,(StartScreen)
|
||||
ld bc,(LFrame)
|
||||
ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_READ
|
||||
jr c,FinPlay ;si error, fin de lectura
|
||||
ld a,b
|
||||
or c
|
||||
jr z,FinPlay ;si no hay más que leer, fin de lectura
|
||||
|
||||
call SwitchScreens
|
||||
ld bc,7ffeh
|
||||
in a,(c) ;Detectar si se ha pulsado SPACE
|
||||
and 1
|
||||
jr z,FinPlay
|
||||
|
||||
jr BucPlayVideo
|
||||
|
||||
FinPlay ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_CLOSE
|
||||
|
||||
call RestoreVideoMemory
|
||||
|
||||
or a ;Volver sin errores a ESXDOS
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
ReadHeader proc
|
||||
ld a,(FHandle)
|
||||
ld bc,256
|
||||
ld hl,32768
|
||||
rst 08h
|
||||
db F_READ
|
||||
ret c
|
||||
|
||||
ld hl,32768
|
||||
ld a,'Z'
|
||||
cpi
|
||||
jr nz,NoZXM
|
||||
ld a,'X'
|
||||
cpi
|
||||
jr nz,NoZXM
|
||||
ld a,'M'
|
||||
cpi
|
||||
jr nz,NoZXM
|
||||
|
||||
ld hl,32768+18
|
||||
ld a,(hl)
|
||||
add a,a
|
||||
ld b,a
|
||||
ld c,0
|
||||
ld (LFrame),bc
|
||||
|
||||
ld bc,49152 ;Principio buffer pantalla para BW
|
||||
ld (StartScreen),bc
|
||||
ld a,(hl)
|
||||
cp 12 ;B/W ?
|
||||
jr z,ZXMOk
|
||||
|
||||
ld bc,49152-256 ;Principio buffer pantalla para color
|
||||
ld (StartScreen),bc
|
||||
|
||||
ZXMOk or a
|
||||
ret
|
||||
|
||||
NoZXM scf
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
SetupVideoMemory proc
|
||||
di
|
||||
ld bc,BANKM
|
||||
ld a,00010111b ;banco 7, pantalla normal, ROM 3
|
||||
ld (Banco),a
|
||||
out (c),a
|
||||
|
||||
ld hl,0c000h + 6144
|
||||
ld de,04000h + 6144
|
||||
ld bc,768
|
||||
SetAttr ld a,64+56
|
||||
ld (hl),a
|
||||
ld (de),a
|
||||
inc hl
|
||||
inc de
|
||||
dec bc
|
||||
ld a,b
|
||||
or c
|
||||
jr nz,SetAttr
|
||||
|
||||
xor a
|
||||
ld bc,0fc3bh
|
||||
out (c),a
|
||||
inc b
|
||||
in a,(c)
|
||||
ld (BackupConfig),a
|
||||
or 20h
|
||||
out (c),a
|
||||
|
||||
xor a
|
||||
out (254),a
|
||||
|
||||
ei
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
RestoreVideoMemory proc
|
||||
di
|
||||
xor a
|
||||
ld bc,0fc3bh
|
||||
out (c),a
|
||||
inc b
|
||||
ld a,(BackupConfig)
|
||||
out (c),a
|
||||
|
||||
ld bc,BANKM
|
||||
ld a,00010000b ;banco 0, pantalla normal, ROM 3
|
||||
out (c),a
|
||||
ei
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
SwitchScreens proc
|
||||
halt
|
||||
ld a,(Banco)
|
||||
xor 00001010b ;conmutamos de la pantalla normal a la shadow y de la 7 a la 5
|
||||
ld (Banco),a
|
||||
ld bc,BANKM
|
||||
out (c),a
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
Cls proc
|
||||
ld a,(BORDERCLR)
|
||||
sra a
|
||||
sra a
|
||||
sra a
|
||||
and 7
|
||||
out (254),a
|
||||
ld hl,16384
|
||||
ld de,16385
|
||||
ld bc,6143
|
||||
ld (hl),l
|
||||
ldir
|
||||
inc hl
|
||||
inc de
|
||||
ld bc,767
|
||||
ld a,(PAPERCOLOR)
|
||||
ld (hl),a
|
||||
ldir
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
; 01234567890123456789012345678901
|
||||
Uso db " playzxm moviefile.zxm",13,13
|
||||
db "Plays a video file encoded in",13
|
||||
db "ZXM (colour/BW) format.",13,0
|
||||
|
||||
FHandle db 0
|
||||
Banco db 0
|
||||
BackupSP dw 0
|
||||
BackupConfig db 0
|
||||
StartScreen dw 0
|
||||
LFrame dw 0
|
||||
|
||||
BufferNFich equ $ ;resto de la RAM para el nombre del fichero
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,77 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OFSBMP 0x76
|
||||
#define OFSPAL 0x36
|
||||
|
||||
void AddFrame (FILE *fmov, FILE *fbmp)
|
||||
{
|
||||
unsigned char paleta[16][4];
|
||||
unsigned char bmp[96][64];
|
||||
int i;
|
||||
unsigned char color;
|
||||
|
||||
fseek (fbmp, OFSPAL, SEEK_SET);
|
||||
fread (paleta, 1, 64, fbmp);
|
||||
fseek (fbmp, OFSBMP, SEEK_SET);
|
||||
fread (bmp, 96, 64, fbmp);
|
||||
|
||||
for (i=95;i>=0;i--)
|
||||
{
|
||||
fwrite (bmp[i], 1, 64, fmov);
|
||||
}
|
||||
|
||||
for (i=0;i<16;i++)
|
||||
{
|
||||
color = (paleta[i][0]/64) | (paleta[i][1]/32)<<5 | (paleta[i][2]/32)<<2;
|
||||
fwrite (&color, 1, 1, fmov);
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
FILE *fbmp, *fmov;
|
||||
int i;
|
||||
char nfile[80];
|
||||
char *prefix;
|
||||
int paso;
|
||||
|
||||
if (argc<2)
|
||||
{
|
||||
fprintf (stderr, "USO: makevideoradas prefijo [paso]\n\n");
|
||||
fprintf (stderr, "Donde: 'prefijo' es el comienzo del nombre de cada uno de los ficheros.\n");
|
||||
fprintf (stderr, " 'paso' es opcional e indica el incremento en frames (defecto: 1)\n");
|
||||
fprintf (stderr, "El nombre completo de cada fichero sera prefijo + codigo de 5 digitos\ncomenzando en 0 + .BMP\n\n");
|
||||
fprintf (stderr, "Ejemplo: con el prefijo 'vid' se procesaran los ficheros:\nvid00000.bmp , vid00001.bmp , vid00002.bmp , etc...\n");
|
||||
fprintf (stderr, "Si se indica un valor para paso distinto de 1, por ejemplo 2, se\nprocesaran los ficheros: vid00000.bmp , vid00002.bmp , vid00004.bmp , etc...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
prefix = argv[1];
|
||||
if (argc>=3)
|
||||
paso = atoi(argv[2]);
|
||||
else
|
||||
paso = 1;
|
||||
|
||||
sprintf (nfile, "%s.rdm", prefix);
|
||||
fmov = fopen (nfile, "wb");
|
||||
i=0;
|
||||
while (1)
|
||||
{
|
||||
sprintf (nfile, "%s%05.5d.bmp", prefix, i);
|
||||
fbmp = fopen (nfile, "rb");
|
||||
if (!fbmp)
|
||||
break;
|
||||
|
||||
fprintf (stderr, "Procesando frame %5d \r", i);
|
||||
AddFrame (fmov, fbmp);
|
||||
fclose (fbmp);
|
||||
i+=paso;
|
||||
}
|
||||
fclose (fmov);
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,225 @@
|
|||
; API de ESXDOS.
|
||||
include "esxdos.inc"
|
||||
include "errors.inc"
|
||||
|
||||
; PLAYRMOV : un comando para ESXDOS 0.8.5 que permite reproducir videos en formato
|
||||
; Radastaniano (ficheros .RDM) usando DIVMMC en el ZX-Uno.
|
||||
|
||||
; Video: secuencia lineal de frames.
|
||||
; Cada frame: 6144 bytes con el bitmap en el formato radastaniano +
|
||||
; 16 bytes para la paleta (entradas 0-15)
|
||||
|
||||
; Version 0.2 : arreglado el problema del stack. Gracias a Miguel Ângelo Guerreiro
|
||||
; Se borra la pantalla al terminar la reproducción
|
||||
; Version 0.1 : necesita que el stack esté por debajo de 49152
|
||||
; (ej. CLEAR 49151 antes de ejecutar el comando)
|
||||
|
||||
;Para ensamblar con PASMO como archivo binario (no TAP)
|
||||
|
||||
BORDERCLR equ 23624
|
||||
PAPERCOLOR equ 23693
|
||||
ULAPLUSADDR equ 0bf3bh
|
||||
ULAPLUSDATA equ 0ff3bh
|
||||
ZXUNOADDR equ 0fc3bh
|
||||
ZXUNODATA equ 0fd3bh
|
||||
RADASCTRL equ 40h
|
||||
BANKM equ 7ffdh
|
||||
PILA equ 3deah ;valor sugerido por Miguel Ângelo para poner la pila en el área de comando
|
||||
|
||||
org 2000h ;comienzo de la ejecución de los comandos ESXDOS.
|
||||
|
||||
Main proc
|
||||
ld a,h
|
||||
or l
|
||||
jr z,PrintUso ;si no se ha especificado nombre de fichero, imprimir uso
|
||||
call RecogerNFile
|
||||
|
||||
di
|
||||
ld (BackupSP),sp
|
||||
ld sp,PILA
|
||||
ei
|
||||
call PlayFichero
|
||||
call Cls
|
||||
|
||||
ld sp,(BackupSP)
|
||||
ret
|
||||
|
||||
PrintUso ld hl,Uso
|
||||
BucPrintMsg ld a,(hl)
|
||||
or a
|
||||
ret z
|
||||
rst 10h
|
||||
inc hl
|
||||
jr BucPrintMsg
|
||||
endp
|
||||
|
||||
|
||||
RecogerNFile proc ;HL apunta a los argumentos (nombre del fichero)
|
||||
ld de,BufferNFich
|
||||
CheckCaracter ld a,(hl)
|
||||
or a
|
||||
jr z,FinRecoger
|
||||
cp " "
|
||||
jr z,FinRecoger
|
||||
cp ":"
|
||||
jr z,FinRecoger
|
||||
cp 13
|
||||
jr z,FinRecoger
|
||||
ldi
|
||||
jr CheckCaracter
|
||||
FinRecoger xor a
|
||||
ld (de),a
|
||||
inc de ;DE queda apuntando al buffer este que se necesita en OPEN, no sé pa qué.
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
PlayFichero proc
|
||||
xor a
|
||||
rst 08h
|
||||
db M_GETSETDRV ;A = unidad actual
|
||||
ld b,FA_READ ;B = modo de apertura
|
||||
ld hl,BufferNFich ;HL = Puntero al nombre del fichero (ASCIIZ)
|
||||
rst 08h
|
||||
db F_OPEN
|
||||
ret c ;Volver si hay error
|
||||
ld (FHandle),a
|
||||
|
||||
call SetupVideoMemory
|
||||
|
||||
BucPlayVideo ld hl,0c000h
|
||||
ld bc,6144+16 ;Bitmap + paleta
|
||||
ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_READ
|
||||
jr c,FinPlay ;si error, fin de lectura
|
||||
ld a,b
|
||||
or c
|
||||
jr z,FinPlay ;si no hay más que leer, fin de lectura
|
||||
|
||||
call SwitchScreens
|
||||
ld bc,7ffeh
|
||||
in a,(c) ;Detectar si se ha pulsado SPACE
|
||||
and 1
|
||||
jr z,FinPlay
|
||||
|
||||
jr BucPlayVideo
|
||||
|
||||
FinPlay ld a,(FHandle)
|
||||
rst 08h
|
||||
db F_CLOSE
|
||||
|
||||
call RestoreVideoMemory
|
||||
|
||||
or a ;Volver sin errores a ESXDOS
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
SetupVideoMemory proc
|
||||
di
|
||||
|
||||
ld bc,ZXUNOADDR
|
||||
ld a,RADASCTRL
|
||||
out (c),a
|
||||
ld bc,ZXUNODATA
|
||||
ld a,3 ;modo radastaniano
|
||||
out (c),a
|
||||
|
||||
ld bc,BANKM
|
||||
ld a,00010111b ;banco 7, pantalla normal, ROM 3
|
||||
ld (Banco),a
|
||||
out (c),a
|
||||
ei
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
RestoreVideoMemory proc
|
||||
di
|
||||
ld bc,BANKM
|
||||
ld a,00010000b ;banco 0, pantalla normal, ROM 3
|
||||
out (c),a
|
||||
|
||||
ld bc,ZXUNOADDR
|
||||
ld a,RADASCTRL
|
||||
out (c),a
|
||||
ld bc,ZXUNODATA
|
||||
ld a,0 ;modo ULA normal
|
||||
out (c),a
|
||||
|
||||
ei
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
SwitchScreens proc
|
||||
halt
|
||||
|
||||
ld ix,0c000h + 6144 ;apuntamos a donde está la paleta
|
||||
ld d,0 ;D es el indice a la paleta que se está escribiendo
|
||||
ld h,0ffh ;H contiene el color más oscuro, para poner en el borde después
|
||||
ld l,0 ;L contiene el índice al color más oscuro
|
||||
BucUpdPaleta ld bc,ULAPLUSADDR
|
||||
out (c),d
|
||||
ld b,0ffh
|
||||
ld a,(ix)
|
||||
out (c),a
|
||||
ld a,d
|
||||
cp 8 ;Solo hacemos el test para los indices 0-7
|
||||
jr nc,NoTestColorOscuro
|
||||
ld a,(ix)
|
||||
cp h
|
||||
jr nc,NoTestColorOscuro
|
||||
ld h,a
|
||||
ld l,d
|
||||
NoTestColorOscuro inc ix
|
||||
inc d
|
||||
ld a,16
|
||||
cp d
|
||||
jr nz,BucUpdPaleta
|
||||
|
||||
ld a,l
|
||||
out (254),a ;El borde lo más oscuro posible
|
||||
|
||||
ld a,(Banco)
|
||||
xor 00001010b ;conmutamos de la pantalla normal a la shadow y de la 7 a la 5
|
||||
ld (Banco),a
|
||||
ld bc,BANKM
|
||||
out (c),a
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
Cls proc
|
||||
ld a,(BORDERCLR)
|
||||
sra a
|
||||
sra a
|
||||
sra a
|
||||
and 7
|
||||
out (254),a
|
||||
ld hl,16384
|
||||
ld de,16385
|
||||
ld bc,6143
|
||||
ld (hl),l
|
||||
ldir
|
||||
inc hl
|
||||
inc de
|
||||
ld bc,767
|
||||
ld a,(PAPERCOLOR)
|
||||
ld (hl),a
|
||||
ldir
|
||||
ret
|
||||
endp
|
||||
|
||||
|
||||
; 01234567890123456789012345678901
|
||||
Uso db " playrmov moviefile.rdm",13,13
|
||||
db "Plays a video file encoded for",13
|
||||
db "the ",34,"Radastan",34," video mode.",13,0
|
||||
|
||||
FHandle db 0
|
||||
Banco db 0
|
||||
BackupSP dw 0
|
||||
|
||||
BufferNFich equ $ ;resto de la RAM para el nombre del fichero
|
||||
Binary file not shown.
Loading…
Reference in New Issue