Files
86Box/src/cdrom/cdrom_mitsumi.c
2025-02-14 07:39:21 +01:00

472 lines
14 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Mitsumi CD-ROM emulation for the ISA bus.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2022 Miran Grca.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/pic.h>
#include <86box/dma.h>
#include <86box/cdrom.h>
#include <86box/cdrom_interface.h>
#include <86box/cdrom_mitsumi.h>
#include <86box/plat.h>
#include <86box/sound.h>
#define MCD_DEFAULT_IOPORT 0x310
#define MCD_DEFAULT_IRQ 5
#define MCD_DEFAULT_DMA 5
#define RAW_SECTOR_SIZE 2352
#define COOKED_SECTOR_SIZE 2048
enum {
STAT_CMD_CHECK = 0x01,
STAT_PLAY_CDDA = 0x02,
STAT_ERROR = 0x04,
STAT_DISK_CDDA = 0x08,
STAT_SPIN = 0x10,
STAT_CHANGE = 0x20,
STAT_READY = 0x40,
STAT_OPEN = 0x80
};
enum {
CMD_GET_INFO = 0x10,
CMD_GET_Q = 0x20,
CMD_GET_STAT = 0x40,
CMD_SET_MODE = 0x50,
CMD_SOFT_RESET = 0x60,
CMD_STOPCDDA = 0x70,
CMD_CONFIG = 0x90,
CMD_SET_VOL = 0xae,
CMD_READ1X = 0xc0,
CMD_READ2X = 0xc1,
CMD_GET_VER = 0xdc,
CMD_STOP = 0xf0,
CMD_EJECT = 0xf6,
CMD_LOCK = 0xfe
};
enum {
MODE_MUTE = 0x01,
MODE_GET_TOC = 0x04,
MODE_STOP = 0x08,
MODE_ECC = 0x20,
MODE_DATA = 0x40
};
enum {
DRV_MODE_STOP,
DRV_MODE_READ,
DRV_MODE_CDDA
};
enum {
FLAG_NODATA = 2,
FLAG_NOSTAT = 4,
FLAG_UNK = 8, //??
FLAG_OPEN = 16
};
enum {
IRQ_DATAREADY = 1,
IRQ_DATACOMP = 2,
IRQ_ERROR = 4
};
typedef struct mcd_t {
int dma;
int irq;
int change;
int data;
uint8_t stat;
uint8_t buf[RAW_SECTOR_SIZE];
int buf_count;
int buf_idx;
uint8_t cmdbuf[16];
int cmdbuf_count;
int cmdrd_count;
int cmdbuf_idx;
uint8_t mode;
uint8_t cmd;
uint8_t conf;
uint8_t enable_irq;
uint8_t enable_dma;
uint16_t dmalen;
uint32_t readmsf;
uint32_t readcount;
int locked;
int drvmode;
int cur_toc_track;
int pos;
int newstat;
} mcd_t;
#define CD_BCD(x) (((x) % 10) | (((x) / 10) << 4))
#define CD_DCB(x) ((((x) &0xf0) >> 4) * 10 + ((x) &0x0f))
#ifdef ENABLE_MITSUMI_CDROM_LOG
int mitsumi_cdrom_do_log = ENABLE_MITSUMI_CDROM_LOG;
void
mitsumi_cdrom_log(const char *fmt, ...)
{
va_list ap;
if (mitsumi_cdrom_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define mitsumi_cdrom_log(fmt, ...)
#endif
static int
mitsumi_cdrom_is_ready(const cdrom_t *dev)
{
return (dev->image_path[0] != 0x00);
}
static void
mitsumi_cdrom_reset(mcd_t *dev)
{
cdrom_t *cdrom = calloc(1, sizeof(cdrom_t));
dev->stat = mitsumi_cdrom_is_ready(cdrom) ? (STAT_READY | STAT_CHANGE) : 0;
dev->cmdrd_count = 0;
dev->cmdbuf_count = 0;
dev->buf_count = 0;
dev->cur_toc_track = 0;
dev->enable_dma = 0;
dev->enable_irq = 0;
dev->conf = 0;
dev->dmalen = COOKED_SECTOR_SIZE;
dev->locked = 0;
dev->change = 1;
dev->newstat = 1;
dev->data = 0;
}
static int
mitsumi_cdrom_read_sector(mcd_t *dev, int first)
{
cdrom_t *cdrom = calloc(1, sizeof(cdrom_t));
uint8_t status;
int ret = 0;
if (dev->drvmode == DRV_MODE_CDDA) {
status = cdrom_mitsumi_audio_play(cdrom, dev->readmsf, dev->readcount);
if (status == 1)
return status;
else
dev->drvmode = DRV_MODE_READ;
}
if ((dev->enable_irq & IRQ_DATACOMP) && !first) {
picint(1 << dev->irq);
}
if (!dev->readcount) {
dev->data = 0;
return 0;
}
cdrom_stop(cdrom);
ret = cdrom_readsector_raw(cdrom, dev->buf, cdrom->seek_pos, 0, 2, 0x10, (int *) &dev->readcount, 0);
if (ret <= 0)
return 0;
if (dev->mode & 0x40) {
dev->buf[12] = CD_BCD((dev->readmsf >> 16) & 0xff);
dev->buf[13] = CD_BCD((dev->readmsf >> 8) & 0xff);
}
dev->readmsf = cdrom_lba_to_msf_accurate(cdrom->seek_pos + 1);
dev->buf_count = dev->dmalen + 1;
dev->buf_idx = 0;
dev->data = 1;
if (dev->enable_dma) {
while (dev->pos < dev->readcount) {
dma_channel_write(dev->dma, dev->buf[dev->pos]);
dev->pos++;
}
dev->pos = 0;
}
dev->readcount--;
if ((dev->enable_irq & IRQ_DATAREADY) && first)
picint(1 << dev->irq);
return 1;
}
static uint8_t
mitsumi_cdrom_in(uint16_t port, void *priv)
{
mcd_t *dev = (mcd_t *) priv;
uint8_t ret = 0xff;
pclog("Mitsumi CD-ROM IN=%03x\n", port);
switch (port & 1) {
case 0:
if (dev->cmdbuf_count) {
dev->cmdbuf_count--;
return dev->cmdbuf[dev->cmdbuf_idx++];
} else if (dev->buf_count) {
ret = (dev->buf_idx < RAW_SECTOR_SIZE) ? dev->buf[dev->buf_idx] : 0;
dev->buf_idx++;
dev->buf_count--;
if (!dev->buf_count)
mitsumi_cdrom_read_sector(dev, 0);
pclog("Read port 0: ret = %02x\n", ret);
return ret;
}
pclog("Read port 0: stat = %02x\n", dev->stat);
return dev->stat;
case 1:
ret = 0;
picintc(1 << dev->irq);
if (!dev->buf_count || !dev->data || dev->enable_dma)
ret |= FLAG_NODATA;
if (!dev->cmdbuf_count || !dev->newstat)
ret |= FLAG_NOSTAT;
pclog("Read port 1: ret = %02x\n", ret | FLAG_UNK);
return ret | FLAG_UNK;
case 2:
break;
default:
break;
}
return ret;
}
static void
mitsumi_cdrom_out(uint16_t port, uint8_t val, void *priv)
{
mcd_t *dev = (mcd_t *) priv;
cdrom_t *cdrom = calloc(1, sizeof(cdrom_t));
pclog("Mitsumi CD-ROM OUT=%03x, val=%02x\n", port, val);
switch (port & 1) {
case 0:
if (dev->cmdrd_count) {
dev->cmdrd_count--;
switch (dev->cmd) {
case CMD_SET_MODE:
dev->mode = val;
dev->cmdbuf[1] = 0;
dev->cmdbuf_count = 2;
break;
case CMD_LOCK:
dev->locked = val & 1;
dev->cmdbuf[1] = 0;
dev->cmdbuf[2] = 0;
dev->cmdbuf_count = 3;
break;
case CMD_CONFIG:
switch (dev->cmdrd_count) {
case 0:
switch (dev->conf) {
case 0x01:
dev->dmalen |= val;
break;
case 0x02:
dev->enable_dma = val;
break;
case 0x10:
dev->enable_irq = val;
break;
default:
break;
}
dev->cmdbuf[1] = 0;
dev->cmdbuf_count = 2;
dev->conf = 0;
break;
case 1:
if (dev->conf == 1) {
dev->dmalen = val << 8;
break;
}
dev->conf = val;
if (dev->conf == 1)
dev->cmdrd_count++;
break;
default:
break;
}
break;
case CMD_READ1X:
case CMD_READ2X:
switch (dev->cmdrd_count) {
case 0:
dev->readcount |= val;
mitsumi_cdrom_read_sector(dev, 1);
dev->cmdbuf_count = 1;
dev->cmdbuf[0] = STAT_SPIN | STAT_READY;
break;
case 1:
dev->readcount |= (val << 8);
break;
case 2:
dev->readcount = (val << 16);
break;
case 5:
dev->readmsf = 0;
fallthrough;
case 4:
case 3:
dev->readmsf |= CD_DCB(val) << ((dev->cmdrd_count - 3) << 3);
break;
default:
break;
}
break;
default:
break;
}
if (!dev->cmdrd_count)
dev->stat = mitsumi_cdrom_is_ready(cdrom) ? (STAT_READY | (dev->change ? STAT_CHANGE : 0)) : 0;
return;
}
dev->cmd = val;
dev->cmdbuf_idx = 0;
dev->cmdrd_count = 0;
dev->cmdbuf_count = 1;
dev->cmdbuf[0] = mitsumi_cdrom_is_ready(cdrom) ? (STAT_READY | (dev->change ? STAT_CHANGE : 0)) : 0;
dev->data = 0;
switch (val) {
case CMD_GET_INFO:
if (mitsumi_cdrom_is_ready(cdrom)) {
cdrom_get_track_buffer(cdrom, &(dev->cmdbuf[1]));
dev->cmdbuf_count = 10;
dev->readcount = 0;
} else {
dev->cmdbuf_count = 1;
dev->cmdbuf[0] = STAT_CMD_CHECK;
}
break;
case CMD_GET_Q:
if (mitsumi_cdrom_is_ready(cdrom)) {
cdrom_get_q(cdrom, &(dev->cmdbuf[1]), &dev->cur_toc_track, dev->mode & MODE_GET_TOC);
dev->cmdbuf_count = 11;
dev->readcount = 0;
} else {
dev->cmdbuf_count = 1;
dev->cmdbuf[0] = STAT_CMD_CHECK;
}
break;
case CMD_GET_STAT:
dev->change = 0;
break;
case CMD_SET_MODE:
dev->cmdrd_count = 1;
break;
case CMD_STOPCDDA:
case CMD_STOP:
cdrom_stop(cdrom);
dev->drvmode = DRV_MODE_STOP;
dev->cur_toc_track = 0;
break;
case CMD_CONFIG:
dev->cmdrd_count = 2;
break;
case CMD_READ1X:
case CMD_READ2X:
if (mitsumi_cdrom_is_ready(cdrom)) {
dev->readcount = 0;
dev->drvmode = (val == CMD_READ1X) ? DRV_MODE_CDDA : DRV_MODE_READ;
dev->cmdrd_count = 6;
} else {
dev->cmdbuf_count = 1;
dev->cmdbuf[0] = STAT_CMD_CHECK;
}
break;
case CMD_GET_VER:
dev->cmdbuf[0] = 1;
dev->cmdbuf[1] = 'D';
dev->cmdbuf[2] = 0;
dev->cmdbuf_count = 3;
break;
case CMD_EJECT:
cdrom_stop(cdrom);
cdrom_eject(0);
dev->readcount = 0;
break;
case CMD_LOCK:
dev->cmdrd_count = 1;
break;
case CMD_SOFT_RESET:
pclog("Soft Reset\n");
mitsumi_cdrom_reset(dev);
break;
default:
dev->cmdbuf[0] = dev->stat | STAT_CMD_CHECK;
break;
}
break;
case 1:
mitsumi_cdrom_reset(dev);
break;
case 2:
break;
default:
break;
}
}
static void *
mitsumi_cdrom_init(UNUSED(const device_t *info))
{
mcd_t *dev = calloc(1, sizeof(mcd_t));
dev->irq = MCD_DEFAULT_IRQ;
dev->dma = MCD_DEFAULT_DMA;
io_sethandler(MCD_DEFAULT_IOPORT, 3,
mitsumi_cdrom_in, NULL, NULL, mitsumi_cdrom_out, NULL, NULL, dev);
mitsumi_cdrom_reset(dev);
return dev;
}
static void
mitsumi_cdrom_close(void *priv)
{
mcd_t *dev = (mcd_t *) priv;
if (dev) {
free(dev);
dev = NULL;
}
}
const device_t mitsumi_cdrom_device = {
.name = "Mitsumi CD-ROM interface",
.internal_name = "mcd",
.flags = DEVICE_ISA16,
.local = 0,
.init = mitsumi_cdrom_init,
.close = mitsumi_cdrom_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = NULL
};