Bug fixes and joystick drivers!

This commit is contained in:
dschmenk 2013-09-09 21:12:08 -07:00
parent 786e79a756
commit 61a5a50024
9 changed files with 448 additions and 32 deletions

12
src/11-joy.conf Executable file
View File

@ -0,0 +1,12 @@
#
# Make sure joysticks don't control the mouse
#
Section "InputClass"
Identifier "evdev joystick catchall"
MatchIsJoystick "off"
MatchDevicePath "/dev/input/event*"
Driver "evdev"
Option "StartMouseEnabled" "False"
Option "StartKeysEnabled" "False"
EndSection

Binary file not shown.

View File

@ -1,4 +1,4 @@
BIN=a2serclk a2pid a2mon a2term dskread dskwrite bintomon bload brun text2merlin merlin2text
BIN= a2serclk a2pid a2joy a2joymou a2mon a2term dskread dskwrite bintomon bload brun text2merlin merlin2text
all: $(BIN)
fusea2pi: fusea2pi.c a2lib.c

177
src/a2joy.c Executable file
View File

@ -0,0 +1,177 @@
/*
* Copyright 2013, David Schmenk
*/
#include "a2lib.c"
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#define FALSE 0
#define TRUE (!FALSE)
#define POLL_HZ 20 /* must be greater than 1 */
struct timespec tv;
struct input_event evkey, evabsx, evabsy, evsync;
#define BTTN_IO 0xC061
#define READGP0 0x380
#define READGP1 0x388
char readgp[] = {
0xA2, 0x00, // LDX #PADDLE
0x78, // SEI
0x20, 0x1E, 0xFB, // JSR PREAD
0x98, // TYA
0x60, // RTS
};
/*
* Error handling.
*/
int isdebug, stop = FALSE;
#define prdbg(s) do{if(isdebug)fprintf(stderr,(s));}while(0)
#define die(str, args...) do { \
prdbg(str); \
exit(-1); \
} while(0)
static void sig_bye(int signo)
{
stop = TRUE;
}
void main(int argc, char **argv)
{
struct uinput_user_dev uidev;
int a2fd, joyfd, absx, absy, gptoggle;
unsigned char prevbttns[2], bttns[2];
int pifd = a2open("127.0.0.1");
if (pifd < 0)
{
perror("Unable to connect to Apple II Pi");
exit(EXIT_FAILURE);
}
/*
* Are we running debug?
*/
if ((argc > 1) && (strcmp(argv[1], "-D") == 0))
{
isdebug = TRUE;
}
else
{
if (daemon(0, 0) != 0)
die("a2joy: daemon() failure");
isdebug = FALSE;
}
/*
* Create joystick input device
*/
prdbg("a2joy: Create joystick input device\n");
joyfd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (joyfd < 0)
die("error: uinput open");
if (ioctl(joyfd, UI_SET_EVBIT, EV_KEY) < 0)
die("error: uinput ioctl EV_KEY");
if (ioctl(joyfd, UI_SET_KEYBIT, BTN_TRIGGER) < 0)
die("error: uinput ioctl BTN_LEFT");
if (ioctl(joyfd, UI_SET_KEYBIT, BTN_THUMB) < 0)
die("error: uinput ioctl BTN_RIGHT");
if (ioctl(joyfd, UI_SET_EVBIT, EV_ABS) < 0)
die("error: ioctl EV_ABS");
if (ioctl(joyfd, UI_SET_ABSBIT, ABS_X) < 0)
die("error: ioctl ABS_X");
if (ioctl(joyfd, UI_SET_ABSBIT, ABS_Y) < 0)
die("error: ioctl ABS_Y");
if (ioctl(joyfd, UI_SET_EVBIT, EV_SYN) < 0)
die("error: ioctl EV_SYN");
bzero(&uidev, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "Apple2 Pi Joystick");
uidev.id.bustype = BUS_RS232;
uidev.id.vendor = 0x05ac; /* apple */
uidev.id.product = 0x2e;
uidev.id.version = 1;
uidev.absmax[0] = 255;
uidev.absmin[0] = 0;
uidev.absfuzz[0] = 0;
uidev.absflat[0] = 0;
uidev.absmax[1] = 255;
uidev.absmin[1] = 0;
uidev.absfuzz[1] = 0;
uidev.absflat[1] = 0;
write(joyfd, &uidev, sizeof(uidev));
if (ioctl(joyfd, UI_DEV_CREATE) < 0)
die("error: ioctl DEV_CREATE");
/*
* Initialize event structures.
*/
bzero(&evkey, sizeof(evkey));
bzero(&evsync, sizeof(evsync));
bzero(&evabsx, sizeof(evabsx));
bzero(&evabsy, sizeof(evabsy));
evkey.type = EV_KEY;
evabsx.type = EV_ABS;
evabsx.code = ABS_X;
evabsy.type = EV_ABS;
evabsy.code = ABS_Y;
evsync.type = EV_SYN;
/*
* Add signal handlers.
*/
if (signal(SIGINT, sig_bye) == SIG_ERR)
die("signal");
if (signal(SIGHUP, sig_bye) == SIG_ERR)
die("signal");
/*
* Set poll rate.
*/
tv.tv_sec = 0;
tv.tv_nsec = isdebug ? 1E+9/2 : 1E+9/POLL_HZ;
/*
* Download 6502 code.
*/
readgp[1] = 0;
a2write(pifd, READGP0, sizeof(readgp), readgp);
readgp[1] = 1;
a2write(pifd, READGP1, sizeof(readgp), readgp);
a2call(pifd, READGP0, &evabsx.value);
a2read(pifd, BTTN_IO, 2, prevbttns);
a2call(pifd, READGP1, &evabsy.value);
gptoggle = 0;
/*
* Poll joystick loop.
*/
prdbg("a2joy: Enter poll loop\n");
while (!stop)
{
if (gptoggle)
a2quickcall(pifd, READGP0, &absx);
else
a2quickcall(pifd, READGP1, &absy);
a2read(pifd, BTTN_IO, 2, bttns);
gptoggle ^= 1;
if (isdebug) fprintf(stderr, "a2joy (%d, %d) [%d %d]\n", absx, absy, bttns[0] >> 7, bttns[1] >> 7);
evabsx.value = absx;
evabsy.value = absy;
write(joyfd, &evabsx, sizeof(evabsx));
write(joyfd, &evabsy, sizeof(evabsy));
write(joyfd, &evsync, sizeof(evsync));
if ((bttns[0] & 0x80) != prevbttns[0])
{
prevbttns[0] = bttns[0] & 0x80;
evkey.code = BTN_TRIGGER;
evkey.value = bttns[0] >> 7;
write(joyfd, &evkey, sizeof(evkey));
write(joyfd, &evsync, sizeof(evsync));
}
if ((bttns[1] & 0x80) != prevbttns[1])
{
prevbttns[1] = bttns[1] & 0x80;
evkey.code = BTN_THUMB;
evkey.value = bttns[1] >> 7;
write(joyfd, &evkey, sizeof(evkey));
write(joyfd, &evsync, sizeof(evsync));
}
nanosleep(&tv, NULL);
}
a2close(pifd);
ioctl(joyfd, UI_DEV_DESTROY);
close(joyfd);
prdbg("\na2joy: Exit\n");
}

