

/* vdp50.h */

/* DEC_VDP-50 header file for videodisc driver */
/* 
	Russ Sasnett
	MIT Project Athena
	Visual Courseware Group
	russ@athena
*/

#ifndef	VDP50_H
#define	VDP50_H

/* 
	a command/response packet is made up of:

	[cmd/resp byte] [argument bytes] [cmd/resp end byte]

	where the [end byte] is the result of subracting
	the [cmd/resp byte] from 0xff; the following macro 
	computes what the end byte should be
*/

/* macro to compute a packet begin or end byte from its opposite */
#define vdp50_OtherByte(c)	(0xff - ((c) & 0xff))

/* macros to compute a resp byte from its cmd byte, & vice-versa */
#define vdp50_GetResp(c)	((c) - 0x4a)
#define vdp50_GetCmd(c)		((c) + 0x4a)

typedef unsigned char	UBYTE;

/* define a couple of bit-twiddling macros */

#define BITS_ON(b,m)		((b) |= (m))
#define BITS_OFF(b,m)		((b) &= ~(m))
#define BITS_TOGGLE(b,m)	((b) ^= (m))

/* ----------------------- Driver Table for VDP50 ----------------------- */

/* 
In the following table, there are a few discrepancies from the VDP50
manual. Apparently, commands which need to wait a long time, such as
"Search" and "Play Until", receive an "Ack" packet immediately. They
then get the rest of the bytes as outlined in the manual upon
completion. This was determined experimentally, and apparently
represents a revision beyond the manual. The responses have been 
added to the following packets:

	Scan Until
	Search
	Play Until
	Play Until w/Step

The extra two bytes are 0xB5 followed by 0x4A; see the "Okay" 
packet below.
*/

static struct cmdtbl
{
	char *	cmd_name;
	UBYTE	arg_bytes;
	UBYTE	resp_bytes;
	UBYTE	cmd_code;
	UBYTE	resp_code;
	UBYTE	cmd_end;
	UBYTE	resp_end;	
} vdp50_table[] =

/*
=======================================================================
cmd_name, arg_bytes, resp_bytes, cmd_code, resp_code, cmd_end, resp_end
=======================================================================
*/
	{

	"RC On/Off",		1, 0, 0xE9, 0x9F, 0x16, 0x60, 
	"Eject",		0, 1, 0xEA, 0xA0, 0x15, 0x5F,
	"Audio",		1, 0, 0xEB, 0xA1, 0x14, 0x5E,
	"Disc I.D.",		0, 3, 0xEC, 0xA2, 0x13, 0x5D,
	"Halt",			0, 0, 0xED, 0xA3, 0x12, 0x5C,
	"Index",		1, 0, 0xEE, 0xA4, 0x11, 0x5B,
	"Load",			0, 3, 0xEF, 0xA5, 0x10, 0x5A,
	"Play",			1, 0, 0xF0, 0xA6, 0x0F, 0x59,

	/* Play Until gets an ACK packet first, then its response */
	"Play Until", 		3, 1, 0xF1, 0xA7, 0x0E, 0x58,

	/* Play Until w/Step gets an ACK packet first, then its response */
	"Play Until w/Step",	6, 1, 0xF2, 0xA8, 0x0D, 0x57,

	"Quick Jump",		2, 0, 0xF3, 0xA9, 0x0C, 0x56,
	"Revision",		0, 4, 0xF4, 0xAA, 0x0B, 0x55,
	"Scan",			1, 0, 0xF5, 0xAB, 0x0A, 0x54,

	/* Scan Until gets an ACK packet first, then its response */
	"Scan Until",		3, 1, 0xF6, 0xAC, 0x09, 0x53,

	/* Search gets an ACK packet first, then its response */
	"Search",		3, 4, 0xF7, 0xAD, 0x08, 0x52, 

	"Speed",		2, 0, 0xF8, 0xAE, 0x07, 0x51,
	"Status",		0, 5, 0xF9, 0xAF, 0x06, 0x50,
	"Step",			1, 1, 0xFA, 0xB0, 0x05, 0x4F,
	"Trigger",		3, 3, 0xFB, 0xB1, 0x04, 0x4E,
	"Trigger CL",		0, 0, 0xFC, 0xB2, 0x03, 0x4D,
	"Unload",		0, 1, 0xFD, 0xB3, 0x02, 0x4C,
	"Video Mute",		1, 0, 0xFE, 0xB4, 0x01, 0x4B, 

	/* special error packet: no command (0xFF), just a response */
	"Error",		0, 3, 0xFF, 0xC0, 0xFF, 0x3F,

	/* special ack packet: no command (0xFF), just a response */
	"Ack",			0, 0, 0xFF, 0xB5, 0xFF, 0x4A,

	};

