#include "shared.h"
#include "../cinterface/callbacks.h"
static const uint8 lut_BCD_8[100] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
};
static const uint16 lut_BCD_16[100] =
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109,
0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209,
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309,
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509,
0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609,
0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, 0x0708, 0x0709,
0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809,
0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909,
};
static const uint16 toc_snatcher[21] =
{
56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681,
8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485,
31380
};
static const uint16 toc_lunar[52] =
{
5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870,
6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263,
6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205,
1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549,
1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132,
685, 3167
};
static const uint32 toc_shadow[15] =
{
10226, 70054, 11100, 12532, 12444, 11923, 10059, 10167, 10138, 13792,
11637, 2547, 2521, 3856, 900
};
static const uint32 toc_dungeon[13] =
{
2250, 22950, 16350, 24900, 13875, 19950, 13800, 15375, 17400, 17100,
3325, 6825, 25275
};
static const uint32 toc_ffight[26] =
{
11994, 9742, 10136, 9685, 9553, 14588, 9430, 8721, 9975, 9764,
9704, 12796, 585, 754, 951, 624, 9047, 1068, 817, 9191, 1024,
14562, 10320, 8627, 3795, 3047
};
static const uint32 toc_ffightj[29] =
{
11994, 9752, 10119, 9690, 9567, 14575, 9431, 8731, 9965, 9763,
9716, 12791, 579, 751, 958, 630, 9050, 1052, 825, 9193, 1026,
14553, 9834, 10542, 1699, 1792, 1781, 3783, 3052
};
static const unsigned char waveHeader[32] =
{
0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61
};
static blip_t* blip[2];
typedef struct
{
toc_t toc;
} frontendcd_t;
int cdd_load(const char *key, char *header)
{
frontendcd_t fecd;
char data[2048];
int startoffs;
int bytes = sizeof(frontendcd_t);
if (load_archive(key, (unsigned char *)&fecd, bytes, NULL) != bytes)
return 0;
cdd_readcallback(0, data, 0);
if (memcmp("SEGADISCSYSTEM", data, 14) == 0)
startoffs = 0;
else if (memcmp("SEGADISCSYSTEM", data + 16, 14) == 0)
startoffs = 16;
else
return 0;
memcpy(header, data + startoffs, 0x210);
memcpy(&cdd.toc, &fecd.toc, sizeof(toc_t));
cdd.loaded = 1;
return 1;
}
void cdd_init(blip_t* left, blip_t* right)
{
blip[0] = left;
blip[1] = right;
blip_set_rates(left, 44100, snd.sample_rate);
blip_set_rates(right, 44100, snd.sample_rate);
}
void cdd_reset(void)
{
cdd.cycles = 0;
cdd.latency = 0;
cdd.index = 0;
cdd.lba = 0;
cdd.sampleOffset = 0;
cdd.sampleLba = 0;
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
cdd.volume = 0x400;
cdd.audio[0] = cdd.audio[1] = 0;
}
void cdd_unload(void)
{
cdd.loaded = 0;
cdd_readcallback = NULL;
memset(&cdd.toc, 0x00, sizeof(cdd.toc));
}
void cdd_read_data(uint8 *dst)
{
if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end))
{
cdd_readcallback(cdd.lba, dst, 0);
}
}
void cdd_read_audio(unsigned int samples)
{
int16 l = cdd.audio[0];
int16 r = cdd.audio[1];
samples = blip_clocks_needed(blip[0], samples);
if (!scd.regs[0x36>>1].byte.h)
{
int i, mul, delta;
int curVol = cdd.volume;
int endVol = scd.regs[0x34>>1].w >> 4;
{
#ifdef LSB_FIRST
int16 *ptr = (int16 *) (cdc.ram);
#else
uint8 *ptr = cdc.ram;
#endif
{
char scratch[2352];
int nsampreq = samples;
unsigned char *dest = cdc.ram;
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(cdc.ram, scratch + cdd.sampleOffset * 4, 2352 - cdd.sampleOffset * 4);
cdd.sampleLba++;
nsampreq -= 588 - cdd.sampleOffset;
dest += 2352 - cdd.sampleOffset * 4;
cdd.sampleOffset = 0;
while (nsampreq >= 588)
{
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(dest, scratch, 2352);
cdd.sampleLba++;
nsampreq -= 588;
dest += 2352;
}
if (nsampreq > 0)
{
cdd_readcallback(cdd.sampleLba, scratch, 1);
memcpy(dest, scratch, nsampreq * 4);
cdd.sampleOffset = nsampreq;
dest += nsampreq * 4;
nsampreq = 0;
}
}
for (i=0; i<samples; i++)
{
mul = (curVol & 0x7fc) ? (curVol & 0x7fc) : (curVol & 0x03);
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - l;
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - l;
ptr += 2;
#endif
l += delta;
blip_add_delta_fast(blip[0], i, delta);
#ifdef LSB_FIRST
delta = ((ptr[0] * mul) / 1024) - r;
ptr++;
#else
delta = (((int16)((ptr[0] + ptr[1]*256)) * mul) / 1024) - r;
ptr += 2;
#endif
r += delta;
blip_add_delta_fast(blip[1], i, delta);
if (curVol < endVol)
{
curVol++;
}
else if (curVol > endVol)
{
curVol--;
}
else if (!curVol)
{
break;
}
}
}
cdd.volume = curVol;
cdd.audio[0] = l;
cdd.audio[1] = r;
}
else
{
if (l) blip_add_delta_fast(blip[0], 0, -l);
if (r) blip_add_delta_fast(blip[1], 0, -r);
cdd.audio[0] = 0;
cdd.audio[1] = 0;
}
blip_end_frame(blip[0], samples);
blip_end_frame(blip[1], samples);
}
void cdd_update(void)
{
#ifdef LOG_CDD
error("LBA = %d (track n�%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency);
#endif
if (cdd.status == CD_SEEK)
{
if (cdd.latency > 0)
{
cdd.latency--;
return;
}
cdd.status = CD_READY;
}
else if (cdd.status == CD_PLAY)
{
if (cdd.latency > 0)
{
cdd.latency--;
return;
}
if (!cdd.index)
{
uint8 header[4];
uint32 msf = cdd.lba + 150;
header[0] = lut_BCD_8[(msf / 75) / 60];
header[1] = lut_BCD_8[(msf / 75) % 60];
header[2] = lut_BCD_8[(msf % 75)];
header[3] = 0x01;
cdd.lba += cdc_decoder_update(*(uint32 *)(header));
}
else if (cdd.index < cdd.toc.last)
{
if (cdd.lba >= cdd.toc.tracks[cdd.index].start)
{
if (scd.regs[0x36>>1].byte.h)
{
cdd.sampleLba = cdd.lba + 1;
cdd.sampleOffset = 0;
}
scd.regs[0x36>>1].byte.h = 0x00;
}
cdc_decoder_update(0);
cdd.lba++;
}
else
{
cdd.status = CD_END;
return;
}
if (cdd.lba >= cdd.toc.tracks[cdd.index].end)
{
cdd.index++;
scd.regs[0x36>>1].byte.h = 0x01;
}
}
else if (cdd.status == CD_SCAN)
{
cdd.lba += cdd.scanOffset;
cdd.sampleLba += cdd.scanOffset;
if (cdd.lba >= cdd.toc.tracks[cdd.index].end)
{
cdd.index++;
cdd.lba = cdd.toc.tracks[cdd.index].start;
if (cdd.status == CD_PLAY)
{
scd.regs[0x36>>1].byte.h = 0x00;
cdd.sampleLba = cdd.lba;
cdd.sampleOffset = 0;
}
}
else if (cdd.lba < cdd.toc.tracks[cdd.index].start)
{
cdd.index--;
cdd.lba = cdd.toc.tracks[cdd.index].end;
}
if (cdd.index < 0)
{
cdd.index = 0;
cdd.lba = 0;
}
else if (cdd.index >= cdd.toc.last)
{
scd.regs[0x36>>1].byte.h = 0x01;
cdd.index = cdd.toc.last;
cdd.lba = cdd.toc.end;
cdd.status = CD_END;
return;
}
}
}
void cdd_process(void)
{
switch (scd.regs[0x42>>1].byte.h & 0x0f)
{
case 0x00:
{
scd.regs[0x38>>1].byte.h = cdd.status;
if (scd.regs[0x38>>1].byte.l == 0x0f)
{
if (cdd.status != CD_SEEK)
{
scd.regs[0x38>>1].byte.l = 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A;
}
}
break;
}
case 0x01:
{
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
scd.regs[0x36>>1].byte.h = 0x01;
scd.regs[0x38>>1].w = 0x0000;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = 0x000f;
return;
}
case 0x02:
{
switch (scd.regs[0x44>>1].byte.l)
{
case 0x00:
{
int lba = cdd.lba + 150;
scd.regs[0x38>>1].w = cdd.status << 8;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04;
break;
}
case 0x01:
{
int lba = cdd.lba - cdd.toc.tracks[cdd.index].start;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x01;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04;
break;
}
case 0x02:
{
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x03:
{
int lba = cdd.toc.end + 150;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x03;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x04:
{
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x04;
scd.regs[0x3a>>1].w = 0x0001;
scd.regs[0x3c>>1].w = lut_BCD_16[cdd.toc.last];
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x05:
{
int track = scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l;
int lba = cdd.toc.tracks[track-1].start + 150;
scd.regs[0x38>>1].w = (cdd.status << 8) | 0x05;
scd.regs[0x3a>>1].w = lut_BCD_16[(lba/75)/60];
scd.regs[0x3c>>1].w = lut_BCD_16[(lba/75)%60];
scd.regs[0x3e>>1].w = lut_BCD_16[(lba%75)];
scd.regs[0x40>>1].byte.h = track % 10;
if (track == 1)
{
scd.regs[0x3e>>1].byte.h |= 0x08;
}
break;
}
default:
{
#ifdef LOG_ERROR
error("Unknown CDD Command %02X (%X)\n", scd.regs[0x44>>1].byte.l, s68k.pc);
#endif
return;
}
}
break;
}
case 0x03:
{
int index = 0;
int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 +
(scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 +
(scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150;
if (!cdd.latency)
{
cdd.latency = 10;
}
if (lba > cdd.lba)
{
cdd.latency += (((lba - cdd.lba) * 120) / 270000);
}
else
{
cdd.latency += (((cdd.lba - lba) * 120) / 270000);
}
cdd.lba = lba;
while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++;
cdd.index = index;
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = CD_PLAY;
scd.regs[0x38>>1].w = (CD_PLAY << 8) | 0x02;
scd.regs[0x3a>>1].w = (cdd.index < cdd.toc.last) ? lut_BCD_16[index + 1] : 0x0A0A;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].byte.h = 0x00;
break;
}
case 0x04:
{
int index = 0;
int lba = ((scd.regs[0x44>>1].byte.h * 10 + scd.regs[0x44>>1].byte.l) * 60 +
(scd.regs[0x46>>1].byte.h * 10 + scd.regs[0x46>>1].byte.l)) * 75 +
(scd.regs[0x48>>1].byte.h * 10 + scd.regs[0x48>>1].byte.l) - 150;
if (lba > cdd.lba)
{
cdd.latency = ((lba - cdd.lba) * 120) / 270000;
}
else
{
cdd.latency = ((cdd.lba - lba) * 120) / 270000;
}
cdd.lba = lba;
while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++;
cdd.index = index;
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = CD_SEEK;
scd.regs[0x38>>1].w = (CD_SEEK << 8) | 0x0f;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = ~(CD_SEEK + 0xf) & 0x0f;
return;
}
case 0x06:
{
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = scd.regs[0x38>>1].byte.h = CD_READY;
break;
}
case 0x07:
{
cdd.status = scd.regs[0x38>>1].byte.h = CD_PLAY;
break;
}
case 0x08:
{
cdd.scanOffset = CD_SCAN_SPEED;
cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN;
break;
}
case 0x09:
{
cdd.scanOffset = -CD_SCAN_SPEED;
cdd.status = scd.regs[0x38>>1].byte.h = CD_SCAN;
break;
}
case 0x0a:
{
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = scd.regs[0x38>>1].byte.h = CD_READY;
break;
}
case 0x0c:
{
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = cdd.loaded ? CD_STOP : NO_DISC;
scd.regs[0x38>>1].w = 0x0000;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = 0x000f;
#ifdef CD_TRAY_CALLBACK
CD_TRAY_CALLBACK
#endif
return;
}
case 0x0d:
{
scd.regs[0x36>>1].byte.h = 0x01;
cdd.status = CD_OPEN;
scd.regs[0x38>>1].w = CD_OPEN << 8;
scd.regs[0x3a>>1].w = 0x0000;
scd.regs[0x3c>>1].w = 0x0000;
scd.regs[0x3e>>1].w = 0x0000;
scd.regs[0x40>>1].w = ~CD_OPEN & 0x0f;
#ifdef CD_TRAY_CALLBACK
CD_TRAY_CALLBACK
#endif
return;
}
default:
#ifdef LOG_CDD
error("Unknown CDD Command !!!\n");
#endif
scd.regs[0x38>>1].byte.h = cdd.status;
break;
}
scd.regs[0x40>>1].byte.l = ~(scd.regs[0x38>>1].byte.h + scd.regs[0x38>>1].byte.l +
scd.regs[0x3a>>1].byte.h + scd.regs[0x3a>>1].byte.l +
scd.regs[0x3c>>1].byte.h + scd.regs[0x3c>>1].byte.l +
scd.regs[0x3e>>1].byte.h + scd.regs[0x3e>>1].byte.l +
scd.regs[0x40>>1].byte.h) & 0x0f;
}