190
src/a2joymou.c Executable file
View File

@ -0,0 +1,190 @@
/*
* Copyright 2013, David Schmenk
*/
#include "a2lib.c"
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#define FALSE 0
#define TRUE (!FALSE)
#define POLL_HZ 30 /* must be greater than 1 */
struct timespec tv;
struct input_event evkey, evrelx, evrely, evsync;
#define BTTN_IO 0xC061
#define READGP0 0x380
#define READGP1 0x388
char readgp[] = {
0xA2, 0x00, // LDX #PADDLE
0x78, // SEI
0x20, 0x1E, 0xFB, // JSR PREAD
0x98, // TYA
0x60, // RTS
};
int accel[20] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 7, 9, 10};
/*
* Error handling.
*/
int isdebug, stop = FALSE;
#define prdbg(s) do{if(isdebug)fprintf(stderr,(s));}while(0)
#define die(str, args...) do { \
prdbg(str); \
exit(-1); \
} while(0)
static void sig_bye(int signo)
{
stop = TRUE;
}
void main(int argc, char **argv)
{
struct uinput_user_dev uidev;
int a2fd, joyfd, relx, rely, cntrx, cntry, gptoggle;
unsigned char prevbttns[2], bttns[2];
int pifd = a2open("127.0.0.1");
if (pifd < 0)
{
perror("Unable to connect to Apple II Pi");
exit(EXIT_FAILURE);
}
/*
* Are we running debug?
*/
if ((argc > 1) && (strcmp(argv[1], "-D") == 0))
{
isdebug = TRUE;
}
else
{
if (daemon(0, 0) != 0)
die("a2joy: daemon() failure");
isdebug = FALSE;
}
/*
* Create joystick input device
*/
prdbg("a2joy: Create joystick input device\n");
joyfd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (joyfd < 0)
die("error: uinput open");
if (ioctl(joyfd, UI_SET_EVBIT, EV_KEY) < 0)
die("error: uinput ioctl EV_KEY");
if (ioctl(joyfd, UI_SET_KEYBIT, BTN_LEFT) < 0)
die("error: uinput ioctl BTN_LEFT");
if (ioctl(joyfd, UI_SET_KEYBIT, BTN_RIGHT) < 0)
die("error: uinput ioctl BTN_RIGHT");
if (ioctl(joyfd, UI_SET_EVBIT, EV_REL) < 0)
die("error: ioctl EV_rel");
if (ioctl(joyfd, UI_SET_RELBIT, REL_X) < 0)
die("error: ioctl rel_X");
if (ioctl(joyfd, UI_SET_RELBIT, REL_Y) < 0)
die("error: ioctl rel_Y");
if (ioctl(joyfd, UI_SET_EVBIT, EV_SYN) < 0)
die("error: ioctl EV_SYN");
bzero(&uidev, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "Apple2 Pi Joystick");
uidev.id.bustype = BUS_RS232;
uidev.id.vendor = 0x05ac; /* apple */
uidev.id.product = 0x2e;
uidev.id.version = 1;
write(joyfd, &uidev, sizeof(uidev));
if (ioctl(joyfd, UI_DEV_CREATE) < 0)
die("error: ioctl DEV_CREATE");
/*
* Initialize event structures.
*/
bzero(&evkey, sizeof(evkey));
bzero(&evsync, sizeof(evsync));
bzero(&evrelx, sizeof(evrelx));
bzero(&evrely, sizeof(evrely));
evkey.type = EV_KEY;
evrelx.type = EV_REL;
evrelx.code = REL_X;
evrely.type = EV_REL;
evrely.code = REL_Y;
evsync.type = EV_SYN;
/*
* Add signal handlers.
*/
if (signal(SIGINT, sig_bye) == SIG_ERR)
die("signal");
if (signal(SIGHUP, sig_bye) == SIG_ERR)
die("signal");
/*
* Set poll rate.
*/
tv.tv_sec = 0;
tv.tv_nsec = isdebug ? 1E+9/2 : 1E+9/POLL_HZ;
/*
* Download 6502 code.
*/
readgp[1] = 0;
a2write(pifd, READGP0, sizeof(readgp), readgp);
readgp[1] = 1;
a2write(pifd, READGP1, sizeof(readgp), readgp);
evrelx.value = 0;
evrely.value = 0;
a2quickcall(pifd, READGP0, &cntrx);
a2read(pifd, BTTN_IO, 2, prevbttns);
a2quickcall(pifd, READGP1, &cntry);
gptoggle = 0;
/*
* Poll joystick loop.
*/
prdbg("a2joymou: Enter poll loop\n");
while (!stop)
{
if (gptoggle)
{
a2quickcall(pifd, READGP0, &relx);
if (relx >= cntrx + 20)
evrelx.value = (relx - cntrx) / 2;
else if (relx >= cntrx)
evrelx.value = accel[relx - cntrx];
else if (relx <= cntrx - 20)
evrelx.value = (relx - cntrx) / 2;
else
evrelx.value = -accel[cntrx - relx];
}
else
{
a2quickcall(pifd, READGP1, &rely);
if (rely >= cntry + 20)
evrely.value = (rely - cntry) / 2;
else if (rely >= cntry)
evrely.value = accel[rely - cntry];
else if (rely <= cntry - 20)
evrely.value = (rely - cntry) / 2;
else
evrely.value = -accel[cntry - rely];
}
a2read(pifd, BTTN_IO, 2, bttns);
gptoggle ^= 1;
if (isdebug) fprintf(stderr, "a2joymou (%d, %d) [%d %d]\n", relx, rely, bttns[0] >> 7, bttns[1] >> 7);
write(joyfd, &evrelx, sizeof(evrelx));
write(joyfd, &evrely, sizeof(evrely));
write(joyfd, &evsync, sizeof(evsync));
if ((bttns[0] & 0x80) != prevbttns[0])
{
prevbttns[0] = bttns[0] & 0x80;
evkey.code = BTN_LEFT;
evkey.value = bttns[0] >> 7;
write(joyfd, &evkey, sizeof(evkey));
write(joyfd, &evsync, sizeof(evsync));
}
if ((bttns[1] & 0x80) != prevbttns[1])
{
prevbttns[1] = bttns[1] & 0x80;
evkey.code = BTN_RIGHT;
evkey.value = bttns[1] >> 7;
write(joyfd, &evkey, sizeof(evkey));
write(joyfd, &evsync, sizeof(evsync));
}
nanosleep(&tv, NULL);
}
a2close(pifd);
ioctl(joyfd, UI_DEV_DESTROY);
close(joyfd);
prdbg("\na2joymou: Exit\n");
}