#define VDP50_NDEFS	(sizeof(vdp50_table) / sizeof(struct cmdtbl))

/* if command byte is the following, the packet is just a response */ 
#define VDP50_NO_COMMAND	0xFF	
				
/* now define the table indices for the cmd/resp packets */

#define VDP50_REMOTE_PKT	0
#define VDP50_EJECT_PKT 	1
#define VDP50_AUDIO_PKT		2
#define VDP50_ID_PKT		3
#define VDP50_HALT_PKT		4
#define VDP50_INDEX_PKT		5
#define VDP50_LOAD_PKT		6
#define VDP50_PLAY_PKT		7
#define VDP50_PLAY_U_PKT	8	/* Play Until */
#define VDP50_PLAY_US_PKT	9	/* Play Until w/Step */
#define VDP50_JUMP_PKT		10
#define VDP50_REV_PKT		11	/* Revision */
#define VDP50_SCAN_PKT		12
#define VDP50_SCAN_U_PKT	13	/* Scan Until */
#define VDP50_SEARCH_PKT	14
#define VDP50_SPEED_PKT		15
#define VDP50_STATUS_PKT	16
#define VDP50_STEP_PKT		17
#define VDP50_TRIGGER_PKT	18
#define VDP50_TRIGGER_CL_PKT	19	/* Trigger Clear */
#define VDP50_UNLOAD_PKT	20
#define VDP50_VIDEO_PKT		21	/* Video Mute */
#define VDP50_ERROR_PKT		22
#define VDP50_ACK_PKT		23	/* Acknowlege for 'Until' and Search Commands */

/* ------------------ BCD Frame Number Conversion --------------------- */

	/* define 3-byte BCD frame number format */

typedef UBYTE	VDP50_Frame[3];

/* ---------------Argument Byte Defs for Commands ------------------ */

/* === Audio Command === */

	/* the Audio cmd argument is 1 byte, with following bits */

#define VDP50_CH1_ON	0x01
#define VDP50_IGN_CH1	0x02
#define VDP50_CH2_ON	0x04
#define VDP50_IGN_CH2	0x08
#define VDP50_CX_ON	0x10
#define VDP50_IGN_CX	0x20

/* === Disc I.D. Response === */

	/* Disc I.D. contains 3 bytes */
	/* the macro determines if ID is present 
	   in the returned info (if not, all zeroes) */
	/* ERROR: manual says all zeroes, actually all 'ff' */

	/* 
		if disc does have id, format is:
		byte 0:		0x8?
		byte 1:		0xD?
		byte 2:		0x?? 
	*/

typedef UBYTE	VDP50_DiscID[3];
#define vdp50_DiscHasID(x) ((x)[0]!=0xff && (x)[1]!=0xff || (x)[2]!=0xff)

/* === Eject Response === */

	/* the Eject cmd response is 1 byte, bits as follows */
	/* if the byte is all zeroes, no error occurred;
	   else if one of these bits is set, that component
	   is still 'on' or running when it should be 'off' */

	/* the LOAD_TIMEOUT is valid for Eject, Load, Unload cmds */

#define VDP50_LOAD_TIMEOUT	20000

#define VDP50_EJECT_OK		0x00	/* byte value */
#define	VDP50_LASER_ON		0x01	/* bit mask */
#define VDP50_MOTOR_ON		0x02	/* bit mask */
#define VDP50_LID_CLOSED	0x04	/* bit mask */

/* === Halt Command === */

	/* takes no args, has no response */

/* === Index Command === */

	/* the Index cmd arg is 1 byte, 1 bit as follows */
	/* if set, the bit turns on the index */

#define VDP50_INDEX_ON		0x01

/* === Load Response === */

	/* load response is a 3-byte VDP50_Frame, which is the
	   'home' frame: the first valid frame after the lead-in.
	   Most often, this number will be 1. */

#define vdp50_LoadIsNormal(f)	( (f)[0]==0xf0 && (f)[1]==0 && (f)[2]==0x01 )

