#include "compiler.h"
#ifdef TRACEOUT
#undef TRACEOUT
#endif
#define TRACEOUT(s) trace_fmt s
// これ、scsicmdとどう統合するのよ?
#if defined(SUPPORT_IDEIO)
#include "dosio.h"
#include "cpucore.h"
#include "pccore.h"
#include "iocore.h"
#include "ideio.h"
#include "atapicmd.h"
#include "sxsi.h"
#define YUIDEBUG
// INQUIRY
static const UINT8 cdrom_inquiry[] = {
#ifdef YUIDEBUG
// うちのドライブの奴 NECCDは Product Level 3.00以上で modesense10のコードがちげー
0x05, // CD-ROM
0x80, // bit7: Removable Medium Bit, other: Reserved
0x00, // version [7-6: ISO, ECMA: 5-3, 2-0: ANSI(00)]
0x21, // 7-4: ATAPI version, 3-0: Response Data Format
0x1f, // Additional length
0x00,0x00,0x00, // Reserved
'N', 'E', 'C', ' ', ' ', ' ', ' ', ' ', // Vendor ID
'C', 'D', '-', 'R', 'O', 'M', ' ', 'D', // Product ID
'R', 'I', 'V', 'E', ':', '2', '5', '1', // Product ID
'4', '.', '0', '9' // Product Revision Level
#else
0x05, // CD-ROM
0x80, // bit7: Removable Medium Bit, other: Reserved
0x00, // version [7-6: ISO, ECMA: 5-3, 2-0: ANSI(00)]
0x21, // 7-4: ATAPI version, 3-0: Response Data Format
0x1f, // Additional length
0x00,0x00,0x00, // Reserved
'N', 'E', 'C', ' ', ' ', ' ', ' ', ' ', // Vendor ID
'C', 'D', '-', 'R', 'O', 'M', ' ', 'D', // Product ID
'R', 'I', 'V', 'E', ' ', ' ', ' ', ' ', // Product ID
'1', '.', '0', ' ' // Product Revision Level
#endif
};
static void senddata(IDEDRV drv, UINT size, UINT limit) {
size = min(size, limit);
drv->sc = IDEINTR_IO;
drv->cy = size;
drv->status &= ~(IDESTAT_BSY|IDESTAT_DMRD|IDESTAT_SERV|IDESTAT_CHK);
drv->status |= IDESTAT_DRQ;
drv->error = 0;
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NO_SENSE);
drv->asc = ATAPI_ASC_NO_ADDITIONAL_SENSE_INFORMATION;
drv->bufdir = IDEDIR_IN;
drv->buftc = IDETC_TRANSFEREND;
drv->bufpos = 0;
drv->bufsize = size;
if (!(drv->ctrl & IDECTRL_NIEN)) {
TRACEOUT(("atapicmd: senddata()"));
ideio.bank[0] = ideio.bank[1] | 0x80; // ????
pic_setirq(IDE_IRQ);
}
}
static void cmddone(IDEDRV drv) {
drv->sc = IDEINTR_IO|IDEINTR_CD;
drv->status &= ~(IDESTAT_BSY|IDESTAT_DRQ|IDESTAT_SERV|IDESTAT_CHK);
drv->status |= IDESTAT_DRDY;
drv->error = 0;
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NO_SENSE);
drv->asc = ATAPI_ASC_NO_ADDITIONAL_SENSE_INFORMATION;
if (!(drv->ctrl & IDECTRL_NIEN)) {
TRACEOUT(("atapicmd: cmddone()"));
ideio.bank[0] = ideio.bank[1] | 0x80; // ????
pic_setirq(IDE_IRQ);
}
}
static void senderror(IDEDRV drv) {
drv->sc = IDEINTR_IO;
drv->status &= ~(IDESTAT_BSY|IDESTAT_DMRD|IDESTAT_SERV);
drv->status |= IDESTAT_CHK;
if (!(drv->ctrl & IDECTRL_NIEN)) {
TRACEOUT(("atapicmd: senderror()"));
ideio.bank[0] = ideio.bank[1] | 0x80; // ????
pic_setirq(IDE_IRQ);
}
}
static void sendabort(IDEDRV drv) {
drv->sk = ATAPI_SK_ABORTED_COMMAND;
drv->error = IDEERR_ABRT;
senderror(drv);
}
// ----- ATAPI packet command
static void atapi_cmd_start_stop_unit(IDEDRV drv);
static void atapi_cmd_prevent_allow_medium_removal(IDEDRV drv);
static void atapi_cmd_read_capacity(IDEDRV drv);
static void atapi_cmd_read(IDEDRV drv, UINT32 lba, UINT32 leng);
static void atapi_cmd_mode_select(IDEDRV drv);
static void atapi_cmd_mode_sense(IDEDRV drv);
static void atapi_cmd_readsubch(IDEDRV drv);
static void atapi_cmd_readtoc(IDEDRV drv);
static void atapi_cmd_playaudiomsf(IDEDRV drv);
static void atapi_cmd_pauseresume(IDEDRV drv);
void atapicmd_a0(IDEDRV drv) {
UINT32 lba, leng;
UINT8 cmd;
cmd = drv->buf[0];
switch (cmd) {
case 0x00: // test unit ready
TRACEOUT(("atapicmd: test unit ready"));
if (!(drv->media & IDEIO_MEDIA_LOADED)) {
/* medium not present */
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NOT_READY);
drv->asc = ATAPI_ASC_MEDIUM_NOT_PRESENT;
senderror(drv);
break;
}
if (drv->media & IDEIO_MEDIA_CHANGED) {
drv->media &= ~IDEIO_MEDIA_CHANGED;
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NOT_READY);
drv->asc = ATAPI_ASC_NOT_READY_TO_READY_TRANSITION;
senderror(drv);
break;
}
cmddone(drv);
break;
case 0x03: // request sense
TRACEOUT(("atapicmd: request sense"));
leng = drv->buf[4];
ZeroMemory(drv->buf, 18);
drv->buf[0] = 0x70;
drv->buf[2] = drv->sk;
drv->buf[7] = 11; // length
drv->buf[12] = (BYTE)(drv->asc & 0xff);
senddata(drv, 18, leng);
break;
case 0x12: // inquiry
TRACEOUT(("atapicmd: inquiry"));
leng = drv->buf[4];
CopyMemory(drv->buf, cdrom_inquiry, sizeof(cdrom_inquiry));
senddata(drv, sizeof(cdrom_inquiry), leng);
break;
case 0x1b: // start stop unit
TRACEOUT(("atapicmd: start stop unit"));
atapi_cmd_start_stop_unit(drv);
break;
case 0x1e: // prevent allow medium removal
TRACEOUT(("atapicmd: prevent allow medium removal"));
atapi_cmd_prevent_allow_medium_removal(drv);
break;
case 0x25: // read capacity
TRACEOUT(("atapicmd: read capacity"));
atapi_cmd_read_capacity(drv);
break;
case 0x28: // read(10)
TRACEOUT(("atapicmd: read(10)"));
lba = (drv->buf[2] << 24) + (drv->buf[3] << 16) + (drv->buf[4] << 8) + drv->buf[5];
leng = (drv->buf[7] << 8) + drv->buf[8];
atapi_cmd_read(drv, lba, leng);
break;
case 0x55: // mode select
TRACEOUT(("atapicmd: mode select"));
atapi_cmd_mode_select(drv);
break;
case 0x5a: // mode sense(10)
TRACEOUT(("atapicmd: mode sense(10)"));
atapi_cmd_mode_sense(drv);
break;
case 0x42:
TRACEOUT(("atapicmd: read sub channel"));
atapi_cmd_readsubch(drv);
break;
case 0x43: // read TOC
TRACEOUT(("atapicmd: read TOC"));
atapi_cmd_readtoc(drv);
break;
case 0x47: // Play Audio MSF
TRACEOUT(("atapicmd: Play Audio MSF"));
atapi_cmd_playaudiomsf(drv);
break;
case 0x4b:
TRACEOUT(("atapicmd: pause resume"));
atapi_cmd_pauseresume(drv);
break;
default:
TRACEOUT(("atapicmd: unknown command = %.2x", cmd));
sendabort(drv);
break;
}
}
//-- command
// 0x1b: START/STOP UNIT
static void atapi_cmd_start_stop_unit(IDEDRV drv) {
UINT power;
power = (drv->buf[4] >> 4);
if (power != 0) {
/* power control is not supported */
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_INVALID_FIELD_IN_CDB;
goto send_error;
}
if (drv->buf[4] & 2) {
/* lock/eject op. is not supported */
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_INVALID_FIELD_IN_CDB;
goto send_error;
}
if (!(drv->media & IDEIO_MEDIA_LOADED)) {
/* medium not present */
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NOT_READY);
drv->asc = ATAPI_ASC_MEDIUM_NOT_PRESENT;
goto send_error;
}
/* XXX play/read TOC, stop */
cmddone(drv);
return;
send_error:
senderror(drv);
}
// 0x1e: PREVENT/ALLOW MEDIUM REMOVAL
static void atapi_cmd_prevent_allow_medium_removal(IDEDRV drv) {
/* XXX */
cmddone(drv);
}
// 0x25: READ CAPACITY
static void atapi_cmd_read_capacity(IDEDRV drv) {
/* XXX */
cmddone(drv);
}
// 0x28: READ(10)
void atapi_dataread(IDEDRV drv) {
// エラー処理目茶苦茶〜
if (drv->nsectors == 0) {
sendabort(drv);
return;
}
if (sxsi_read(drv->sxsidrv, drv->sector, drv->buf, 2048) != 0) {
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = 0x21;
senderror(drv);
return;
}
drv->sector++;
drv->nsectors--;
drv->sc = IDEINTR_IO;
drv->cy = 2048;
drv->status &= ~(IDESTAT_BSY|IDESTAT_DMRD|IDESTAT_SERV|IDESTAT_CHK);
drv->status |= IDESTAT_DRQ;
drv->error = 0;
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_NO_SENSE);
drv->asc = ATAPI_ASC_NO_ADDITIONAL_SENSE_INFORMATION;
drv->bufdir = IDEDIR_IN;
drv->buftc = (drv->nsectors)?IDETC_ATAPIREAD:IDETC_TRANSFEREND;
drv->bufpos = 0;
drv->bufsize = 2048;
if (!(drv->ctrl & IDECTRL_NIEN)) {
TRACEOUT(("atapicmd: senddata()"));
ideio.bank[0] = ideio.bank[1] | 0x80; // ????
pic_setirq(IDE_IRQ);
}
}
static void atapi_cmd_read(IDEDRV drv, UINT32 lba, UINT32 nsec) {
drv->sector = lba;
drv->nsectors = nsec;
atapi_dataread(drv);
}
// -- MODE SELECT/SENSE
#define PC_01_SIZE 8
#define PC_0D_SIZE 8
#define PC_0E_SIZE 16
#define PC_2A_SIZE 20
// page code changeable value
static const BYTE chgval_pagecode_01[PC_01_SIZE] = {
0x00, 0x00, 0x37, 0xff, 0x00, 0x00, 0x00, 0x00,
};
static const BYTE chgval_pagecode_0d[PC_0D_SIZE] = {
0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff,
};
static const BYTE chgval_pagecode_0e[PC_0E_SIZE] = {
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff,
0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00,
};
static const BYTE chgval_pagecode_2a[PC_2A_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0xc2, 0x00, 0x02, 0x00, 0x00, 0x02, 0xc2,
0x00, 0x00, 0x00, 0x00,
};
// page code default value
static const BYTE defval_pagecode_01[PC_01_SIZE] = {
0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const BYTE defval_pagecode_0d[PC_0D_SIZE] = {
0x0d, 0x06, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x4b,
};
static const BYTE defval_pagecode_0e[PC_0E_SIZE] = {
0x0e, 0x0e, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4b,
0x01, 0xff, 0x02, 0xff, 0x00, 0x00, 0x00, 0x00,
};
static const BYTE defval_pagecode_2a[PC_2A_SIZE] = {
#ifdef YUIDEBUG
0x2a, 0x12, 0x00, 0x00, 0x71, 0x65, 0x89, 0x07,
0x02, 0xc2, 0x00, 0xff, 0x00, 0x80, 0x02, 0xc2,
0x00, 0x00, 0x00, 0x00,
#else
0x2a, 0x12, 0x00, 0x00, 0x00, 0x00, 0x20, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
#endif
};
// 0x55: MODE SELECT
static void atapi_cmd_mode_select(IDEDRV drv) {
if (drv->buf[1] & 1) {
/* Saved Page is not supported */
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_INVALID_FIELD_IN_CDB;
senderror(drv);
return;
}
sendabort(drv); /* XXX */
}
// 0x5a: MODE SENSE
static void atapi_cmd_mode_sense(IDEDRV drv) {
const BYTE *ptr;
UINT leng;
UINT cnt;
UINT8 pctrl, pcode;
leng = (drv->buf[7] << 8) + drv->buf[8];
pctrl = (drv->buf[2] >> 6) & 3; // 0: current, 1: changeable, 2: default
pcode = drv->buf[2] & 0x3f;
if (pctrl == 3) {
/* Saved Page is not supported */
TRACEOUT(("Saved Page is not supported"));
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED;
senderror(drv);
return;
}
/* Mode Parameter Header */
ZeroMemory(drv->buf, 8);
if (!(drv->media & IDEIO_MEDIA_LOADED)) {
drv->buf[2] = 0x70; // Door closed, no disc present
}
else if ((drv->media & (IDEIO_MEDIA_COMBINE)) == IDEIO_MEDIA_AUDIO) {
drv->buf[2] = 0x02; // 120mm CD-ROM audio only
}
else if ((drv->media & (IDEIO_MEDIA_COMBINE)) == IDEIO_MEDIA_COMBINE) {
drv->buf[2] = 0x03; // 120mm CD-ROM data & audio combined
}
else {
drv->buf[2] = 0x01; // 120mm CD-ROM data only
}
cnt = 8;
if (cnt > leng) {
goto length_exceeded;
}
/* Mode Page */
TRACEOUT(("pcode = %.2x", pcode));
switch (pcode) {
case 0x3f:
/*FALLTHROUGH*/
case 0x01: /* Read Error Recovery Parameters Page */
if (pctrl == 1) {
ptr = chgval_pagecode_01;
}
else {
ptr = defval_pagecode_01;
}
CopyMemory(drv->buf + cnt, ptr, min((leng - cnt), PC_01_SIZE));
cnt += PC_01_SIZE;
if (cnt > leng) {
goto length_exceeded;
}
if (pcode == 0x01) {
break;
}
/*FALLTHROUGH*/
case 0x0d: /* CD-ROM Device Parameters Page */
if (pctrl == 1) {
ptr = chgval_pagecode_0d;
}
else {
ptr = defval_pagecode_0d;
}
CopyMemory(drv->buf + cnt, ptr, min((leng - cnt), PC_0D_SIZE));
cnt += PC_0D_SIZE;
if (cnt > leng) {
goto length_exceeded;
}
if (pcode == 0x0d) {
break;
}
/*FALLTHROUGH*/
case 0x0e: /* CD-ROM Audio Control Paramater Page */
if (pctrl == 1) {
ptr = chgval_pagecode_0e;
}
else {
ptr = defval_pagecode_0e;
}
CopyMemory(drv->buf + cnt, ptr, min((leng - cnt), PC_0E_SIZE));
cnt += PC_0E_SIZE;
if (cnt > leng) {
goto length_exceeded;
}
if (pcode == 0x0e) {
break;
}
/*FALLTHROUGH*/
case 0x2a: /* CD-ROM Capabilities & Mechanical Status Page */
if (pctrl == 1) {
ptr = chgval_pagecode_2a;
}
else {
ptr = defval_pagecode_2a;
}
CopyMemory(drv->buf + cnt, ptr, min((leng - cnt), PC_2A_SIZE));
cnt += PC_2A_SIZE;
if (cnt > leng) {
goto length_exceeded;
}
#if 0
/*FALLTHROUGH*/
case 0x00:
#endif
break;
default:
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_INVALID_FIELD_IN_CDB;
senderror(drv);
return;
}
drv->buf[0] = (UINT8)((cnt - 2) >> 8);
drv->buf[1] = (UINT8)(cnt - 2);
senddata(drv, cnt, leng);
return;
length_exceeded:
if (cnt >= 65536) {
ATAPI_SET_SENSE_KEY(drv, ATAPI_SK_ILLEGAL_REQUEST);
drv->asc = ATAPI_ASC_INVALID_FIELD_IN_CDB;
senderror(drv);
return;
}
drv->buf[0] = (UINT8)((leng - 2) >> 8);
drv->buf[1] = (UINT8)(leng - 2);
senddata(drv, cnt, leng);
}
// ---- Audio
static void storemsf(UINT8 *ptr, UINT32 pos) {
UINT f;
UINT m;
f = pos % 75;
pos = pos / 75;
m = pos % 60;
pos = pos / 60;
ptr[0] = 0;
ptr[1] = (UINT8)pos;
ptr[2] = (UINT8)m;
ptr[3] = (UINT8)f;
}
// 0x42: READ SUB CHANNEL
static void atapi_cmd_readsubch(IDEDRV drv) {
SXSIDEV sxsi;
UINT subq;
UINT cmd;
UINT leng;
CDTRK trk;
UINT tracks;
UINT r;
UINT32 pos;
sxsi = sxsi_getptr(drv->sxsidrv);
if ((sxsi == NULL) || (sxsi->devtype != SXSIDEV_CDROM) ||
(!(sxsi->flag & SXSIFLAG_READY))) {
senderror(drv);
return;
}
trk = sxsicd_gettrk(sxsi, &tracks);
leng = (drv->buf[7] << 8) + drv->buf[8];
subq = drv->buf[2] & 0x40;
cmd = drv->buf[3];
drv->buf[0] = 0;
drv->buf[1] = (UINT8)drv->daflag;
drv->buf[2] = 0;
drv->buf[3] = 0;
if (!subq) {
senddata(drv, 4, leng);
return;
}
switch(cmd) {
case 0x01: // CD-ROM current pos
ZeroMemory(drv->buf + 4, 12);
drv->buf[3] = 0x12;
drv->buf[4] = 0x01;
if (drv->daflag != 0x15) {
pos = drv->dacurpos;
if ((drv->daflag == 0x12) || (drv->daflag == 0x13)) {
pos += (rand() & 7);
}
r = tracks;
while(r) {
r--;
if (trk[r].pos <= pos) {
break;
}
}
drv->buf[5] = trk[r].type;
drv->buf[6] = trk[r].track;
drv->buf[7] = 1;
storemsf(drv->buf + 8, pos + 150);
storemsf(drv->buf + 12, pos - trk[r].pos);
}
senddata(drv, 16, leng);
break;
default:
senderror(drv);
break;
}
}
// 0x43: READ TOC
static void atapi_cmd_readtoc(IDEDRV drv) {
SXSIDEV sxsi;
UINT leng;
UINT format;
CDTRK trk;
UINT tracks;
UINT datasize;
UINT8 *ptr;
UINT i;
sxsi = sxsi_getptr(drv->sxsidrv);
if ((sxsi == NULL) || (sxsi->devtype != SXSIDEV_CDROM) ||
(!(sxsi->flag & SXSIFLAG_READY))) {
senderror(drv);
return;
}
trk = sxsicd_gettrk(sxsi, &tracks);
leng = (drv->buf[7] << 8) + drv->buf[8];
format = (drv->buf[9] >> 6);
TRACEOUT(("atapi_cmd_readtoc fmt=%d leng=%d", format, leng));
switch (format) {
case 0: // track info
datasize = (tracks * 8) + 10;
drv->buf[0] = (UINT8)(datasize >> 8);
drv->buf[1] = (UINT8)(datasize >> 0);
drv->buf[2] = 1;
drv->buf[3] = (UINT8)tracks;
ptr = drv->buf + 4;
for (i=0; i<=tracks; i++) {
ptr[0] = 0;
ptr[1] = trk[i].type;
ptr[2] = trk[i].track;
ptr[3] = 0;
storemsf(ptr + 4, trk[i].pos + 150);
ptr += 8;
}
senddata(drv, (tracks * 8) + 12, leng);
break;
case 1: // multi session
ZeroMemory(drv->buf, 12);
drv->buf[1] = 0x0a;
drv->buf[2] = 0x01;
drv->buf[3] = 0x01;
drv->buf[5] = 0x14;
drv->buf[6] = 0x01;
drv->buf[10] = 0x02;
senddata(drv, 12, leng);
break;
default:
senderror(drv);
break;
}
}
// 0x47: Play Audio MSF
static void atapi_cmd_playaudiomsf(IDEDRV drv) {
UINT32 pos;
UINT32 leng;
pos = (((drv->buf[3] * 60) + drv->buf[4]) * 75) + drv->buf[5];
leng = (((drv->buf[6] * 60) + drv->buf[7]) * 75) + drv->buf[8];
if (leng > pos) {
leng -= pos;
}
else {
leng = 0;
}
if (pos >= 150) {
pos -= 150;
}
else {
pos = 0;
}
ideio.daplaying |= 1 << (drv->sxsidrv & 3);
drv->daflag = 0x11;
drv->dacurpos = pos;
drv->dalength = leng;
drv->dabufrem = 0;
cmddone(drv);
}
// 0x4B: PAUSE RESUME
static void atapi_cmd_pauseresume(IDEDRV drv) {
if (drv->buf[8] & 1) {
// resume
if (drv->daflag == 0x12) {
ideio.daplaying |= 1 << (drv->sxsidrv & 3);
drv->daflag = 0x11;
}
}
else {
// pause
if (drv->daflag == 0x11) {
ideio.daplaying &= ~(1 << (drv->sxsidrv & 3));
drv->daflag = 0x12;
}
}
cmddone(drv);
}
#endif /* SUPPORT_IDEIO */
RetroPC.NET-CVS <cvs@retropc.net>