zxuno-git/software/joyconf/joyconf.c

700 lines
14 KiB
C

/*
* joyconf - configure/test protocols for keyboard, joystick and DB9 joystick.
*
* Copyright (C) 2016-2021 Antonio Villena
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Copyright (C) 2016-2021 Antonio Villena
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#define PROGRAM "joyconf"
#define PROGRAMUC "JOYCONF" // uppercase
#define VERSION "0.1"
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: " PROGRAMUC " [-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: ." PROGRAM " -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;
}