View File

@ -76,6 +76,18 @@ int a2write(int fd, int address, int count, char *buffer)
return ((unsigned char)writepkt[0] == 0x9E);
}
int a2call(int fd, int address, int *result)
{
char callpkt[4];
callpkt[0] = 0x9A; // call with keyboard flush
callpkt[1] = address;
callpkt[2] = address >> 8;
write(fd, callpkt, 3);
read(fd, callpkt, 2);
if (result)
*result = (unsigned char)callpkt[1];
return ((unsigned char)callpkt[0] == 0x9E);
}
int a2quickcall(int fd, int address, int *result)
{
char callpkt[4];
callpkt[0] = 0x94; // call

View File

@ -186,11 +186,11 @@ int keycode[256] = {
KEY_X, // x code 78
KEY_Y, // y code 79
KEY_Z, // z code 7A
MOD_SHIFT | KEY_LEFTBRACE, // { code 7B
MOD_SHIFT | KEY_BACKSLASH, // | code 7C
MOD_SHIFT | KEY_RIGHTBRACE, // } code 7D
MOD_SHIFT | KEY_GRAVE, // ~ code 7E
KEY_BACKSPACE, // BS code 7F
MOD_SHIFT | KEY_LEFTBRACE, // { code 7B
MOD_SHIFT | KEY_BACKSLASH, // | code 7C
MOD_SHIFT | KEY_RIGHTBRACE, // } code 7D
MOD_SHIFT | KEY_GRAVE, // ~ code 7E
KEY_BACKSPACE, // BS code 7F
/*
* w/ closed apple scancodes
*/
@ -323,6 +323,7 @@ int keycode[256] = {
MOD_SHIFT | KEY_GRAVE, // ~ code 7E
KEY_DELETE // DELETE code 7F
};
#define KEYCODE_MAX 0x10000
#define RUN 0
#define STOP 1
#define RESET 2
@ -400,18 +401,18 @@ void sendkey(int fd, int mod, int key)
}
else
{
sendkeycodedown(fd, code);
if (!(key & KEY_PRESS))
/*
* missed a key down event
* already synthesized one
*/
sendkeycodeup(fd, code);
if (code != -prevkeycode) /* key may have been released before client call */
{
sendkeycodedown(fd, code);
if (!(key & KEY_PRESS))
/*
* missed a key down event
* already synthesized one
*/
sendkeycodeup(fd, code);
}
}
prevkeycode = (key & KEY_PRESS) ? code : -1;
}
void flushkey(int fd)
{
prevkeycode = (key & KEY_PRESS) ? code : -KEYCODE_MAX;
}
void sendbttn(int fd, int mod, int bttn)
{
@ -880,21 +881,22 @@ reset:
else
state = RESET;
break;
case 0x9A: /* acknowledge call with keyboard flush*/
if (prevkeycode >= 0) /* flush keyboard if going away for awhile */
{
sendkeycodeup(kbdfd, prevkeycode);
prevkeycode = -prevkeycode;
}
case 0x94: /* acknowledge call */
if (a2reqlist) /* better have an outstanding request */
{
//printf("a2pid: call address 0x%04X\n", a2reqlist->addr);
newtio.c_cc[VMIN] = 1; /* blocking read until 1 char received */
tcsetattr(a2fd, TCSANOW, &newtio);
if (!writeword(a2fd, a2reqlist->addr, 0x95))
if (!writeword(a2fd, a2reqlist->addr, iopkt[0] + 1))
state = RESET;
newtio.c_cc[VMIN] = 3; /* blocking read until 3 chars received */
tcsetattr(a2fd, TCSANOW, &newtio);
if (prevkeycode >= 0)
{
sendkeycodeup(kbdfd, prevkeycode);
prevkeycode = -1;
}
}
else
state = RESET;
@ -917,6 +919,7 @@ reset:
for (i = 0; i < MAX_CLIENT; i++)
if (a2client[i].flags & CLIENT_COUT)
write(a2client[i].fd, iopkt, 2);
break;
case 0x9E: /* request complete ok */
case 0x9F: /* request complete error */
@ -1033,10 +1036,11 @@ reset:
}
break;
case 0x94: /* call */
if (read(a2client[i].fd, iopkt, 2) == 2)
case 0x9A: /* call with keyboard flush */
if (read(a2client[i].fd, iopkt + 1, 2) == 2)
{
addr = (unsigned char)iopkt[0] | ((unsigned char)iopkt[1] << 8);
addreq(a2fd, i, 0x94, addr, 0, NULL);
addr = (unsigned char)iopkt[1] | ((unsigned char)iopkt[2] << 8);
addreq(a2fd, i, iopkt[0], addr, 0, NULL);
}
break;
case 0x96: /* send input char to Apple II */

