diff --git a/software/joyconf/JOYCONF b/software/joyconf/JOYCONF new file mode 100644 index 0000000..e75fbb2 Binary files /dev/null and b/software/joyconf/JOYCONF differ diff --git a/software/joyconf/joyconf.c b/software/joyconf/joyconf.c new file mode 100644 index 0000000..0e84af1 --- /dev/null +++ b/software/joyconf/joyconf.c @@ -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; +} diff --git a/software/joyconf/joyconftap.c b/software/joyconf/joyconftap.c new file mode 100644 index 0000000..248321d --- /dev/null +++ b/software/joyconf/joyconftap.c @@ -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; +} diff --git a/software/joyconf/make.bat b/software/joyconf/make.bat new file mode 100644 index 0000000..2d09753 --- /dev/null +++ b/software/joyconf/make.bat @@ -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 + + diff --git a/software/joyconf/s19tozx.c b/software/joyconf/s19tozx.c new file mode 100644 index 0000000..883f3c6 --- /dev/null +++ b/software/joyconf/s19tozx.c @@ -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 + +#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=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;i3) + { + 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; +} + diff --git a/software/loadpzx/make.bat b/software/loadpzx/make.bat new file mode 100644 index 0000000..b8a1485 --- /dev/null +++ b/software/loadpzx/make.bat @@ -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 + + diff --git a/software/loadpzx/pzx_format.txt b/software/loadpzx/pzx_format.txt new file mode 100644 index 0000000..dfd7bb0 --- /dev/null +++ b/software/loadpzx/pzx_format.txt @@ -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. diff --git a/software/playzxm/PLAYZXM b/software/playzxm/PLAYZXM new file mode 100644 index 0000000..92ee8db Binary files /dev/null and b/software/playzxm/PLAYZXM differ diff --git a/software/playzxm/badapple.zip b/software/playzxm/badapple.zip new file mode 100644 index 0000000..e953290 Binary files /dev/null and b/software/playzxm/badapple.zip differ diff --git a/software/playzxm/makevideozxm.c b/software/playzxm/makevideozxm.c new file mode 100644 index 0000000..844c043 --- /dev/null +++ b/software/playzxm/makevideozxm.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#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); +} + diff --git a/software/playzxm/playzxm.asm b/software/playzxm/playzxm.asm new file mode 100644 index 0000000..0c90b3b --- /dev/null +++ b/software/playzxm/playzxm.asm @@ -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 \ No newline at end of file diff --git a/software/playzxm/videos_mazinger_formato_zxm.zip b/software/playzxm/videos_mazinger_formato_zxm.zip new file mode 100644 index 0000000..e2c8d8b Binary files /dev/null and b/software/playzxm/videos_mazinger_formato_zxm.zip differ diff --git a/software/videos_radastanianos/PLAYRMOV b/software/videos_radastanianos/PLAYRMOV new file mode 100644 index 0000000..4dec916 Binary files /dev/null and b/software/videos_radastanianos/PLAYRMOV differ diff --git a/software/videos_radastanianos/javi.rdm b/software/videos_radastanianos/javi.rdm new file mode 100644 index 0000000..e13ed07 Binary files /dev/null and b/software/videos_radastanianos/javi.rdm differ diff --git a/software/videos_radastanianos/makevideoradas.c b/software/videos_radastanianos/makevideoradas.c new file mode 100644 index 0000000..77d4d9d --- /dev/null +++ b/software/videos_radastanianos/makevideoradas.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#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; +} + diff --git a/software/videos_radastanianos/makevideoradas.exe b/software/videos_radastanianos/makevideoradas.exe new file mode 100644 index 0000000..8f4c6eb Binary files /dev/null and b/software/videos_radastanianos/makevideoradas.exe differ diff --git a/software/videos_radastanianos/nia.rdm b/software/videos_radastanianos/nia.rdm new file mode 100644 index 0000000..7562a0d Binary files /dev/null and b/software/videos_radastanianos/nia.rdm differ diff --git a/software/videos_radastanianos/playrmov.asm b/software/videos_radastanianos/playrmov.asm new file mode 100644 index 0000000..cc972cc --- /dev/null +++ b/software/videos_radastanianos/playrmov.asm @@ -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 \ No newline at end of file diff --git a/software/videos_radastanianos/sabrina.rdm b/software/videos_radastanianos/sabrina.rdm new file mode 100644 index 0000000..d3a6a44 Binary files /dev/null and b/software/videos_radastanianos/sabrina.rdm differ