Genesis-Plus-GX/core/cd_hw/cdc.c
2019-11-28 00:20:35 +01:00

724 lines
18 KiB
C

/***************************************************************************************
* Genesis Plus
* CD data controller (LC8951x compatible)
*
* Copyright (C) 2012-2019 Eke-Eke (Genesis Plus GX)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************************/
#include "shared.h"
/* IFSTAT register bitmasks */
#define BIT_DTEI 0x40
#define BIT_DECI 0x20
#define BIT_DTBSY 0x08
#define BIT_DTEN 0x02
/* IFCTRL register bitmasks */
#define BIT_DTEIEN 0x40
#define BIT_DECIEN 0x20
#define BIT_DOUTEN 0x02
/* CTRL0 register bitmasks */
#define BIT_DECEN 0x80
#define BIT_AUTORQ 0x10
#define BIT_WRRQ 0x04
/* CTRL1 register bitmasks */
#define BIT_MODRQ 0x08
#define BIT_FORMRQ 0x04
#define BIT_SHDREN 0x01
/* STAT3 register bitmask */
#define BIT_VALST 0x80
/* TODO: figure exact DMA transfer rate */
#define DMA_BYTES_PER_LINE 512
void cdc_init(void)
{
memset(&cdc, 0, sizeof(cdc_t));
}
void cdc_reset(void)
{
/* reset CDC register index */
scd.regs[0x04>>1].byte.l = 0x00;
/* reset CDC registers */
cdc.ifstat = 0xff;
cdc.ifctrl = 0x00;
cdc.ctrl[0] = 0x00;
cdc.ctrl[1] = 0x00;
cdc.stat[0] = 0x00;
cdc.stat[1] = 0x00;
cdc.stat[2] = 0x00;
cdc.stat[3] = 0x80;
cdc.head[0][0] = 0x00;
cdc.head[0][1] = 0x00;
cdc.head[0][2] = 0x00;
cdc.head[0][3] = 0x01;
cdc.head[1][0] = 0x00;
cdc.head[1][1] = 0x00;
cdc.head[1][2] = 0x00;
cdc.head[1][3] = 0x00;
/* reset CDC cycle counter */
cdc.cycles = 0;
/* DMA transfer disabled */
cdc.dma_w = 0;
/* clear any pending IRQ */
if (scd.pending & (1 << 5))
{
/* clear any pending interrupt level 5 */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
int cdc_context_save(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
if (cdc.dma_w == pcm_ram_dma_w)
{
tmp8 = 1;
}
else if (cdc.dma_w == prg_ram_dma_w)
{
tmp8 = 2;
}
else if (cdc.dma_w == word_ram_0_dma_w)
{
tmp8 = 3;
}
else if (cdc.dma_w == word_ram_1_dma_w)
{
tmp8 = 4;
}
else if (cdc.dma_w == word_ram_2M_dma_w)
{
tmp8 = 5;
}
else
{
tmp8 = 0;
}
save_param(&cdc, sizeof(cdc));
save_param(&tmp8, 1);
return bufferptr;
}
int cdc_context_load(uint8 *state)
{
uint8 tmp8;
int bufferptr = 0;
load_param(&cdc, sizeof(cdc));
load_param(&tmp8, 1);
switch (tmp8)
{
case 1:
cdc.dma_w = pcm_ram_dma_w;
break;
case 2:
cdc.dma_w = prg_ram_dma_w;
break;
case 3:
cdc.dma_w = word_ram_0_dma_w;
break;
case 4:
cdc.dma_w = word_ram_1_dma_w;
break;
case 5:
cdc.dma_w = word_ram_2M_dma_w;
break;
default:
cdc.dma_w = 0;
break;
}
return bufferptr;
}
void cdc_dma_update(void)
{
/* maximal transfer length */
int length = DMA_BYTES_PER_LINE;
/* end of DMA transfer ? */
if (cdc.dbc.w < DMA_BYTES_PER_LINE)
{
/* transfer remaining words using 16-bit DMA */
cdc.dma_w((cdc.dbc.w + 1) >> 1);
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
cdc.dbc.w = 0xf000;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* clear DSR bit & set EDT bit (CD register $04) */
scd.regs[0x04>>1].byte.h = (scd.regs[0x04>>1].byte.h & 0x07) | 0x80;
/* SUB-CPU idle on register $04 polling ? */
if (s68k.stopped & (1<<0x04))
{
/* sync SUB-CPU with CDC */
s68k.cycles = scd.cycles;
/* restart SUB-CPU */
s68k.stopped = 0;
#ifdef LOG_SCD
error("s68k started from %d cycles\n", s68k.cycles);
#endif
}
/* disable DMA transfer */
cdc.dma_w = 0;
}
else
{
/* transfer all words using 16-bit DMA */
cdc.dma_w(DMA_BYTES_PER_LINE >> 1);
/* decrement data byte counter */
cdc.dbc.w -= length;
}
}
void cdc_decoder_update(uint32 header)
{
/* data decoding enabled ? */
if (cdc.ctrl[0] & BIT_DECEN)
{
/* update HEADx registers with current block header */
*(uint32 *)(cdc.head[0]) = header;
/* set !VALST */
cdc.stat[3] = 0x00;
/* pending decoder interrupt */
cdc.ifstat &= ~BIT_DECI;
/* decoder interrupt enabled ? */
if (cdc.ifctrl & BIT_DECIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* buffer RAM write enabled ? */
if (cdc.ctrl[0] & BIT_WRRQ)
{
int offset;
/* increment block pointer */
cdc.pt.w += 2352;
/* increment write address */
cdc.wa.w += 2352;
/* CDC buffer address */
offset = cdc.pt.w & 0x3fff;
/* write current block header to RAM buffer (4 bytes) */
*(uint32 *)(cdc.ram + offset) = header;
offset += 4;
/* check decoded block mode */
if (cdc.head[0][3] == 0x01)
{
/* write Mode 1 user data to RAM buffer (2048 bytes) */
cdd_read_data(cdc.ram + offset, NULL);
offset += 2048;
}
else
{
/* check if CD-ROM Mode 2 decoding is enabled */
if (cdc.ctrl[1] & BIT_MODRQ)
{
/* update HEADx registers with current block sub-header & write Mode 2 user data to RAM buffer (max 2328 bytes) */
cdd_read_data(cdc.ram + offset + 8, cdc.head[1]);
/* write current block sub-header to RAM buffer (4 bytes x 2) */
*(uint32 *)(cdc.ram + offset) = *(uint32 *)(cdc.head[1]);
*(uint32 *)(cdc.ram + offset + 4) = *(uint32 *)(cdc.head[1]);
offset += 2336;
}
else
{
/* update HEADx registers with current block sub-header & write Mode 2 user data to RAM buffer (max 2328 bytes) */
/* NB: when Mode 2 decoding is disabled, sub-header is apparently not written to RAM buffer (required by Wonder Library) */
cdd_read_data(cdc.ram + offset, cdc.head[1]);
offset += 2328;
}
/* set STAT2 register FORM bit according to sub-header FORM bit when CTRL0 register AUTORQ bit is set */
if (cdc.ctrl[0] & BIT_AUTORQ)
{
cdc.stat[2] = (cdc.ctrl[1] & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
}
/* take care of buffer overrun */
if (offset > 0x4000)
{
/* data should be written at the start of buffer */
memcpy(cdc.ram, cdc.ram + 0x4000, offset - 0x4000);
}
}
}
}
void cdc_reg_w(unsigned char data)
{
#ifdef LOG_CDC
error("CDC register %X write 0x%04x (%X)\n", scd.regs[0x04>>1].byte.l & 0x0F, data, s68k.pc);
#endif
switch (scd.regs[0x04>>1].byte.l & 0x0F)
{
case 0x01: /* IFCTRL */
{
/* pending interrupts ? */
if (((data & BIT_DTEIEN) && !(cdc.ifstat & BIT_DTEI)) ||
((data & BIT_DECIEN) && !(cdc.ifstat & BIT_DECI)))
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
else if (scd.pending & (1 << 5))
{
/* clear pending level 5 interrupts */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
/* abort any data transfer if data output is disabled */
if (!(data & BIT_DOUTEN))
{
/* clear !DTBSY and !DTEN */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
}
cdc.ifctrl = data;
scd.regs[0x04>>1].byte.l = 0x02;
break;
}
case 0x02: /* DBCL */
cdc.dbc.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x03;
break;
case 0x03: /* DBCH */
cdc.dbc.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x04;
break;
case 0x04: /* DACL */
cdc.dac.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x05;
break;
case 0x05: /* DACH */
cdc.dac.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x06;
break;
case 0x06: /* DTRG */
{
/* start data transfer if data output is enabled */
if (cdc.ifctrl & BIT_DOUTEN)
{
/* set !DTBSY and !DTEN */
cdc.ifstat &= ~(BIT_DTBSY | BIT_DTEN);
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
/* clear EDT & DSR bits (SCD register $04) */
scd.regs[0x04>>1].byte.h &= 0x07;
/* setup data transfer destination */
switch (scd.regs[0x04>>1].byte.h & 0x07)
{
case 2: /* MAIN-CPU host read */
case 3: /* SUB-CPU host read */
{
/* set DSR bit (SCD register $04) */
scd.regs[0x04>>1].byte.h |= 0x40;
break;
}
case 4: /* PCM RAM DMA */
{
cdc.dma_w = pcm_ram_dma_w;
break;
}
case 5: /* PRG-RAM DMA */
{
cdc.dma_w = prg_ram_dma_w;
break;
}
case 7: /* WORD-RAM DMA */
{
/* check memory mode */
if (scd.regs[0x02 >> 1].byte.l & 0x04)
{
/* 1M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x01)
{
/* Word-RAM bank 0 is assigned to SUB-CPU */
cdc.dma_w = word_ram_0_dma_w;
}
else
{
/* Word-RAM bank 1 is assigned to SUB-CPU */
cdc.dma_w = word_ram_1_dma_w;
}
}
else
{
/* 2M mode */
if (scd.regs[0x02 >> 1].byte.l & 0x02)
{
/* only process DMA if Word-RAM is assigned to SUB-CPU */
cdc.dma_w = word_ram_2M_dma_w;
}
}
break;
}
default: /* invalid */
{
#ifdef LOG_CDC
error("invalid CDC tranfer destination (%d)\n", scd.regs[0x04>>1].byte.h & 0x07);
#endif
break;
}
}
}
scd.regs[0x04>>1].byte.l = 0x07;
break;
}
case 0x07: /* DTACK */
{
/* clear pending data transfer end interrupt */
cdc.ifstat |= BIT_DTEI;
/* clear DBCH bits 4-7 */
cdc.dbc.byte.h &= 0x0f;
#if 0
/* no pending decoder interrupt ? */
if ((cdc.ifstat | BIT_DECI) || !(cdc.ifctrl & BIT_DECIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
scd.regs[0x04>>1].byte.l = 0x08;
break;
}
case 0x08: /* WAL */
cdc.wa.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x09;
break;
case 0x09: /* WAH */
cdc.wa.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0a;
break;
case 0x0a: /* CTRL0 */
{
/* set CRCOK bit only if decoding is enabled */
cdc.stat[0] = data & BIT_DECEN;
/* update STAT2 register */
if (data & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register MODRQ bit & set FORM bit according to sub-header FORM bit*/
cdc.stat[2] = (cdc.ctrl[1] & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
else
{
/* set MODE & FORM bits according to CTRL1 register MODRQ & FORMRQ bits */
cdc.stat[2] = cdc.ctrl[1] & (BIT_MODRQ | BIT_FORMRQ);
}
cdc.ctrl[0] = data;
scd.regs[0x04>>1].byte.l = 0x0b;
break;
}
case 0x0b: /* CTRL1 */
{
/* update STAT2 register */
if (cdc.ctrl[0] & BIT_AUTORQ)
{
/* set MODE bit according to CTRL1 register MODRQ bit & set FORM bit according to sub-header FORM bit*/
cdc.stat[2] = (data & BIT_MODRQ) | ((cdc.head[1][2] & 0x20) >> 3);
}
else
{
/* set MODE & FORM bits according to CTRL1 register MODRQ & FORMRQ bits */
cdc.stat[2] = data & (BIT_MODRQ | BIT_FORMRQ);
}
cdc.ctrl[1] = data;
scd.regs[0x04>>1].byte.l = 0x0c;
break;
}
case 0x0c: /* PTL */
cdc.pt.byte.l = data;
scd.regs[0x04>>1].byte.l = 0x0d;
break;
case 0x0d: /* PTH */
cdc.pt.byte.h = data;
scd.regs[0x04>>1].byte.l = 0x0e;
break;
case 0x0e: /* CTRL2 (unused) */
scd.regs[0x04>>1].byte.l = 0x0f;
break;
case 0x0f: /* RESET */
cdc_reset();
break;
default: /* by default, SBOUT is not used */
break;
}
}
unsigned char cdc_reg_r(void)
{
switch (scd.regs[0x04>>1].byte.l & 0x0F)
{
case 0x01: /* IFSTAT */
scd.regs[0x04>>1].byte.l = 0x02;
return cdc.ifstat;
case 0x02: /* DBCL */
scd.regs[0x04>>1].byte.l = 0x03;
return cdc.dbc.byte.l;
case 0x03: /* DBCH */
scd.regs[0x04>>1].byte.l = 0x04;
return cdc.dbc.byte.h;
case 0x04: /* HEAD0 */
scd.regs[0x04>>1].byte.l = 0x05;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][0];
case 0x05: /* HEAD1 */
scd.regs[0x04>>1].byte.l = 0x06;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][1];
case 0x06: /* HEAD2 */
scd.regs[0x04>>1].byte.l = 0x07;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][2];
case 0x07: /* HEAD3 */
scd.regs[0x04>>1].byte.l = 0x08;
return cdc.head[cdc.ctrl[1] & BIT_SHDREN][3];
case 0x08: /* PTL */
scd.regs[0x04>>1].byte.l = 0x09;
return cdc.pt.byte.l;
case 0x09: /* PTH */
scd.regs[0x04>>1].byte.l = 0x0a;
return cdc.pt.byte.h;
case 0x0a: /* WAL */
scd.regs[0x04>>1].byte.l = 0x0b;
return cdc.wa.byte.l;
case 0x0b: /* WAH */
scd.regs[0x04>>1].byte.l = 0x0c;
return cdc.wa.byte.h;
case 0x0c: /* STAT0 */
scd.regs[0x04>>1].byte.l = 0x0d;
return cdc.stat[0];
case 0x0d: /* STAT1 (always return 0) */
scd.regs[0x04>>1].byte.l = 0x0e;
return 0x00;
case 0x0e: /* STAT2 */
scd.regs[0x04>>1].byte.l = 0x0f;
return cdc.stat[2];
case 0x0f: /* STAT3 */
{
uint8 data = cdc.stat[3];
/* clear !VALST (note: this is not 100% correct but BIOS do not seem to care) */
cdc.stat[3] = BIT_VALST;
/* clear pending decoder interrupt */
cdc.ifstat |= BIT_DECI;
#if 0
/* no pending data transfer end interrupt */
if ((cdc.ifstat | BIT_DTEI) || !(cdc.ifctrl & BIT_DTEIEN))
{
/* clear pending level 5 interrupt */
scd.pending &= ~(1 << 5);
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
#endif
scd.regs[0x04>>1].byte.l = 0x00;
return data;
}
default: /* by default, COMIN is always empty */
return 0xff;
}
}
unsigned short cdc_host_r(void)
{
/* check if data is available */
if (scd.regs[0x04>>1].byte.h & 0x40)
{
/* read 16-bit word from CDC RAM buffer (big-endian format) */
uint16 data = READ_WORD(cdc.ram, cdc.dac.w & 0x3ffe);
#ifdef LOG_CDC
error("CDC host read 0x%04x -> 0x%04x (dbc=0x%x) (%X)\n", cdc.dac.w, data, cdc.dbc.w, s68k.pc);
#endif
/* increment data address counter */
cdc.dac.w += 2;
/* decrement data byte counter */
cdc.dbc.w -= 2;
/* end of transfer ? */
if ((int16)cdc.dbc.w <= 0)
{
/* reset data byte counter (DBCH bits 4-7 should be set to 1) */
cdc.dbc.w = 0xf000;
/* clear !DTEN and !DTBSY */
cdc.ifstat |= (BIT_DTBSY | BIT_DTEN);
/* pending Data Transfer End interrupt */
cdc.ifstat &= ~BIT_DTEI;
/* Data Transfer End interrupt enabled ? */
if (cdc.ifctrl & BIT_DTEIEN)
{
/* pending level 5 interrupt */
scd.pending |= (1 << 5);
/* level 5 interrupt enabled ? */
if (scd.regs[0x32>>1].byte.l & 0x20)
{
/* update IRQ level */
s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
}
}
/* clear DSR bit & set EDT bit (SCD register $04) */
scd.regs[0x04>>1].byte.h = (scd.regs[0x04>>1].byte.h & 0x07) | 0x80;
}
return data;
}
#ifdef LOG_CDC
error("error reading CDC host (data transfer disabled)\n");
#endif
return 0xffff;
}