You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
13 KiB
491 lines
13 KiB
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <malloc.h>
|
|
#include <memory.h>
|
|
#include "Victor_XV-D701.h"
|
|
#include "Video.h"
|
|
#include "DVDEmu.h"
|
|
|
|
/* Only works if VERBOSE_DEBUG is active in DVDEmu.cpp */
|
|
#define PACKET_DEBUG 0
|
|
|
|
/* Wait for serial */
|
|
#define WAIT() usleep( 100000 )
|
|
#define LONGWAIT() usleep( 1000000 )
|
|
|
|
/* Requests */
|
|
#define PREAMBLE_LOC 0
|
|
#define JLIP_ID_LOC 2
|
|
#define COMMAND_LOC 3
|
|
#define PARAMS_LOC 6
|
|
|
|
/* Responses */
|
|
#define PREAMBLE_LOC 0
|
|
#define JLIP_ID_LOC 2
|
|
#define STATUS_LOC 3
|
|
#define DATA_LOC 4
|
|
|
|
/* Generic */
|
|
#define PACKET_LEN 11
|
|
#define CRC_LOC 10
|
|
|
|
unsigned char NO_RESPONSE[6] = { 0, 0, 0, 0, 0, 0 };
|
|
|
|
// The command sent was unknown
|
|
#define STATUS_UNKNOWN_CMD 0x01
|
|
// The command sent was executed properly
|
|
#define STATUS_OK 0x03
|
|
// The command sent was unable to be executed
|
|
#define STATUS_ERROR 0x05
|
|
|
|
typedef unsigned char * packet_t;
|
|
|
|
/* Emulator state */
|
|
struct victor_state
|
|
{
|
|
unsigned int jlip_id;
|
|
unsigned int dvd_type;
|
|
unsigned char packet[PACKET_LEN];
|
|
packet_t response;
|
|
} victor_state;
|
|
|
|
int VictorGetJLIPID()
|
|
{
|
|
int jlip_id = 0;
|
|
int count = 0;
|
|
FILE * fp = fopen( "jlip_id", "r" );
|
|
|
|
if( fp )
|
|
{
|
|
count = fscanf( fp, "%d", &jlip_id );
|
|
fclose( fp );
|
|
}
|
|
|
|
if( count != 1 )
|
|
{
|
|
/* Default for Twinkle */
|
|
jlip_id = 33;
|
|
}
|
|
|
|
return jlip_id;
|
|
}
|
|
|
|
void VictorSetJLIPID( int jlip_id )
|
|
{
|
|
FILE * fp = fopen( "jlip_id", "w" );
|
|
|
|
if( fp )
|
|
{
|
|
fprintf( fp, "%d", jlip_id );
|
|
fclose( fp );
|
|
}
|
|
}
|
|
|
|
unsigned char CalcChecksum( const packet_t packet )
|
|
{
|
|
unsigned char sum = 0x80;
|
|
|
|
for( uint32_t i = 0; i < (PACKET_LEN - 1); i++ )
|
|
{
|
|
sum = sum - (packet[i] & 0x7F);
|
|
}
|
|
|
|
return sum & 0x7F;
|
|
}
|
|
|
|
packet_t CreatePacket( unsigned int id, unsigned int status, unsigned char response[6] )
|
|
{
|
|
packet_t data = (packet_t)malloc(PACKET_LEN);
|
|
|
|
/* Create preamble */
|
|
data[PREAMBLE_LOC + 0] = 0xFC;
|
|
data[PREAMBLE_LOC + 1] = 0xFF;
|
|
|
|
/* Stuff in ID */
|
|
data[JLIP_ID_LOC] = id & 0xFF;
|
|
|
|
/* Stuff in response status */
|
|
data[STATUS_LOC] = status & 0xFF;
|
|
|
|
/* Stuff in data */
|
|
memcpy(&data[DATA_LOC], response, 6);
|
|
|
|
/* Calculate CRC */
|
|
data[CRC_LOC] = CalcChecksum(data);
|
|
|
|
/* Send it */
|
|
return data;
|
|
}
|
|
|
|
int IsPacketGood( packet_t packet )
|
|
{
|
|
if( packet[PREAMBLE_LOC + 0] != 0xFF ||
|
|
packet[PREAMBLE_LOC + 1] != 0xFF )
|
|
{
|
|
/* Not a response packet */
|
|
return 0;
|
|
}
|
|
|
|
if( CalcChecksum( packet ) != packet[CRC_LOC] )
|
|
{
|
|
/* Invalid CRC */
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void DestroyPacket( packet_t packet )
|
|
{
|
|
free( packet );
|
|
}
|
|
|
|
packet_t HandleMediaPacket( packet_t packet )
|
|
{
|
|
switch( packet[COMMAND_LOC + 1] )
|
|
{
|
|
case 0x41:
|
|
{
|
|
/* TODO: Drive commands */
|
|
break;
|
|
}
|
|
case 0x43:
|
|
{
|
|
/* Play commands */
|
|
if( packet[COMMAND_LOC + 2] == 0x6D )
|
|
{
|
|
/* Pause */
|
|
verbose_printf( "Device pause request.\n" );
|
|
|
|
Pause();
|
|
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
else if( packet[COMMAND_LOC + 2] == 0x75 )
|
|
{
|
|
/* Play */
|
|
verbose_printf( "Device play request.\n" );
|
|
|
|
Play();
|
|
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 0x44:
|
|
{
|
|
/* Stop commands */
|
|
if( packet[COMMAND_LOC + 2] == 0x60 )
|
|
{
|
|
/* Stop */
|
|
verbose_printf( "Device stop request.\n" );
|
|
|
|
Stop();
|
|
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 0x4C:
|
|
{
|
|
/* TODO; Disk parameter commands */
|
|
break;
|
|
}
|
|
case 0x4E:
|
|
{
|
|
/* TODO: Disk status commands */
|
|
break;
|
|
}
|
|
case 0x50:
|
|
{
|
|
/* Seek commands */
|
|
if( packet[COMMAND_LOC + 2] == 0x20 )
|
|
{
|
|
/* Seek to chapter */
|
|
unsigned int chapter = ((packet[PARAMS_LOC + 0] % 10) * 100) + ((packet[PARAMS_LOC + 1] % 10) * 10) + (packet[PARAMS_LOC + 2] % 10);
|
|
|
|
if( victor_state.dvd_type == VICTOR_VCD )
|
|
{
|
|
/* VCD can only go to 99, so it sicks the data in the first two spots */
|
|
chapter /= 10;
|
|
}
|
|
|
|
verbose_printf( "Device seek to chapter %d request.\n", chapter );
|
|
|
|
if( SeekToChapter( chapter ) )
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
else
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
|
}
|
|
}
|
|
else if( packet[COMMAND_LOC + 2] == 0x61 )
|
|
{
|
|
/* Previous chapter */
|
|
verbose_printf( "Device seek to previous chapter request.\n" );
|
|
|
|
unsigned int chapter = GetChapter() - 1;
|
|
|
|
/* Special case where if we're idle and seek backwards, we start at chapter 1 */
|
|
if( !IsPlaying() && chapter == 0 )
|
|
{
|
|
chapter = 1;
|
|
}
|
|
|
|
if( SeekToChapter( chapter ) )
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
else
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
|
}
|
|
}
|
|
else if( packet[COMMAND_LOC + 2] == 0x73 )
|
|
{
|
|
/* Next chapter */
|
|
verbose_printf( "Device seek to next chapter request.\n" );
|
|
|
|
unsigned int chapter = GetChapter() + 1;
|
|
|
|
if( SeekToChapter( chapter ) )
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
}
|
|
else
|
|
{
|
|
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
packet_t HandlePowerPacket( packet_t packet )
|
|
{
|
|
switch( packet[COMMAND_LOC + 1] )
|
|
{
|
|
case 0x40:
|
|
{
|
|
/* Power commands */
|
|
if( packet[COMMAND_LOC + 2] == 0x60 )
|
|
{
|
|
/* Turn off device */
|
|
verbose_printf( "Device turn off request.\n" );
|
|
|
|
int status = IsPowered() ? STATUS_OK : STATUS_ERROR;
|
|
if( status == STATUS_OK )
|
|
{
|
|
PowerOff();
|
|
}
|
|
return CreatePacket( victor_state.jlip_id, status, NO_RESPONSE );
|
|
}
|
|
else if( packet[COMMAND_LOC + 2] == 0x70 )
|
|
{
|
|
/* Turn on device */
|
|
verbose_printf( "Device turn on request.\n" );
|
|
|
|
int status = IsPowered() ? STATUS_ERROR : STATUS_OK;
|
|
if( status == STATUS_OK )
|
|
{
|
|
PowerOn();
|
|
}
|
|
return CreatePacket( victor_state.jlip_id, status, NO_RESPONSE );
|
|
}
|
|
break;
|
|
}
|
|
case 0x4E:
|
|
{
|
|
/* Power status commands */
|
|
if( packet[COMMAND_LOC + 2] == 0x20 )
|
|
{
|
|
/* Status */
|
|
verbose_printf( "Device power status request.\n" );
|
|
|
|
unsigned char response[6] = { 0, 0x20, 0, 0, 0, 0 };
|
|
response[0] = IsPowered() ? 0x01 : 0x0;
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Unknown, let the master handler send back a failure */
|
|
return NULL;
|
|
}
|
|
|
|
packet_t HandleDevicePacket( packet_t packet )
|
|
{
|
|
if( packet[COMMAND_LOC + 1] == 0x41 )
|
|
{
|
|
/* Change JLIP ID packet */
|
|
unsigned int new_id = packet[COMMAND_LOC + 2];
|
|
|
|
if( new_id >= 1 && new_id <= 63 )
|
|
{
|
|
/* Switch IDs */
|
|
verbose_printf( "Change JLIP ID to %d\n", new_id );
|
|
|
|
packet_t response = CreatePacket( victor_state.jlip_id, STATUS_OK, NO_RESPONSE );
|
|
victor_state.jlip_id = new_id;
|
|
VictorSetJLIPID( new_id );
|
|
return response;
|
|
}
|
|
else
|
|
{
|
|
/* Out of range */
|
|
return CreatePacket( victor_state.jlip_id, STATUS_ERROR, NO_RESPONSE );
|
|
}
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x45 &&
|
|
packet[COMMAND_LOC + 2] == 0x00 )
|
|
{
|
|
/* Get machine code */
|
|
verbose_printf( "Machine code request.\n");
|
|
|
|
unsigned char response[6] = { 0x00, 0x01, 0x03, 0x00, 0x03, 0x01 };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x48 &&
|
|
packet[COMMAND_LOC + 2] == 0x20 )
|
|
{
|
|
/* Get max baud rate */
|
|
verbose_printf( "Baud rate request.\n");
|
|
|
|
/* Hardcode to 9600 baud for now */
|
|
unsigned char response[6] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x49 &&
|
|
packet[COMMAND_LOC + 2] == 0x00 )
|
|
{
|
|
/* Get machine code */
|
|
verbose_printf( "Device code request.\n");
|
|
|
|
unsigned char response[6] = { 0x03, 0x0C, 0x7F, 0x7F, 0x7F, 0x7F };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x4C &&
|
|
packet[COMMAND_LOC + 2] == 0x00 )
|
|
{
|
|
/* Get first 6 bytes of name */
|
|
verbose_printf( "Device name first half request.\n" );
|
|
|
|
unsigned char response[6] = { 'D', 'V', 'D', ' ', 'P', 'L' };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x4D &&
|
|
packet[COMMAND_LOC + 2] == 0x00 )
|
|
{
|
|
/* Get first 6 bytes of name */
|
|
verbose_printf( "Device name last half request.\n" );
|
|
|
|
unsigned char response[6] = { 'A', 'Y', 'E', 'R', 0x7F, 0x7F };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
else if( packet[COMMAND_LOC + 1] == 0x4E &&
|
|
packet[COMMAND_LOC + 2] == 0x20 )
|
|
{
|
|
/* Get machine code */
|
|
verbose_printf( "NOP request.\n");
|
|
|
|
unsigned char response[6] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
return CreatePacket( victor_state.jlip_id, STATUS_OK, response );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
packet_t HandlePacket( packet_t packet )
|
|
{
|
|
/* Check that the checksum is good first */
|
|
if( !IsPacketGood( packet ) )
|
|
{
|
|
/* In the case its invalid or unrecognized, we do nothing */
|
|
return NULL;
|
|
}
|
|
|
|
/* Check to see that it's the right ID */
|
|
if( packet[JLIP_ID_LOC] != victor_state.jlip_id )
|
|
{
|
|
/* If it isn't destined for us, we do nothing */
|
|
return NULL;
|
|
}
|
|
|
|
if( PACKET_DEBUG ) { PrintHex( "= Incoming: ", packet, PACKET_LEN ); }
|
|
|
|
/* Parse the packet out */
|
|
packet_t response = NULL;
|
|
switch( packet[COMMAND_LOC] )
|
|
{
|
|
case 0x0C:
|
|
response = HandleMediaPacket( packet );
|
|
break;
|
|
case 0x3E:
|
|
response = HandlePowerPacket( packet );
|
|
break;
|
|
case 0x7C:
|
|
response = HandleDevicePacket( packet );
|
|
break;
|
|
}
|
|
|
|
if( !response )
|
|
{
|
|
/* Unknown */
|
|
PrintHex( "Unknown packet ", packet, PACKET_LEN );
|
|
response = CreatePacket( victor_state.jlip_id, STATUS_UNKNOWN_CMD, NO_RESPONSE );
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
void VictorInit( int type )
|
|
{
|
|
victor_state.jlip_id = VictorGetJLIPID();
|
|
victor_state.dvd_type = type;
|
|
memset( victor_state.packet, 0, PACKET_LEN );
|
|
victor_state.response = NULL;
|
|
|
|
verbose_printf( "Emulating Victor XV-D701 %s.\n", victor_state.dvd_type == VICTOR_VCD ? "VCD" : "DVD" );
|
|
}
|
|
|
|
int VictorReceiveByte( unsigned char byte )
|
|
{
|
|
memmove( &victor_state.packet[0], &victor_state.packet[1], PACKET_LEN - 1 );
|
|
victor_state.packet[PACKET_LEN - 1] = byte;
|
|
|
|
if( victor_state.response )
|
|
{
|
|
/* Destroy previous packet */
|
|
DestroyPacket( victor_state.response );
|
|
}
|
|
|
|
victor_state.response = HandlePacket( victor_state.packet );
|
|
if( victor_state.response )
|
|
{
|
|
if( PACKET_DEBUG ) { PrintHex( "= Outgoing: ", victor_state.response, PACKET_LEN ); }
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *VictorGetResponse( int *length )
|
|
{
|
|
if( victor_state.response )
|
|
{
|
|
*length = PACKET_LEN;
|
|
return (unsigned char *)victor_state.response;
|
|
}
|
|
|
|
*length = 0;
|
|
return NULL;
|
|
}
|
|
|