/* device.c
 *   Atari SIO device simulation
 *   currently simulates disk drives D1 through D8
 *   with incomplete support for RS232 interfaces R1-R4
 *   and printers P1-P2
 * by Frank Barrus 
 */

#include <stdio.h>
#include "memtype.h"
#include "sio.h"
#include "vdisk.h"

extern char errmsg[];

static int devnum = -1;
static int unit;
static byte cmd;
static byte aux1, aux2;
static word aux;
static int sendsize, recvsize;
static byte recvbuf[260];

static int disk_send_cmd(void);
static int disk_send_frame(byte *frame, int n);
static int rs232_send_cmd(void);
static int rs232_send_frame(byte *frame, int n);
static int rs232_exists(int dev);
static int printer_send_cmd(void);
static int printer_send_frame(byte *frame, int n);
static int printer_exists(int dev);

static struct {
	byte dev;
	char name[4];
	int unit;
	int (*send_cmd)(void);
	int (*send_frame)(byte *frame, int n);
	int (*check_exists)(int n);
} devtable[] = {
	{D1, "D1", 1, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D2, "D2", 2, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D3, "D3", 3, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D4, "D4", 4, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D5, "D5", 5, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D6, "D6", 6, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D7, "D7", 7, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{D8, "D8", 8, disk_send_cmd, disk_send_frame, vdisk_mounted},
	{R1, "R1", 1, rs232_send_cmd, rs232_send_frame, rs232_exists},
	{R2, "R2", 2, rs232_send_cmd, rs232_send_frame, rs232_exists},
	{R3, "R3", 3, rs232_send_cmd, rs232_send_frame, rs232_exists},
	{R4, "R4", 4, rs232_send_cmd, rs232_send_frame, rs232_exists},
	{P1, "P1", 1, printer_send_cmd, printer_send_frame, printer_exists},
	{P2, "P2", 2, printer_send_cmd, printer_send_frame, printer_exists}
};

#define arraysize(a)	(sizeof(a)/sizeof(a[0]))


static int disk_send_cmd(void)
{
	switch(cmd) {
	case Read:
		printf("D%d: Read %d\n", unit, aux);
		if(!(recvsize = vdisk_read_sec(unit, aux, &recvbuf[1])))
			return False;
		recvbuf[0] = Complete;
		recvsize++;
		break;
	case Write:
		printf("D%d: Write %d\n", unit, aux);
		if(!(sendsize = vdisk_get_secsize(unit, aux)))
			return False;
		break;
	case Status:
		printf("D%d: Status ", unit);
		if(!(recvsize = vdisk_status(unit, &recvbuf[1]))) {
			printf("failed\n");
			return False;
		}
		printf("(%02X %02X %02X %02X)\n", 
			recvbuf[1], recvbuf[2], recvbuf[3], recvbuf[4]); 
		recvbuf[0] = Complete;
		recvsize++;
		break;
	default:
		sprintf(errmsg, "%s: unknown disk command: %02x (%02x %02x)",
			devtable[devnum].name, cmd, aux1, aux2);
		return False;
	}
	return True;
}

static int disk_send_frame(byte *frame, int n)
{
	return False;
}

static int rs232_send_cmd(void)
{
	return False;
}

static int rs232_send_frame(byte *frame, int n)
{
	return False;
}

static int rs232_exists(int n)
{
	return False;
}


static int printer_send_cmd(void)
{
	return False;
}

static int printer_send_frame(byte *frame, int n)
{
	return False;
}

static int printer_exists(int n)
{
	return False;
}

	


int dev_send_cmd(byte *buf, int n)
{
byte chksum;
byte dev;
	dev = buf[0];
	cmd = buf[1];
	aux1 = buf[2];
	aux2 = buf[3];
	aux = WORD(aux1, aux2);
	for(devnum=0; devnum<arraysize(devtable); devnum++) {
		if(devtable[devnum].dev == dev)
			goto valid_device;
	}
	sprintf(errmsg, "%02X: no such device (%02x %02x %02x)", dev,
						cmd, aux1, aux2);
	devnum = -1;
	return False;
valid_device:
	recvsize = 0;
	sendsize = 0;
	unit = devtable[devnum].unit;
	if(n != 5) {
		sprintf(errmsg, "%s: bad command frame size (%d)",
			devtable[devnum].name, n);
		goto nak; 
	}
	chksum = compute_checksum(buf, 4);
	if(buf[4] != chksum) {
		sprintf(errmsg, "%s: bad checksum (%02x != %02x)",
			devtable[devnum].name, buf[4], chksum);
		goto nak; 
	}
	if(!devtable[devnum].check_exists(unit)) {
		sprintf(errmsg, "%s: does not exist", devtable[devnum].name);
		return False;
	}
	if(devtable[devnum].send_cmd()) {
		buf[0] = Ack;
		buf[1] = (recvsize > 0);
		return True;
	}
nak:
	buf[0] = Nak;
	devnum = -1;
	return True;
}


int dev_send_frame(byte *buf, int n)
{
byte chksum;
	if(devnum < 0) {
		sprintf(errmsg, "sio: %d bytes of data w/o command", n);
		return False;
	}
	if(n != sendsize+1) {
		sprintf(errmsg, "%s: bad send frame size (%d != %d)",
			devtable[devnum].name, n, sendsize);
		return False;
	}
	chksum = compute_checksum(buf, n-1);
	if(chksum != buf[n-1]) {
		sprintf(errmsg, "%s: bad send checksum (%02x != %02x)",
			devtable[devnum].name, chksum, buf[n-1]);
		return False;
	}
	buf[0] = Complete;
	devnum = -1;
	return 1;
}


int dev_recv_frame(byte *buf)
{
	if(devnum < 0 || !recvsize) 
		return 0;
	memcpy(buf, recvbuf, recvsize);
	buf[recvsize] = compute_checksum(&buf[1], recvsize-1);
	return recvsize+1;
}



int device_init(int argc, char **argv)
{
static int init = True;
int narg = 0;
char **argp = argv;
	if(init) { 
	}
	while(argc--) {
		{
			*argp++ = *argv; narg++;
		}
		if(!strcmp(*argv++,"-h")) {
			printf("Device options:\n");
		} 
	}
	init = False;
	return narg;
}

