mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
1859 lines
40 KiB
C++
1859 lines
40 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* CDTV DMAC/CDROM controller emulation
|
|
*
|
|
* Copyright 2004/2007-2010 Toni Wilen
|
|
*
|
|
* Thanks to Mark Knibbs <markk@clara.co.uk> for CDTV Technical information
|
|
*
|
|
*/
|
|
|
|
//#define CDTV_SUB_DEBUG
|
|
//#define CDTV_DEBUG
|
|
//#define CDTV_DEBUG_CMD
|
|
//#define CDTV_DEBUG_6525
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include "options.h"
|
|
#include "memory.h"
|
|
#include "custom.h"
|
|
#include "newcpu.h"
|
|
#include "debug.h"
|
|
#include "cdtv.h"
|
|
#include "blkdev.h"
|
|
#include "gui.h"
|
|
#include "zfile.h"
|
|
#include "threaddep/thread.h"
|
|
#include "a2091.h"
|
|
#include "uae.h"
|
|
#include "savestate.h"
|
|
#include "scsi.h"
|
|
|
|
/* DMAC CNTR bits. */
|
|
#define CNTR_TCEN (1<<7)
|
|
#define CNTR_PREST (1<<6)
|
|
#define CNTR_PDMD (1<<5)
|
|
#define CNTR_INTEN (1<<4)
|
|
#define CNTR_DDIR (1<<3)
|
|
/* ISTR bits. */
|
|
#define ISTR_INTX (1<<8)
|
|
#define ISTR_INT_F (1<<7)
|
|
#define ISTR_INTS (1<<6)
|
|
#define ISTR_E_INT (1<<5)
|
|
#define ISTR_INT_P (1<<4)
|
|
#define ISTR_UE_INT (1<<3)
|
|
#define ISTR_OE_INT (1<<2)
|
|
#define ISTR_FF_FLG (1<<1)
|
|
#define ISTR_FE_FLG (1<<0)
|
|
|
|
#define MODEL_NAME "MATSHITA0.96"
|
|
/* also MATSHITA0.97 exists but is apparently rare */
|
|
|
|
#define MAX_SUBCODEBUFFER 36
|
|
static volatile int subcodebufferoffset, subcodebufferoffsetw, subcodeoffset;
|
|
static uae_u8 subcodebufferinuse[MAX_SUBCODEBUFFER];
|
|
static uae_u8 subcodebuffer[MAX_SUBCODEBUFFER * SUB_CHANNEL_SIZE];
|
|
static uae_sem_t sub_sem;
|
|
|
|
static smp_comm_pipe requests;
|
|
static volatile int thread_alive;
|
|
|
|
static int configured;
|
|
static int cdtvscsi;
|
|
static uae_u8 dmacmemory[128];
|
|
|
|
static struct cd_toc_head toc;
|
|
static uae_u32 last_cd_position, play_start, play_end;
|
|
static uae_u8 cdrom_qcode[4 + 12], cd_audio_status;
|
|
static int datatrack;
|
|
|
|
static volatile uae_u8 dmac_istr, dmac_cntr;
|
|
static volatile uae_u16 dmac_dawr;
|
|
static volatile uae_u32 dmac_acr;
|
|
static volatile int dmac_wtc;
|
|
static volatile int dmac_dma;
|
|
|
|
static volatile int activate_stch, cdrom_command_done;
|
|
static volatile int cdrom_sector, cdrom_sectors, cdrom_length, cdrom_offset;
|
|
static volatile int cd_playing, cd_paused, cd_motor, cd_media, cd_error, cd_finished, cd_isready, cd_audio_finished;
|
|
static uae_u32 last_play_pos, last_play_end;
|
|
|
|
static volatile int cdtv_hsync, dma_finished, cdtv_sectorsize;
|
|
static volatile uae_u64 dma_wait;
|
|
static int cd_volume, cd_volume_stored;
|
|
static int cd_led;
|
|
static int frontpanel;
|
|
|
|
static uae_u8 cdrom_command_input[16];
|
|
static int cdrom_command_cnt_in;
|
|
|
|
static uae_u8 tp_a, tp_b, tp_c, tp_ad, tp_bd, tp_cd;
|
|
static uae_u8 tp_imask, tp_cr, tp_air, tp_ilatch, tp_ilatch2;
|
|
|
|
static void do_stch (void);
|
|
|
|
static void INT2 (void)
|
|
{
|
|
if (!(intreq & 8)) {
|
|
INTREQ_0 (0x8000 | 0x0008);
|
|
}
|
|
cd_led ^= LED_CD_ACTIVE2;
|
|
}
|
|
|
|
static volatile int cdrom_command_cnt_out, cdrom_command_size_out;
|
|
static uae_u8 cdrom_command_output[16];
|
|
|
|
static volatile int stch, sten, scor, sbcp;
|
|
static volatile int cmd, enable, xaen, dten;
|
|
|
|
static int unitnum = -1;
|
|
|
|
static void subreset (void)
|
|
{
|
|
uae_sem_wait (&sub_sem);
|
|
memset (subcodebufferinuse, 0, sizeof subcodebufferinuse);
|
|
subcodebufferoffsetw = subcodebufferoffset = 0;
|
|
subcodeoffset = -1;
|
|
sbcp = 0;
|
|
scor = 0;
|
|
uae_sem_post (&sub_sem);
|
|
}
|
|
|
|
static int get_toc (void)
|
|
{
|
|
datatrack = 0;
|
|
if (!sys_command_cd_toc (unitnum, &toc))
|
|
return 0;
|
|
last_cd_position = toc.lastaddress;
|
|
if (toc.first_track == 1 && (toc.toc[toc.first_track_offset].control & 0x0c) == 0x04)
|
|
datatrack = 1;
|
|
return 1;
|
|
}
|
|
|
|
static int get_qcode (void)
|
|
{
|
|
if (!sys_command_cd_qcode (unitnum, cdrom_qcode, -1, false))
|
|
return 0;
|
|
cdrom_qcode[1] = cd_audio_status;
|
|
return 1;
|
|
}
|
|
|
|
static void cdaudiostop_do (void)
|
|
{
|
|
if (unitnum < 0)
|
|
return;
|
|
sys_command_cd_pause (unitnum, 0);
|
|
sys_command_cd_stop (unitnum);
|
|
}
|
|
|
|
static void cdaudiostop (void)
|
|
{
|
|
cd_finished = 0;
|
|
cd_audio_status = AUDIO_STATUS_NO_STATUS;
|
|
if (cd_playing) {
|
|
cd_audio_status = AUDIO_STATUS_PLAY_COMPLETE;
|
|
cd_finished = 1;
|
|
}
|
|
cd_playing = 0;
|
|
cd_paused = 0;
|
|
cd_motor = 0;
|
|
cd_audio_finished = 0;
|
|
write_comm_pipe_u32 (&requests, 0x0104, 1);
|
|
}
|
|
|
|
static void cdaudiostopfp (void)
|
|
{
|
|
cdaudiostop_do ();
|
|
cd_audio_status = AUDIO_STATUS_NO_STATUS;
|
|
activate_stch = 1;
|
|
cd_finished = 0;
|
|
cd_playing = 0;
|
|
cd_paused = 0;
|
|
cd_motor = 0;
|
|
}
|
|
|
|
static int pause_audio (int pause)
|
|
{
|
|
sys_command_cd_pause (unitnum, pause);
|
|
if (!cd_playing) {
|
|
cd_paused = 0;
|
|
cd_audio_status = AUDIO_STATUS_NO_STATUS;
|
|
return 0;
|
|
}
|
|
cd_paused = pause;
|
|
cd_audio_status = pause ? AUDIO_STATUS_PAUSED : AUDIO_STATUS_IN_PROGRESS;
|
|
subreset ();
|
|
return 1;
|
|
}
|
|
|
|
static int read_sectors (int start, int length)
|
|
{
|
|
if (cd_playing)
|
|
cdaudiostop ();
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("READ DATA sector %d, %d sectors (blocksize=%d)\n"), start, length, cdtv_sectorsize);
|
|
#endif
|
|
cdrom_sector = start;
|
|
cdrom_sectors = length;
|
|
cdrom_offset = start * cdtv_sectorsize;
|
|
cdrom_length = length * cdtv_sectorsize;
|
|
cd_motor = 1;
|
|
cd_audio_status = AUDIO_STATUS_NOT_SUPPORTED;
|
|
return 0;
|
|
}
|
|
|
|
static int ismedia (void)
|
|
{
|
|
if (unitnum < 0)
|
|
return 0;
|
|
return sys_command_ismedia (unitnum, 0);
|
|
}
|
|
|
|
static int issub (void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void subfunc (uae_u8 *data, int cnt)
|
|
{
|
|
if (!issub ())
|
|
return;
|
|
uae_sem_wait (&sub_sem);
|
|
#ifdef CDTV_SUB_DEBUG
|
|
int total = 0;
|
|
for (int i = 0; i < MAX_SUBCODEBUFFER; i++) {
|
|
if (subcodebufferinuse[i])
|
|
total++;
|
|
}
|
|
write_log (_T("%d "), total);
|
|
#endif
|
|
if (subcodebufferinuse[subcodebufferoffsetw]) {
|
|
memset (subcodebufferinuse, 0, sizeof subcodebufferinuse);
|
|
subcodebufferoffsetw = subcodebufferoffset = 0;
|
|
subcodeoffset = -1;
|
|
uae_sem_post (&sub_sem);
|
|
#ifdef CDTV_SUB_DEBUG
|
|
write_log (_T("CDTV: subcode buffer overflow 1\n"));
|
|
#endif
|
|
return;
|
|
}
|
|
int offset = subcodebufferoffsetw;
|
|
while (cnt > 0) {
|
|
if (subcodebufferinuse[offset]) {
|
|
#ifdef CDTV_SUB_DEBUG
|
|
write_log (_T("CDTV: subcode buffer overflow 2\n"));
|
|
#endif
|
|
break;
|
|
}
|
|
subcodebufferinuse[offset] = 1;
|
|
memcpy (&subcodebuffer[offset * SUB_CHANNEL_SIZE], data, SUB_CHANNEL_SIZE);
|
|
data += SUB_CHANNEL_SIZE;
|
|
offset++;
|
|
if (offset >= MAX_SUBCODEBUFFER)
|
|
offset = 0;
|
|
cnt--;
|
|
}
|
|
subcodebufferoffsetw = offset;
|
|
uae_sem_post (&sub_sem);
|
|
}
|
|
static int statusfunc (int status, int playpos)
|
|
{
|
|
if (status == -1)
|
|
return 500;
|
|
if (status == -2)
|
|
return 75;
|
|
if (cd_audio_status != status) {
|
|
if (status == AUDIO_STATUS_PLAY_COMPLETE || status == AUDIO_STATUS_PLAY_ERROR) {
|
|
cd_audio_finished = 1;
|
|
} else {
|
|
if (status == AUDIO_STATUS_IN_PROGRESS)
|
|
cd_playing = 1;
|
|
activate_stch = 1;
|
|
}
|
|
}
|
|
if (status == AUDIO_STATUS_IN_PROGRESS)
|
|
last_play_pos = playpos;
|
|
cd_audio_status = status;
|
|
return 0;
|
|
}
|
|
|
|
static void do_play (void)
|
|
{
|
|
uae_u32 start = read_comm_pipe_u32_blocking (&requests);
|
|
uae_u32 end = read_comm_pipe_u32_blocking (&requests);
|
|
uae_u32 scan = read_comm_pipe_u32_blocking (&requests);
|
|
subreset ();
|
|
sys_command_cd_pause (unitnum, 0);
|
|
sys_command_cd_volume (unitnum, (cd_volume_stored << 5) | (cd_volume_stored >> 5), (cd_volume_stored << 5) | (cd_volume_stored >> 5));
|
|
sys_command_cd_play (unitnum, start, end, 0, statusfunc, subfunc);
|
|
}
|
|
|
|
static void startplay (void)
|
|
{
|
|
subreset ();
|
|
write_comm_pipe_u32 (&requests, 0x0110, 0);
|
|
write_comm_pipe_u32 (&requests, play_start, 0);
|
|
write_comm_pipe_u32 (&requests, play_end, 0);
|
|
write_comm_pipe_u32 (&requests, 0, 1);
|
|
if (!cd_motor) {
|
|
cd_motor = 1;
|
|
activate_stch = 1;
|
|
}
|
|
}
|
|
|
|
static int play_cdtrack (uae_u8 *p)
|
|
{
|
|
int track_start = p[1];
|
|
int index_start = p[2];
|
|
int track_end = p[3];
|
|
int index_end = p[4];
|
|
int start_found, end_found;
|
|
uae_u32 start, end;
|
|
int j;
|
|
|
|
if (track_start == 0 && track_end == 0)
|
|
return 0;
|
|
|
|
end = last_cd_position;
|
|
start_found = end_found = 0;
|
|
for (j = toc.first_track_offset; j <= toc.last_track_offset; j++) {
|
|
struct cd_toc *s = &toc.toc[j];
|
|
if (track_start == s->track) {
|
|
start_found++;
|
|
start = s->paddress;
|
|
}
|
|
if (track_end == s->track) {
|
|
end = s->paddress;
|
|
end_found++;
|
|
}
|
|
}
|
|
if (start_found == 0) {
|
|
cdaudiostop ();
|
|
cd_error = 1;
|
|
activate_stch = 1;
|
|
write_log (_T("PLAY CD AUDIO: illegal start track %d\n"), track_start);
|
|
return 0;
|
|
}
|
|
play_end = end;
|
|
play_start = start;
|
|
last_play_pos = start;
|
|
last_play_end = end;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("PLAY CD AUDIO from %d-%d, %06X (%d) to %06X (%d)\n"),
|
|
track_start, track_end, start, start, end, end);
|
|
#endif
|
|
startplay ();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int play_cd (uae_u8 *p)
|
|
{
|
|
uae_u32 start, end;
|
|
|
|
start = (p[1] << 16) | (p[2] << 8) | p[3];
|
|
end = (p[4] << 16) | (p[5] << 8) | p[6];
|
|
if (p[0] == 0x09) /* end is length in lsn-mode */
|
|
end += start;
|
|
if (start == 0 && end == 0) {
|
|
cd_finished = 0;
|
|
if (cd_playing)
|
|
cd_finished = 1;
|
|
cd_playing = 0;
|
|
cd_paused = 0;
|
|
cd_motor = 0;
|
|
write_comm_pipe_u32 (&requests, 0x0104, 1);
|
|
cd_audio_status = AUDIO_STATUS_NO_STATUS;
|
|
cd_error = 1;
|
|
activate_stch = 1;
|
|
return 0;
|
|
}
|
|
if (p[0] != 0x09) { /* msf */
|
|
start = msf2lsn (start);
|
|
if (end < 0x00ffffff)
|
|
end = msf2lsn (end);
|
|
}
|
|
if (end >= 0x00ffffff || end > last_cd_position)
|
|
end = last_cd_position;
|
|
play_end = end;
|
|
play_start = start;
|
|
last_play_pos = start;
|
|
last_play_end = end;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("PLAY CD AUDIO from %06X (%d) to %06X (%d)\n"),
|
|
lsn2msf (start), start, lsn2msf (end), end);
|
|
#endif
|
|
startplay ();
|
|
return 0;
|
|
}
|
|
|
|
static int cdrom_subq (uae_u8 *out, int msflsn)
|
|
{
|
|
uae_u8 *s = cdrom_qcode;
|
|
uae_u32 trackposlsn, trackposmsf;
|
|
uae_u32 diskposlsn, diskposmsf;
|
|
|
|
out[0] = cd_audio_status;
|
|
s += 4;
|
|
out[1] = (s[0] >> 4) | (s[0] << 4);
|
|
out[2] = frombcd (s[1]); // track
|
|
out[3] = frombcd (s[2]); // index
|
|
trackposmsf = fromlongbcd (s + 3);
|
|
diskposmsf = fromlongbcd (s + 7);
|
|
trackposlsn = msf2lsn (trackposmsf);
|
|
diskposlsn = msf2lsn (diskposmsf);
|
|
out[4] = 0;
|
|
out[5] = (msflsn ? diskposmsf : diskposlsn) >> 16;
|
|
out[6] = (msflsn ? diskposmsf : diskposlsn) >> 8;
|
|
out[7] = (msflsn ? diskposmsf : diskposlsn) >> 0;
|
|
out[8] = 0;
|
|
out[9] = (msflsn ? trackposmsf : trackposlsn) >> 16;
|
|
out[10] = (msflsn ? trackposmsf : trackposlsn) >> 8;
|
|
out[11] = (msflsn ? trackposmsf : trackposlsn) >> 0;
|
|
out[12] = 0;
|
|
if (cd_audio_status == AUDIO_STATUS_IN_PROGRESS)
|
|
last_play_pos = diskposlsn;
|
|
return 13;
|
|
}
|
|
|
|
static int cdrom_info (uae_u8 *out)
|
|
{
|
|
uae_u32 size;
|
|
|
|
if (ismedia () <= 0)
|
|
return -1;
|
|
cd_motor = 1;
|
|
out[0] = toc.first_track;
|
|
out[1] = toc.last_track;
|
|
size = lsn2msf (toc.lastaddress);
|
|
out[2] = size >> 16;
|
|
out[3] = size >> 8;
|
|
out[4] = size >> 0;
|
|
cd_finished = 1;
|
|
return 5;
|
|
}
|
|
|
|
static int read_toc (int track, int msflsn, uae_u8 *out)
|
|
{
|
|
int j;
|
|
|
|
if (ismedia () <= 0)
|
|
return -1;
|
|
if (!out)
|
|
return 0;
|
|
cd_motor = 1;
|
|
for (j = 0; j < toc.points; j++) {
|
|
if (track == toc.toc[j].point) {
|
|
int lsn = toc.toc[j].paddress;
|
|
int msf = lsn2msf (lsn);
|
|
out[0] = 0;
|
|
out[1] = (toc.toc[j].adr << 4) | (toc.toc[j].control << 0);
|
|
out[2] = toc.toc[j].point;
|
|
out[3] = toc.tracks;
|
|
out[4] = 0;
|
|
out[5] = (msflsn ? msf : lsn) >> 16;
|
|
out[6] = (msflsn ? msf : lsn) >> 8;
|
|
out[7] = (msflsn ? msf : lsn) >> 0;
|
|
cd_finished = 1;
|
|
return 8;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int cdrom_modeset (uae_u8 *cmd)
|
|
{
|
|
cdtv_sectorsize = (cmd[2] << 8) | cmd[3];
|
|
if (cdtv_sectorsize != 2048 && cdtv_sectorsize != 2336 && cdtv_sectorsize != 2352 && cdtv_sectorsize != 2328) {
|
|
write_log (_T("CDTV: tried to set unknown sector size %d\n"), cdtv_sectorsize);
|
|
cdtv_sectorsize = 2048;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void cdrom_command_accepted (int size, uae_u8 *cdrom_command_input, int *cdrom_command_cnt_in)
|
|
{
|
|
#ifdef CDTV_DEBUG_CMD
|
|
TCHAR tmp[200];
|
|
int i;
|
|
#endif
|
|
cdrom_command_size_out = size;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
tmp[0] = 0;
|
|
for (i = 0; i < *cdrom_command_cnt_in; i++)
|
|
_stprintf (tmp + i * 3, _T("%02X%c"), cdrom_command_input[i], i < *cdrom_command_cnt_in - 1 ? '.' : ' ');
|
|
write_log (_T("CD<-: %s\n"), tmp);
|
|
if (size > 0) {
|
|
tmp[0] = 0;
|
|
for (i = 0; i < size; i++)
|
|
_stprintf (tmp + i * 3, _T("%02X%c"), cdrom_command_output[i], i < size - 1 ? '.' : ' ');
|
|
write_log (_T("CD->: %s\n"), tmp);
|
|
}
|
|
#endif
|
|
*cdrom_command_cnt_in = 0;
|
|
cdrom_command_cnt_out = 0;
|
|
cdrom_command_done = 1;
|
|
}
|
|
|
|
static void cdrom_command_thread (uae_u8 b)
|
|
{
|
|
uae_u8 *s;
|
|
|
|
cdrom_command_input[cdrom_command_cnt_in] = b;
|
|
cdrom_command_cnt_in++;
|
|
s = cdrom_command_input;
|
|
|
|
switch (cdrom_command_input[0])
|
|
{
|
|
case 0x01: /* seek */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
if (currprefs.cd_speed)
|
|
sleep_millis (500);
|
|
activate_stch = 1;
|
|
}
|
|
break;
|
|
case 0x02: /* read */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
read_sectors ((s[1] << 16) | (s[2] << 8) | (s[3] << 0), (s[4] << 8) | (s[5] << 0));
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x04: /* motor on */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cd_motor = 1;
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
case 0x05: /* motor off */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cd_motor = 0;
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
case 0x09: /* play (lsn) */
|
|
case 0x0a: /* play (msf) */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (play_cd (cdrom_command_input), s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x0b:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (play_cdtrack (cdrom_command_input), s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x81:
|
|
if (cdrom_command_cnt_in == 1) {
|
|
uae_u8 flag = 0;
|
|
if (!cd_isready)
|
|
flag |= 1 << 0; // 01
|
|
if (cd_playing)
|
|
flag |= 1 << 2; // 04
|
|
if (cd_finished)
|
|
flag |= 1 << 3; // 08
|
|
if (cd_error)
|
|
flag |= 1 << 4; // 10
|
|
if (cd_motor)
|
|
flag |= 1 << 5; // 20
|
|
if (cd_media)
|
|
flag |= 1 << 6; // 40
|
|
cdrom_command_output[0] = flag;
|
|
cdrom_command_accepted (1, s, &cdrom_command_cnt_in);
|
|
cd_finished = 0;
|
|
}
|
|
break;
|
|
case 0x82:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
if (cd_error)
|
|
cdrom_command_output[2] |= 1 << 4;
|
|
cd_error = 0;
|
|
cd_isready = 0;
|
|
cdrom_command_accepted (6, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
case 0x83:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
memcpy (cdrom_command_output, MODEL_NAME, strlen (MODEL_NAME));
|
|
cdrom_command_accepted (strlen (MODEL_NAME), s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
case 0x84:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (cdrom_modeset (cdrom_command_input), s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
case 0x87: /* subq */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (cdrom_subq (cdrom_command_output, cdrom_command_input[1] & 2), s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x89:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (cdrom_info (cdrom_command_output), s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x8a: /* read toc */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
cdrom_command_accepted (read_toc (cdrom_command_input[2], cdrom_command_input[1] & 2, cdrom_command_output), s, &cdrom_command_cnt_in);
|
|
}
|
|
break;
|
|
case 0x8b:
|
|
if (cdrom_command_cnt_in == 7) {
|
|
pause_audio (s[1] == 0x00 ? 1 : 0);
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
case 0xa3: /* front panel */
|
|
if (cdrom_command_cnt_in == 7) {
|
|
frontpanel = s[1] ? 1 : 0;
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
cd_finished = 1;
|
|
}
|
|
break;
|
|
default:
|
|
write_log (_T("unknown CDROM command %02X!\n"), s[0]);
|
|
cd_error = 1;
|
|
cdrom_command_accepted (0, s, &cdrom_command_cnt_in);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void dma_do_thread (void)
|
|
{
|
|
static int readsector;
|
|
int didread = 0;
|
|
int cnt;
|
|
|
|
while (dma_finished)
|
|
sleep_millis (2);
|
|
|
|
if (!cdtv_sectorsize)
|
|
return;
|
|
cnt = dmac_wtc;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("DMAC DMA: sector=%d, addr=%08X, words=%d (of %d)\n"),
|
|
cdrom_offset / cdtv_sectorsize, dmac_acr, cnt, cdrom_length / 2);
|
|
#endif
|
|
dma_wait += cnt * (uae_u64)312 * 50 / 75 + 1;
|
|
if (currprefs.cd_speed == 0)
|
|
dma_wait = 1;
|
|
while (cnt > 0 && dmac_dma) {
|
|
uae_u8 buffer[2352];
|
|
if (!didread || readsector != (cdrom_offset / cdtv_sectorsize)) {
|
|
readsector = cdrom_offset / cdtv_sectorsize;
|
|
if (cdtv_sectorsize != 2048)
|
|
didread = sys_command_cd_rawread (unitnum, buffer, readsector, 1, cdtv_sectorsize);
|
|
else
|
|
didread = sys_command_cd_read (unitnum, buffer, readsector, 1);
|
|
if (!didread) {
|
|
cd_error = 1;
|
|
activate_stch = 1;
|
|
write_log (_T("CDTV: CD read error!\n"));
|
|
break;
|
|
}
|
|
|
|
}
|
|
put_byte (dmac_acr, buffer[(cdrom_offset % cdtv_sectorsize) + 0]);
|
|
put_byte (dmac_acr + 1, buffer[(cdrom_offset % cdtv_sectorsize) + 1]);
|
|
cnt--;
|
|
dmac_acr += 2;
|
|
cdrom_length -= 2;
|
|
cdrom_offset += 2;
|
|
}
|
|
dmac_wtc = 0;
|
|
dmac_dma = 0;
|
|
dma_finished = 1;
|
|
cd_finished = 1;
|
|
}
|
|
|
|
static void *dev_thread (void *p)
|
|
{
|
|
write_log (_T("CDTV: CD thread started\n"));
|
|
thread_alive = 1;
|
|
for (;;) {
|
|
|
|
uae_u32 b = read_comm_pipe_u32_blocking (&requests);
|
|
if (b == 0xffff) {
|
|
thread_alive = -1;
|
|
return NULL;
|
|
}
|
|
if (unitnum < 0)
|
|
continue;
|
|
|
|
switch (b)
|
|
{
|
|
case 0x0100:
|
|
dma_do_thread ();
|
|
break;
|
|
case 0x0101:
|
|
{
|
|
int m = ismedia ();
|
|
if (m < 0) {
|
|
write_log (_T("CDTV: device %d lost\n"), unitnum);
|
|
activate_stch = 1;
|
|
cd_media = 0;
|
|
} else if (m != cd_media) {
|
|
cd_media = m;
|
|
get_toc ();
|
|
activate_stch = 1;
|
|
if (cd_playing)
|
|
cd_error = 1;
|
|
}
|
|
if (cd_media)
|
|
get_qcode ();
|
|
}
|
|
break;
|
|
case 0x0102: // pause
|
|
sys_command_cd_pause (unitnum, 1);
|
|
break;
|
|
case 0x0103: // unpause
|
|
sys_command_cd_pause (unitnum, 0);
|
|
break;
|
|
case 0x0104: // stop
|
|
cdaudiostop_do ();
|
|
break;
|
|
case 0x0105: // pause
|
|
pause_audio (1);
|
|
break;
|
|
case 0x0106: // unpause
|
|
pause_audio (0);
|
|
break;
|
|
case 0x0107: // frontpanel stop
|
|
cdaudiostopfp ();
|
|
break;
|
|
case 0x0110: // do_play!
|
|
do_play ();
|
|
break;
|
|
default:
|
|
cdrom_command_thread (b);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void cdrom_command (uae_u8 b)
|
|
{
|
|
write_comm_pipe_u32 (&requests, b, 1);
|
|
}
|
|
|
|
static void init_play (int start, int end)
|
|
{
|
|
play_end = end;
|
|
play_start = start;
|
|
last_play_pos = start;
|
|
last_play_end = end;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("PLAY CD AUDIO from %06X (%d) to %06X (%d)\n"),
|
|
lsn2msf (start), start, lsn2msf (end), end);
|
|
#endif
|
|
startplay ();
|
|
}
|
|
|
|
bool cdtv_front_panel (int button)
|
|
{
|
|
if (!frontpanel || configured <= 0)
|
|
return false;
|
|
if (button < 0)
|
|
return true;
|
|
switch (button)
|
|
{
|
|
case 0: // stop
|
|
if (cd_paused)
|
|
write_comm_pipe_u32 (&requests, 0x0106, 1);
|
|
write_comm_pipe_u32 (&requests, 0x0107, 1);
|
|
break;
|
|
case 1: // playpause
|
|
if (cd_playing) {
|
|
write_comm_pipe_u32 (&requests, cd_paused ? 0x0106 : 0x0105, 1);
|
|
} else if (cd_media) {
|
|
init_play (0, last_cd_position);
|
|
}
|
|
break;
|
|
case 2: // prev
|
|
case 3: // next
|
|
if (!cd_playing)
|
|
return true;
|
|
uae_u8 *sq = cdrom_qcode + 4;
|
|
int track = frombcd (sq[1]);
|
|
int pos = 0;
|
|
for (int j = 0; j < toc.points; j++) {
|
|
int t = toc.toc[j].track;
|
|
pos = toc.toc[j].paddress;
|
|
if (t == 1 && track == 1 && button == 2)
|
|
break;
|
|
else if (j == toc.points - 1 && t == track && button == 3)
|
|
break;
|
|
else if (t == track - 1 && track > 1 && button == 2)
|
|
break;
|
|
else if (t == track + 1 && track < 99 && button == 3)
|
|
break;
|
|
}
|
|
init_play (pos - 150, last_cd_position);
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static uae_u8 get_tp_c (void)
|
|
{
|
|
uae_u8 v = (sbcp ? 0 : (1 << 0)) | (scor ? 0 : (1 << 1)) |
|
|
(stch ? 0 : (1 << 2)) | (sten ? 0 : (1 << 3) | (1 << 4));
|
|
return v;
|
|
}
|
|
static uae_u8 get_tp_c_level (void)
|
|
{
|
|
uae_u8 v = (sbcp == 1 ? 0 : (1 << 0)) | (scor == 1 ? 0 : (1 << 1)) |
|
|
(stch == 1 ? 0 : (1 << 2)) | (sten == 1 ? 0 : (1 << 3)) | (1 << 4);
|
|
if (sten == 1)
|
|
sten = -1;
|
|
if (scor == 1)
|
|
scor = -1;
|
|
if (sbcp == 1)
|
|
sbcp = -1;
|
|
return v;
|
|
}
|
|
|
|
static void tp_check_interrupts (void)
|
|
{
|
|
/* MC = 1 ? */
|
|
if ((tp_cr & 1) != 1) {
|
|
get_tp_c_level ();
|
|
return;
|
|
}
|
|
|
|
tp_ilatch |= get_tp_c_level () ^ 0x1f;
|
|
stch = 0;
|
|
if (!(tp_ilatch & (1 << 5)) && (tp_ilatch & tp_imask)) {
|
|
tp_air = 0;
|
|
int mask = 0x10;
|
|
while (((tp_ilatch & tp_imask) & mask) == 0)
|
|
mask >>= 1;
|
|
tp_air |= tp_ilatch & mask;
|
|
tp_ilatch |= 1 << 5; // IRQ
|
|
tp_ilatch2 = tp_ilatch & mask;
|
|
tp_ilatch &= ~mask;
|
|
}
|
|
if (tp_ilatch & (1 << 5))
|
|
INT2 ();
|
|
}
|
|
|
|
// MC=1, C lines 0-4 = input irq lines, 5 = irq out, 6-7 IO
|
|
|
|
static void tp_bput (int addr, uae_u8 v)
|
|
{
|
|
static int volstrobe1, volstrobe2;
|
|
#ifdef CDTV_DEBUG_6525
|
|
if (addr != 1)
|
|
write_log (_T("6525 write %x=%02X PC=%x %d\n"), addr, v, M68K_GETPC, regs.s);
|
|
#endif
|
|
switch (addr)
|
|
{
|
|
case 0:
|
|
tp_a = v;
|
|
break;
|
|
case 1:
|
|
tp_b = v;
|
|
break;
|
|
case 2:
|
|
if (tp_cr & 1) {
|
|
// 0 = clear, 1 = ignored
|
|
tp_ilatch &= 0xe0 | v;
|
|
} else {
|
|
tp_c = get_tp_c () & ~tp_cd;
|
|
tp_c |= v & tp_cd;
|
|
if (tp_c & (1 << 5))
|
|
INT2 ();
|
|
}
|
|
break;
|
|
case 3:
|
|
tp_ad = v;
|
|
break;
|
|
case 4:
|
|
tp_bd = v;
|
|
break;
|
|
case 5:
|
|
// data direction (mode=0), interrupt mask (mode=1)
|
|
if (tp_cr & 1) {
|
|
tp_imask = v & 0x1f;
|
|
} else {
|
|
tp_cd = v;
|
|
}
|
|
break;
|
|
case 6:
|
|
tp_cr = v;
|
|
break;
|
|
case 7:
|
|
tp_air = v;
|
|
break;
|
|
}
|
|
cmd = (tp_b >> 0) & 1;
|
|
enable = (tp_b >> 1) & 1;
|
|
xaen = (tp_b >> 2) & 1;
|
|
dten = (tp_b >> 3) & 1;
|
|
|
|
if (!volstrobe1 && ((tp_b >> 6) & 1)) {
|
|
cd_volume >>= 1;
|
|
cd_volume |= ((tp_b >> 5) & 1) << 11;
|
|
volstrobe1 = 1;
|
|
} else if (volstrobe1 && !((tp_b >> 6) & 1)) {
|
|
volstrobe1 = 0;
|
|
}
|
|
if (!volstrobe2 && ((tp_b >> 7) & 1)) {
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("CDTV CD volume = %d\n"), cd_volume);
|
|
#endif
|
|
if (cd_volume > 1023)
|
|
cd_volume = 1023;
|
|
if (unitnum >= 0)
|
|
sys_command_cd_volume (unitnum, (cd_volume << 5) | (cd_volume >> 5), (cd_volume << 5) | (cd_volume >> 5));
|
|
cd_volume_stored = cd_volume;
|
|
cd_volume = 0;
|
|
volstrobe2 = 1;
|
|
} else if (volstrobe2 && !((tp_b >> 7) & 1)) {
|
|
volstrobe2 = 0;
|
|
}
|
|
tp_check_interrupts ();
|
|
}
|
|
|
|
static uae_u8 subtransferbuf[SUB_CHANNEL_SIZE];
|
|
|
|
#define SUBCODE_CYCLES (2 * maxhpos)
|
|
static int subcode_activecnt;
|
|
|
|
static void subcode_interrupt (uae_u32 v)
|
|
{
|
|
subcode_activecnt--;
|
|
if (subcode_activecnt > 0) {
|
|
if (subcode_activecnt > 1)
|
|
subcode_activecnt = 1;
|
|
return;
|
|
}
|
|
|
|
if (subcodeoffset < -1)
|
|
return;
|
|
if (sbcp && scor == 0) {
|
|
sbcp = 0;
|
|
// CD+G interrupt didn't read data fast enough, just abort until next packet
|
|
return;
|
|
}
|
|
if (scor < 0) {
|
|
scor = 0;
|
|
if (issub ()) {
|
|
subcodeoffset = 0;
|
|
}
|
|
tp_check_interrupts ();
|
|
}
|
|
if (subcodeoffset >= SUB_CHANNEL_SIZE)
|
|
return;
|
|
sbcp = 1;
|
|
tp_check_interrupts ();
|
|
subcode_activecnt++;
|
|
event2_newevent2 (SUBCODE_CYCLES, 0, subcode_interrupt);
|
|
}
|
|
|
|
static uae_u8 tp_bget (int addr)
|
|
{
|
|
uae_u8 v = 0;
|
|
switch (addr)
|
|
{
|
|
case 0:
|
|
// A = subchannel byte input from serial to parallel converter
|
|
if (subcodeoffset < 0 || subcodeoffset >= SUB_CHANNEL_SIZE) {
|
|
#ifdef CDTV_SUB_DEBUG
|
|
write_log (_T("CDTV: requested non-existing subchannel data!? %d\n"), subcodeoffset);
|
|
#endif
|
|
v = 0;
|
|
} else {
|
|
v = subtransferbuf[subcodeoffset];
|
|
tp_a = 0;
|
|
tp_a |= (v >> 7) & 1;
|
|
tp_a |= (v >> 5) & 2;
|
|
tp_a |= (v >> 3) & 4;
|
|
tp_a |= (v >> 1) & 8;
|
|
tp_a |= (v << 1) & 16;
|
|
tp_a |= (v << 3) & 32;
|
|
tp_a |= (v << 5) & 64;
|
|
tp_a |= (v << 7) & 128;
|
|
v = tp_a;
|
|
subcodeoffset++;
|
|
sbcp = 0;
|
|
if (subcodeoffset >= SUB_CHANNEL_SIZE)
|
|
subcodeoffset = -2;
|
|
}
|
|
break;
|
|
case 1:
|
|
v = tp_b;
|
|
break;
|
|
case 2:
|
|
if (tp_cr & 1) {
|
|
v = tp_ilatch | tp_ilatch2;
|
|
} else {
|
|
v = get_tp_c ();
|
|
}
|
|
break;
|
|
case 3:
|
|
v = tp_ad;
|
|
break;
|
|
case 4:
|
|
v = tp_bd;
|
|
break;
|
|
case 5:
|
|
// data direction (mode=0), interrupt mask (mode=1)
|
|
if (tp_cr & 1)
|
|
v = tp_imask;
|
|
else
|
|
v = tp_cd;
|
|
break;
|
|
case 6:
|
|
v = tp_cr;
|
|
break;
|
|
case 7:
|
|
v = tp_air;
|
|
if (tp_cr & 1) {
|
|
tp_ilatch &= ~(1 << 5);
|
|
tp_ilatch2 = 0;
|
|
}
|
|
tp_air = 0;
|
|
break;
|
|
}
|
|
|
|
tp_check_interrupts ();
|
|
|
|
#ifdef CDTV_DEBUG_6525
|
|
if (addr < 7 && addr != 1)
|
|
write_log (_T("6525 read %x=%02X PC=%x %d\n"), addr, v, M68K_GETPC, regs.s);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM3 dmac_lget (uaecptr) REGPARAM;
|
|
static uae_u32 REGPARAM3 dmac_wget (uaecptr) REGPARAM;
|
|
static uae_u32 REGPARAM3 dmac_bget (uaecptr) REGPARAM;
|
|
static void REGPARAM3 dmac_lput (uaecptr, uae_u32) REGPARAM;
|
|
static void REGPARAM3 dmac_wput (uaecptr, uae_u32) REGPARAM;
|
|
static void REGPARAM3 dmac_bput (uaecptr, uae_u32) REGPARAM;
|
|
|
|
static void dmac_start_dma (void)
|
|
{
|
|
if (!(dmac_cntr & CNTR_PDMD)) { // non-scsi dma
|
|
write_comm_pipe_u32 (&requests, 0x0100, 1);
|
|
} else {
|
|
scsi_dmac_a2091_start_dma (wd_cdtv);
|
|
}
|
|
}
|
|
static void dmac_stop_dma (void)
|
|
{
|
|
if (!(dmac_cntr & CNTR_PDMD)) { // non-scsi dma
|
|
;
|
|
} else {
|
|
scsi_dmac_a2091_stop_dma (wd_cdtv);
|
|
}
|
|
}
|
|
|
|
void cdtv_getdmadata (uae_u32 *acr)
|
|
{
|
|
*acr = dmac_acr;
|
|
}
|
|
|
|
static void checkint (void)
|
|
{
|
|
int irq = 0;
|
|
|
|
if (cdtvscsi && (wdscsi_getauxstatus (&wd_cdtv->wc) & 0x80)) {
|
|
dmac_istr |= ISTR_INTS;
|
|
if ((dmac_cntr & CNTR_INTEN) && (dmac_istr & ISTR_INTS))
|
|
irq = 1;
|
|
}
|
|
if ((dmac_cntr & CNTR_INTEN) && (dmac_istr & ISTR_E_INT))
|
|
irq = 1;
|
|
if (irq)
|
|
INT2 ();
|
|
}
|
|
|
|
void cdtv_scsi_int (void)
|
|
{
|
|
checkint ();
|
|
}
|
|
void cdtv_scsi_clear_int (void)
|
|
{
|
|
dmac_istr &= ~ISTR_INTS;
|
|
}
|
|
|
|
void rethink_cdtv (void)
|
|
{
|
|
checkint ();
|
|
tp_check_interrupts ();
|
|
}
|
|
|
|
|
|
void CDTV_hsync_handler (void)
|
|
{
|
|
static int subqcnt;
|
|
|
|
if (!currprefs.cs_cdtvcd || configured <= 0 || currprefs.cs_cdtvcr)
|
|
return;
|
|
|
|
cdtv_hsync++;
|
|
|
|
if (dma_wait >= 1024)
|
|
dma_wait -= 1024;
|
|
if (dma_wait >= 0 && dma_wait < 1024 && dma_finished) {
|
|
if ((dmac_cntr & (CNTR_INTEN | CNTR_TCEN)) == (CNTR_INTEN | CNTR_TCEN)) {
|
|
dmac_istr |= ISTR_INT_P | ISTR_E_INT;
|
|
#ifdef CDTV_DEBUG_CMD
|
|
write_log (_T("DMA finished\n"));
|
|
#endif
|
|
}
|
|
dma_finished = 0;
|
|
cdtv_hsync = -1;
|
|
}
|
|
checkint ();
|
|
|
|
if (cdrom_command_done) {
|
|
cdrom_command_done = 0;
|
|
sten = 1;
|
|
stch = 0;
|
|
tp_check_interrupts ();
|
|
}
|
|
|
|
if (sten < 0) {
|
|
sten--;
|
|
if (sten < -3)
|
|
sten = 0;
|
|
}
|
|
|
|
if (cd_audio_finished) {
|
|
cd_audio_finished = 0;
|
|
cd_playing = 0;
|
|
cd_finished = 1;
|
|
cd_paused = 0;
|
|
//cd_error = 1;
|
|
write_log (_T("audio finished\n"));
|
|
activate_stch = 1;
|
|
}
|
|
|
|
static int subchannelcounter;
|
|
int cntmax = (int)(maxvpos * vblank_hz / 75 - 6);
|
|
if (subchannelcounter > 0)
|
|
subchannelcounter--;
|
|
if (subchannelcounter <= 0) {
|
|
if (cd_playing || cd_media) {
|
|
if (subcodebufferoffset != subcodebufferoffsetw) {
|
|
uae_sem_wait (&sub_sem);
|
|
#ifdef CDTV_SUB_DEBUG
|
|
if (subcodeoffset >= 0)
|
|
write_log (_T("CDTV: frame interrupt, subchannel not empty! %d\n"), subcodeoffset);
|
|
#endif
|
|
subcodeoffset = -1;
|
|
if (subcodebufferinuse[subcodebufferoffset]) {
|
|
subcodebufferinuse[subcodebufferoffset] = 0;
|
|
memcpy (subtransferbuf, subcodebuffer + subcodebufferoffset * SUB_CHANNEL_SIZE, SUB_CHANNEL_SIZE);
|
|
subcodebufferoffset++;
|
|
if (subcodebufferoffset >= MAX_SUBCODEBUFFER)
|
|
subcodebufferoffset -= MAX_SUBCODEBUFFER;
|
|
sbcp = 0;
|
|
scor = 1;
|
|
subcode_activecnt++;
|
|
event2_newevent2 (SUBCODE_CYCLES, 0, subcode_interrupt);
|
|
tp_check_interrupts ();
|
|
}
|
|
uae_sem_post (&sub_sem);
|
|
subchannelcounter = cntmax;
|
|
}
|
|
}
|
|
if (!scor && !cd_playing) {
|
|
// frame interrupts happen all the time motor is running
|
|
scor = 1;
|
|
tp_check_interrupts ();
|
|
scor = 0;
|
|
subchannelcounter = cntmax;
|
|
}
|
|
}
|
|
|
|
if (cdtv_hsync < cntmax && cdtv_hsync >= 0)
|
|
return;
|
|
cdtv_hsync = 0;
|
|
#if 0
|
|
if (cd_isready > 0) {
|
|
cd_isready--;
|
|
if (!cd_isready)
|
|
do_stch ();
|
|
}
|
|
#endif
|
|
if (dmac_dma || dma_finished)
|
|
cd_led |= LED_CD_ACTIVE;
|
|
else
|
|
cd_led &= ~LED_CD_ACTIVE;
|
|
if ((cd_led & ~LED_CD_ACTIVE2) && !cd_playing)
|
|
gui_flicker_led (LED_CD, 0, cd_led);
|
|
|
|
subqcnt--;
|
|
if (subqcnt < 0) {
|
|
write_comm_pipe_u32 (&requests, 0x0101, 1);
|
|
if (cd_playing)
|
|
subqcnt = 10;
|
|
else
|
|
subqcnt = 75;
|
|
}
|
|
|
|
if (activate_stch)
|
|
do_stch ();
|
|
}
|
|
|
|
static void do_stch (void)
|
|
{
|
|
if ((tp_cr & 1) && !(tp_air & (1 << 2))) {
|
|
stch = 1;
|
|
activate_stch = 0;
|
|
tp_check_interrupts ();
|
|
#ifdef CDTV_DEBUG
|
|
static int stch_cnt;
|
|
write_log (_T("STCH %d\n"), stch_cnt++);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void cdtv_reset_int (void)
|
|
{
|
|
write_log (_T("CDTV: reset\n"));
|
|
cdaudiostop ();
|
|
cd_playing = cd_paused = 0;
|
|
cd_motor = 0;
|
|
cd_media = 0;
|
|
cd_error = 0;
|
|
cd_finished = 0;
|
|
cd_led = 0;
|
|
stch = 1;
|
|
frontpanel = 1;
|
|
}
|
|
|
|
static uae_u32 dmac_bget2 (uaecptr addr)
|
|
{
|
|
static uae_u8 last_out;
|
|
uae_u8 v = 0;
|
|
|
|
if (addr < 0x40)
|
|
return dmacmemory[addr];
|
|
|
|
if (addr >= 0xb0 && addr < 0xc0)
|
|
return tp_bget ((addr - 0xb0) / 2);
|
|
|
|
switch (addr)
|
|
{
|
|
case 0x41:
|
|
v = dmac_istr;
|
|
if (v)
|
|
v |= ISTR_INT_P;
|
|
dmac_istr &= ~0xf;
|
|
break;
|
|
case 0x43:
|
|
v = dmac_cntr;
|
|
break;
|
|
case 0x91:
|
|
if (cdtvscsi)
|
|
v = wdscsi_getauxstatus (&wd_cdtv->wc);
|
|
break;
|
|
case 0x93:
|
|
if (cdtvscsi) {
|
|
v = wdscsi_get (&wd_cdtv->wc, wd_cdtv);
|
|
checkint ();
|
|
}
|
|
break;
|
|
case 0xa1:
|
|
sten = 0;
|
|
if (cdrom_command_cnt_out >= 0) {
|
|
v = last_out = cdrom_command_output[cdrom_command_cnt_out];
|
|
cdrom_command_output[cdrom_command_cnt_out++] = 0;
|
|
if (cdrom_command_cnt_out >= cdrom_command_size_out) {
|
|
cdrom_command_size_out = 0;
|
|
cdrom_command_cnt_out = -1;
|
|
sten = 0;
|
|
tp_check_interrupts ();
|
|
} else {
|
|
sten = 1;
|
|
tp_check_interrupts ();
|
|
}
|
|
} else {
|
|
write_log (_T("CDTV: command register read while empty\n"));
|
|
v = last_out;
|
|
}
|
|
break;
|
|
case 0xe8:
|
|
case 0xe9:
|
|
dmac_istr |= ISTR_FE_FLG;
|
|
break;
|
|
/* XT IO */
|
|
case 0xa3:
|
|
case 0xa5:
|
|
case 0xa7:
|
|
v = 0xff;
|
|
break;
|
|
}
|
|
|
|
#ifdef CDTV_DEBUG
|
|
if (addr != 0x41)
|
|
write_log (_T("dmac_bget %04X=%02X PC=%08X\n"), addr, v, M68K_GETPC);
|
|
#endif
|
|
|
|
return v;
|
|
}
|
|
|
|
static void dmac_bput2 (uaecptr addr, uae_u32 b)
|
|
{
|
|
if (addr >= 0xb0 && addr < 0xc0) {
|
|
tp_bput ((addr - 0xb0) / 2, b);
|
|
return;
|
|
}
|
|
|
|
#ifdef CDTV_DEBUG
|
|
write_log (_T("dmac_bput %04X=%02X PC=%08X\n"), addr, b & 255, M68K_GETPC);
|
|
#endif
|
|
|
|
switch (addr)
|
|
{
|
|
case 0x43:
|
|
dmac_cntr = b;
|
|
if (dmac_cntr & CNTR_PREST)
|
|
cdtv_reset_int ();
|
|
break;
|
|
case 0x80:
|
|
dmac_wtc &= 0x00ffffff;
|
|
dmac_wtc |= b << 24;
|
|
break;
|
|
case 0x81:
|
|
dmac_wtc &= 0xff00ffff;
|
|
dmac_wtc |= b << 16;
|
|
break;
|
|
case 0x82:
|
|
dmac_wtc &= 0xffff00ff;
|
|
dmac_wtc |= b << 8;
|
|
break;
|
|
case 0x83:
|
|
dmac_wtc &= 0xffffff00;
|
|
dmac_wtc |= b << 0;
|
|
break;
|
|
case 0x84:
|
|
dmac_acr &= 0x00ffffff;
|
|
dmac_acr |= b << 24;
|
|
break;
|
|
case 0x85:
|
|
dmac_acr &= 0xff00ffff;
|
|
dmac_acr |= b << 16;
|
|
break;
|
|
case 0x86:
|
|
dmac_acr &= 0xffff00ff;
|
|
dmac_acr |= b << 8;
|
|
break;
|
|
case 0x87:
|
|
dmac_acr &= 0xffffff01;
|
|
dmac_acr |= (b & ~ 1) << 0;
|
|
break;
|
|
case 0x8e:
|
|
dmac_dawr &= 0x00ff;
|
|
dmac_dawr |= b << 8;
|
|
break;
|
|
case 0x8f:
|
|
dmac_dawr &= 0xff00;
|
|
dmac_dawr |= b << 0;
|
|
break;
|
|
case 0x91:
|
|
if (cdtvscsi) {
|
|
wdscsi_sasr (&wd_cdtv->wc, b);
|
|
checkint ();
|
|
}
|
|
break;
|
|
case 0x93:
|
|
if (cdtvscsi) {
|
|
wdscsi_put (&wd_cdtv->wc, wd_cdtv, b);
|
|
checkint ();
|
|
}
|
|
break;
|
|
case 0xa1:
|
|
cdrom_command (b);
|
|
break;
|
|
case 0xe0:
|
|
case 0xe1:
|
|
if (!dmac_dma) {
|
|
dmac_dma = 1;
|
|
dmac_start_dma ();
|
|
}
|
|
break;
|
|
case 0xe2:
|
|
case 0xe3:
|
|
if (dmac_dma) {
|
|
dmac_dma = 0;
|
|
dmac_stop_dma ();
|
|
}
|
|
dma_finished = 0;
|
|
break;
|
|
case 0xe4:
|
|
case 0xe5:
|
|
dmac_istr = 0;
|
|
checkint ();
|
|
break;
|
|
case 0xe8:
|
|
case 0xe9:
|
|
dmac_istr |= ISTR_FE_FLG;
|
|
break;
|
|
}
|
|
|
|
tp_check_interrupts ();
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dmac_lget (uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
addr &= 65535;
|
|
v = (dmac_bget2 (addr) << 24) | (dmac_bget2 (addr + 1) << 16) |
|
|
(dmac_bget2 (addr + 2) << 8) | (dmac_bget2 (addr + 3));
|
|
#ifdef CDTV_DEBUG
|
|
write_log (_T("dmac_lget %08X=%08X PC=%08X\n"), addr, v, M68K_GETPC);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dmac_wget (uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
addr &= 65535;
|
|
v = (dmac_bget2 (addr) << 8) | dmac_bget2 (addr + 1);
|
|
#ifdef CDTV_DEBUG
|
|
write_log (_T("dmac_wget %08X=%04X PC=%08X\n"), addr, v, M68K_GETPC);
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dmac_bget (uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
addr &= 65535;
|
|
v = dmac_bget2 (addr);
|
|
if (configured <= 0)
|
|
return v;
|
|
return v;
|
|
}
|
|
|
|
static void REGPARAM2 dmac_lput (uaecptr addr, uae_u32 l)
|
|
{
|
|
addr &= 65535;
|
|
#ifdef CDTV_DEBUG
|
|
write_log (_T("dmac_lput %08X=%08X PC=%08X\n"), addr, l, M68K_GETPC);
|
|
#endif
|
|
dmac_bput2 (addr, l >> 24);
|
|
dmac_bput2 (addr + 1, l >> 16);
|
|
dmac_bput2 (addr + 2, l >> 8);
|
|
dmac_bput2 (addr + 3, l);
|
|
}
|
|
|
|
static void REGPARAM2 dmac_wput (uaecptr addr, uae_u32 w)
|
|
{
|
|
addr &= 65535;
|
|
#ifdef CDTV_DEBUG
|
|
write_log (_T("dmac_wput %04X=%04X PC=%08X\n"), addr, w & 65535, M68K_GETPC);
|
|
#endif
|
|
dmac_bput2 (addr, w >> 8);
|
|
dmac_bput2 (addr + 1, w);
|
|
}
|
|
|
|
static void REGPARAM2 dmac_bput (uaecptr addr, uae_u32 b)
|
|
{
|
|
addr &= 65535;
|
|
b &= 0xff;
|
|
if (addr == 0x48) {
|
|
map_banks_z2 (&dmac_bank, b, 0x10000 >> 16);
|
|
configured = b;
|
|
expamem_next(&dmac_bank, NULL);
|
|
return;
|
|
}
|
|
if (addr == 0x4c) {
|
|
configured = -1;
|
|
expamem_shutup(&dmac_bank);
|
|
return;
|
|
}
|
|
if (configured <= 0)
|
|
return;
|
|
dmac_bput2 (addr, b);
|
|
}
|
|
|
|
static void open_unit (void)
|
|
{
|
|
struct device_info di;
|
|
unitnum = get_standard_cd_unit (CD_STANDARD_UNIT_CDTV);
|
|
sys_command_info (unitnum, &di, 0);
|
|
write_log (_T("using drive %s (unit %d, media %d)\n"), di.label, unitnum, di.media_inserted);
|
|
}
|
|
static void close_unit (void)
|
|
{
|
|
if (unitnum >= 0)
|
|
sys_command_close (unitnum);
|
|
unitnum = -1;
|
|
}
|
|
|
|
static void ew (int addr, uae_u32 value)
|
|
{
|
|
addr &= 0xffff;
|
|
if (addr == 00 || addr == 02 || addr == 0x40 || addr == 0x42) {
|
|
dmacmemory[addr] = (value & 0xf0);
|
|
dmacmemory[addr + 2] = (value & 0x0f) << 4;
|
|
} else {
|
|
dmacmemory[addr] = ~(value & 0xf0);
|
|
dmacmemory[addr + 2] = ~((value & 0x0f) << 4);
|
|
}
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dmac_wgeti (uaecptr addr)
|
|
{
|
|
uae_u32 v = 0xffff;
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 dmac_lgeti (uaecptr addr)
|
|
{
|
|
uae_u32 v = 0xffff;
|
|
return v;
|
|
}
|
|
|
|
addrbank dmac_bank = {
|
|
dmac_lget, dmac_wget, dmac_bget,
|
|
dmac_lput, dmac_wput, dmac_bput,
|
|
default_xlate, default_check, NULL, NULL, _T("CDTV DMAC/CD Controller"),
|
|
dmac_lgeti, dmac_wgeti,
|
|
ABFLAG_IO, S_READ, S_WRITE
|
|
};
|
|
|
|
/* CDTV batterybacked RAM emulation */
|
|
#define CDTV_NVRAM_MASK 16383
|
|
#define CDTV_NVRAM_SIZE 32768
|
|
static uae_u8 cdtv_battram[CDTV_NVRAM_SIZE];
|
|
|
|
void cdtv_loadcardmem (uae_u8 *p, int size)
|
|
{
|
|
struct zfile *f;
|
|
|
|
memset (p, 0, size);
|
|
f = zfile_fopen (currprefs.flashfile, _T("rb"), ZFD_NORMAL);
|
|
if (!f)
|
|
return;
|
|
zfile_fseek (f, CDTV_NVRAM_SIZE, SEEK_SET);
|
|
zfile_fread (p, size, 1, f);
|
|
zfile_fclose (f);
|
|
}
|
|
|
|
void cdtv_savecardmem (uae_u8 *p, int size)
|
|
{
|
|
struct zfile *f;
|
|
|
|
f = zfile_fopen (currprefs.flashfile, _T("rb+"), ZFD_NORMAL);
|
|
if (!f)
|
|
return;
|
|
zfile_fseek (f, CDTV_NVRAM_SIZE, SEEK_SET);
|
|
zfile_fwrite (p, size, 1, f);
|
|
zfile_fclose (f);
|
|
}
|
|
|
|
static void cdtv_battram_reset (void)
|
|
{
|
|
struct zfile *f;
|
|
int v;
|
|
|
|
memset (cdtv_battram, 0, CDTV_NVRAM_SIZE);
|
|
f = zfile_fopen (currprefs.flashfile, _T("rb+"), ZFD_NORMAL);
|
|
if (!f) {
|
|
f = zfile_fopen (currprefs.flashfile, _T("wb"), 0);
|
|
if (f) {
|
|
zfile_fwrite (cdtv_battram, CDTV_NVRAM_SIZE, 1, f);
|
|
zfile_fclose (f);
|
|
}
|
|
return;
|
|
}
|
|
v = zfile_fread (cdtv_battram, 1, CDTV_NVRAM_SIZE, f);
|
|
if (v < CDTV_NVRAM_SIZE)
|
|
zfile_fwrite (cdtv_battram + v, 1, CDTV_NVRAM_SIZE - v, f);
|
|
zfile_fclose (f);
|
|
}
|
|
|
|
void cdtv_battram_write (int addr, int v)
|
|
{
|
|
struct zfile *f;
|
|
int offset = addr & CDTV_NVRAM_MASK;
|
|
|
|
if (offset >= CDTV_NVRAM_SIZE)
|
|
return;
|
|
gui_flicker_led (LED_MD, 0, 2);
|
|
if (cdtv_battram[offset] == v)
|
|
return;
|
|
cdtv_battram[offset] = v;
|
|
f = zfile_fopen (currprefs.flashfile, _T("rb+"), ZFD_NORMAL);
|
|
if (!f)
|
|
return;
|
|
zfile_fseek (f, offset, SEEK_SET);
|
|
zfile_fwrite (cdtv_battram + offset, 1, 1, f);
|
|
zfile_fclose (f);
|
|
}
|
|
|
|
uae_u8 cdtv_battram_read (int addr)
|
|
{
|
|
uae_u8 v;
|
|
int offset;
|
|
offset = addr & CDTV_NVRAM_MASK;
|
|
if (offset >= CDTV_NVRAM_SIZE)
|
|
return 0;
|
|
gui_flicker_led (LED_MD, 0, 1);
|
|
v = cdtv_battram[offset];
|
|
return v;
|
|
}
|
|
|
|
void cdtv_free (void)
|
|
{
|
|
if (thread_alive > 0) {
|
|
dmac_dma = 0;
|
|
dma_finished = 0;
|
|
cdaudiostop ();
|
|
write_comm_pipe_u32 (&requests, 0xffff, 1);
|
|
while (thread_alive > 0)
|
|
sleep_millis (10);
|
|
uae_sem_destroy (&sub_sem);
|
|
}
|
|
thread_alive = 0;
|
|
close_unit ();
|
|
configured = 0;
|
|
}
|
|
|
|
bool cdtv_init (struct autoconfig_info *aci)
|
|
{
|
|
memset(dmacmemory, 0xff, sizeof dmacmemory);
|
|
ew(0x00, 0xc0 | 0x01);
|
|
ew(0x04, 0x03);
|
|
ew(0x08, 0x40);
|
|
ew(0x10, 0x02);
|
|
ew(0x14, 0x02);
|
|
|
|
ew(0x18, 0x00); /* ser.no. Byte 0 */
|
|
ew(0x1c, 0x00); /* ser.no. Byte 1 */
|
|
ew(0x20, 0x00); /* ser.no. Byte 2 */
|
|
ew(0x24, 0x00); /* ser.no. Byte 3 */
|
|
|
|
if (aci) {
|
|
aci->label = dmac_bank.name;
|
|
aci->hardwired = true;
|
|
if (!aci->doinit) {
|
|
memcpy(aci->autoconfig_raw, dmacmemory, sizeof dmacmemory);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
close_unit ();
|
|
if (!thread_alive) {
|
|
init_comm_pipe (&requests, 100, 1);
|
|
uae_start_thread (_T("cdtv"), dev_thread, NULL, NULL);
|
|
while (!thread_alive)
|
|
sleep_millis (10);
|
|
uae_sem_init (&sub_sem, 0, 1);
|
|
}
|
|
write_comm_pipe_u32 (&requests, 0x0104, 1);
|
|
|
|
cdrom_command_cnt_out = -1;
|
|
cmd = enable = xaen = dten = 0;
|
|
|
|
/* KS autoconfig handles the rest */
|
|
if (!savestate_state) {
|
|
cdtv_reset_int ();
|
|
configured = 0;
|
|
tp_a = tp_b = tp_c = tp_ad = tp_bd = tp_cd = 0;
|
|
tp_imask = tp_cr = tp_air = tp_ilatch = 0;
|
|
stch = 0;
|
|
sten = 0;
|
|
scor = 0;
|
|
sbcp = 0;
|
|
cdtvscsi = 0;
|
|
}
|
|
|
|
cdtv_battram_reset ();
|
|
open_unit ();
|
|
gui_flicker_led (LED_CD, 0, -1);
|
|
if (aci)
|
|
aci->addrbank = &dmac_bank;
|
|
return true;
|
|
}
|
|
|
|
bool cdtvscsi_init(struct autoconfig_info *aci)
|
|
{
|
|
aci->parent_name = _T("CDTV DMAC");
|
|
aci->hardwired = true;
|
|
if (!aci->doinit)
|
|
return true;
|
|
cdtvscsi = true;
|
|
init_wd_scsi(wd_cdtv);
|
|
wd_cdtv->dmac_type = COMMODORE_DMAC;
|
|
if (configured > 0)
|
|
map_banks_z2(&dmac_bank, configured, 0x10000 >> 16);
|
|
return true;
|
|
}
|
|
|
|
#ifdef SAVESTATE
|
|
|
|
uae_u8 *save_cdtv_dmac (int *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
if (!currprefs.cs_cdtvcd || currprefs.cs_cdtvcr)
|
|
return NULL;
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc (uae_u8, 1000);
|
|
|
|
// model (0=original,1=rev2,2=superdmac)
|
|
save_u32 (1);
|
|
save_u32 (0); // reserved flags
|
|
save_u8 (dmac_istr);
|
|
save_u8 (dmac_cntr);
|
|
save_u32 (dmac_wtc);
|
|
save_u32 (dmac_acr);
|
|
save_u16 (dmac_dawr);
|
|
save_u32 (dmac_dma ? 1 : 0);
|
|
save_u8 (configured);
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
|
|
}
|
|
|
|
uae_u8 *restore_cdtv_dmac (uae_u8 *src)
|
|
{
|
|
restore_u32 ();
|
|
restore_u32 ();
|
|
dmac_istr = restore_u8 ();
|
|
dmac_cntr = restore_u8 ();
|
|
dmac_wtc = restore_u32 ();
|
|
dmac_acr = restore_u32 ();
|
|
dmac_dawr = restore_u16 ();
|
|
restore_u32 ();
|
|
configured = restore_u8 ();
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *save_cdtv (int *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
if (!currprefs.cs_cdtvcd || currprefs.cs_cdtvcr)
|
|
return NULL;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc (uae_u8, 1000);
|
|
|
|
save_u32 (1);
|
|
|
|
// tri-port
|
|
save_u8 (tp_a);
|
|
save_u8 (tp_b);
|
|
save_u8 (tp_c);
|
|
save_u8 (tp_ad);
|
|
save_u8 (tp_bd);
|
|
save_u8 (tp_cd);
|
|
save_u8 (tp_cr);
|
|
save_u8 (tp_air);
|
|
save_u8 (tp_imask);
|
|
save_u8 (tp_ilatch);
|
|
save_u8 (tp_ilatch2);
|
|
save_u8 (0);
|
|
// misc cd stuff
|
|
save_u32 ((cd_playing ? 1 : 0) | (cd_paused ? 2 : 0) | (cd_media ? 4 : 0) |
|
|
(cd_motor ? 8 : 0) | (cd_error ? 16 : 0) | (cd_finished ? 32 : 0) | (cdrom_command_done ? 64 : 0) |
|
|
(activate_stch ? 128 : 0) | (sten ? 256 : 0) | (stch ? 512 : 0) | (frontpanel ? 1024 : 0));
|
|
save_u8 (cd_isready);
|
|
save_u8 (0);
|
|
save_u16 (cd_volume_stored);
|
|
if (cd_playing)
|
|
get_qcode ();
|
|
save_u32 (last_play_pos);
|
|
save_u32 (last_play_end);
|
|
save_u64 (dma_wait);
|
|
for (int i = 0; i < sizeof (cdrom_command_input); i++)
|
|
save_u8 (cdrom_command_input[i]);
|
|
save_u8 (cdrom_command_cnt_in);
|
|
save_u16 (cdtv_sectorsize);
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_cdtv (uae_u8 *src)
|
|
{
|
|
cdtv_free ();
|
|
if (!currprefs.cs_cdtvcd) {
|
|
changed_prefs.cs_cdtvcd = changed_prefs.cs_cdtvram = true;
|
|
currprefs.cs_cdtvcd = currprefs.cs_cdtvram = true;
|
|
cdtv_init(NULL);
|
|
}
|
|
restore_u32 ();
|
|
|
|
// tri-port
|
|
tp_a = restore_u8 ();
|
|
tp_b = restore_u8 ();
|
|
tp_c = restore_u8 ();
|
|
tp_ad = restore_u8 ();
|
|
tp_bd = restore_u8 ();
|
|
tp_cd = restore_u8 ();
|
|
tp_cr = restore_u8 ();
|
|
tp_air = restore_u8 ();
|
|
tp_imask = restore_u8 ();
|
|
tp_ilatch = restore_u8 ();
|
|
tp_ilatch2 = restore_u8 ();
|
|
restore_u8 ();
|
|
// misc cd stuff
|
|
uae_u32 v = restore_u32 ();
|
|
cd_playing = (v & 1) ? 1 : 0;
|
|
cd_paused = (v & 2) ? 1 : 0;
|
|
cd_media = (v & 4) ? 1 : 0;
|
|
cd_motor = (v & 8) ? 1 : 0;
|
|
cd_error = (v & 16) ? 1 : 0;
|
|
cd_finished = (v & 32) ? 1 : 0;
|
|
cdrom_command_done = (v & 64) ? 1 : 0;
|
|
activate_stch = (v & 128) ? 1 : 0;
|
|
sten = (v & 256) ? 1 : 0;
|
|
stch = (v & 512) ? 1 : 0;
|
|
frontpanel = (v & 1024) ? 1 : 0;
|
|
cd_isready = restore_u8 ();
|
|
restore_u8 ();
|
|
cd_volume_stored = restore_u16 ();
|
|
last_play_pos = restore_u32 ();
|
|
last_play_end = restore_u32 ();
|
|
dma_wait = restore_u64 ();
|
|
for (int i = 0; i < sizeof (cdrom_command_input); i++)
|
|
cdrom_command_input[i] = restore_u8 ();
|
|
cdrom_command_cnt_in = restore_u8 ();
|
|
cdtv_sectorsize = restore_u16 ();
|
|
|
|
return src;
|
|
}
|
|
|
|
void restore_cdtv_finish (void)
|
|
{
|
|
if (!currprefs.cs_cdtvcd || currprefs.cs_cdtvcr)
|
|
return;
|
|
cdtv_init (0);
|
|
get_toc ();
|
|
write_comm_pipe_u32 (&requests, 0x0104, 1);
|
|
if (cd_playing) {
|
|
write_comm_pipe_u32 (&requests, 0x0103, 1); // unpause
|
|
write_comm_pipe_u32 (&requests, 0x0110, 0); // play
|
|
write_comm_pipe_u32 (&requests, last_play_pos, 0);
|
|
write_comm_pipe_u32 (&requests, last_play_end, 0);
|
|
write_comm_pipe_u32 (&requests, 0, 1);
|
|
}
|
|
}
|
|
|
|
#endif
|