View File

@ -956,7 +956,6 @@ static int a2pi_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
/*
* Root directory, fill with volume names.
*/
memset(&straw, 0, sizeof(struct stat));
unix_stat(&stentry, 0x0F, 0xC3, 0, 0, 0, 0);
filler(buf, ".", &stentry, 0);
filler(buf, "..", &stentry, 0);
@ -969,17 +968,22 @@ static int a2pi_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
strncpy(filename, volumes + i + 1, l);
filename[l] = '\0';
filler(buf, filename, &stentry, 0);
}
memset(&straw, 0, sizeof(struct stat));
for (i = 0; i < 16; i++)
if (volblks[i] > 0)
{
/*
* Add volume raw device.
* Add raw device.
*/
slot = (volumes[i] >> 4) & 0x07;
drive = (volumes[i] >> 7) & 0x01;
slot = i & 0x07;
drive = (i >> 3) & 0x01;
strcpy(filename, "s0d0.po");
filename[1] = slot + '0';
filename[3] = drive + '1';
straw.st_mode = S_IFREG | 0644;
straw.st_nlink = 1;
straw.st_blocks = volblks[slot | (drive << 3)];
straw.st_blocks = volblks[i];
straw.st_size = straw.st_blocks * 512;
filler(buf, filename, &straw, 0);
}
@ -1342,6 +1346,13 @@ int main(int argc, char *argv[])
prodos_get_file_info(volpath, &access, &type, &aux, &storage, &numblks, &mod, &create);
volblks[volumes[i] >> 4] = aux;
}
/*
* Always add 5 1/4 floppy raw devices.
*/
if (volblks[0x06] == 0)
volblks[0x06] = 280;
if (volblks[0x0E] == 0)
volblks[0x0E] = 280;
umask(0);
return fuse_main(argc, argv, &a2pi_oper, NULL);
}

View File

@ -24,4 +24,14 @@ if [ -f /etc/rc.local ] ; then
sed -e '/^exit/i\# Start Apple II Pi' -e '/^exit/i\/usr/local/bin/a2serclk' -e '/^exit/i\/usr/local/bin/a2pid --daemon' /etc/rc.local.bak > /etc/rc.local
chmod +x /etc/rc.local
fi
fi
fi
#
# Disable joystick as a mouse in X
#
cp 11-joy.conf /usr/share/X11/xorg.conf.d
#
# Create link to new joystick driver for gsportx
#
if [ ! -e /dev/js0 ] ; then
ln -s /dev/input/js0 /dev/js0
fi