/* === Play Command === */

	/* play command takes 1 byte; has 1 direction bit;
	   if set, play fwd; if cleared, play rev */
	/* use the macro to set direction in a UBYTE arg */
	/* VDP50_FWD and VDP50_REV work for all direction
	   macros */

#define VDP50_PLAY_DIR	0x01
#define VDP50_FWD	1
#define VDP50_REV	0
#define vdp50_SetPlayDirection(p,d)	\
	( (d) ? BITS_ON((p),VDP50_PLAY_DIR) : BITS_OFF((p),VDP50_PLAY_DIR) )

/* === Play Until Command/Response === */

	/* Play Until takes a 3-byte frame number argument */
	/* the first (immediate) response is an ACK packet; 
	   the actual response has one bit in a bytes which 
	   tells whether or not the frame was found */

#define VDP50_FRAME_NOT_FOUND	0x01	/* bit mask */

/* === Play Until w/Step Command/Response === */

	/* 
	   this command takes two 3-byte frame numbers as args;
	   the first nibble of the first frame is 0xf0; the
	   first nibble of the second frame is 0x00; the macro
	   will set them both up for you, after you have stuff
	   in the appropriate frame numbers 

	   the response should be the same as Play Until above
	*/
	
#define vdp50_SetPlayWithStepFrames( f1, f2 ) \
	((f1)[0] |= 0xf0; (f2)[0] &= 0x0f)

/* === Quick Jump Command/Response === */

	/* can make one leap up to 99 frames in vertical interval;
	   for continuous jumping, 20 frames is max */

#define VDP50_MAX_SINGLE_JUMP		99
#define VDP50_MAX_MULTI_JUMP		20

	/* argument is 2 bytes; 1 bit for dir, 3 nibbles for BCD */

typedef UBYTE VDP50_JumpArg[2];

	/* to use the JumpArg, FIRST set the #frames
	   with IntToJumpArg(), THEN set the direction 
	   with this macro */

#define VDP50_JUMP_DIR	0x10	/* a bit mask */
#define vdp50_SetJumpDirection(j,d)	\
  ( (d==VDP50_FWD) ? BITS_ON((j)[0],VDP50_JUMP_DIR) : BITS_OFF((j)[0],VDP50_JUMP_DIR) )

/* === Remote Control On/Off Command === */

	/* allows or disallows hand-held controller */
	/* argument is 1 byte, with 1 bit */

	/* manual unclear; maybe no response bytes? */

#define VDP50_REMOTE_ON	0x01	/* bit mask; on if set */

/* === Revision Response === */

	/* revision command gets back 4 bytes containing
	   2 numbers; first one is hardware, second firmware;
	   manual is unclear, but I think they are meant to
	   be 16-bit values 
	*/

typedef UBYTE	VDP50_Revision[4];

/* === Scan Command === */

	/* 1-byte arg set scan direction */

#define VDP50_SCAN_DIR	0x01
#define vdp50_SetScanDirection(p,d)	\
	( (d) ? BITS_ON((p),VDP50_SCAN_DIR) : BITS_OFF((p),VDP50_SCAN_DIR) )
	
/* === Scan Until Command/Response === */

	/* takes BCD frame as arg; response should be same
	   as on Play Until */

/* === Search Command/Response === */

	/* 
	   takes standard BCD frame as argument;
	   response is total of 6 bytes; first you get
	   an immediate ACK packet, as with Play Until;
	   the remaining packet contains a frame-found
	   byte as in Play Until followed by
	   a standard 3-byte BCD frame (closest
	   valid frame to landing spot); routine
	   should time out after 8 seconds
	*/

#define VDP50_SEARCH_TIMEOUT	8000	/* # millisecs */

/* === Speed Command === */

	/* 
	   speed command has a 2-byte arg; flags are
	   in first byte for speed range; remaining
	   3 nibbles set the BCD-encoded speed if range
	   is not 'normal'. if range is 'fast', speed
	   is computed as [speed]*30 fps. if range is
	   'slow', speed is 1/[speed]*30 fps. if range
	   is 'normal', speed is 30 fps.

	   to use: declare a VDP50_Speed arg; set the
	   rate using IntToSpeedArg() if range is not
	   'normal'; then set range with SetSpeedRange().
	   New Speed will take effect after next Play
	   command (also Play Until w/Step & Play Until).
	*/

