Files
86Box/src/cdrom/cdrom_mke.c

1000 lines
34 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.
*
* Panasonic/MKE CD-ROM emulation for the ISA bus.
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Kevin Moonlight, <me@yyzkevin.com>
* Cacodemon345
*
* Copyright (C) 2025 Miran Grca.
* Copyright (C) 2025 Cacodemon345.
* Copyright (C) 2024 Kevin Moonlight.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdbool.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_mke.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/sound.h>
#include <86box/fifo8.h>
#include <86box/timer.h>
#ifdef ENABLE_MKE_LOG
#include "cpu.h"
#endif
/*
https://elixir.bootlin.com/linux/2.0.29/source/include/linux/sbpcd.h
CR-562-B is classified as Family1 in this driver, so uses the CMD1_ prefix.
*/
#define CDROM_STATUS_DOOR 0x80
#define CDROM_STATUS_DISC_IN 0x40
#define CDROM_STATUS_SPIN_UP 0x20
#define CDROM_STATUS_ERROR 0x10
#define CDROM_STATUS_DOUBLE_SPEED 0x02
#define CDROM_STATUS_READY 0x01
// Status returned from device
#define STAT_READY 0x01
#define STAT_PLAY 0x08
#define STAT_ERROR 0x10
#define STAT_DISK 0x40
#define STAT_TRAY 0x80 // Seems Correct
#define CMD1_PAUSERESUME 0x0D
#define CMD1_RESET 0x0a
#define CMD1_LOCK_CTL 0x0c
#define CMD1_TRAY_CTL 0x07
#define CMD1_MULTISESS 0x8d
#define CMD1_SUBCHANINF 0x11
#define CMD1_ABORT 0x08
// #define CMD1_PATH_CHECK 0x???
#define CMD1_SEEK 0x01
#define CMD1_READ 0x10
#define CMD1_SPINUP 0x02
#define CMD1_SPINDOWN 0x06
#define CMD1_READ_UPC 0x88
// #define CMD1_PLAY 0x???
#define CMD1_PLAY_MSF 0x0e
#define CMD1_PLAY_TI 0x0f
#define CMD1_STATUS 0x05
#define CMD1_READ_ERR 0x82
#define CMD1_READ_VER 0x83
#define CMD1_SETMODE 0x09
#define CMD1_GETMODE 0x84
#define CMD1_CAPACITY 0x85
#define CMD1_READSUBQ 0x87
#define CMD1_DISKINFO 0x8b
#define CMD1_READTOC 0x8c
#define CMD1_PAU_RES 0x0d
#define CMD1_PACKET 0x8e
#define CMD1_SESSINFO 0x8d
typedef struct mke_t {
bool present;
bool tray_open;
uint8_t command_buffer[7];
uint8_t command_buffer_pending;
uint8_t medium_changed;
uint8_t vol0, vol1, patch0, patch1;
uint8_t mode_select[5];
uint8_t media_selected; // temporary hack
Fifo8 data_fifo;
Fifo8 info_fifo;
cdrom_t * cdrom_dev;
uint32_t sector_type;
uint32_t sector_flags;
uint32_t unit_attention;
uint8_t cdbuffer[624240 * 2];
uint32_t data_to_push;
pc_timer_t timer;
char ver[512];
uint8_t is_error;
uint8_t sense[8];
uint8_t temp_buf[65536];
} mke_t;
typedef struct mke_interface_t {
mke_t mke[4];
uint8_t is_sb;
uint8_t drvsel;
uint8_t data_select;
} mke_interface_t;
#ifdef ENABLE_MKE_LOG
int mke_do_log = ENABLE_MKE_LOG;
static void
mke_log(const char *fmt, ...)
{
va_list ap;
if (mke_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define mke_log(fmt, ...)
#endif
#define CHECK_READY() \
{ \
if (!mke_pre_execution_check(mke)) \
return; \
}
#define CHECK_READY_READ() \
{ \
if (!mke_pre_execution_check(mke)) { \
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke)); \
return; \
} \
}
static void
mke_update_sense(mke_t *mke, uint8_t error)
{
/* FreeBSD calls this addrval, but what is it? */
mke->sense[0] = 0x00;
mke->sense[1] = mke->command_buffer[0];
mke->sense[2] = error;
mke->is_error = 1;
}
static void
mke_cdrom_insert(void *priv)
{
mke_t *dev = (mke_t *) priv;
if ((dev == NULL) || (dev->cdrom_dev == NULL))
return;
if (dev->cdrom_dev->ops == NULL) {
dev->medium_changed = 0;
dev->cdrom_dev->cd_status = CD_STATUS_EMPTY;
if (timer_is_enabled(&dev->timer)) {
timer_disable(&dev->timer);
dev->data_to_push = 0;
}
mke_log("Media removal\n");
} else if (dev->cdrom_dev->cd_status & CD_STATUS_TRANSITION) {
dev->medium_changed = 1;
/* Turn off the medium changed status. */
dev->cdrom_dev->cd_status &= ~CD_STATUS_TRANSITION;
mke_log("Media insert\n");
} else {
dev->medium_changed = 0;
dev->cdrom_dev->cd_status |= CD_STATUS_TRANSITION;
mke_log("Media transition\n");
}
}
static int
mke_pre_execution_check(mke_t *mke)
{
int ready = 1;
if ((mke->cdrom_dev->cd_status == CD_STATUS_PLAYING) ||
(mke->cdrom_dev->cd_status == CD_STATUS_PAUSED)) {
ready = 1;
goto skip_ready_check;
}
if (mke->cdrom_dev->cd_status & CD_STATUS_TRANSITION) {
if (mke->command_buffer[0] == 0x82)
ready = 0;
else {
mke_cdrom_insert(mke);
ready = ((mke->cdrom_dev->cd_status != CD_STATUS_EMPTY) && (mke->cdrom_dev->cd_status != CD_STATUS_DVD_REJECTED));
}
} else
ready = ((mke->cdrom_dev->cd_status != CD_STATUS_EMPTY) && (mke->cdrom_dev->cd_status != CD_STATUS_DVD_REJECTED));
skip_ready_check:
/*
If the drive is not ready, there is no reason to keep the
UNIT ATTENTION condition present, as we only use it to mark
disc changes.
*/
if (!ready && (mke->medium_changed > 0))
mke->medium_changed = 0;
/*
If the UNIT ATTENTION condition is set and the command does not allow
execution under it, error out and report the condition.
*/
if (mke->medium_changed == 1) {
/*
Only increment the unit attention phase if the command can
not pass through it.
*/
mke_log("Unit attention now 2\n");
mke->medium_changed++;
mke_update_sense(mke, 0x11); \
return 0;
} else if (mke->medium_changed == 2) {
if (mke->command_buffer[0] != 0x82) {
mke_log("Unit attention now 0\n");
mke->medium_changed = 0;
}
}
/*
Unless the command is REQUEST SENSE, clear the sense. This will *NOT* clear
the UNIT ATTENTION condition if it's set.
*/
if (mke->command_buffer[0] != 0x82) {
memset(mke->sense, 0x00, 8);
mke->is_error = 0;
}
if (!ready && (mke->command_buffer[0] != 0x05)) {
mke_log("Not ready (%02X)\n", mke->command_buffer[0]);
mke_update_sense(mke, 0x03);
return 0;
}
return 1;
}
uint8_t
mke_cdrom_status(cdrom_t *dev, mke_t *mke)
{
uint8_t status = 0;
/*
This bit seems to always be set?
Bit 4 never set?
*/
status |= 2;
if (dev->cd_status == CD_STATUS_PLAYING)
status |= STAT_PLAY;
if (dev->cd_status == CD_STATUS_PAUSED)
status |= STAT_PLAY;
if (mke->is_error)
status |= 0x10;
/* Always set? */
status |= 0x20;
status |= STAT_TRAY;
if (mke->cdrom_dev->cd_status != CD_STATUS_EMPTY) {
status |= STAT_DISK;
status |= STAT_READY;
}
return status;
}
void
mke_get_subq(mke_t *mke, uint8_t *b)
{
cdrom_t *dev = mke->cdrom_dev;
cdrom_get_current_subchannel_sony(dev, mke->temp_buf, 1);
/* ? */
b[0] = 0x80;
b[1] = ((mke->temp_buf[0] & 0xf) << 4) | ((mke->temp_buf[0] & 0xf0) >> 4);
b[2] = mke->temp_buf[1];
b[3] = mke->temp_buf[2];
b[4] = mke->temp_buf[6];
b[5] = mke->temp_buf[7];
b[6] = mke->temp_buf[8];
b[7] = mke->temp_buf[3];
b[8] = mke->temp_buf[4];
b[9] = mke->temp_buf[5];
/* ? */
b[10] = 0;
mke_log("mke_get_subq: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10]);
}
/* Lifted from FreeBSD */
static void blk_to_msf(int blk, unsigned char *msf)
{
blk = blk + 150; /* 2 seconds skip required to
reach ISO data */
msf[0] = blk / 4500;
blk = blk % 4500;
msf[1] = blk / 75;
msf[2] = blk % 75;
return;
}
uint8_t mke_read_toc(mke_t *mke, unsigned char *b, uint8_t track) {
cdrom_t *dev = mke->cdrom_dev;
track_info_t ti;
int last_track;
cdrom_read_toc(dev, mke->temp_buf, CD_TOC_NORMAL, 0, 0, 65536);
last_track = mke->temp_buf[3];
/* Should we allow +1 here? */
if (track > last_track)
return 0;
dev->ops->get_track_info(dev->local, track, 0, &ti);
b[0]=0;
b[1]=ti.attr;
b[2]=ti.number;
b[3]=0;
b[4]=ti.m;
b[5]=ti.s;
b[6]=ti.f;
b[7]=0;
mke_log("mke_read_toc: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
return 1;
}
uint8_t
mke_disc_info(mke_t *mke, unsigned char *b)
{
cdrom_t *dev = mke->cdrom_dev;
uint8_t disc_type_buf[34];
int first_track;
int last_track;
cdrom_read_toc(dev, mke->temp_buf, CD_TOC_NORMAL, 0, 2 << 8, 65536);
cdrom_read_disc_information(dev, disc_type_buf);
first_track = mke->temp_buf[2];
last_track = mke->temp_buf[3];
b[0] = disc_type_buf[8];
b[1] = first_track;
b[2] = last_track;
b[3] = 0;
b[4] = 0;
b[5] = 0;
blk_to_msf(dev->cdrom_capacity, &b[3]);
mke_log("mke_disc_info: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
b[0], b[1], b[2], b[3], b[4], b[5]);
return 1;
}
uint8_t
mke_disc_capacity(cdrom_t *dev, unsigned char *b)
{
b[0] = 0x00;
b[1] = 0x00;
b[2] = 0x00;
b[3] = 0x08;
b[4] = 0x00;
blk_to_msf(dev->cdrom_capacity, &b[0]);
mke_log("mke_disc_capacity: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
b[0], b[1], b[2], b[3], b[4]);
return 1;
}
void
mke_read_multisess(mke_t *mke)
{
if ((mke->temp_buf[9] != 0) || (mke->temp_buf[10] != 0) || (mke->temp_buf[11] != 0)) {
/* Multi-session disc. */
fifo8_push(&mke->info_fifo, 0x80);
fifo8_push(&mke->info_fifo, mke->temp_buf[9]);
fifo8_push(&mke->info_fifo, mke->temp_buf[10]);
fifo8_push(&mke->info_fifo, mke->temp_buf[11]);
fifo8_push(&mke->info_fifo, 0);
fifo8_push(&mke->info_fifo, 0);
} else {
uint8_t no_multisess[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
fifo8_push_all(&mke->info_fifo, no_multisess, 6);
}
}
static void
mke_reset(mke_t *mke)
{
cdrom_stop(mke->cdrom_dev);
timer_disable(&mke->timer);
mke->sector_type = 0x08 | (1 << 4);
mke->sector_flags = 0x10;
memset(mke->mode_select, 0, 5);
mke->mode_select[2] = 0x08;
mke->patch0 = 0x01;
mke->patch1 = 0x02;
mke->vol0 = 255;
mke->vol1 = 255;
mke->cdrom_dev->sector_size = 2048;
}
void
mke_command_callback(void *priv)
{
mke_t *mke = (mke_t *) priv;
switch (mke->command_buffer[0]) {
case CMD1_SEEK: {
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
}
case CMD1_READ: {
fifo8_push_all(&mke->data_fifo, mke->cdbuffer, mke->data_to_push);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
mke->data_to_push = 0;
ui_sb_update_icon(SB_CDROM | mke->cdrom_dev->id, 0);
break;
}
}
}
void
mke_command(mke_t *mke, uint8_t value)
{
uint16_t i;
/* This is wasteful handling of buffers for compatibility, but will optimize later. */
uint8_t x[12];
int old_cd_status;
if (mke->command_buffer_pending) {
mke->command_buffer[6 - mke->command_buffer_pending + 1] = value;
mke->command_buffer_pending--;
}
if (mke->command_buffer[0] == CMD1_ABORT) {
mke_log("CMD_ABORT\n");
fifo8_reset(&mke->info_fifo);
fifo8_reset(&mke->data_fifo);
timer_disable(&mke->timer);
mke->command_buffer[0] = 0;
mke->command_buffer_pending = 7;
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
}
if (!mke->command_buffer_pending && mke->command_buffer[0]) {
mke->command_buffer_pending = 7;
mke_log("mke_command: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
mke->command_buffer[0], mke->command_buffer[1],
mke->command_buffer[2], mke->command_buffer[3],
mke->command_buffer[4], mke->command_buffer[5],
mke->command_buffer[6]);
switch (mke->command_buffer[0]) {
case 0x03:
fifo8_reset(&mke->info_fifo);
cdrom_stop(mke->cdrom_dev);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case 0x06:
fifo8_reset(&mke->info_fifo);
cdrom_stop(mke->cdrom_dev);
cdrom_eject(mke->cdrom_dev->id);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case 0x07:
fifo8_reset(&mke->info_fifo);
cdrom_reload(mke->cdrom_dev->id);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_RESET:
mke_reset(mke);
break;
case CMD1_READ: {
uint32_t count = mke->command_buffer[6];
uint8_t *buf = mke->cdbuffer;
int res = 0;
uint64_t lba = MSFtoLBA(mke->command_buffer[1], mke->command_buffer[2],
mke->command_buffer[3]) - 150;
int len __attribute__((unused)) = 0;
CHECK_READY_READ();
mke->data_to_push = 0;
while (count) {
if ((res = cdrom_readsector_raw(mke->cdrom_dev, buf, lba, 0,
mke->sector_type, mke->sector_flags, &len, 0)) > 0) {
lba++;
buf += mke->cdrom_dev->sector_size;
mke->data_to_push += mke->cdrom_dev->sector_size;
} else {
mke_update_sense(mke, (res == 0) ? 0x10 : 0x05);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
}
count--;
}
if (count != 0) {
fifo8_reset(&mke->data_fifo);
mke->data_to_push = 0;
} else {
ui_sb_update_icon(SB_CDROM | mke->cdrom_dev->id, 1);
timer_on_auto(&mke->timer, (1000000.0 / (176400.0 * 2.)) * mke->data_to_push);
}
break;
} case CMD1_READSUBQ:
CHECK_READY();
mke_get_subq(mke, (uint8_t *) &x);
fifo8_reset(&mke->info_fifo);
fifo8_push_all(&mke->info_fifo, x, 11);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_SETMODE:
/* Returns 1 */
fifo8_reset(&mke->info_fifo);
mke_log("CMD: SET MODE:");
for (i = 0; i < 6; i++) {
mke_log("%02x ", mke->command_buffer[i + 1]);
}
mke_log("\n");
switch (mke->command_buffer[1]) {
case 0:
switch (mke->command_buffer[2]) {
case 0x00: /* Cooked */
mke->sector_type = 0x08 | (1 << 4);
mke->sector_flags = 0x10;
mke->cdrom_dev->sector_size = 2048;
break;
case 0x81: /* XA */
case 0x01: /* User */ {
uint32_t sector_size = (mke->command_buffer[3] << 8) |
mke->command_buffer[4];
if (!sector_size) {
mke_update_sense(mke, 0x0e);
return;
} else {
switch (sector_size) {
case 2048:
mke->sector_type = 0x08 | (1 << 4);
mke->sector_flags = 0x10;
mke->cdrom_dev->sector_size = 2048;
break;
case 2052:
mke->sector_type = 0x18;
mke->sector_flags = 0x30;
mke->cdrom_dev->sector_size = 2052;
break;
case 2324:
mke->sector_type = 0x1b;
mke->sector_flags = 0x18;
mke->cdrom_dev->sector_size = 2324;
break;
case 2336:
mke->sector_type = 0x1c;
mke->sector_flags = 0x58;
mke->cdrom_dev->sector_size = 2336;
break;
case 2340:
mke->sector_type = 0x18;
mke->sector_flags = 0x78;
mke->cdrom_dev->sector_size = 2340;
break;
case 2352:
mke->sector_type = 0x00;
mke->sector_flags = 0xf8;
mke->cdrom_dev->sector_size = 2352;
break;
default:
mke_update_sense(mke, 0x0e);
return;
}
}
break;
} case 0x82: /* DA */
mke->sector_type = 0x00;
mke->sector_flags = 0xf8;
mke->cdrom_dev->sector_size = 2352;
break;
default:
mke_update_sense(mke, 0x0e);
return;
}
memcpy(mke->mode_select, &mke->command_buffer[2], 5);
break;
case 5:
mke->vol0 = mke->command_buffer[4];
mke->vol1 = mke->command_buffer[6];
mke->patch0 = mke->command_buffer[3];
mke->patch1 = mke->command_buffer[5];
break;
}
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_GETMODE:
/* 6 */
mke_log("GET MODE\n");
if (mke->command_buffer[1] == 5) {
uint8_t volsettings[5] = { 0, mke->patch0, mke->vol0, mke->patch1, mke->vol1 };
fifo8_push_all(&mke->info_fifo, volsettings, 5);
} else
fifo8_push_all(&mke->info_fifo, mke->mode_select, 5);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_PAUSERESUME:
CHECK_READY();
cdrom_audio_pause_resume(mke->cdrom_dev, mke->command_buffer[1] >> 7);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_CAPACITY:
/* 6 */
mke_log("DISK CAPACITY\n");
CHECK_READY();
mke_disc_capacity(mke->cdrom_dev, (uint8_t *) &x);
fifo8_push_all(&mke->info_fifo, x, 5);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_DISKINFO:
/* 7 */
mke_log("DISK INFO\n");
CHECK_READY();
mke_disc_info(mke, (uint8_t *) &x);
fifo8_push_all(&mke->info_fifo, x, 6);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_READTOC:
CHECK_READY();
fifo8_reset(&mke->info_fifo);
mke_read_toc(mke, (uint8_t *) &x, mke->command_buffer[2]);
fifo8_push_all(&mke->info_fifo, x, 8);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_PLAY_TI:
CHECK_READY();
/* Index is ignored for now. */
fifo8_reset(&mke->info_fifo);
if (cdrom_audio_play(mke->cdrom_dev, mke->command_buffer[1], mke->command_buffer[3], 2))
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
else
mke_update_sense(mke, 0x10);
break;
case CMD1_PLAY_MSF:
CHECK_READY();
fifo8_reset(&mke->info_fifo);
mke_log("PLAY MSF:");
for (i = 0; i < 6; i++) {
mke_log("%02x ", mke->command_buffer[i + 1]);
}
mke_log("\n");
{
int msf = 1;
int pos = (mke->command_buffer[1] << 16) | (mke->command_buffer[2] << 8) |
mke->command_buffer[3];
int len = (mke->command_buffer[4] << 16) | (mke->command_buffer[5] << 8) |
mke->command_buffer[6];
if (!cdrom_audio_play(mke->cdrom_dev, pos, len, msf))
mke_update_sense(mke, 0x10);
else
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
}
break;
case CMD1_SEEK:
CHECK_READY();
old_cd_status = mke->cdrom_dev->cd_status;
fifo8_reset(&mke->info_fifo);
/* TODO: DOES THIS IMPACT CURRENT PLAY LENGTH? */
mke_log("SEEK MSF:");
for (i = 0; i < 6; i++) {
mke_log("%02x ", mke->command_buffer[i + 1]);
}
cdrom_stop(mke->cdrom_dev);
/* Note for self: Panasonic/MKE drives send seek commands in MSF format. */
cdrom_seek(mke->cdrom_dev, MSFtoLBA(mke->command_buffer[1], mke->command_buffer[2],
mke->command_buffer[3]) - 150, 0);
if ((old_cd_status == CD_STATUS_PLAYING) || (old_cd_status == CD_STATUS_PAUSED)) {
cdrom_audio_play(mke->cdrom_dev, mke->cdrom_dev->seek_pos, -1, 0);
cdrom_audio_pause_resume(mke->cdrom_dev, old_cd_status == CD_STATUS_PLAYING);
}
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_SESSINFO:
CHECK_READY();
fifo8_reset(&mke->info_fifo);
mke_log("CMD: READ SESSION INFO\n");
mke_read_multisess(mke);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_READ_UPC:
CHECK_READY();
fifo8_reset(&mke->info_fifo);
mke_log("CMD: READ UPC\n");
uint8_t upc[8] = { [0] = 80 };
fifo8_push_all(&mke->info_fifo, upc, 8);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_READ_ERR:
fifo8_reset(&mke->info_fifo);
mke_log("CMD: READ ERR\n");
mke_log("ERROR: %02X %02X %02X %02X %02X %02X %02X %02X\n",
mke->sense[0], mke->sense[1], mke->sense[2], mke->sense[3],
mke->sense[4], mke->sense[5], mke->sense[6], mke->sense[7]);
{
uint8_t temp[8];
memset(temp, mke->sense[2], 8);
fifo8_push_all(&mke->info_fifo, mke->sense, 8);
}
mke->is_error = 0;
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_READ_VER:
/* SB2CD Expects 12 bytes, but drive only returns 11. */
fifo8_reset(&mke->info_fifo);
fifo8_push_all(&mke->info_fifo, (uint8_t *) mke->ver, 10);
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
case CMD1_STATUS:
fifo8_reset(&mke->info_fifo);
CHECK_READY_READ();
fifo8_push(&mke->info_fifo, mke_cdrom_status(mke->cdrom_dev, mke));
break;
default:
mke_log("MKE: Unknown Commnad [%02x]\n", mke->command_buffer[0]);
break;
}
} else if (!mke->command_buffer_pending) {
/*
We are done but not in a command.
Should we make sure it is a valid command here?
*/
mke->command_buffer[0] = value;
mke->command_buffer_pending = 6;
}
}
void
mke_write(uint16_t port, uint8_t val, void *priv)
{
mke_interface_t *mki = (mke_interface_t *) priv;
mke_t *mke = &(mki->mke[mki->drvsel & 0x03]);
uint8_t sb[8] = { 0x00, 0x02, 0x01, 0x03 };
mke_log("[%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, port, val);
/* if (mke->present || ((port & 0x0003) == 0x0003)) */ switch (port & 0x0003) {
case 0:
if (mke->present)
mke_command(mke, val);
break;
case 1:
if (mki->is_sb)
mki->data_select = val;
break;
case 2:
if (mke->present)
mke_reset(mke);
break;
case 3:
if (mki->is_sb)
mki->drvsel = (val & 0xfc) | sb[val & 0x03];
else
mki->drvsel = val;
break;
default:
break;
}
}
uint8_t
mke_read(uint16_t port, void *priv)
{
mke_interface_t *mki = (mke_interface_t *) priv;
mke_t *mke = &(mki->mke[mki->drvsel & 0x03]);
uint8_t ret = 0x00;
if (mke->present) switch (port & 0x0003) {
case 0:
/* Info */
if (mki->is_sb && mki->data_select)
ret = fifo8_num_used(&mke->data_fifo) ? fifo8_pop(&mke->data_fifo) : 0x00;
else
ret = fifo8_num_used(&mke->info_fifo) ? fifo8_pop(&mke->info_fifo) : 0x00;
break;
case 1:
/*
Status:
- 1 = Status Change;
- 2 = Data Ready;
- 4 = Response Ready;
- 8 = Attention / Issue?
*/
ret = 0xff;
if (fifo8_num_used(&mke->data_fifo))
/* Data FIFO */
ret ^= 2;
if (fifo8_num_used(&mke->info_fifo))
/* Status FIFO */
ret ^= 4;
if (mke->is_error)
ret ^= 8;
break;
case 2:
/* Data */
if (!mki->is_sb)
ret = fifo8_num_used(&mke->data_fifo) ? fifo8_pop(&mke->data_fifo) : 0x00;
break;
default:
mke_log("MKE Unknown Read Port: %04X\n", port);
ret = 0xff;
break;
} else if ((port & 0x0003) == 0x0003)
/* This is needed for the Windows 95 built-in driver to function correctly. */
ret = 0xff;
mke_log("[%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, port, ret);
return ret;
}
uint32_t
mke_get_volume(void *priv, int channel)
{
mke_t *dev = (mke_t *) priv;
return channel == 0 ? dev->vol0 : dev->vol1;
}
uint32_t
mke_get_channel(void *priv, int channel)
{
mke_t *dev = (mke_t *) priv;
return channel == 0 ? dev->patch0 : dev->patch1;
}
void
mke_close(void *priv)
{
mke_interface_t *mki = (mke_interface_t *) calloc(1, sizeof(mke_interface_t));
for (uint8_t i = 0; i < 4; i++) {
mke_t *mke = &(mki->mke[i]);
fifo8_destroy(&mke->info_fifo);
fifo8_destroy(&mke->data_fifo);
timer_disable(&mke->timer);
}
free(mki);
}
void *
mke_init(const device_t *info)
{
mke_interface_t *mki = (mke_interface_t *) calloc(1, sizeof(mke_interface_t));
int num = 0;
for (uint8_t i = 0; i < CDROM_NUM; i++) {
if (cdrom[i].bus_type == CDROM_BUS_MKE) {
cdrom_t *dev = &cdrom[i];
mke_t *mke = &(mki->mke[dev->mke_channel]);
mke->present = 1;
cdrom_generate_name_mke(dev->type, mke->ver);
fifo8_create(&mke->info_fifo, 128);
fifo8_create(&mke->data_fifo, 624240 * 2);
fifo8_reset(&mke->info_fifo);
fifo8_reset(&mke->data_fifo);
mke->cdrom_dev = dev;
mke->command_buffer_pending = 7;
mke->sector_type = 0x08 | (1 << 4);
mke->sector_flags = 0x10;
mke->mode_select[2] = 0x08;
mke->patch0 = 0x01;
mke->patch1 = 0x02;
mke->vol0 = 255;
mke->vol1 = 255;
dev->sector_size = 2048;
dev->priv = mke;
dev->insert = mke_cdrom_insert;
dev->get_volume = mke_get_volume;
dev->get_channel = mke_get_channel;
dev->cached_sector = -1;
timer_add(&mke->timer, mke_command_callback, mke, 0);
num++;
if (num == 4)
break;
}
}
mki->is_sb = info->local;
uint16_t base = device_get_config_hex16("base");
io_sethandler(base, 4, mke_read, NULL, NULL, mke_write, NULL, NULL, mki);
return mki;
}
static const device_config_t mke_config[] = {
// clang-format off
{
.name = "base",
.description = "Address",
.type = CONFIG_HEX16,
.default_string = NULL,
.default_int = 0x250,
.file_filter = NULL,
.spinner = { 0 },
.selection = {
{ .description = "220H", .value = 0x220 },
{ .description = "230H", .value = 0x230 },
{ .description = "250H", .value = 0x250 },
{ .description = "260H", .value = 0x260 },
{ .description = "270H", .value = 0x270 },
{ .description = "290H", .value = 0x290 },
{ .description = "300H", .value = 0x300 },
{ .description = "310H", .value = 0x310 },
{ .description = "320H", .value = 0x320 },
{ .description = "330H", .value = 0x330 },
{ .description = "340H", .value = 0x340 },
{ NULL }
},
.bios = { { 0 } }
},
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format off
};
const device_t mke_cdrom_device = {
.name = "Panasonic/MKE CD-ROM interface (Creative)",
.internal_name = "mkecd",
.flags = DEVICE_ISA16,
.local = 1,
.init = mke_init,
.close = mke_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = mke_config
};
const device_t mke_cdrom_noncreative_device = {
.name = "Panasonic/MKE CD-ROM interface",
.internal_name = "mkecd_normal",
.flags = DEVICE_ISA16,
.local = 0,
.init = mke_init,
.close = mke_close,
.reset = NULL,
.available = NULL,
.speed_changed = NULL,
.force_redraw = NULL,
.config = mke_config
};