typedef UBYTE VDP50_Speed[2];

	/* conversion to 2-byte BCD is the same as
	   IntToJumpArg(); limit is 20 when range is 'fast' */

#define VDP50_MAX_FAST_SPEED	20
#define vdp50_IntToSpeedArg(n,s)	vdp50_IntToJumpArg(n,s)

	/* bit masks for mutually-exclusive speed ranges */

#define VDP50_NORMAL_PLAY	0x10
#define VDP50_SLOW_PLAY		0x20
#define VDP50_FAST_PLAY		0x40

#define vdp50_SetSpeedRange(s,r)	BITS_ON( (s)[0], (r) )

	/* 
	   macro to compute millsecs required to play
	   a given number of frames with a given range
	   and speed
	*/

#define ONE_SECOND	(float)1000
#define vdp50_TimeToPlay(num,r,s)	\
	((int)( ((r)==VDP50_NORMAL_PLAY ? (ONE_SECOND*(num))/30 : 	\
	  ((r)==VDP50_FAST_PLAY ? (ONE_SECOND*(num))/((s)*30) : \
	  ((r)==VDP50_SLOW_PLAY ? (ONE_SECOND*(num))/((1.0/(s))*30) : \
	  -1 ))) ))

/* === Status Response === */

	/* status command returns 2 flag bytes, plus
	   a standard 3-byte frame number holding current location
	*/

	/* status bit masks */

typedef UBYTE VDP50_Status[2];
	
	/* these bits are in the first status byte */

#define VDP50_ST_ERR1		0x80	/* undefined? */
#define VDP50_ST_ERR2		0x40	/* undefined? */
#define VDP50_ST_LOADED		0x20
#define VDP50_ST_PLAYING	0x10
#define VDP50_ST_SEARCHING	0x08
#define VDP50_ST_CH1_ON		0x04
#define VDP50_ST_INDEX_ON	0x02
#define VDP50_ST_CH2_ON		0x01

	/* these bits are in the second status byte */

#define VDP50_ST_REMOTE		0x80
#define VDP50_ST_BAUD_FAST	0x40	/* 9600; 4800 is clear */
#define VDP50_ST_GENLOCKED	0x20
#define VDP50_ST_STEREO		0x10
#define VDP50_ST_BILINGUAL	0x08
#define VDP50_ST_DIGITAL_VIDEO	0x04
#define VDP50_ST_TELETEXT	0x02
#define VDP50_ST_CX		0x01	/* has noise reduction */

/* === Step Command === */

	/* command takes a 1-byte arg, with 1 bit for direction */

#define VDP50_STEP_DIR	0x01
#define vdp50_SetStepDirection(p,d)	\
	( (d) ? BITS_ON((p),VDP50_STEP_DIR) : BITS_OFF((p),VDP50_STEP_DIR) )

	/* response is standard 1-byte frame-found arg */

/* === Trigger Command === */

	/* command takes a standard 3-byte frame number */

/* === Trigger Clear === */

	/* no args; purges all triggers that were set */

/* === Unload Response */

	/* returns 1 byte, bits as follows */

#define VDP50_UNLOAD_OK	0x00	/* byte value */

	/* 2 low-order bits are same as for Eject Command:
		VDP50_LASER_ON and VDP50_MOTOR_ON */

/* === Video Mute Command === */

	/* takes a 1-byte arg, bits as follows */

#define VDP50_VIDEO_MUTED	0x01	/* bit mask */

/* === Error Response === */

	/* error response has a 3-byte arg */

typedef UBYTE VDP50_Error[3];

	/* this macro gets you the offending command */
#define vdp50_ErrorCommand(e)	((e)[0])
	/* this macro gets you the flags for checking */
#define vdp50_ErrorFlags(e)	((e)[2])

	/* these bits are in the 3rd byte */
#define VDP50_ERR_LEADOUT	0x01	/* hit lead-out */
#define VDP50_ERR_GENLOCK	0x02	/* lost genlock */
#define VDP50_ERR_LASER		0x04	/* laser off */
#define VDP50_ERR_MOTOR		0x08	/* motor off */
#define VDP50_ERR_COMMAND	0x10	/* invalid command */
#define VDP50_ERR_LID		0x20	/* lid is open */
	
#endif	!VDP50_H
