3631 lines
80 KiB
C++
3631 lines
80 KiB
C++
/******************************************************************************/
|
|
/* Mednafen Sega Saturn Emulation Module */
|
|
/******************************************************************************/
|
|
/* cdb.cpp - CD Block Emulation
|
|
** Copyright (C) 2016 Mednafen Team
|
|
**
|
|
** This program is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU General Public License
|
|
** as published by the Free Software Foundation; either version 2
|
|
** of the License, or (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software Foundation, Inc.,
|
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
// TODO: Respect auth type with COMMAND_AUTH_DEVICE.
|
|
|
|
// TODO: Test INIT 0x00 side effects(and see if it affects CD device connection)
|
|
|
|
// TODO: edc_lec_check_and_correct
|
|
|
|
// TODO: Proper seek delays(for "Gungriffon" FMV)
|
|
|
|
// TODO: Some filesys commands(at least change dir, read file) seem to reset the saved command start and end positions(accessed via 0xFFFFFF to PLAY command) to something
|
|
// akin to 0.
|
|
|
|
// TODO: SMPC CD off/on
|
|
|
|
// TODO: Consider serializing HIRQ_PEND and HIRQ_SCDQ with command execution.
|
|
|
|
// TODO: Look into weird issue where WAIT status for Change Dir might be set to 1 slightly after CMOK IRQ is triggered...
|
|
|
|
// TODO: Test state reset on Init with SW reset.
|
|
|
|
// TODO: Scan command.
|
|
|
|
// TODO: assert()s
|
|
|
|
// TODO: Reject all commands during disc startup?
|
|
|
|
// TODO: for(unsigned i = 0; i < 5; i++) DT_ReadIntoFIFO();
|
|
|
|
// TODO: Auth and auth type.
|
|
|
|
// TODO: Test play command while filesys op is in progress.
|
|
|
|
#include "ss.h"
|
|
#include "scu.h"
|
|
#include "sound.h"
|
|
|
|
#include "cdrom/CDUtility.h"
|
|
#include "cdrom/cdromif.h"
|
|
using namespace CDUtility;
|
|
|
|
namespace MDFN_IEN_SS
|
|
{
|
|
|
|
enum
|
|
{
|
|
SECLEN__FIRST = 0,
|
|
|
|
SECLEN_2048 = 0,
|
|
SECLEN_2336 = 1,
|
|
SECLEN_2340 = 2,
|
|
SECLEN_2352 = 3,
|
|
|
|
SECLEN__LAST = 3,
|
|
};
|
|
|
|
static uint8 GetSecLen, PutSecLen;
|
|
|
|
static uint8 AuthDiscType;
|
|
enum
|
|
{
|
|
COMMAND_GET_CDSTATUS = 0x00,
|
|
COMMAND_GET_HWINFO = 0x01,
|
|
COMMAND_GET_TOC = 0x02,
|
|
COMMAND_GET_SESSINFO = 0x03,
|
|
COMMAND_INIT = 0x04,
|
|
COMMAND_OPEN = 0x05,
|
|
COMMAND_END_DATAXFER = 0x06,
|
|
|
|
COMMAND_PLAY = 0x10,
|
|
COMMAND_SEEK = 0x11,
|
|
COMMAND_SCAN = 0x12,
|
|
|
|
COMMAND_GET_SUBCODE = 0x20,
|
|
|
|
COMMAND_SET_CDDEVCONN = 0x30,
|
|
COMMAND_GET_CDDEVCONN = 0x31,
|
|
COMMAND_GET_LASTBUFDST = 0x32,
|
|
|
|
COMMAND_SET_FILTRANGE = 0x40,
|
|
COMMAND_GET_FILTRANGE = 0x41,
|
|
COMMAND_SET_FILTSUBHC = 0x42,
|
|
COMMAND_GET_FILTSUBHC = 0x43,
|
|
COMMAND_SET_FILTMODE = 0x44,
|
|
COMMAND_GET_FILTMODE = 0x45,
|
|
COMMAND_SET_FILTCONN = 0x46,
|
|
COMMAND_GET_FILTCONN = 0x47,
|
|
COMMAND_RESET_SEL = 0x48,
|
|
|
|
COMMAND_GET_BUFSIZE = 0x50,
|
|
COMMAND_GET_SECNUM = 0x51,
|
|
COMMAND_CALC_ACTSIZE = 0x52,
|
|
COMMAND_GET_ACTSIZE = 0x53,
|
|
COMMAND_GET_SECINFO = 0x54,
|
|
COMMAND_EXEC_FADSRCH = 0x55,
|
|
COMMAND_GET_FADSRCH = 0x56,
|
|
|
|
COMMAND_SET_SECLEN = 0x60,
|
|
COMMAND_GET_SECDATA = 0x61,
|
|
COMMAND_DEL_SECDATA = 0x62,
|
|
COMMAND_GETDEL_SECDATA = 0x63,
|
|
COMMAND_PUT_SECDATA = 0x64,
|
|
COMMAND_COPY_SECDATA = 0x65,
|
|
COMMAND_MOVE_SECDATA = 0x66,
|
|
COMMAND_GET_COPYERR = 0x67,
|
|
|
|
COMMAND_CHANGE_DIR = 0x70,
|
|
COMMAND_READ_DIR = 0x71,
|
|
COMMAND_GET_FSSCOPE = 0x72,
|
|
COMMAND_GET_FINFO = 0x73,
|
|
COMMAND_READ_FILE = 0x74,
|
|
COMMAND_ABORT_FILE = 0x75,
|
|
|
|
COMMAND_AUTH_DEVICE = 0xE0,
|
|
COMMAND_GET_AUTH = 0xE1
|
|
};
|
|
|
|
static MDFN_COLD void GetCommandDetails(const uint16* CD, char* s, size_t sl)
|
|
{
|
|
switch(CD[0] >> 8)
|
|
{
|
|
default:
|
|
trio_snprintf(s, sl, "UNKNOWN: 0x%04x, 0x%04x, 0x%04x, 0x%04x", CD[0], CD[1], CD[2], CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GET_CDSTATUS:
|
|
trio_snprintf(s, sl, "Get CD Status");
|
|
break;
|
|
|
|
case COMMAND_GET_HWINFO:
|
|
trio_snprintf(s, sl, "Get Hardware Info");
|
|
break;
|
|
|
|
case COMMAND_GET_TOC:
|
|
trio_snprintf(s, sl, "Get TOC");
|
|
break;
|
|
|
|
case COMMAND_GET_SESSINFO:
|
|
trio_snprintf(s, sl, "Get Session Info; Type=0x%02x", CD[0] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_INIT:
|
|
trio_snprintf(s, sl, "Initialize; Flags=0x%02x", CD[0] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_OPEN:
|
|
trio_snprintf(s, sl, "Open Tray");
|
|
break;
|
|
|
|
case COMMAND_END_DATAXFER:
|
|
trio_snprintf(s, sl, "End Data Transfer");
|
|
break;
|
|
|
|
case COMMAND_PLAY:
|
|
trio_snprintf(s, sl, "Play; Start=0x%06x, End=0x%06x, Mode=0x%02x", ((CD[0] & 0xFF) << 16) | CD[1], ((CD[2] & 0xFF) << 16) | CD[3], CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_SEEK:
|
|
// 0xFFFFFF=pause, 0=stop
|
|
trio_snprintf(s, sl, "Seek; Target=0x%06x", ((CD[0] & 0xFF) << 16) | CD[1]);
|
|
break;
|
|
|
|
case COMMAND_SCAN:
|
|
trio_snprintf(s, sl, "Scan; Direction=0x%02x", CD[0] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_GET_SUBCODE:
|
|
trio_snprintf(s, sl, "Get Subcode; Type=0x%02x", CD[0] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_SET_CDDEVCONN:
|
|
trio_snprintf(s, sl, "Set CD Device Connection; Filter=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_GET_CDDEVCONN:
|
|
trio_snprintf(s, sl, "Get CD Device Connection");
|
|
break;
|
|
|
|
case COMMAND_GET_LASTBUFDST:
|
|
trio_snprintf(s, sl, "Get Last Buffer Destination");
|
|
break;
|
|
|
|
case COMMAND_SET_FILTRANGE:
|
|
trio_snprintf(s, sl, "Set Filter Range; Filter=0x%02x, FAD=0x%06x, Count=0x%06x", CD[2] >> 8, ((CD[0] & 0xFF) << 16) | CD[1], ((CD[2] & 0xFF) << 16) | CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GET_FILTRANGE:
|
|
trio_snprintf(s, sl, "Get Filter Range; Filter=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_SET_FILTSUBHC:
|
|
trio_snprintf(s, sl, "Set Filter Subheader Conditions; Filter=0x%02x, Channel=0x%02x, Sub Mode: 0x%02x(Mask=0x%02x), Coding Info: 0x%02x(Mask=0x%02x), File: 0x%02x", (CD[2] >> 8), CD[0] & 0xFF, CD[3] >> 8, CD[1] >> 8, CD[3] & 0xFF, CD[1] & 0xFF, CD[2] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_GET_FILTSUBHC:
|
|
trio_snprintf(s, sl, "Get Filter Subheader Conditions; Filter=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_SET_FILTMODE:
|
|
trio_snprintf(s, sl, "Set Filter Mode; Filter=0x%02x, Mode=0x%02x", (CD[2] >> 8), CD[0] & 0xFF);
|
|
break;
|
|
|
|
case COMMAND_GET_FILTMODE:
|
|
trio_snprintf(s, sl, "Get Filter Mode; Filter=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_SET_FILTCONN:
|
|
trio_snprintf(s, sl, "Set Filter Connection; Filter=0x%02x, Flags=0x%02x, True=0x%02x, False=0x%02x", (CD[2] >> 8), (CD[0] & 0xFF), (CD[1] >> 8), (CD[1] & 0xFF));
|
|
break;
|
|
|
|
case COMMAND_GET_FILTCONN:
|
|
trio_snprintf(s, sl, "Get Filter Connection; Filter=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_RESET_SEL:
|
|
trio_snprintf(s, sl, "Reset Selector; Flags=0x%02x, pn=0x%04x", CD[0] & 0xFF, CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_GET_BUFSIZE:
|
|
trio_snprintf(s, sl, "Get Buffer Size");
|
|
break;
|
|
|
|
case COMMAND_GET_SECNUM:
|
|
trio_snprintf(s, sl, "Get Sector Number; Source=0x%02x", CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_CALC_ACTSIZE:
|
|
trio_snprintf(s, sl, "Calculate Actual Size; Source=0x%02x[0x%04x], Count=0x%02x", CD[2] >> 8, CD[1], CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GET_ACTSIZE:
|
|
trio_snprintf(s, sl, "Get Actual Size");
|
|
break;
|
|
|
|
case COMMAND_GET_SECINFO:
|
|
trio_snprintf(s, sl, "Get Sector Info; Source=0x%02x[0x%04x]", CD[2] >> 8, CD[1]);
|
|
break;
|
|
|
|
case COMMAND_EXEC_FADSRCH:
|
|
trio_snprintf(s, sl, "Execute FAD Search; Source=0x%02x[0x%04x], FAD=0x%06x", CD[2] >> 8, CD[1], ((CD[2] & 0xFF) << 16) | CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GET_FADSRCH:
|
|
trio_snprintf(s, sl, "Get FAD Search Results");
|
|
break;
|
|
|
|
case COMMAND_SET_SECLEN:
|
|
trio_snprintf(s, sl, "Set Sector Length; Get: 0x%02x, Put: 0x%02x", (CD[0] & 0xFF), (CD[1] >> 8));
|
|
break;
|
|
|
|
case COMMAND_GET_SECDATA:
|
|
trio_snprintf(s, sl, "Get Sector Data; Source=0x%02x[0x%04x], Count=0x%04x", CD[2] >> 8, CD[1], CD[3]);
|
|
break;
|
|
|
|
case COMMAND_DEL_SECDATA:
|
|
trio_snprintf(s, sl, "Delete Sector Data; Source=0x%02x[0x%04x], Count=0x%04x", CD[2] >> 8, CD[1], CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GETDEL_SECDATA:
|
|
trio_snprintf(s, sl, "Get and Delete Sector Data; Source=0x%02x[0x%04x], Count=0x%04x", CD[2] >> 8, CD[1], CD[3]);
|
|
break;
|
|
|
|
case COMMAND_PUT_SECDATA:
|
|
trio_snprintf(s, sl, "Put Sector Data; Filter=0x%02x, Count=0x%04x", CD[2] >> 8, CD[3]);
|
|
break;
|
|
|
|
case COMMAND_COPY_SECDATA:
|
|
trio_snprintf(s, sl, "Copy Sector Data; Source=0x%02x[0x%04x], Dest=0x%02x, Count=0x%04x", CD[2] >> 8, CD[1], CD[0] & 0xFF, CD[3]);
|
|
break;
|
|
|
|
case COMMAND_MOVE_SECDATA:
|
|
trio_snprintf(s, sl, "Move Sector Data; Source=0x%02x[0x%04x], Dest=0x%02x, Count=0x%04x", CD[2] >> 8, CD[1], CD[0] & 0xFF, CD[3]);
|
|
break;
|
|
|
|
case COMMAND_GET_COPYERR:
|
|
trio_snprintf(s, sl, "Get Copy Error");
|
|
break;
|
|
|
|
case COMMAND_CHANGE_DIR:
|
|
trio_snprintf(s, sl, "Change Directory; ID=0x%06x, Filter: 0x%02x", ((CD[2] & 0xFF) << 16) | CD[3], (CD[2] >> 8));
|
|
break;
|
|
|
|
case COMMAND_READ_DIR:
|
|
trio_snprintf(s, sl, "Read Directory; ID=0x%06x, Filter=0x%02x", ((CD[2] & 0xFF) << 16) | CD[3], (CD[2] >> 8));
|
|
break;
|
|
|
|
case COMMAND_GET_FSSCOPE:
|
|
trio_snprintf(s, sl, "Get Filesystem Scope");
|
|
break;
|
|
|
|
case COMMAND_GET_FINFO:
|
|
trio_snprintf(s, sl, "Get File Info; ID=0x%06x", ((CD[2] & 0xFF) << 16) | CD[3]);
|
|
break;
|
|
|
|
case COMMAND_READ_FILE:
|
|
trio_snprintf(s, sl, "Read File; Offset=0x%06x, ID=0x%06x, Filter=0x%02x\n", ((CD[0] & 0xFF) << 16) | CD[1], ((CD[2] & 0xFF) << 16) | CD[3], CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_ABORT_FILE:
|
|
trio_snprintf(s, sl, "Abort File");
|
|
break;
|
|
|
|
case COMMAND_AUTH_DEVICE:
|
|
trio_snprintf(s, sl, "Authenticate Device; Type=0x%04x, Filter=0x%02x", CD[1], CD[2] >> 8);
|
|
break;
|
|
|
|
case COMMAND_GET_AUTH:
|
|
trio_snprintf(s, sl, "Get Device Authentication Status");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
enum
|
|
{
|
|
STATUS_BUSY = 0x00,
|
|
STATUS_PAUSE = 0x01,
|
|
STATUS_STANDBY = 0x02,
|
|
STATUS_PLAY = 0x03,
|
|
STATUS_SEEK = 0x04,
|
|
STATUS_SCAN = 0x05,
|
|
STATUS_OPEN = 0x06,
|
|
STATUS_NODISC = 0x07,
|
|
STATUS_RETRY = 0x08,
|
|
STATUS_ERROR = 0x09,
|
|
STATUS_FATAL = 0x0A,
|
|
|
|
//
|
|
STATUS_PERIODIC = 0x20,
|
|
STATUS_DTREQ = 0x40,
|
|
STATUS_WAIT = 0x80,
|
|
|
|
//
|
|
STATUS_REJECTED = 0xFF
|
|
};
|
|
|
|
enum
|
|
{
|
|
HIRQ_CMOK = 0x0001, // command ok?
|
|
HIRQ_DRDY = 0x0002, // data transfer ready
|
|
HIRQ_CSCT = 0x0004, // 1 sector read complete?
|
|
HIRQ_BFUL = 0x0008, // buffer full
|
|
HIRQ_PEND = 0x0010, // play end (FIXME: Status must indicate a "Pause" state at the time this is triggered)
|
|
HIRQ_DCHG = 0x0020, // disc change
|
|
HIRQ_ESEL = 0x0040, // end of selector something?
|
|
HIRQ_EHST = 0x0080, // end of host I/O?
|
|
HIRQ_ECPY = 0x0100, // end of copy/move
|
|
HIRQ_EFLS = 0x0200, // end of filesystem something?
|
|
HIRQ_SCDQ = 0x0400, // Sub-channel Q update complete?
|
|
|
|
HIRQ_MPED = 0x0800,
|
|
HIRQ_MPCM = 0x1000,
|
|
HIRQ_MPST = 0x2000
|
|
};
|
|
static uint16 HIRQ, HIRQ_Mask;
|
|
static uint16 CData[4];
|
|
static uint16 Results[4];
|
|
static bool CommandPending;
|
|
|
|
static uint8 CDDevConn;
|
|
static uint8 LastBufDest;
|
|
|
|
enum { NumBuffers = 0xC8 };
|
|
static struct BufferT
|
|
{
|
|
uint8 Data[2352];
|
|
uint8 Prev;
|
|
uint8 Next;
|
|
} Buffers[NumBuffers];
|
|
|
|
static struct FilterS
|
|
{
|
|
enum
|
|
{
|
|
MODE_SEL_FILE = 0x01,
|
|
MODE_SEL_CHANNEL = 0x02,
|
|
MODE_SEL_SUBMODE = 0x04,
|
|
MODE_SEL_CINFO = 0x08,
|
|
MODE_SEL_SHREV = 0x10, // Reverse sub-header conditions
|
|
MODE_SEL_FADR = 0x40,
|
|
MODE_INIT = 0x80
|
|
};
|
|
|
|
uint8 Mode;
|
|
uint8 TrueConn;
|
|
uint8 FalseConn;
|
|
//
|
|
uint32 FAD;
|
|
uint32 Range;
|
|
|
|
uint8 Channel;
|
|
uint8 File;
|
|
|
|
uint8 SubMode;
|
|
uint8 SubModeMask;
|
|
|
|
uint8 CInfo;
|
|
uint8 CInfoMask;
|
|
} Filters[0x18];
|
|
|
|
static struct
|
|
{
|
|
uint8 FirstBuf;
|
|
uint8 LastBuf;
|
|
uint8 Count;
|
|
} Partitions[0x18];
|
|
|
|
static uint8 FirstFreeBuf;
|
|
static uint8 FreeBufferCount;
|
|
|
|
static struct
|
|
{
|
|
uint32 fad;
|
|
uint16 spos;
|
|
uint8 pnum;
|
|
} FADSearch;
|
|
|
|
static uint32 CalcedActualSize;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
static bool TrayOpen;
|
|
static CDIF* Cur_CDIF;
|
|
static CDUtility::TOC toc;
|
|
static sscpu_timestamp_t lastts;
|
|
static int32 CommandPhase;
|
|
//static bool CommandYield;
|
|
static int64 CommandClockCounter;
|
|
static uint32 CDB_ClockRatio;
|
|
|
|
static struct
|
|
{
|
|
uint8 Command;
|
|
uint16 CD[4];
|
|
} CTR;
|
|
|
|
static struct
|
|
{
|
|
bool Active;
|
|
bool Writing;
|
|
bool NeedBufFree;
|
|
|
|
unsigned CurBufIndex;
|
|
unsigned BufCount;
|
|
|
|
unsigned InBufOffs;
|
|
unsigned InBufCounter;
|
|
|
|
unsigned TotalCounter;
|
|
|
|
uint8 FNum;
|
|
|
|
uint16 FIFO[6];
|
|
uint8 FIFO_RP;
|
|
uint8 FIFO_WP;
|
|
uint8 FIFO_In;
|
|
|
|
uint8 BufList[NumBuffers];
|
|
} DT;
|
|
|
|
static uint16 StandbyTime;
|
|
static uint8 ECCEnable;
|
|
static uint8 RetryCount;
|
|
|
|
static bool ResultsRead;
|
|
|
|
static int32 SeekIndexPhase;
|
|
static uint32 CurSector;
|
|
static int32 DrivePhase;
|
|
|
|
enum
|
|
{
|
|
DRIVEPHASE_STOPPED = 0,
|
|
DRIVEPHASE_PLAY,
|
|
|
|
DRIVEPHASE_SEEK_START,
|
|
DRIVEPHASE_SEEK,
|
|
DRIVEPHASE_SCAN,
|
|
|
|
DRIVEPHASE_EJECTED0,
|
|
DRIVEPHASE_EJECTED1,
|
|
DRIVEPHASE_EJECTED_WAITING,
|
|
DRIVEPHASE_STARTUP,
|
|
|
|
DRIVEPHASE_RESETTING
|
|
};
|
|
static int64 DriveCounter;
|
|
static int64 PeriodicIdleCounter;
|
|
enum { PeriodicIdleCounter_Reload = (int64)187065 << 32 };
|
|
|
|
static uint8 PlayRepeatCounter;
|
|
static uint8 CurPlayRepeat;
|
|
|
|
static uint32 CurPlayStart;
|
|
static uint32 CurPlayEnd;
|
|
static uint32 PlayEndIRQType;
|
|
//static uint32 PlayEndIRQPending;
|
|
|
|
static uint32 PlayCmdStartPos, PlayCmdEndPos;
|
|
static uint8 PlayCmdRepCnt;
|
|
|
|
enum { CDDABuf_PrefillCount = 4 };
|
|
enum { CDDABuf_MaxCount = 4 + 588 + 4 };
|
|
static uint16 CDDABuf[CDDABuf_MaxCount][2];
|
|
static uint32 CDDABuf_RP, CDDABuf_WP;
|
|
static uint32 CDDABuf_Count;
|
|
|
|
static uint8 SecPreBuf[2352 + 96];
|
|
static int SecPreBuf_In;
|
|
|
|
static uint8 TOC_Buffer[(99 + 3) * 4];
|
|
|
|
static struct
|
|
{
|
|
uint8 status;
|
|
uint32 fad;
|
|
uint32 rel_fad;
|
|
uint8 ctrl_adr;
|
|
uint8 idx;
|
|
uint8 tno;
|
|
|
|
bool is_cdrom;
|
|
} CurPosInfo;
|
|
|
|
// Higher-level:
|
|
static uint8 SubCodeQBuf[10];
|
|
static uint8 SubCodeRWBuf[24];
|
|
|
|
// SubQBuf for ADR=1 only for now.
|
|
static uint8 SubQBuf[0xC];
|
|
static uint8 SubQBuf_Safe[0xC];
|
|
static bool SubQBuf_Safe_Valid;
|
|
|
|
static bool DecodeSubQ(uint8 *subpw)
|
|
{
|
|
uint8 tmp_q[0xC];
|
|
|
|
memset(tmp_q, 0, 0xC);
|
|
|
|
for(int i = 0; i < 96; i++)
|
|
tmp_q[i >> 3] |= ((subpw[i] & 0x40) >> 6) << (7 - (i & 7));
|
|
|
|
if((tmp_q[0] & 0xF) == 1)
|
|
{
|
|
memcpy(SubQBuf, tmp_q, 0xC);
|
|
|
|
if(subq_check_checksum(tmp_q))
|
|
{
|
|
memcpy(SubQBuf_Safe, tmp_q, 0xC);
|
|
SubQBuf_Safe_Valid = true;
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
static void ResetBuffers(void)
|
|
{
|
|
Buffers[0].Prev = 0xFF;
|
|
Buffers[0].Next = 1;
|
|
for(unsigned i = 1; i < (NumBuffers - 1); i++)
|
|
{
|
|
Buffers[i].Prev = i - 1;
|
|
Buffers[i].Next = i + 1;
|
|
}
|
|
Buffers[NumBuffers - 1].Prev = NumBuffers - 1 - 1;
|
|
Buffers[NumBuffers - 1].Next = 0xFF;
|
|
|
|
FirstFreeBuf = 0;
|
|
FreeBufferCount = NumBuffers;
|
|
|
|
for(unsigned i = 0; i < 0x18; i++)
|
|
{
|
|
Partitions[i].FirstBuf = 0xFF;
|
|
Partitions[i].LastBuf = 0xFF;
|
|
Partitions[i].Count = 0;
|
|
}
|
|
}
|
|
|
|
static void Filter_ResetCond(const unsigned fnum)
|
|
{
|
|
auto& f = Filters[fnum];
|
|
|
|
f.Mode = 0;
|
|
|
|
f.FAD = 0;
|
|
f.Range = 0;
|
|
|
|
f.Channel = 0;
|
|
f.File = 0;
|
|
|
|
f.SubMode = 0;
|
|
f.SubModeMask = 0;
|
|
|
|
f.CInfo = 0;
|
|
f.CInfoMask = 0;
|
|
}
|
|
|
|
static uint8 Buffer_Allocate(const bool zero_clear)
|
|
{
|
|
const unsigned bfsidx = FirstFreeBuf;
|
|
assert(bfsidx != 0xFF && FreeBufferCount > 0);
|
|
|
|
if(zero_clear)
|
|
memset(Buffers[bfsidx].Data, 0x00, sizeof(Buffers[bfsidx].Data));
|
|
|
|
if(Buffers[bfsidx].Prev == 0xFF)
|
|
FirstFreeBuf = Buffers[bfsidx].Next;
|
|
else
|
|
Buffers[Buffers[bfsidx].Prev].Next = Buffers[bfsidx].Next;
|
|
|
|
if(Buffers[bfsidx].Next == 0xFF)
|
|
{
|
|
|
|
}
|
|
else
|
|
Buffers[Buffers[bfsidx].Next].Prev = Buffers[bfsidx].Prev;
|
|
|
|
FreeBufferCount--;
|
|
|
|
//
|
|
Buffers[bfsidx].Prev = 0xFF;
|
|
Buffers[bfsidx].Next = 0xFF;
|
|
//
|
|
|
|
return bfsidx;
|
|
}
|
|
|
|
// Must not alter "Data" member, as it may be used while "free"'d.
|
|
static void Buffer_Free(const uint8 bfsidx)
|
|
{
|
|
assert((FirstFreeBuf == 0xFF && FreeBufferCount == 0) || (FirstFreeBuf != 0xFF && FreeBufferCount > 0));
|
|
assert(Buffers[bfsidx].Next == 0xFF && Buffers[bfsidx].Prev == 0xFF);
|
|
|
|
Buffers[bfsidx].Prev = 0xFF;
|
|
Buffers[bfsidx].Next = FirstFreeBuf;
|
|
|
|
if(FirstFreeBuf != 0xFF)
|
|
{
|
|
assert(Buffers[FirstFreeBuf].Prev == 0xFF);
|
|
Buffers[FirstFreeBuf].Prev = bfsidx;
|
|
}
|
|
|
|
FreeBufferCount++;
|
|
FirstFreeBuf = bfsidx;
|
|
}
|
|
|
|
static void Partition_LinkBuffer(const unsigned pnum, const unsigned bfsidx)
|
|
{
|
|
assert(Buffers[bfsidx].Next == 0xFF && Buffers[bfsidx].Prev == 0xFF);
|
|
|
|
Buffers[bfsidx].Next = 0xFF;
|
|
|
|
if(Partitions[pnum].FirstBuf == 0xFF)
|
|
{
|
|
assert(Partitions[pnum].LastBuf == 0xFF);
|
|
Partitions[pnum].FirstBuf = bfsidx;
|
|
Partitions[pnum].LastBuf = bfsidx;
|
|
Buffers[bfsidx].Prev = 0xFF;
|
|
}
|
|
else
|
|
{
|
|
assert(Partitions[pnum].LastBuf != 0xFF);
|
|
Buffers[Partitions[pnum].LastBuf].Next = bfsidx;
|
|
Buffers[bfsidx].Prev = Partitions[pnum].LastBuf;
|
|
}
|
|
|
|
Partitions[pnum].LastBuf = bfsidx;
|
|
Partitions[pnum].Count++;
|
|
}
|
|
|
|
static int Partition_GetBuffer(unsigned pnum, unsigned rbi)
|
|
{
|
|
for(unsigned ii = Partitions[pnum].FirstBuf; ii != 0xFF; ii = Buffers[ii].Next)
|
|
{
|
|
if(!rbi)
|
|
return ii;
|
|
|
|
rbi--;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Must not alter "Data" member, as it may be used while unlinked.
|
|
static void Partition_UnlinkBuffer(unsigned pnum, unsigned bfsidx)
|
|
{
|
|
assert(Partitions[pnum].Count > 0);
|
|
|
|
Partitions[pnum].Count--;
|
|
|
|
if(Buffers[bfsidx].Prev == 0xFF)
|
|
{
|
|
assert(Partitions[pnum].FirstBuf == bfsidx);
|
|
Partitions[pnum].FirstBuf = Buffers[bfsidx].Next;
|
|
}
|
|
else
|
|
{
|
|
assert(Partitions[pnum].FirstBuf != bfsidx);
|
|
Buffers[Buffers[bfsidx].Prev].Next = Buffers[bfsidx].Next;
|
|
}
|
|
|
|
if(Buffers[bfsidx].Next == 0xFF)
|
|
{
|
|
assert(Partitions[pnum].LastBuf == bfsidx);
|
|
Partitions[pnum].LastBuf = Buffers[bfsidx].Prev;
|
|
}
|
|
else
|
|
{
|
|
assert(Partitions[pnum].LastBuf != bfsidx);
|
|
Buffers[Buffers[bfsidx].Next].Prev = Buffers[bfsidx].Prev;
|
|
}
|
|
|
|
//
|
|
Buffers[bfsidx].Prev = 0xFF;
|
|
Buffers[bfsidx].Next = 0xFF;
|
|
}
|
|
|
|
static void SetCDDeviceConn(const uint8 fnum)
|
|
{
|
|
for(unsigned fs = 0; fs < 0x18; fs++)
|
|
if(Filters[fs].FalseConn == fnum)
|
|
Filters[fs].FalseConn = 0xFF;
|
|
|
|
CDDevConn = fnum;
|
|
}
|
|
|
|
static void Filter_SetRange(const uint8 fnum, const uint32 fad, const uint32 range)
|
|
{
|
|
Filters[fnum].FAD = fad;
|
|
Filters[fnum].Range = range;
|
|
}
|
|
|
|
static void Filter_SetTrueConn(const uint8 fnum, const uint8 tconn)
|
|
{
|
|
Filters[fnum].TrueConn = tconn;
|
|
}
|
|
|
|
static void Filter_SetFalseConn(const uint8 fnum, const uint8 fconn)
|
|
{
|
|
if(CDDevConn == fconn)
|
|
CDDevConn = 0xFF;
|
|
|
|
for(unsigned fs = 0; fs < 0x18; fs++)
|
|
if(Filters[fs].FalseConn == fconn)
|
|
Filters[fs].FalseConn = 0xFF;
|
|
|
|
Filters[fnum].FalseConn = fconn;
|
|
}
|
|
|
|
static void Partition_Clear(const unsigned pnum)
|
|
{
|
|
while(Partitions[pnum].Count > 0)
|
|
{
|
|
const unsigned bfi = Partitions[pnum].FirstBuf;
|
|
|
|
Partition_UnlinkBuffer(pnum, bfi);
|
|
Buffer_Free(bfi);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
static struct FileInfoS
|
|
{
|
|
uint8 fad_be[4];
|
|
uint8 size_be[4];
|
|
|
|
INLINE uint32 fad(void) const { return MDFN_de32msb(fad_be); }
|
|
INLINE uint32 size(void) const { return MDFN_de32msb(size_be); }
|
|
|
|
uint8 unit_size;
|
|
uint8 gap_size;
|
|
uint8 fnum;
|
|
uint8 attr;
|
|
} __attribute__((__packed__)) FileInfo[256];
|
|
static bool FileInfoValid;
|
|
|
|
enum
|
|
{
|
|
FATTR_DIR = 0x02,
|
|
FATTR_XA_M2F1 = 0x08,
|
|
FATTR_XA_M2F2 = 0x10,
|
|
FATTR_XA_ILEAVE = 0x20,
|
|
FATTR_XA_CDDA = 0x40,
|
|
FATTR_XA_DIR = 0x80
|
|
};
|
|
|
|
|
|
static FileInfoS RootDirInfo;
|
|
static bool RootDirInfoValid;
|
|
|
|
static struct
|
|
{
|
|
bool Active;
|
|
bool DoAuth;
|
|
bool Abort;
|
|
uint8 pnum;
|
|
|
|
uint32 CurDirFAD;
|
|
|
|
uint8 FileInfoValidCount; // 0 ... 254
|
|
uint32 FileInfoOffs; // 2 ... whatever
|
|
uint32 FileInfoOnDiscCount; // 0 ... infinities
|
|
|
|
uint32 Phase;
|
|
|
|
uint8 pbuf[2048];
|
|
uint32 pbuf_offs;
|
|
uint32 pbuf_read_i;
|
|
|
|
uint32 total_counter;
|
|
uint32 total_max;
|
|
|
|
uint8 record[256];
|
|
uint32 record_counter;
|
|
|
|
uint32 finfo_read_start_offs;
|
|
uint32 finfo_offs;
|
|
} FLS;
|
|
|
|
|
|
enum { FLSPhaseBias = __COUNTER__ + 1 };
|
|
|
|
#define FLS_PROLOGUE switch(FLS.Phase + FLSPhaseBias) { for(;;) { default: case __COUNTER__: ;
|
|
#define FLS_EPILOGUE } } FLSGetOut:;
|
|
|
|
#define FLS_YIELD { \
|
|
FLS.Phase = __COUNTER__ - FLSPhaseBias + 1; \
|
|
goto FLSGetOut; \
|
|
case __COUNTER__:; \
|
|
}
|
|
|
|
#define FLS_WAIT_UNTIL_COND(n) { \
|
|
case __COUNTER__: \
|
|
if(!(n)) \
|
|
{ \
|
|
FLS.Phase = __COUNTER__ - FLSPhaseBias - 1; \
|
|
goto FLSGetOut; \
|
|
} \
|
|
}
|
|
|
|
#define FLS_WAIT_GRAB_BUF \
|
|
FLS_WAIT_UNTIL_COND(Partitions[FLS.pnum].Count > 0); \
|
|
{ \
|
|
const unsigned bfi = Partitions[FLS.pnum].FirstBuf; \
|
|
const uint8* const dptr = Buffers[bfi].Data; \
|
|
Partition_UnlinkBuffer(FLS.pnum, bfi); \
|
|
memcpy(FLS.pbuf, &dptr[(dptr[15] == 0x2) ? 24 : 16], 2048); \
|
|
Buffer_Free(bfi); \
|
|
}
|
|
|
|
#define FLS_READ(buffer, count) \
|
|
for(FLS.pbuf_read_i = 0; FLS.pbuf_read_i < (count); FLS.pbuf_read_i++) \
|
|
{ \
|
|
if(FLS.pbuf_offs == 0) \
|
|
{ \
|
|
FLS_WAIT_GRAB_BUF; \
|
|
} \
|
|
if((buffer) != NULL) \
|
|
(buffer)[FLS.pbuf_read_i] = FLS.pbuf[FLS.pbuf_offs]; \
|
|
FLS.pbuf_offs = (FLS.pbuf_offs + 1) % 2048; \
|
|
FLS.total_counter++; \
|
|
}
|
|
|
|
static void ReadRecord(FileInfoS* fi, const uint8* rr)
|
|
{
|
|
const uint8 rec_len = rr[0];
|
|
const uint8 fi_len = rr[32];
|
|
|
|
MDFN_en32msb(fi->fad_be, 150 + MDFN_de32msb(&rr[6]));
|
|
MDFN_en32msb(fi->size_be, MDFN_de32msb(&rr[14]));
|
|
|
|
fi->attr = rr[25] & 0x2;
|
|
fi->unit_size = rr[26];
|
|
fi->gap_size = rr[27];
|
|
fi->fnum = 0;
|
|
|
|
int su_offs = 33 + (fi_len | 1);
|
|
int su_len = (rec_len - su_offs);
|
|
|
|
//printf("%d %d %d %d\n", rec_len, fi_len, su_offs, su_len);
|
|
|
|
if(su_len >= 14 && (su_offs + su_len) <= 256)
|
|
{
|
|
if(rr[su_offs + 6] == 'X' && rr[su_offs + 7] == 'A')
|
|
{
|
|
fi->attr |= rr[su_offs + 4] & 0xF8;
|
|
fi->fnum = rr[su_offs + 8];
|
|
}
|
|
}
|
|
|
|
//printf("Meow: fad=%08x size=%08x attr=%02x us=%02x gs=%02x fnum=%02x %s\n", fi->fad(), fi->size(), fi->attr, fi->unit_size, fi->gap_size, fi->fnum, &FLS.record[33]);
|
|
}
|
|
|
|
static void ClearPendingSec(void);
|
|
|
|
static bool FLS_Run(void)
|
|
{
|
|
bool ret = false;
|
|
//printf("%d, %d\n", Partitions[FLS.pnum].Count, FreeBufferCount);
|
|
|
|
if(FLS.Abort)
|
|
{
|
|
if(FLS.Active)
|
|
{
|
|
SS_DBG(SS_DBG_CDB, "[CDB] FLS Abort: %d %d\n", FLS.Active, FLS.DoAuth);
|
|
}
|
|
|
|
goto Abort;
|
|
}
|
|
|
|
//
|
|
FLS_PROLOGUE;
|
|
//
|
|
if(FLS.Active)
|
|
{
|
|
if(FLS.DoAuth)
|
|
{
|
|
RootDirInfoValid = false;
|
|
AuthDiscType = 0x04; // FIXME: //0x02;
|
|
|
|
for(;;)
|
|
{
|
|
static const char stdid[5] = { 'C', 'D', '0', '0', '1' };
|
|
FLS_WAIT_GRAB_BUF;
|
|
|
|
if(memcmp(FLS.pbuf + 1, stdid, 5) || FLS.pbuf[0] == 0xFF)
|
|
break;
|
|
else if(FLS.pbuf[0] == 0x01) // PVD
|
|
{
|
|
//printf("MEOW MEOW:");
|
|
//for(unsigned i = 0; i < 64; i++)
|
|
// printf("%02x ", FLS.pbuf[156 + i]);
|
|
//printf("\n");
|
|
|
|
ReadRecord(&RootDirInfo, &FLS.pbuf[156]);
|
|
RootDirInfoValid = true;
|
|
break;
|
|
}
|
|
}
|
|
SetCDDeviceConn(0xFF);
|
|
}
|
|
else
|
|
{
|
|
FileInfoValid = false;
|
|
//
|
|
//
|
|
FLS.total_counter = 0;
|
|
FLS.pbuf_offs = 0;
|
|
FLS.FileInfoValidCount = 0;
|
|
FLS.record_counter = 0;
|
|
FLS.finfo_offs = 0;
|
|
|
|
//printf("Start\n");
|
|
while(FLS.total_counter < FLS.total_max)
|
|
{
|
|
memset(FLS.record, 0, sizeof(FLS.record));
|
|
|
|
FLS_READ(&FLS.record[0], 1);
|
|
if(!FLS.record[0])
|
|
continue;
|
|
FLS_READ(&FLS.record[1], FLS.record[0] - 1);
|
|
|
|
if(FLS.finfo_offs < 256)
|
|
{
|
|
if(FLS.record_counter < 2 || FLS.record_counter >= FLS.FileInfoOffs)
|
|
{
|
|
ReadRecord(&FileInfo[FLS.finfo_offs], FLS.record);
|
|
|
|
FLS.finfo_offs++;
|
|
|
|
if(FLS.record_counter >= 2)
|
|
FLS.FileInfoValidCount++;
|
|
}
|
|
FLS.record_counter++;
|
|
}
|
|
}
|
|
|
|
FLS.FileInfoOnDiscCount = FLS.record_counter;
|
|
//
|
|
//
|
|
FileInfoValid = true;
|
|
}
|
|
|
|
Partition_Clear(FLS.pnum); // Place before Abort:;
|
|
//
|
|
//
|
|
//
|
|
Abort:;
|
|
FLS.Active = false;
|
|
FLS.DoAuth = false;
|
|
FLS.Abort = false;
|
|
//
|
|
// Pause
|
|
//
|
|
PlayEndIRQType = 0;
|
|
CurPlayStart = 0x800000;
|
|
CurPlayEnd = 0x800000;
|
|
CurPlayRepeat = 0;
|
|
//
|
|
//
|
|
//
|
|
|
|
ret = true;
|
|
}
|
|
FLS_YIELD;
|
|
//
|
|
FLS_EPILOGUE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Sector size can change in the middle of the transfer, and takes effect around sector buffer boundaries
|
|
//
|
|
static void DT_SetIBOffsCount(const uint8* sd)
|
|
{
|
|
if(DT.Writing)
|
|
{
|
|
static const unsigned DTW_OffsTab[4] = { 12, 8, 6, 0 };
|
|
static const unsigned DTW_CountTab[4] = { 1024, 1168, 1170, 1176 };
|
|
|
|
DT.InBufOffs = DTW_OffsTab[PutSecLen];
|
|
DT.InBufCounter = DTW_CountTab[PutSecLen];
|
|
}
|
|
else switch(GetSecLen)
|
|
{
|
|
case SECLEN_2048:
|
|
if(sd[12 + 3] == 0x1) // Mode 1
|
|
{
|
|
DT.InBufOffs = 8;
|
|
DT.InBufCounter = 1024;
|
|
}
|
|
else // Mode 2
|
|
{
|
|
if(sd[16 + 2] & 0x20) // Form 2
|
|
{
|
|
DT.InBufOffs = 12;
|
|
DT.InBufCounter = 1162;
|
|
}
|
|
else // Form 1
|
|
{
|
|
DT.InBufOffs = 12;
|
|
DT.InBufCounter = 1024;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SECLEN_2336:
|
|
DT.InBufOffs = 8;
|
|
DT.InBufCounter = 1168;
|
|
break;
|
|
|
|
case SECLEN_2340:
|
|
DT.InBufOffs = 6;
|
|
DT.InBufCounter = 1170;
|
|
break;
|
|
|
|
case SECLEN_2352:
|
|
DT.InBufOffs = 0;
|
|
DT.InBufCounter = 1176;
|
|
break;
|
|
}
|
|
|
|
if(!DT.Writing)
|
|
{
|
|
const uint32 fad = AMSF_to_ABA(BCD_to_U8(sd[12 + 0]), BCD_to_U8(sd[12 + 1]), BCD_to_U8(sd[12 + 2]));
|
|
SS_DBG(SS_DBG_CDB, "[CDB] DT FAD: %08x --- %d %d\n", fad, DT.InBufOffs, DT.InBufCounter);
|
|
}
|
|
}
|
|
|
|
static void DT_ReadIntoFIFO(void)
|
|
{
|
|
uint16 tmp;
|
|
|
|
if(MDFN_UNLIKELY(DT.BufList[DT.CurBufIndex] >= 0xF0))
|
|
{
|
|
const uint8 t = DT.BufList[DT.CurBufIndex];
|
|
|
|
if(t == 0xFF)
|
|
tmp = MDFN_de16msb(&TOC_Buffer[DT.InBufOffs << 1]);
|
|
else if(t == 0xFE)
|
|
tmp = MDFN_de16msb(&SubCodeQBuf[DT.InBufOffs << 1]);
|
|
else if(t == 0xFD)
|
|
tmp = MDFN_de16msb(&SubCodeRWBuf[DT.InBufOffs << 1]);
|
|
else
|
|
tmp = MDFN_de16msb((uint8*)FileInfo + (DT.InBufOffs << 1));
|
|
}
|
|
else
|
|
tmp = MDFN_de16msb(&Buffers[DT.BufList[DT.CurBufIndex]].Data[DT.InBufOffs << 1]);
|
|
|
|
//printf("%02x %02x\n", DT.BufList[DT.CurBufIndex], DT.CurBufIndex);
|
|
DT.FIFO[DT.FIFO_WP] = tmp;
|
|
DT.FIFO_WP = (DT.FIFO_WP + 1) % (sizeof(DT.FIFO) / sizeof(DT.FIFO[0]));
|
|
DT.FIFO_In++;
|
|
DT.InBufOffs++;
|
|
DT.InBufCounter--;
|
|
DT.TotalCounter++;
|
|
|
|
if(!DT.InBufCounter)
|
|
{
|
|
DT.CurBufIndex++;
|
|
if(DT.CurBufIndex < DT.BufCount)
|
|
{
|
|
DT_SetIBOffsCount(Buffers[DT.BufList[DT.CurBufIndex]].Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Ratio between SH-2 clock and sound subsystem 68K clock (sound clock / 2)
|
|
// (needs to match the algorithm precision in sound.cpp, for proper CD-DA stream synch without having to get into more complicated designs)
|
|
void CDB_SetClockRatio(uint32 ratio)
|
|
{
|
|
CDB_ClockRatio = ratio;
|
|
}
|
|
|
|
static void SWReset(void)
|
|
{
|
|
GetSecLen = SECLEN_2048;
|
|
PutSecLen = SECLEN_2048;
|
|
|
|
CDDevConn = 0xFF;
|
|
|
|
LastBufDest = 0xFF;
|
|
|
|
memset(&DT, 0, sizeof(DT));
|
|
DT.Active = false;
|
|
|
|
for(unsigned i = 0; i < 0x18; i++)
|
|
{
|
|
auto& f = Filters[i];
|
|
|
|
f.TrueConn = i;
|
|
f.FalseConn = 0xFF;
|
|
|
|
Filter_ResetCond(i);
|
|
}
|
|
|
|
ResetBuffers();
|
|
|
|
FADSearch.fad = 0;
|
|
FADSearch.spos = 0;
|
|
FADSearch.pnum = 0;
|
|
|
|
CalcedActualSize = 0;
|
|
|
|
PlayEndIRQType = 0;
|
|
CurPlayEnd = 0x800000;
|
|
CurPlayRepeat = 0;
|
|
ClearPendingSec();
|
|
//
|
|
//
|
|
//
|
|
memset(&FLS, 0, sizeof(FLS));
|
|
FileInfoValid = false;
|
|
FLS.Phase = 0;
|
|
}
|
|
|
|
void CDB_ResetCD(void) // TODO
|
|
{
|
|
PeriodicIdleCounter = 0x7FFFFFFFFFFFFFFFLL;
|
|
DrivePhase = DRIVEPHASE_RESETTING;
|
|
DriveCounter = 0x7FFFFFFFFFFFFFFFLL;
|
|
|
|
Results[0] = 0;
|
|
Results[1] = 0;
|
|
Results[2] = 0;
|
|
Results[3] = 0;
|
|
ResultsRead = true;
|
|
|
|
CommandPhase = -1;
|
|
CommandClockCounter = 0;
|
|
// TODO: Set next event, timestamp + 1.
|
|
}
|
|
|
|
void CDB_SetCDActive(bool active) // TODO
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDB_Init(void)
|
|
{
|
|
lastts = 0;
|
|
Cur_CDIF = NULL;
|
|
TrayOpen = false;
|
|
}
|
|
|
|
void CDB_SetDisc(bool tray_open, CDIF *cdif)
|
|
{
|
|
TrayOpen = tray_open;
|
|
Cur_CDIF = tray_open ? NULL : cdif;
|
|
|
|
if(!Cur_CDIF)
|
|
{
|
|
if(DrivePhase != DRIVEPHASE_RESETTING)
|
|
{
|
|
AuthDiscType = 0x00;
|
|
DrivePhase = DRIVEPHASE_EJECTED0;
|
|
DriveCounter = (int64)1000 << 32;
|
|
}
|
|
}
|
|
}
|
|
|
|
static INLINE void RecalcIRQOut(void)
|
|
{
|
|
SCU_SetInt(16, (bool)(HIRQ & HIRQ_Mask));
|
|
}
|
|
|
|
void CDB_Reset(bool powering_up)
|
|
{
|
|
HIRQ = 0;
|
|
HIRQ_Mask = 0;
|
|
RecalcIRQOut();
|
|
|
|
CDB_ResetCD();
|
|
}
|
|
|
|
static INLINE void TriggerIRQ(unsigned bs)
|
|
{
|
|
HIRQ |= bs;
|
|
|
|
RecalcIRQOut();
|
|
}
|
|
|
|
enum { CommandPhaseBias = __COUNTER__ + 1 };
|
|
|
|
#define CMD_YIELD { \
|
|
CommandPhase = __COUNTER__ - CommandPhaseBias + 1; \
|
|
CommandClockCounter = -(500LL << 32); \
|
|
/*CommandYield = true;*/ \
|
|
goto CommandGetOut; \
|
|
case __COUNTER__: \
|
|
/*CommandYield = false;*/ \
|
|
CommandClockCounter = 0; \
|
|
}
|
|
|
|
#define CMD_EAT_CLOCKS(n) { \
|
|
CommandClockCounter -= (int64)(n) << 32; \
|
|
{ \
|
|
case __COUNTER__: \
|
|
if(CommandClockCounter < 0) \
|
|
{ \
|
|
CommandPhase = __COUNTER__ - CommandPhaseBias - 1; \
|
|
goto CommandGetOut; \
|
|
} \
|
|
/*printf("%f\n", (double)ClockCounter / (1LL << 32));*/ \
|
|
} \
|
|
}
|
|
|
|
|
|
// Drive commands: Init, Open, Play, Seek, Scan
|
|
// return busy status
|
|
// Commands that change drive status:
|
|
// Init, Open, Play, Seek, Scan
|
|
//
|
|
|
|
static uint8 MakeBaseStatus(const bool rejected = false, const uint8 hb = 0)
|
|
{
|
|
if(rejected)
|
|
return STATUS_REJECTED;
|
|
|
|
uint8 ret = 0;
|
|
|
|
if(TrayOpen)
|
|
ret = STATUS_OPEN;
|
|
else if(!Cur_CDIF)
|
|
ret = STATUS_NODISC;
|
|
else
|
|
ret = CurPosInfo.status;
|
|
|
|
return ret | hb;
|
|
}
|
|
|
|
static bool TestFilterCond(const unsigned fnum, const uint8* data)
|
|
{
|
|
auto& f = Filters[fnum];
|
|
|
|
if(f.Mode & FilterS::MODE_SEL_FADR)
|
|
{
|
|
const uint32 fad = AMSF_to_ABA(BCD_to_U8(data[12 + 0]), BCD_to_U8(data[12 + 1]), BCD_to_U8(data[12 + 2]));
|
|
|
|
if(fad < f.FAD || fad >= (f.FAD + f.Range))
|
|
return false;
|
|
}
|
|
|
|
uint8 file, channel, submode, cinfo;
|
|
|
|
if(data[15] == 0x2)
|
|
{
|
|
file = data[16];
|
|
channel = data[17];
|
|
submode = data[18];
|
|
cinfo = data[19];
|
|
}
|
|
else
|
|
file = channel = submode = cinfo = 0x00;
|
|
|
|
const bool shinv = (bool)(f.Mode & FilterS::MODE_SEL_SHREV) && (bool)(f.Mode & 0x0F);
|
|
|
|
if(f.Mode & FilterS::MODE_SEL_FILE)
|
|
{
|
|
if((bool)(file != f.File))
|
|
return shinv;
|
|
}
|
|
|
|
if(f.Mode & FilterS::MODE_SEL_CHANNEL)
|
|
{
|
|
if((bool)(channel != f.Channel))
|
|
return shinv;
|
|
}
|
|
|
|
if(f.Mode & FilterS::MODE_SEL_SUBMODE)
|
|
{
|
|
if((bool)((submode & f.SubModeMask) != f.SubMode))
|
|
return shinv;
|
|
}
|
|
|
|
if(f.Mode & FilterS::MODE_SEL_CINFO)
|
|
{
|
|
if((bool)((cinfo & f.CInfoMask) != f.CInfo))
|
|
return shinv;
|
|
}
|
|
|
|
return !shinv;
|
|
}
|
|
|
|
static uint8 FilterBuf(const unsigned fnum, const unsigned bfsidx)
|
|
{
|
|
assert(bfsidx != 0xFF);
|
|
|
|
unsigned cur = fnum;
|
|
unsigned max_iter = 0x18;
|
|
//uint32 done = 0;
|
|
|
|
SS_DBG(SS_DBG_CDB, "[CDB] DT FilterBuf: fad=0x%08x -- %02x %02x --- %02x %02x\n", AMSF_to_ABA(BCD_to_U8(Buffers[bfsidx].Data[12 + 0]), BCD_to_U8(Buffers[bfsidx].Data[12 + 1]), BCD_to_U8(Buffers[bfsidx].Data[12 + 2])), fnum, bfsidx, Filters[fnum].TrueConn, Filters[fnum].FalseConn);
|
|
|
|
while(cur != 0xFF && max_iter--)
|
|
{
|
|
if(TestFilterCond(cur, Buffers[bfsidx].Data))
|
|
{
|
|
if(Filters[cur].TrueConn != 0xFF)
|
|
{
|
|
Partition_LinkBuffer(Filters[cur].TrueConn, bfsidx);
|
|
return cur;
|
|
}
|
|
cur = 0xFF;
|
|
}
|
|
else
|
|
cur = Filters[cur].FalseConn;
|
|
}
|
|
|
|
// Discarded. Poor buffer.
|
|
Buffer_Free(bfsidx);
|
|
|
|
return 0xFF;
|
|
}
|
|
|
|
static void TranslateTOC(void)
|
|
{
|
|
uint8* td = TOC_Buffer;
|
|
|
|
for(unsigned i = 1; i < 100; i++)
|
|
{
|
|
const auto& t = toc.tracks[i];
|
|
|
|
if(t.valid)
|
|
{
|
|
const uint32 fad = t.lba + 150;
|
|
|
|
td[0] = (t.control << 4) | t.adr;
|
|
td[1] = fad >> 16;
|
|
td[2] = fad >> 8;
|
|
td[3] = fad >> 0;
|
|
}
|
|
else
|
|
td[0] = td[1] = td[2] = td[3] = 0xFF;
|
|
|
|
td += 4;
|
|
}
|
|
|
|
// TODO: Better raw TOC support in core code.
|
|
|
|
// POINT=A0
|
|
{
|
|
const auto& t = toc.tracks[toc.first_track];
|
|
|
|
td[0] = (t.control << 4) | t.adr;
|
|
td[1] = toc.first_track;
|
|
td[2] = toc.disc_type;
|
|
td[3] = 0;
|
|
|
|
td += 4;
|
|
}
|
|
|
|
// POINT=A1
|
|
{
|
|
const auto& t = toc.tracks[toc.last_track];
|
|
|
|
td[0] = (t.control << 4) | t.adr;
|
|
td[1] = toc.last_track;
|
|
td[2] = 0;
|
|
td[3] = 0;
|
|
|
|
td += 4;
|
|
}
|
|
|
|
// Lead-out
|
|
{
|
|
const auto& t = toc.tracks[100];
|
|
const uint32 fad = t.lba + 150;
|
|
|
|
td[0] = (t.control << 4) | t.adr;
|
|
td[1] = fad >> 16;
|
|
td[2] = fad >> 8;
|
|
td[3] = fad >> 0;
|
|
td += 4;
|
|
}
|
|
|
|
assert((td - TOC_Buffer) == sizeof(TOC_Buffer));
|
|
assert(sizeof(TOC_Buffer) == 0xCC * 2);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
static void MakeReport(const bool rejected = false, const uint8 hb = 0, const bool nobs = false)
|
|
{
|
|
Results[0] = ((nobs ? 0x00 : MakeBaseStatus(rejected, hb)) << 8) | (CurPosInfo.is_cdrom << 7) | PlayRepeatCounter;
|
|
|
|
Results[1] = (CurPosInfo.ctrl_adr << 8) | CurPosInfo.tno;
|
|
Results[2] = (CurPosInfo.idx << 8) | (CurPosInfo.fad >> 16);
|
|
Results[3] = CurPosInfo.fad;
|
|
}
|
|
|
|
static void CDStatusResults(const bool rejected = false, const uint8 hb = 0, const bool nobs = false)
|
|
{
|
|
MakeReport(rejected, hb, nobs);
|
|
|
|
SS_DBG(SS_DBG_CDB, "[CDB] Results: %04x %04x %04x %04x\n", Results[0], Results[1], Results[2], Results[3]);
|
|
ResultsRead = false;
|
|
CommandPending = false;
|
|
TriggerIRQ(HIRQ_CMOK);
|
|
}
|
|
|
|
static void BasicResults(uint32 res0, uint32 res1, uint32 res2, uint32 res3)
|
|
{
|
|
Results[0] = res0;
|
|
Results[1] = res1;
|
|
Results[2] = res2;
|
|
Results[3] = res3;
|
|
ResultsRead = false;
|
|
|
|
SS_DBG(SS_DBG_CDB, "[CDB] Results: %04x %04x %04x %04x\n", Results[0], Results[1], Results[2], Results[3]);
|
|
|
|
CommandPending = false;
|
|
TriggerIRQ(HIRQ_CMOK);
|
|
}
|
|
|
|
static void StartSeek(const uint32 cmd_target, const bool no_pickup_change = false)
|
|
{
|
|
if(!Cur_CDIF)
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] [BUG] StartSeek() called when no disc present or tray open.\n");
|
|
return;
|
|
}
|
|
|
|
CurPlayStart = cmd_target;
|
|
|
|
if(no_pickup_change)
|
|
{
|
|
if(DrivePhase == DRIVEPHASE_PLAY)
|
|
{
|
|
// Maybe stop scanning? or before the if(no_pickup_change)...
|
|
return;
|
|
}
|
|
}
|
|
else if(cmd_target & 0x800000)
|
|
{
|
|
int32 fad_target = cmd_target & 0x7FFFFF;
|
|
int32 tt = 1;
|
|
|
|
if(fad_target < 150)
|
|
fad_target = 150;
|
|
else if(fad_target >= (150 + (int32)toc.tracks[100].lba))
|
|
fad_target = 150 + toc.tracks[100].lba;
|
|
|
|
for(int32 track = 1; track <= 100; track++)
|
|
{
|
|
if(!toc.tracks[track].valid)
|
|
continue;
|
|
|
|
if(fad_target < (150 + (int32)toc.tracks[track].lba))
|
|
break;
|
|
|
|
tt = track;
|
|
}
|
|
|
|
CurPosInfo.tno = (tt == 100) ? 0xAA : tt;
|
|
CurPosInfo.idx = 1;
|
|
CurPosInfo.fad = fad_target;
|
|
CurPosInfo.rel_fad = fad_target - (150 + toc.tracks[tt].lba);
|
|
CurPosInfo.ctrl_adr = (toc.tracks[tt].control << 4) | (toc.tracks[tt].adr << 0);
|
|
}
|
|
else
|
|
{
|
|
int32 track_target = (cmd_target >> 8) & 0xFF;
|
|
int32 index_target = cmd_target & 0xFF;
|
|
|
|
if(track_target > toc.last_track)
|
|
track_target = toc.last_track;
|
|
else if(track_target < toc.first_track)
|
|
track_target = toc.first_track;
|
|
|
|
if(index_target < 1)
|
|
index_target = 1;
|
|
else if(index_target > 99)
|
|
index_target = 99;
|
|
|
|
CurPosInfo.tno = track_target;
|
|
CurPosInfo.idx = index_target;
|
|
CurPosInfo.fad = 150 + toc.tracks[track_target].lba;
|
|
CurPosInfo.rel_fad = 0;
|
|
CurPosInfo.ctrl_adr = (toc.tracks[track_target].control << 4) | (toc.tracks[track_target].adr << 0);
|
|
}
|
|
//
|
|
CurPosInfo.status = STATUS_BUSY;
|
|
CurPosInfo.is_cdrom = false;
|
|
DrivePhase = DRIVEPHASE_SEEK_START;
|
|
|
|
Cur_CDIF->HintReadSector(CurPosInfo.fad);
|
|
|
|
// PlayEndIRQPending = false;
|
|
PeriodicIdleCounter = PeriodicIdleCounter_Reload;
|
|
DriveCounter = (int64)256000 << 32; //(int64)((44100 * 256) / 150) << 32;
|
|
SeekIndexPhase = 0;
|
|
}
|
|
|
|
static void Drive_Run(int64 clocks)
|
|
{
|
|
DriveCounter -= clocks;
|
|
PeriodicIdleCounter -= clocks;
|
|
while(DriveCounter <= 0)
|
|
{
|
|
switch(DrivePhase)
|
|
{
|
|
case DRIVEPHASE_EJECTED0:
|
|
memset(TOC_Buffer, 0xFF, sizeof(TOC_Buffer)); // TODO: confirm 0xFF(or 0x00?)
|
|
|
|
// TODO: Check DrivePhase and these vars in command processing.
|
|
AuthDiscType = 0x00;
|
|
FileInfoValid = false;
|
|
RootDirInfoValid = false;
|
|
|
|
CurPosInfo.status = STATUS_OPEN;
|
|
CurPosInfo.is_cdrom = false;
|
|
CurPosInfo.fad = 0xFFFFFF;
|
|
CurPosInfo.rel_fad = 0xFFFFFF;
|
|
CurPosInfo.ctrl_adr = 0xFF;
|
|
CurPosInfo.idx = 0xFF;
|
|
CurPosInfo.tno = 0xFF;
|
|
TriggerIRQ(HIRQ_DCHG);
|
|
|
|
DrivePhase = DRIVEPHASE_EJECTED1;
|
|
DriveCounter = (int64)4000 << 32;
|
|
break;
|
|
|
|
case DRIVEPHASE_EJECTED1:
|
|
TriggerIRQ(HIRQ_EFLS);
|
|
DrivePhase = DRIVEPHASE_EJECTED_WAITING;
|
|
DriveCounter = (int64)1 << 32;
|
|
break;
|
|
|
|
case DRIVEPHASE_EJECTED_WAITING:
|
|
if(Cur_CDIF)
|
|
{
|
|
CurPosInfo.status = STATUS_BUSY;
|
|
DrivePhase = DRIVEPHASE_STARTUP;
|
|
DriveCounter = (int64)(1 * 44100 * 256) << 32;
|
|
}
|
|
else
|
|
DriveCounter = (int64)1000 << 32;
|
|
|
|
break;
|
|
|
|
case DRIVEPHASE_STARTUP:
|
|
Cur_CDIF->ReadTOC(&toc);
|
|
TranslateTOC();
|
|
//
|
|
//
|
|
//
|
|
ClearPendingSec();
|
|
StartSeek(0x800096);
|
|
|
|
PlayEndIRQType = 0;
|
|
CurPlayEnd = 0x800000;
|
|
CurPlayRepeat = 0;
|
|
break;
|
|
|
|
case DRIVEPHASE_STOPPED:
|
|
CurPosInfo.status = STATUS_STANDBY;
|
|
DriveCounter += (int64)2000 << 32;
|
|
break;
|
|
|
|
/*
|
|
DoSeek
|
|
*/
|
|
case DRIVEPHASE_SEEK_START:
|
|
{
|
|
int32 seek_base_time = 2 * (44100 * 256) / 150;
|
|
int32 fad_delta;
|
|
|
|
fad_delta = CurPosInfo.fad - CurSector;
|
|
|
|
//if(abs(fad_delta) >= 4000) // FIXME ! ! !something)
|
|
//{
|
|
// seek_base_time += abs(fad_delta) somethingelse; // FIXME ! ! !
|
|
//}
|
|
seek_base_time += abs(fad_delta) * (44100 * 256) / 300000 / 2; // FIXME ! ! !
|
|
|
|
CurPosInfo.status = STATUS_SEEK;
|
|
DrivePhase = DRIVEPHASE_SEEK;
|
|
DriveCounter += (int64)seek_base_time << 32;
|
|
CurSector = CurPosInfo.fad;
|
|
SubQBuf_Safe_Valid = false;
|
|
}
|
|
break;
|
|
|
|
|
|
case DRIVEPHASE_SEEK:
|
|
//
|
|
// Extremely crude approximation.
|
|
//
|
|
{
|
|
static uint8 pwbuf[96];
|
|
const bool old_safe_valid = SubQBuf_Safe_Valid;
|
|
|
|
Cur_CDIF->ReadRawSectorPWOnly(pwbuf, CurSector - 150, false);
|
|
DecodeSubQ(pwbuf);
|
|
|
|
if(!SubQBuf_Safe_Valid)
|
|
{
|
|
CurSector++;
|
|
DriveCounter += (int64)((44100 * 256) / 150) << 32;
|
|
}
|
|
else
|
|
{
|
|
bool index_ok = true;
|
|
|
|
if(!old_safe_valid)
|
|
CurSector = CurPosInfo.fad;
|
|
|
|
if(!(CurPlayStart & 0x800000))
|
|
{
|
|
const unsigned start_track = std::min<unsigned>(toc.last_track, std::max<unsigned>(toc.first_track, (CurPlayStart >> 8) & 0xFF));
|
|
const unsigned start_index = std::min<unsigned>(99, std::max<unsigned>(1, CurPlayStart & 0xFF));
|
|
const unsigned sq_idx = BCD_to_U8(SubQBuf_Safe[0x2]);
|
|
const unsigned sq_tno = (SubQBuf_Safe[0x1] >= 0xA0) ? SubQBuf_Safe[0x1] : BCD_to_U8(SubQBuf_Safe[0x1]);
|
|
|
|
if(sq_idx < start_index && sq_tno <= start_track)
|
|
{
|
|
if(SeekIndexPhase == 2)
|
|
{
|
|
index_ok = false;
|
|
CurSector += 4;
|
|
DriveCounter += (int64)((44100 * 256) / 150) << 32;
|
|
}
|
|
else
|
|
{
|
|
index_ok = false;
|
|
CurSector += 128;
|
|
DriveCounter += (int64)((44100 * 256) / 150) << 32;
|
|
SeekIndexPhase = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(SeekIndexPhase == 1)
|
|
{
|
|
index_ok = false;
|
|
CurSector -= 124;
|
|
DriveCounter += (int64)((44100 * 256) / 150) << 32;
|
|
SeekIndexPhase = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(index_ok)
|
|
{
|
|
DrivePhase = DRIVEPHASE_PLAY;
|
|
DriveCounter += (int64)((44100 * 256) / ((SubQBuf_Safe[0] & 0x40) ? 150 : 75)) << 32;
|
|
}
|
|
//else
|
|
// printf("%d\n", CurSector);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DRIVEPHASE_PLAY:
|
|
if(SecPreBuf_In > 0)
|
|
{
|
|
if(SubQBuf_Safe[0] & 0x40)
|
|
{
|
|
CurPosInfo.is_cdrom = true;
|
|
|
|
//assert(edc_check(SecPreBuf, false));
|
|
|
|
if(FreeBufferCount > 0)
|
|
{
|
|
const uint8 bfi = Buffer_Allocate(false);
|
|
|
|
memcpy(Buffers[bfi].Data, SecPreBuf, 2352);
|
|
|
|
SecPreBuf_In = false;
|
|
LastBufDest = FilterBuf(CDDevConn, bfi);
|
|
TriggerIRQ(HIRQ_CSCT);
|
|
if(FreeBufferCount == 0)
|
|
TriggerIRQ(HIRQ_BFUL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CurPosInfo.is_cdrom = false;
|
|
if(!CDDABuf_Count)
|
|
{
|
|
for(int i = 0; i < CDDABuf_PrefillCount; i++)
|
|
{
|
|
CDDABuf[CDDABuf_WP][0] = 0;
|
|
CDDABuf[CDDABuf_WP][1] = 0;
|
|
CDDABuf_WP = (CDDABuf_WP + 1) % CDDABuf_MaxCount;
|
|
CDDABuf_Count++;
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < 588 && CDDABuf_Count < CDDABuf_MaxCount; i++)
|
|
{
|
|
CDDABuf[CDDABuf_WP][0] = MDFN_de16lsb(&SecPreBuf[i * 4 + 0]);
|
|
CDDABuf[CDDABuf_WP][1] = MDFN_de16lsb(&SecPreBuf[i * 4 + 2]);
|
|
CDDABuf_WP = (CDDABuf_WP + 1) % CDDABuf_MaxCount;
|
|
CDDABuf_Count++;
|
|
}
|
|
|
|
SecPreBuf_In = false;
|
|
}
|
|
|
|
if(!SecPreBuf_In)
|
|
{
|
|
CurPosInfo.status = STATUS_PLAY;
|
|
}
|
|
} // end if(SecPreBuf_In > 0)
|
|
|
|
PeriodicIdleCounter = 17712LL << 32;
|
|
if(DrivePhase == DRIVEPHASE_PLAY)
|
|
{
|
|
if(SecPreBuf_In)
|
|
{
|
|
// TODO: More accurate:
|
|
CurPosInfo.status = STATUS_PAUSE;
|
|
|
|
if(SecPreBuf_In > 0)
|
|
SS_DBG(SS_DBG_CDB, "[CDB] SB Overflow\n");
|
|
}
|
|
else
|
|
{
|
|
Cur_CDIF->ReadRawSector(SecPreBuf, CurSector - 150);
|
|
SecPreBuf_In = true;
|
|
|
|
// TODO:(maybe pointless...)
|
|
//if(SubQBuf_Safe[0] & 0x40)
|
|
// CurPosInfo.fad = SECTOR HEADER
|
|
//else
|
|
// CurPosInfo.fad = SUBQ STUFF
|
|
CurPosInfo.fad = CurSector;
|
|
if(DecodeSubQ(SecPreBuf + 2352))
|
|
{
|
|
CurPosInfo.rel_fad = (BCD_to_U8(SubQBuf[0x3]) * 60 + BCD_to_U8(SubQBuf[0x4])) * 75 + BCD_to_U8(SubQBuf[0x5]);
|
|
CurPosInfo.tno = (SubQBuf[0x1] >= 0xA0) ? SubQBuf[0x1] : BCD_to_U8(SubQBuf[0x1]);
|
|
CurPosInfo.idx = BCD_to_U8(SubQBuf[0x2]);
|
|
}
|
|
CurSector++;
|
|
}
|
|
}
|
|
|
|
DriveCounter += (int64)((44100 * 256) / ((SubQBuf_Safe[0] & 0x40) ? 150 : 75)) << 32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(PeriodicIdleCounter <= 0)
|
|
{
|
|
PeriodicIdleCounter = PeriodicIdleCounter_Reload;
|
|
|
|
//
|
|
//
|
|
//
|
|
if(SecPreBuf_In && DrivePhase == DRIVEPHASE_PLAY)
|
|
{
|
|
bool end_met = (CurPosInfo.tno == 0xAA);
|
|
|
|
if(CurPlayEnd != 0)
|
|
{
|
|
if(CurPlayEnd & 0x800000)
|
|
end_met |= (CurPosInfo.fad >= (CurPlayEnd & 0x7FFFFF));
|
|
else
|
|
{
|
|
const unsigned end_track = std::min<unsigned>(toc.last_track, std::max<unsigned>(toc.first_track, (CurPlayEnd >> 8) & 0xFF));
|
|
const unsigned end_index = std::min<unsigned>(99, std::max<unsigned>(1, CurPlayEnd & 0xFF));
|
|
|
|
end_met |= (CurPosInfo.tno > end_track) || (CurPosInfo.tno == end_track && CurPosInfo.idx > end_index);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Steam Heart's, it's always Steam Heart's...
|
|
//
|
|
if(CurPlayStart & 0x800000)
|
|
{
|
|
end_met |= (CurPosInfo.fad < (CurPlayStart & 0x7FFFFF));
|
|
}
|
|
else
|
|
{
|
|
const unsigned start_track = std::min<unsigned>(toc.last_track, std::max<unsigned>(toc.first_track, (CurPlayStart >> 8) & 0xFF));
|
|
const unsigned start_index = std::min<unsigned>(99, std::max<unsigned>(1, CurPlayStart & 0xFF));
|
|
|
|
end_met |= (CurPosInfo.tno < start_track); //|| (CurPosInfo.tno == start_track && CurPosInfo.idx < start_index);
|
|
}
|
|
|
|
if(end_met)
|
|
{
|
|
SecPreBuf_In = false;
|
|
if(PlayRepeatCounter >= CurPlayRepeat)
|
|
{
|
|
CurSector = CurPosInfo.fad;
|
|
|
|
if(PlayEndIRQType)
|
|
{
|
|
CurPosInfo.status = STATUS_BUSY;
|
|
|
|
PlayEndIRQType += 1 << 30;
|
|
|
|
if((PlayEndIRQType >> 30) >= 3)
|
|
{
|
|
TriggerIRQ(PlayEndIRQType & 0xFFFF); // May not be right for EFLS with Read File, maybe EFLS is only triggered after the buffer is written?
|
|
PlayEndIRQType = 0;
|
|
}
|
|
}
|
|
|
|
if(!PlayEndIRQType)
|
|
CurPosInfo.status = STATUS_PAUSE;
|
|
}
|
|
else
|
|
{
|
|
StartSeek(PlayCmdStartPos);
|
|
|
|
if(PlayRepeatCounter < 0xE)
|
|
PlayRepeatCounter++;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
PeriodicIdleCounter = PeriodicIdleCounter_Reload;
|
|
|
|
if(ResultsRead)
|
|
MakeReport(false, STATUS_PERIODIC);
|
|
|
|
// FIXME: Other ADR types, and correct handling when in STANDBY/STOPPED state?
|
|
SubCodeQBuf[0] = CurPosInfo.ctrl_adr;
|
|
SubCodeQBuf[1] = CurPosInfo.tno;
|
|
SubCodeQBuf[2] = CurPosInfo.idx;
|
|
SubCodeQBuf[3] = CurPosInfo.rel_fad >> 16;
|
|
SubCodeQBuf[4] = CurPosInfo.rel_fad >> 8;
|
|
SubCodeQBuf[5] = CurPosInfo.rel_fad >> 0;
|
|
SubCodeQBuf[6] = 0; // ? OxFF in some cases...
|
|
SubCodeQBuf[7] = CurPosInfo.fad >> 16;
|
|
SubCodeQBuf[8] = CurPosInfo.fad >> 8;
|
|
SubCodeQBuf[9] = CurPosInfo.fad >> 0;
|
|
|
|
TriggerIRQ(HIRQ_SCDQ);
|
|
}
|
|
}
|
|
|
|
void CDB_GetCDDA(uint16* outbuf)
|
|
{
|
|
outbuf[0] = outbuf[1] = 0;
|
|
|
|
if(CDDABuf_Count)
|
|
{
|
|
outbuf[0] = CDDABuf[CDDABuf_RP][0];
|
|
outbuf[1] = CDDABuf[CDDABuf_RP][1];
|
|
|
|
CDDABuf_RP = (CDDABuf_RP + 1) % CDDABuf_MaxCount;
|
|
CDDABuf_Count--;
|
|
}
|
|
|
|
//static int32 counter = 0;
|
|
//outbuf[0] = (counter & 0xFF) << 6;
|
|
//outbuf[1] = (counter & 0xFF) << 6;
|
|
//counter++;
|
|
}
|
|
|
|
static void ClearPendingSec(void)
|
|
{
|
|
//PlayEndIRQPending = 0;
|
|
PlayEndIRQType = 0;
|
|
|
|
SecPreBuf_In = false;
|
|
|
|
CDDABuf_WP = CDDABuf_RP = 0;
|
|
CDDABuf_Count = 0;
|
|
}
|
|
|
|
sscpu_timestamp_t CDB_Update(sscpu_timestamp_t timestamp)
|
|
{
|
|
if(MDFN_UNLIKELY(timestamp < lastts))
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] [BUG] timestamp(%d) < lastts(%d)\n", timestamp, lastts);
|
|
}
|
|
else
|
|
{
|
|
int64 clocks = (int64)(timestamp - lastts) * CDB_ClockRatio;
|
|
lastts = timestamp;
|
|
|
|
Drive_Run(clocks);
|
|
|
|
//
|
|
//
|
|
//
|
|
CommandClockCounter += clocks;
|
|
switch(CommandPhase + CommandPhaseBias)
|
|
{
|
|
for(;;)
|
|
{
|
|
default:
|
|
case __COUNTER__:
|
|
|
|
while(!CommandPending)
|
|
{
|
|
if(FLS_Run())
|
|
{
|
|
CMD_EAT_CLOCKS(60);
|
|
TriggerIRQ(HIRQ_EFLS);
|
|
CMD_EAT_CLOCKS(60);
|
|
}
|
|
else
|
|
{
|
|
CMD_YIELD;
|
|
}
|
|
}
|
|
|
|
for(unsigned i = 0; i < 4; i++)
|
|
CTR.CD[i] = CData[i];
|
|
|
|
CTR.Command = CTR.CD[0] >> 8;
|
|
if(MDFN_UNLIKELY(ss_dbg_mask & SS_DBG_CDB))
|
|
{
|
|
char cdet[128];
|
|
GetCommandDetails(CTR.CD, cdet, sizeof(cdet));
|
|
SS_DBG(SS_DBG_CDB, "[CDB] Command: %s --- HIRQ=0x%04x, HIRQ_Mask=0x%04x\n", cdet, HIRQ, HIRQ_Mask);
|
|
}
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(84);
|
|
|
|
//
|
|
//
|
|
//
|
|
if(CTR.Command == COMMAND_GET_CDSTATUS) // = 0x00,
|
|
{
|
|
CDStatusResults();
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_HWINFO) // = 0x01,
|
|
{
|
|
BasicResults(MakeBaseStatus() << 8,
|
|
0x0002,
|
|
0x0000,
|
|
0x0600); // TODO: Before INIT: 0xFF00;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_TOC) // = 0x02,
|
|
{
|
|
if(DrivePhase == DRIVEPHASE_STARTUP || DT.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
BasicResults(MakeBaseStatus(false, STATUS_DTREQ) << 8, 0xCC, 0, 0);
|
|
|
|
//
|
|
//
|
|
DT.CurBufIndex = 0;
|
|
DT.BufCount = 1;
|
|
|
|
DT.InBufOffs = 0;
|
|
DT.InBufCounter = 0xCC;
|
|
|
|
DT.TotalCounter = 0;
|
|
|
|
DT.FIFO_RP = 0;
|
|
DT.FIFO_WP = 0;
|
|
DT.FIFO_In = 0;
|
|
|
|
DT.BufList[0] = 0xFF;
|
|
|
|
DT.Writing = false;
|
|
DT.NeedBufFree = false;
|
|
DT.Active = true;
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(128);
|
|
TriggerIRQ(HIRQ_DRDY);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_SESSINFO) // = 0x03,
|
|
{
|
|
if(DrivePhase == DRIVEPHASE_STARTUP)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
const unsigned sess = CTR.CD[0] & 0xFF;
|
|
uint32 fad;
|
|
uint8 rsw;
|
|
|
|
if(!sess)
|
|
{
|
|
fad = 150 + toc.tracks[100].lba;
|
|
rsw = 0x01; // Session count;
|
|
}
|
|
else if(sess <= 0x01)
|
|
{
|
|
fad = 0;
|
|
rsw = sess;
|
|
}
|
|
else
|
|
{
|
|
fad = 0xFFFFFF;
|
|
rsw = 0xFF;
|
|
}
|
|
|
|
BasicResults(MakeBaseStatus() << 8, 0, (rsw << 8) | (fad >> 16), fad);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_INIT) // = 0x04,
|
|
{
|
|
CDStatusResults(false, 0x00, true);
|
|
|
|
//
|
|
if(CTR.CD[0] & 0x01) // Software reset of CD block
|
|
{
|
|
SWReset();
|
|
|
|
CMD_EAT_CLOCKS(8192);
|
|
TriggerIRQ(HIRQ_MPED | HIRQ_EFLS | HIRQ_ECPY | HIRQ_EHST | HIRQ_ESEL | HIRQ_CMOK);
|
|
}
|
|
|
|
// TODO TODO TODO
|
|
// & 0x02; // Decode subcode RW
|
|
// & 0x04; // Ignore mode2 subheader?
|
|
// & 0x08; // Retry form2 read
|
|
// & 0x30; // CD read speed(unused?)
|
|
// & 0x80; // No change?
|
|
}
|
|
/*
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_OPEN) // = 0x05,
|
|
{
|
|
}
|
|
*/
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_END_DATAXFER) // = 0x06,
|
|
{
|
|
if(DT.Active)
|
|
{
|
|
DT.Active = false;
|
|
|
|
BasicResults((MakeBaseStatus() << 8) | (DT.TotalCounter >> 16), DT.TotalCounter, 0, 0);
|
|
|
|
if(DT.InBufCounter > 0 || DT.FIFO_In > 0)
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] Data transfer ended prematurely at %u bytes left!\n", (DT.InBufCounter + DT.FIFO_In) * 2);
|
|
}
|
|
|
|
if(DT.Writing)
|
|
{
|
|
if(CDDevConn == DT.FNum)
|
|
CDDevConn = 0xFF;
|
|
|
|
for(unsigned i = 0; i < DT.BufCount; i++)
|
|
{
|
|
FilterBuf(DT.FNum, DT.BufList[i]);
|
|
}
|
|
|
|
CMD_EAT_CLOCKS(270);
|
|
if(FreeBufferCount == 0)
|
|
TriggerIRQ(HIRQ_BFUL);
|
|
TriggerIRQ(HIRQ_EHST);
|
|
}
|
|
else
|
|
{
|
|
if(DT.NeedBufFree)
|
|
{
|
|
for(unsigned i = 0; i < DT.BufCount; i++)
|
|
{
|
|
Buffer_Free(DT.BufList[i]);
|
|
}
|
|
}
|
|
|
|
if(DT.BufList[0] != 0xFE && DT.BufList[0] != 0xFD) // FIXME: Cleanup(use enums for special buffer ids) - Also check for TOC and FINFO
|
|
{
|
|
CMD_EAT_CLOCKS(130);
|
|
TriggerIRQ(HIRQ_EHST);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
BasicResults((MakeBaseStatus() << 8) | 0xFF, 0xFFFF, 0, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_PLAY) // = 0x10,
|
|
{
|
|
uint32 cmd_psp = ((CTR.CD[0] & 0xFF) << 16) | CTR.CD[1];
|
|
uint32 cmd_pep = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
uint8 pm = CTR.CD[2] >> 8;
|
|
|
|
if(cmd_psp == 0xFFFFFF)
|
|
cmd_psp = PlayCmdStartPos;
|
|
|
|
if(cmd_pep == 0xFFFFFF)
|
|
cmd_pep = PlayCmdEndPos;
|
|
else if((cmd_psp & 0x800000) && (cmd_pep & 0x800000))
|
|
cmd_pep = 0x800000 | ((cmd_psp + cmd_pep) & 0x7FFFFF);
|
|
|
|
if(((cmd_psp ^ cmd_pep) & 0x800000) && cmd_pep != 0)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
PlayCmdStartPos = cmd_psp;
|
|
PlayCmdEndPos = cmd_pep;
|
|
//
|
|
|
|
CurPosInfo.status = STATUS_BUSY; // Happens even if (PlayMode & 0x80)...
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
if(!(pm & 0x80))
|
|
ClearPendingSec();
|
|
|
|
StartSeek(PlayCmdStartPos, (bool)(pm & 0x80));
|
|
|
|
PlayEndIRQType = HIRQ_PEND;
|
|
CurPlayEnd = PlayCmdEndPos;
|
|
|
|
if(!(pm & 0x70))
|
|
PlayCmdRepCnt = pm & 0x0F;
|
|
|
|
PlayRepeatCounter = 0;
|
|
CurPlayRepeat = PlayCmdRepCnt;
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SEEK) // = 0x11,
|
|
{
|
|
CurPosInfo.status = STATUS_BUSY;
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
const uint32 cmd_sp = ((CTR.CD[0] & 0xFF) << 16) | CTR.CD[1];
|
|
|
|
if(!cmd_sp) // Stop
|
|
{
|
|
ClearPendingSec();
|
|
CurPosInfo.is_cdrom = false; // FIXME? Correct?
|
|
CurPosInfo.status = STATUS_BUSY;
|
|
CurPosInfo.fad = 0xFFFFFF;
|
|
CurPosInfo.rel_fad = 0xFFFFFF;
|
|
CurPosInfo.ctrl_adr = 0xFF;
|
|
CurPosInfo.idx = 0xFF;
|
|
CurPosInfo.tno = 0xFF;
|
|
|
|
DrivePhase = DRIVEPHASE_STOPPED;
|
|
DriveCounter = (int64)380000 << 32;
|
|
}
|
|
else if(cmd_sp == 0xFFFFFF) // Pause
|
|
{
|
|
if(DrivePhase == DRIVEPHASE_STOPPED) // TODO: Test
|
|
{
|
|
ClearPendingSec();
|
|
StartSeek(0x800096);
|
|
}
|
|
|
|
SecPreBuf_In = -abs(SecPreBuf_In);
|
|
PlayEndIRQType = 0;
|
|
CurPlayEnd = 0x800000;
|
|
CurPlayRepeat = 0;
|
|
}
|
|
else
|
|
{
|
|
ClearPendingSec();
|
|
CurPlayEnd = 0x800000;
|
|
CurPlayRepeat = 0;
|
|
StartSeek(cmd_sp);
|
|
}
|
|
}
|
|
/*
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SCAN) // = 0x12,
|
|
{
|
|
//CurPosInfo.is_cdrom = false;
|
|
Forward scan data track(on Saturn CD):
|
|
4-5 sequential position updates, then jumps like:
|
|
17f-> 1e1 (62)
|
|
96d-> 9d0 (63)
|
|
fd8->103b (63)
|
|
202c->2093 (67)
|
|
2fa1->300a (69)
|
|
3fdf->404a (6b)
|
|
4fa3->5011 (6e)
|
|
5fad->601c (6f)
|
|
6f96->7007 (71)
|
|
7fcd->8040 (73)
|
|
8fd7->904e (77)
|
|
9faf->a028 (79)
|
|
afcd->b047 (7a)
|
|
|
|
97.79888435299837 + 0.0005527097174003169x
|
|
}
|
|
*/
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_SUBCODE) // = 0x20,
|
|
{
|
|
if(DT.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
uint8 type;
|
|
|
|
type = CTR.CD[0] & 0xFF;
|
|
|
|
if(type >= 0x02)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
if(type == 0) // Q (TODO: ADR other than 0x1)
|
|
{
|
|
BasicResults(MakeBaseStatus(false, STATUS_DTREQ) << 8, 0x05, 0, 0);
|
|
DT.BufList[0] = 0xFE;
|
|
DT.InBufCounter = 0x05;
|
|
}
|
|
else // R-W (TODO)
|
|
{
|
|
BasicResults(MakeBaseStatus(false, STATUS_DTREQ) << 8, 0x0C, 0, 0);
|
|
|
|
for(unsigned i = 0; i < 24; i++)
|
|
SubCodeRWBuf[i] = 0xFF;
|
|
|
|
DT.BufList[0] = 0xFD;
|
|
DT.InBufCounter = 0x0C;
|
|
}
|
|
|
|
DT.CurBufIndex = 0;
|
|
DT.BufCount = 1;
|
|
|
|
DT.InBufOffs = 0;
|
|
|
|
DT.TotalCounter = 0;
|
|
DT.FIFO_RP = 0;
|
|
DT.FIFO_WP = 0;
|
|
DT.FIFO_In = 0;
|
|
|
|
DT.Writing = false;
|
|
DT.NeedBufFree = false;
|
|
DT.Active = true;
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(128);
|
|
TriggerIRQ(HIRQ_DRDY);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_AUTH_DEVICE)
|
|
{
|
|
uint8 fnum;
|
|
|
|
fnum = (CTR.CD[2] >> 8);
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else if(FLS.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
//
|
|
bool is_audio_cd;
|
|
uint32 data_track_fad;
|
|
|
|
is_audio_cd = true;
|
|
|
|
// TODO: Check DrivePhase == DRIVEPHASE_STARTUP
|
|
for(int32 track = toc.first_track; track <= toc.last_track; track++)
|
|
{
|
|
if(toc.tracks[track].control & SUBQ_CTRLF_DATA)
|
|
{
|
|
data_track_fad = 150 + toc.tracks[track].lba;
|
|
is_audio_cd = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(is_audio_cd)
|
|
{
|
|
AuthDiscType = 0x01; //0x04;
|
|
CMD_EAT_CLOCKS(200);
|
|
TriggerIRQ(HIRQ_EFLS);
|
|
}
|
|
else
|
|
{
|
|
SetCDDeviceConn(fnum);
|
|
Filter_SetTrueConn(fnum, fnum);
|
|
Filter_SetFalseConn(fnum, 0xFF);
|
|
Filter_SetRange(fnum, 0, 0);
|
|
Filters[fnum].Mode = 0;
|
|
|
|
FLS.pnum = fnum;
|
|
FLS.DoAuth = true;
|
|
FLS.Active = true;
|
|
|
|
ClearPendingSec();
|
|
StartSeek(0x800000 | (data_track_fad + 16));
|
|
|
|
CurPlayEnd = 0;
|
|
CurPlayRepeat = 0;
|
|
PlayRepeatCounter = 0;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_AUTH)
|
|
{
|
|
if(FLS.Active && FLS.DoAuth)
|
|
CDStatusResults(true);
|
|
else
|
|
BasicResults(MakeBaseStatus() << 8, AuthDiscType, 0, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_CDDEVCONN) // = 0x30,
|
|
{
|
|
#define fnum (CTR.CD[2] >> 8)
|
|
|
|
if(fnum >= 0x18 && fnum != 0xFF)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
SetCDDeviceConn(fnum);
|
|
|
|
CDStatusResults();
|
|
|
|
CMD_EAT_CLOCKS(96);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
#undef fnum
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_CDDEVCONN) // = 0x31,
|
|
{
|
|
BasicResults((MakeBaseStatus() << 8), 0, CDDevConn << 8, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_LASTBUFDST) // = 0x32,
|
|
{
|
|
BasicResults(MakeBaseStatus() << 8, 0, LastBufDest << 8, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_FILTRANGE) // = 0x40,
|
|
{
|
|
#define fnum (CTR.CD[2] >> 8)
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
{
|
|
const uint32 fad = ((CTR.CD[0] & 0xFF) << 16) | CTR.CD[1];
|
|
const uint32 range = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
|
|
Filter_SetRange(fnum, fad, range);
|
|
|
|
CDStatusResults();
|
|
}
|
|
CMD_EAT_CLOCKS(96);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
#undef fnum
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FILTRANGE) // = 0x41,
|
|
{
|
|
const unsigned fnum = CTR.CD[2] >> 8;
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
const uint32 fad = Filters[fnum].FAD;
|
|
const uint32 range = Filters[fnum].Range;
|
|
|
|
BasicResults((MakeBaseStatus() << 8) | (fad >> 16),
|
|
fad,
|
|
(fnum << 8) | (range >> 16),
|
|
range);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_FILTSUBHC) // = 0x42,
|
|
{
|
|
#define fnum (CTR.CD[2] >> 8)
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
Filters[fnum].Channel = CTR.CD[0] & 0xFF;
|
|
Filters[fnum].SubModeMask = CTR.CD[1] >> 8;
|
|
Filters[fnum].CInfoMask = CTR.CD[1] & 0xFF;
|
|
Filters[fnum].File = CTR.CD[2] & 0xFF;
|
|
Filters[fnum].SubMode = CTR.CD[3] >> 8;
|
|
Filters[fnum].CInfo = CTR.CD[3] & 0xFF;
|
|
|
|
CDStatusResults();
|
|
|
|
CMD_EAT_CLOCKS(96);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
#undef fnum
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FILTSUBHC) // = 0x43,
|
|
{
|
|
const unsigned fnum = CTR.CD[2] >> 8;
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
const auto& f = Filters[fnum];
|
|
|
|
BasicResults((MakeBaseStatus() << 8) | f.Channel,
|
|
(f.SubModeMask << 8) | f.CInfoMask,
|
|
(fnum << 8) | f.File,
|
|
(f.SubMode << 8) | f.CInfo);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_FILTMODE) // = 0x44,
|
|
{
|
|
#define fnum (CTR.CD[2] >> 8)
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
Filters[fnum].Mode = CTR.CD[0] & 0xFF;
|
|
|
|
if(CTR.CD[0] & 0x80)
|
|
Filter_ResetCond(fnum);
|
|
|
|
CDStatusResults();
|
|
|
|
CMD_EAT_CLOCKS(96);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
#undef fnum
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FILTMODE) // = 0x45,
|
|
{
|
|
const unsigned fnum = CTR.CD[2] >> 8;
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
BasicResults((MakeBaseStatus() << 8) | Filters[fnum].Mode,
|
|
0,
|
|
fnum << 8,
|
|
0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_FILTCONN) // = 0x46,
|
|
{
|
|
#define fnum (CTR.CD[2] >> 8)
|
|
#define fcflags (CTR.CD[0] & 0xFF)
|
|
#define tconn (CTR.CD[1] >> 8)
|
|
#define fconn (CTR.CD[1] & 0xFF)
|
|
|
|
if(fnum >= 0x18 || ((fcflags & 0x1) && (tconn >= 0x18) && tconn != 0xFF) || ((fcflags & 0x2) && (fconn >= 0x18) && fconn != 0xFF))
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
if(fcflags & 0x1)
|
|
Filter_SetTrueConn(fnum, tconn);
|
|
|
|
if(fcflags & 0x2)
|
|
Filter_SetFalseConn(fnum, fconn);
|
|
|
|
CDStatusResults();
|
|
|
|
CMD_EAT_CLOCKS(96);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
#undef fconn
|
|
#undef tconn
|
|
#undef fcflags
|
|
#undef fnum
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FILTCONN) // = 0x47,
|
|
{
|
|
const unsigned fnum = CTR.CD[2] >> 8;
|
|
|
|
if(fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
const auto& f = Filters[fnum];
|
|
|
|
BasicResults((MakeBaseStatus() << 8),
|
|
(f.TrueConn << 8) | f.FalseConn,
|
|
fnum << 8,
|
|
0);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_RESET_SEL) // = 0x48,
|
|
{
|
|
unsigned rflags;
|
|
|
|
rflags = CTR.CD[0] & 0xFF;
|
|
|
|
if(!rflags)
|
|
{
|
|
unsigned pnum;
|
|
|
|
pnum = CTR.CD[2] >> 8;
|
|
|
|
if(pnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
Partition_Clear(pnum);
|
|
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(150);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(unsigned pnum = 0; pnum < 0x18; pnum++)
|
|
{
|
|
if(rflags & 0x04) // Initialize all partition data
|
|
{
|
|
Partition_Clear(pnum);
|
|
}
|
|
|
|
if(rflags & 0x08) // Initialize all partition output connectors? ? ?
|
|
{
|
|
|
|
}
|
|
|
|
if(rflags & 0x10) // Initialize all filter conditions
|
|
{
|
|
Filter_ResetCond(pnum);
|
|
}
|
|
|
|
if(rflags & 0x20) // Initialize all filter input connectors? ? ?
|
|
{
|
|
if(pnum == CDDevConn)
|
|
CDDevConn = 0xFF;
|
|
|
|
if(Filters[pnum].FalseConn < 0x18)
|
|
Filters[pnum].FalseConn = 0xFF;
|
|
}
|
|
|
|
if(rflags & 0x40) // Initialize all true output connectors
|
|
{
|
|
Filters[pnum].TrueConn = pnum;
|
|
}
|
|
|
|
if(rflags & 0x80) // Initialize all false output connectors
|
|
{
|
|
Filters[pnum].FalseConn = 0xFF;
|
|
}
|
|
}
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
//
|
|
// TODO: Accurate timing(while not blocking other command execution and sector reading).
|
|
CMD_EAT_CLOCKS(300);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_BUFSIZE) // = 0x50,
|
|
{
|
|
BasicResults(MakeBaseStatus() << 8,
|
|
FreeBufferCount,
|
|
0x18 << 8,
|
|
NumBuffers);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_SECNUM) // = 0x51,
|
|
{
|
|
const unsigned pnum = CTR.CD[2] >> 8;
|
|
|
|
if(pnum >= 0x18)
|
|
{
|
|
CDStatusResults(true);
|
|
}
|
|
else
|
|
{
|
|
BasicResults(MakeBaseStatus() << 8,
|
|
0,
|
|
0,
|
|
Partitions[pnum].Count);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_CALC_ACTSIZE) // = 0x52,
|
|
{
|
|
unsigned pnum;
|
|
int offs, numsec;
|
|
|
|
offs = CTR.CD[1];
|
|
pnum = CTR.CD[2] >> 8;
|
|
numsec = CTR.CD[3];
|
|
|
|
if(pnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
offs = CTR.CD[1];
|
|
numsec = CTR.CD[3];
|
|
|
|
if(offs == 0xFFFF)
|
|
offs = Partitions[pnum].Count - 1;
|
|
|
|
if(numsec == 0xFFFF)
|
|
numsec = Partitions[pnum].Count - offs;
|
|
|
|
if((DT.Active && DT.Writing) || numsec <= 0 || offs < 0 || (offs + numsec) > (int)Partitions[pnum].Count)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
CDStatusResults();
|
|
|
|
//
|
|
{
|
|
uint32 tmp_accum = 0;
|
|
for(int i = 0, bfi = Partition_GetBuffer(pnum, offs); i < numsec; i++, bfi = Buffers[bfi].Next)
|
|
{
|
|
const uint8* const sd = Buffers[bfi].Data;
|
|
|
|
switch(GetSecLen)
|
|
{
|
|
default:
|
|
case SECLEN_2048:
|
|
if((sd[12 + 3] == 0x2) && (sd[16 + 2] & 0x20)) // Mode 2 Form 2
|
|
tmp_accum += 1162;
|
|
else // M2F1 and Mode 1(weird tested inconsistency with Get Sector Data command, not that it probably matters)
|
|
tmp_accum += 1024;
|
|
break;
|
|
|
|
case SECLEN_2336: tmp_accum += 1168; break;
|
|
case SECLEN_2340: tmp_accum += 1170; break;
|
|
case SECLEN_2352: tmp_accum += 1176; break;
|
|
}
|
|
}
|
|
CalcedActualSize = tmp_accum;
|
|
}
|
|
//
|
|
CMD_EAT_CLOCKS(240); // TODO: proper timing(can be surprisingly large for higher numsec)
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_ACTSIZE) // = 0x53,
|
|
{
|
|
BasicResults((MakeBaseStatus() << 8) | (CalcedActualSize >> 16), CalcedActualSize, 0, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_SECINFO) // = 0x54,
|
|
{
|
|
unsigned offs;
|
|
unsigned pnum;
|
|
|
|
offs = CTR.CD[1];
|
|
pnum = CTR.CD[2] >> 8;
|
|
|
|
if(pnum >= 0x18 || (offs != 0xFFFF && offs >= Partitions[pnum].Count) || Partitions[pnum].Count == 0)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
const int bfi = ((offs == 0xFFFF) ? Partitions[pnum].LastBuf : Partition_GetBuffer(pnum, offs));
|
|
const uint8* sd = Buffers[bfi].Data;
|
|
uint32 fad;
|
|
uint8 file = 0, chan = 0, submode = 0, cinfo = 0;
|
|
|
|
fad = AMSF_to_ABA(BCD_to_U8(sd[12 + 0]), BCD_to_U8(sd[12 + 1]), BCD_to_U8(sd[12 + 2]));
|
|
if(sd[12 + 3] == 0x2)
|
|
{
|
|
file = sd[16];
|
|
chan = sd[17];
|
|
submode = sd[18];
|
|
cinfo = sd[19];
|
|
}
|
|
|
|
BasicResults((MakeBaseStatus() << 8) | (fad >> 16),
|
|
fad,
|
|
(file << 8) | chan,
|
|
(submode << 8) | cinfo);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_EXEC_FADSRCH) // = 0x55,
|
|
{
|
|
unsigned offs;
|
|
unsigned pnum;
|
|
uint32 sfad;
|
|
|
|
offs = CTR.CD[1];
|
|
pnum = CTR.CD[2] >> 8;
|
|
sfad = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
|
|
if(pnum >= 0x18 || (offs != 0xFFFF && offs >= Partitions[pnum].Count) || Partitions[pnum].Count == 0)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
int counter;
|
|
int effoffs, bfi;
|
|
bool match_made;
|
|
|
|
FADSearch.spos = 0xFFFF;
|
|
FADSearch.pnum = pnum;
|
|
FADSearch.fad = 0;
|
|
|
|
counter = 0;
|
|
effoffs = (offs == 0xFFFF) ? (Partitions[pnum].Count - 1) : offs;
|
|
bfi = Partitions[pnum].FirstBuf;
|
|
match_made = false;
|
|
|
|
do
|
|
{
|
|
if(counter >= effoffs)
|
|
{
|
|
const uint8* sd = Buffers[bfi].Data;
|
|
const uint32 fad = AMSF_to_ABA(BCD_to_U8(sd[12 + 0]), BCD_to_U8(sd[12 + 1]), BCD_to_U8(sd[12 + 2]));
|
|
|
|
if(fad <= sfad && fad >= (FADSearch.fad + match_made))
|
|
{
|
|
FADSearch.spos = counter;
|
|
FADSearch.fad = fad;
|
|
match_made = true;
|
|
}
|
|
}
|
|
bfi = Buffers[bfi].Next;
|
|
counter++;
|
|
} while(bfi != 0xFF);
|
|
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(300);
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FADSRCH) // = 0x56,
|
|
{
|
|
BasicResults(MakeBaseStatus() << 8, FADSearch.spos, (FADSearch.pnum << 8) | (FADSearch.fad >> 16), FADSearch.fad);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_SET_SECLEN) // = 0x60,
|
|
{
|
|
const unsigned NewGetSecLen = (CTR.CD[0] & 0xFF);
|
|
const unsigned NewPutSecLen = (CTR.CD[1] >> 8);
|
|
const bool NewGetSecLenBad = NewGetSecLen != 0xFF && (NewGetSecLen < SECLEN__FIRST || NewGetSecLen > SECLEN__LAST);
|
|
const bool NewPutSecLenBad = NewPutSecLen != 0xFF && (NewPutSecLen < SECLEN__FIRST || NewPutSecLen > SECLEN__LAST);
|
|
|
|
if(NewGetSecLenBad || NewPutSecLenBad)
|
|
{
|
|
CDStatusResults(true);
|
|
}
|
|
else
|
|
{
|
|
if(NewGetSecLen != 0xFF)
|
|
GetSecLen = NewGetSecLen;
|
|
|
|
if(NewPutSecLen != 0xFF)
|
|
PutSecLen = NewPutSecLen;
|
|
|
|
CDStatusResults();
|
|
TriggerIRQ(HIRQ_ESEL);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_SECDATA || CTR.Command == COMMAND_DEL_SECDATA || CTR.Command == COMMAND_GETDEL_SECDATA) // = 0x61, 0x62, 0x63
|
|
{
|
|
unsigned pnum;
|
|
int offs, numsec;
|
|
|
|
pnum = CTR.CD[2] >> 8;
|
|
if(pnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
offs = CTR.CD[1];
|
|
numsec = CTR.CD[3];
|
|
|
|
if(offs == 0xFFFF)
|
|
offs = Partitions[pnum].Count - 1;
|
|
|
|
if(numsec == 0xFFFF)
|
|
numsec = Partitions[pnum].Count - offs;
|
|
|
|
if((DT.Active && CTR.Command != COMMAND_DEL_SECDATA) || numsec <= 0 || offs < 0 || (offs + numsec) > (int)Partitions[pnum].Count)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
int sbfi;
|
|
|
|
sbfi = Partition_GetBuffer(pnum, offs);
|
|
|
|
if(CTR.Command != COMMAND_DEL_SECDATA)
|
|
{
|
|
for(int i = 0, bfi = sbfi; i < numsec; i++)
|
|
{
|
|
const int next_bfi = Buffers[bfi].Next;
|
|
DT.BufList[i] = bfi;
|
|
if(CTR.Command == COMMAND_GETDEL_SECDATA)
|
|
Partition_UnlinkBuffer(pnum, bfi);
|
|
bfi = next_bfi;
|
|
}
|
|
|
|
CDStatusResults(false, STATUS_DTREQ);
|
|
|
|
DT.Writing = false;
|
|
DT.NeedBufFree = (CTR.Command == COMMAND_GETDEL_SECDATA);
|
|
DT.CurBufIndex = 0;
|
|
DT.BufCount = numsec;
|
|
|
|
DT_SetIBOffsCount(Buffers[DT.BufList[0]].Data);
|
|
|
|
DT.TotalCounter = 0;
|
|
|
|
DT.FIFO_RP = 0;
|
|
DT.FIFO_WP = 0;
|
|
DT.FIFO_In = 0;
|
|
|
|
for(unsigned i = 0; i < 5; i++)
|
|
DT_ReadIntoFIFO();
|
|
|
|
DT.Active = true;
|
|
}
|
|
else
|
|
CDStatusResults();
|
|
|
|
//
|
|
//
|
|
//
|
|
if(CTR.Command == COMMAND_DEL_SECDATA)
|
|
{
|
|
for(int i = 0, bfi = sbfi; i < numsec; i++)
|
|
{
|
|
const int next_bfi = Buffers[bfi].Next;
|
|
Partition_UnlinkBuffer(pnum, bfi);
|
|
Buffer_Free(bfi);
|
|
bfi = next_bfi;
|
|
}
|
|
|
|
CMD_EAT_CLOCKS(485);
|
|
TriggerIRQ(HIRQ_EHST);
|
|
}
|
|
else
|
|
{
|
|
CMD_EAT_CLOCKS(460);
|
|
TriggerIRQ(HIRQ_DRDY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_PUT_SECDATA) // = 0x64,
|
|
{
|
|
unsigned fnum, numsec;
|
|
|
|
fnum = CTR.CD[2] >> 8;
|
|
numsec = CTR.CD[3];
|
|
|
|
if(fnum >= 0x18)
|
|
{
|
|
CDStatusResults(true);
|
|
}
|
|
else if(numsec == 0 || numsec > FreeBufferCount || DT.Active)
|
|
{
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
}
|
|
else
|
|
{
|
|
if(CDDevConn == fnum)
|
|
CDDevConn = 0xFF;
|
|
|
|
CDStatusResults(false, STATUS_DTREQ);
|
|
|
|
DT.Writing = true;
|
|
DT.NeedBufFree = false;
|
|
DT.FNum = fnum;
|
|
DT.CurBufIndex = 0;
|
|
for(unsigned i = 0; i < numsec; i++)
|
|
{
|
|
DT.BufList[i] = Buffer_Allocate(true);
|
|
}
|
|
|
|
DT.BufCount = numsec;
|
|
|
|
DT_SetIBOffsCount(NULL);
|
|
|
|
DT.TotalCounter = 0;
|
|
|
|
DT.FIFO_RP = 0;
|
|
DT.FIFO_WP = 0;
|
|
DT.FIFO_In = 0;
|
|
DT.Active = true;
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(300);
|
|
TriggerIRQ(HIRQ_DRDY);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_COPY_SECDATA || CTR.Command == COMMAND_MOVE_SECDATA) // = 0x65, =0x66
|
|
{
|
|
unsigned dst_fnum, src_pnum;
|
|
|
|
dst_fnum = CTR.CD[0] & 0xFF;
|
|
src_pnum = CTR.CD[2] >> 8;
|
|
|
|
if(src_pnum >= 0x18 || dst_fnum >= 0x18)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
int src_offs, numsec;
|
|
|
|
src_offs = CTR.CD[1];
|
|
numsec = CTR.CD[3];
|
|
|
|
if(src_offs == 0xFFFF)
|
|
src_offs = Partitions[src_pnum].Count - 1;
|
|
|
|
if(numsec == 0xFFFF)
|
|
numsec = Partitions[src_pnum].Count - src_offs;
|
|
|
|
if(DT.Active || numsec <= 0 || (numsec > (int)FreeBufferCount && CTR.Command != COMMAND_MOVE_SECDATA) || src_offs < 0 || (src_offs + numsec) > (int)Partitions[src_pnum].Count)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else
|
|
{
|
|
if(CDDevConn == dst_fnum)
|
|
CDDevConn = 0xFF;
|
|
|
|
for(int i = 0, bfi = Partition_GetBuffer(src_pnum, src_offs); i < numsec; i++)
|
|
{
|
|
const uint8* bufdata = Buffers[bfi].Data;
|
|
const int next_bfi = Buffers[bfi].Next;
|
|
|
|
if(CTR.Command == COMMAND_MOVE_SECDATA)
|
|
{
|
|
Partition_UnlinkBuffer(src_pnum, bfi);
|
|
FilterBuf(dst_fnum, bfi);
|
|
}
|
|
else
|
|
{
|
|
int abfi = Buffer_Allocate(false);
|
|
|
|
memcpy(Buffers[abfi].Data, bufdata, sizeof(Buffers[abfi].Data));
|
|
|
|
FilterBuf(dst_fnum, abfi);
|
|
}
|
|
|
|
bfi = next_bfi;
|
|
}
|
|
CDStatusResults();
|
|
//
|
|
//
|
|
// TODO: Accurate timing(while not blocking other command execution and sector reading). Note that "move sector data" is much faster than "copy sector data".
|
|
CMD_EAT_CLOCKS(300);
|
|
if(FreeBufferCount == 0)
|
|
TriggerIRQ(HIRQ_BFUL);
|
|
TriggerIRQ(HIRQ_ECPY);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_COPYERR) // = 0x67,
|
|
{
|
|
// TODO: Implement if we ever implement proper asynch copy/moving
|
|
BasicResults((MakeBaseStatus() << 8) | 0x00, 0, 0, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_CHANGE_DIR) // = 0x70,
|
|
{
|
|
bool reject;
|
|
uint8 fnum;
|
|
uint32 fileid;
|
|
uint32 fiaoffs;
|
|
|
|
fnum = (CTR.CD[2] >> 8);
|
|
fileid = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
fiaoffs = (fileid < 2) ? fileid : (2 + fileid - FLS.FileInfoOffs);
|
|
|
|
reject = false;
|
|
|
|
if(fnum >= 0x18)
|
|
reject = true;
|
|
|
|
if(fileid == 0xFFFFFF)
|
|
{
|
|
if(!RootDirInfoValid)
|
|
reject = true;
|
|
}
|
|
else
|
|
{
|
|
if(!FileInfoValid)
|
|
reject = true;
|
|
else if(fileid >= 2 && (fileid < FLS.FileInfoOffs || fileid >= (FLS.FileInfoOffs + FLS.FileInfoValidCount)))
|
|
reject = true;
|
|
else if(!(FileInfo[fiaoffs].attr & 0x2)) // FIXME: test XA directory flag too?
|
|
reject = true;
|
|
}
|
|
|
|
if(FLS.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else if(reject)
|
|
CDStatusResults(true);
|
|
else if(fileid == 0) // NOP, kind of apparently(test after READ_DIR with a largeish file start id)...
|
|
{
|
|
CDStatusResults();
|
|
CMD_EAT_CLOCKS(400);
|
|
TriggerIRQ(HIRQ_EFLS);
|
|
}
|
|
else
|
|
{
|
|
CDStatusResults();
|
|
//
|
|
// Check (attr & 2) first? and & 0x80 for XA?
|
|
//
|
|
const FileInfoS* fi;
|
|
|
|
if(fileid == 0xFFFFFF)
|
|
fi = &RootDirInfo;
|
|
else
|
|
fi = &FileInfo[fiaoffs];
|
|
|
|
Partition_Clear(fnum);
|
|
|
|
SetCDDeviceConn(fnum);
|
|
Filter_SetTrueConn(fnum, fnum);
|
|
Filter_SetFalseConn(fnum, 0xFF);
|
|
Filter_SetRange(fnum, fi->fad(), (fi->size() + 2047) >> 11); // TODO: maybe remove + 2047, actual Saturn drive seems buggy...
|
|
Filters[fnum].Mode = FilterS::MODE_SEL_FADR;
|
|
Filters[fnum].File = fi->fnum;
|
|
|
|
Filters[fnum].Channel = 0;
|
|
Filters[fnum].SubMode = 0;
|
|
Filters[fnum].SubModeMask = 0;
|
|
Filters[fnum].CInfo = 0;
|
|
Filters[fnum].CInfoMask = 0;
|
|
|
|
FLS.pnum = fnum;
|
|
FLS.total_max = fi->size();
|
|
FLS.FileInfoOffs = 2;
|
|
FLS.DoAuth = false;
|
|
FLS.Active = true;
|
|
|
|
ClearPendingSec();
|
|
StartSeek(0x800000 | fi->fad());
|
|
|
|
CurPlayEnd = 0;
|
|
CurPlayRepeat = 0;
|
|
PlayRepeatCounter = 0;
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_READ_DIR) // = 0x71,
|
|
{
|
|
uint8 fnum;
|
|
uint32 start_fileid;
|
|
|
|
fnum = (CTR.CD[2] >> 8);
|
|
start_fileid = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
|
|
if(FLS.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else if(fnum >= 0x18 || !FileInfoValid)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
CDStatusResults();
|
|
//
|
|
// Check (attr & 2) first? and & 0x80 for XA?
|
|
//
|
|
const FileInfoS* fi = &FileInfo[0];
|
|
|
|
if(start_fileid < FLS.FileInfoOffs || start_fileid >= (FLS.FileInfoOffs + FLS.FileInfoValidCount))
|
|
start_fileid = 2;
|
|
|
|
SetCDDeviceConn(fnum);
|
|
Filter_SetTrueConn(fnum, fnum);
|
|
Filter_SetFalseConn(fnum, 0xFF);
|
|
Filter_SetRange(fnum, fi->fad(), (fi->size() + 2047) >> 11);
|
|
Filters[fnum].Mode = FilterS::MODE_SEL_FADR;
|
|
|
|
FLS.pnum = fnum;
|
|
FLS.total_max = fi->size();
|
|
FLS.FileInfoOffs = start_fileid;
|
|
FLS.DoAuth = false;
|
|
FLS.Active = true;
|
|
|
|
ClearPendingSec();
|
|
StartSeek(0x800000 | fi->fad());
|
|
|
|
CurPlayEnd = 0;
|
|
CurPlayRepeat = 0;
|
|
PlayRepeatCounter = 0;
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FSSCOPE) // = 0x72,
|
|
{
|
|
if(FLS.Active) // Maybe add a specific check for directory reading? (Will have to if we chain READ_FILE into FLS.Active someday for whatever reason)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else if(!FileInfoValid)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
// FIXME: Not sure about the [0].fad == [1].fad root dir thing... Might instead be 0x01 to note that the number of files in the directory
|
|
// can be held without using Read Directory?
|
|
BasicResults((MakeBaseStatus() << 8),
|
|
FLS.FileInfoValidCount,
|
|
((FileInfo[0].fad() == FileInfo[1].fad()) << 8) | (FLS.FileInfoOffs >> 16),
|
|
FLS.FileInfoOffs);
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_GET_FINFO) // = 0x73,
|
|
{
|
|
if(FLS.Active || DT.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else if(!FileInfoValid)
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
uint32 fileid;
|
|
|
|
fileid = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
|
|
if(fileid != 0xFFFFFF && (fileid >= 2 && (fileid < FLS.FileInfoOffs || fileid >= (FLS.FileInfoOffs + FLS.FileInfoValidCount))))
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
{
|
|
const uint32 fiaoffs = (fileid < 2) ? fileid : (2 + fileid - FLS.FileInfoOffs);
|
|
|
|
DT.CurBufIndex = 0;
|
|
DT.BufCount = 1;
|
|
|
|
if(fileid == 0xFFFFFF)
|
|
{
|
|
DT.InBufOffs = 6 * 2;
|
|
DT.InBufCounter = 6 * FLS.FileInfoValidCount;
|
|
}
|
|
else
|
|
{
|
|
DT.InBufOffs = 6 * fiaoffs;
|
|
DT.InBufCounter = 6;
|
|
}
|
|
|
|
DT.TotalCounter = 0;
|
|
|
|
DT.FIFO_RP = 0;
|
|
DT.FIFO_WP = 0;
|
|
DT.FIFO_In = 0;
|
|
|
|
DT.BufList[0] = 0xF0;
|
|
|
|
DT.Writing = false;
|
|
DT.NeedBufFree = false;
|
|
DT.Active = true;
|
|
|
|
BasicResults(MakeBaseStatus(false, STATUS_DTREQ) << 8, DT.InBufCounter, 0, 0);
|
|
}
|
|
//
|
|
//
|
|
//
|
|
CMD_EAT_CLOCKS(128);
|
|
TriggerIRQ(HIRQ_DRDY);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_READ_FILE) // = 0x74,
|
|
{
|
|
uint32 offset;
|
|
uint32 fileid;
|
|
uint8 fnum;
|
|
|
|
offset = ((CTR.CD[0] & 0xFF) << 16) | CTR.CD[1];
|
|
fileid = ((CTR.CD[2] & 0xFF) << 16) | CTR.CD[3];
|
|
fnum = CTR.CD[2] >> 8;
|
|
|
|
if(FLS.Active)
|
|
CDStatusResults(false, STATUS_WAIT);
|
|
else if(fnum >= 0x18 || !FileInfoValid || (fileid >= 2 && (fileid < FLS.FileInfoOffs || fileid >= (FLS.FileInfoOffs + FLS.FileInfoValidCount))))
|
|
CDStatusResults(true);
|
|
else
|
|
{
|
|
CDStatusResults();
|
|
|
|
Partition_Clear(fnum);
|
|
|
|
//printf("DT Meow READ: 0x%08x 0x%08x --- offs=0x%08x\n", FileInfo[fileid].fad(), FileInfo[fileid].size(), offset);
|
|
const uint32 fiaoffs = (fileid < 2) ? fileid : (2 + fileid - FLS.FileInfoOffs);
|
|
uint32 start_fad = (FileInfo[fiaoffs].fad() + offset) & 0xFFFFFF;
|
|
uint32 sec_count = ((FileInfo[fiaoffs].size() + 2047) >> 11) - offset; // FIXME: Check offset versus ifile size.
|
|
|
|
ClearPendingSec();
|
|
StartSeek(start_fad | 0x800000);
|
|
|
|
PlayEndIRQType = HIRQ_EFLS;
|
|
CurPlayEnd = 0x800000 | ((start_fad + sec_count) & 0x7FFFFF);
|
|
CurPlayRepeat = 0;
|
|
PlayRepeatCounter = 0;
|
|
|
|
SetCDDeviceConn(fnum);
|
|
Filter_SetTrueConn(fnum, fnum);
|
|
Filter_SetFalseConn(fnum, 0xFF);
|
|
Filter_SetRange(fnum, start_fad, sec_count); // Not sure if correct for XA interleaved files...
|
|
|
|
Filters[fnum].Mode = FilterS::MODE_SEL_FADR | FilterS::MODE_SEL_FILE;
|
|
Filters[fnum].File = FileInfo[fiaoffs].fnum;
|
|
|
|
Filters[fnum].Channel = 0;
|
|
Filters[fnum].SubMode = 0;
|
|
Filters[fnum].SubModeMask = 0;
|
|
Filters[fnum].CInfo = 0;
|
|
Filters[fnum].CInfoMask = 0;
|
|
}
|
|
}
|
|
//
|
|
//
|
|
//
|
|
else if(CTR.Command == COMMAND_ABORT_FILE) // = 0x75,
|
|
{
|
|
// TODO: Does tray opening during a file operation trigger HIRQ_EFLS?
|
|
CDStatusResults();
|
|
|
|
FLS.Abort = true;
|
|
}
|
|
else
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] Unknown Command: 0x%04x 0x%04x 0x%04x 0x%04x --- HIRQ=0x%04x, HIRQ_Mask=0x%04x\n", CTR.CD[0], CTR.CD[1], CTR.CD[2], CTR.CD[3], HIRQ, HIRQ_Mask);
|
|
|
|
ResultsRead = false;
|
|
CommandPending = false;
|
|
}
|
|
continue;
|
|
//
|
|
//
|
|
//
|
|
case -1 + CommandPhaseBias:
|
|
|
|
CMD_EAT_CLOCKS(4880000);
|
|
|
|
StandbyTime = 0;
|
|
ECCEnable = 0xFF;
|
|
RetryCount = 1;
|
|
CommandPending = false;
|
|
ResultsRead = true;
|
|
|
|
memset(&DT, 0, sizeof(DT));
|
|
|
|
PlayCmdStartPos = 0; // TODO: Test for correct value.
|
|
PlayCmdEndPos = 0; // TODO: Test for correct value.
|
|
PlayCmdRepCnt = 0; // TODO: ...
|
|
|
|
CurPlayRepeat = 0; // TODO: . . .
|
|
PlayRepeatCounter = 0;
|
|
|
|
DriveCounter = (int64)1000 << 32;
|
|
DrivePhase = DRIVEPHASE_EJECTED_WAITING;
|
|
|
|
memset(TOC_Buffer, 0xFF, sizeof(TOC_Buffer)); // TODO: confirm 0xFF(or 0x00?)
|
|
|
|
AuthDiscType = 0x00;
|
|
FileInfoValid = false;
|
|
RootDirInfoValid = false;
|
|
PeriodicIdleCounter = PeriodicIdleCounter_Reload;
|
|
|
|
CurPosInfo.status = STATUS_OPEN;
|
|
CurPosInfo.is_cdrom = false;
|
|
CurPosInfo.fad = 0xFFFFFF;
|
|
CurPosInfo.rel_fad = 0xFFFFFF;
|
|
CurPosInfo.ctrl_adr = 0xFF;
|
|
CurPosInfo.idx = 0xFF;
|
|
CurPosInfo.tno = 0xFF;
|
|
|
|
CDDABuf_WP = 0;
|
|
CDDABuf_RP = 0;
|
|
CDDABuf_Count = 0;
|
|
|
|
//
|
|
//
|
|
RootDirInfoValid = false;
|
|
//
|
|
//
|
|
SWReset();
|
|
|
|
CurPosInfo.status = STATUS_BUSY; // FIXME(so it's set long enough from POV of program)
|
|
CurPosInfo.is_cdrom = false;
|
|
CurSector = 0;
|
|
|
|
Results[0] = 0x0043;
|
|
Results[1] = 0x4442;
|
|
Results[2] = 0x4c4f;
|
|
Results[3] = 0x434b;
|
|
ResultsRead = false;
|
|
TriggerIRQ(HIRQ_CMOK | HIRQ_DCHG | HIRQ_ESEL | HIRQ_EHST | HIRQ_MPED | HIRQ_ECPY | HIRQ_EFLS);
|
|
continue;
|
|
}
|
|
}
|
|
CommandGetOut:;
|
|
//
|
|
//
|
|
//
|
|
|
|
//RunFLS(clocks);
|
|
}
|
|
|
|
|
|
assert(DriveCounter > 0);
|
|
|
|
{
|
|
int64 net = -CommandClockCounter;
|
|
|
|
if(DriveCounter < net)
|
|
net = DriveCounter;
|
|
|
|
if(PeriodicIdleCounter < net)
|
|
net = PeriodicIdleCounter;
|
|
|
|
return timestamp + (net + CDB_ClockRatio - 1) / CDB_ClockRatio;
|
|
}
|
|
}
|
|
|
|
void CDB_ResetTS(void)
|
|
{
|
|
lastts = 0;
|
|
}
|
|
|
|
|
|
uint16 CDB_Read(uint32 offset)
|
|
{
|
|
uint16 ret = 0; //0xFFFF;
|
|
|
|
#if 1
|
|
if(offset >= 0x6 && offset <= 0x9 && CommandPending)
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] Read from register 0x%01x while busy processing command!\n", offset);
|
|
}
|
|
#endif
|
|
|
|
switch(offset)
|
|
{
|
|
case 0x0:
|
|
if(DT.Active && !DT.Writing)
|
|
{
|
|
if(DT.InBufCounter > 0)
|
|
DT_ReadIntoFIFO();
|
|
|
|
if(MDFN_UNLIKELY(!DT.FIFO_In))
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] DT FIFO underflow.\n");
|
|
}
|
|
|
|
ret = DT.FIFO[DT.FIFO_RP];
|
|
DT.FIFO_RP = (DT.FIFO_RP + 1) % (sizeof(DT.FIFO) / sizeof(DT.FIFO[0]));
|
|
DT.FIFO_In -= (bool)DT.FIFO_In;
|
|
}
|
|
break;
|
|
|
|
case 0x2: // HIRQ
|
|
ret = HIRQ;
|
|
break;
|
|
|
|
case 0x3: // HIRQ mask
|
|
ret = HIRQ_Mask;
|
|
break;
|
|
|
|
case 0x6: ret = Results[0]; /*if(!ResultsRead) printf(" [CDB] Result0 Read: 0x%04x\n", ret);*/ break;
|
|
case 0x7: ret = Results[1]; /*if(!ResultsRead) printf(" [CDB] Result1 Read: 0x%04x\n", ret);*/ break;
|
|
case 0x8: ret = Results[2]; /*if(!ResultsRead) printf(" [CDB] Result2 Read: 0x%04x\n", ret);*/ break;
|
|
case 0x9: ret = Results[3]; /*if(!ResultsRead) printf(" [CDB] Result3 Read: 0x%04x\n", ret);*/ ResultsRead = true; break;
|
|
}
|
|
|
|
//if(offset == 0)
|
|
// fprintf(stderr, "Read: %02x %04x\n", offset, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void CDB_Write_DBM(uint32 offset, uint16 DB, uint16 mask)
|
|
{
|
|
sscpu_timestamp_t nt = CDB_Update(SH7095_mem_timestamp);
|
|
|
|
//SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] Write %02x %04x %04x\n", offset, DB, mask);
|
|
|
|
#if 1
|
|
if(offset >= 0x6 && offset <= 0x9 && CommandPending)
|
|
{
|
|
SS_DBG(SS_DBG_WARNING | SS_DBG_CDB, "[CDB] Write to register 0x%01x(DB=0x%04x, mask=0x%04x) while busy processing command!\n", offset, DB, mask);
|
|
}
|
|
#endif
|
|
|
|
switch(offset)
|
|
{
|
|
case 0x0:
|
|
if(DT.Active && DT.Writing)
|
|
{
|
|
if(DT.InBufCounter > 0)
|
|
{
|
|
DT.FIFO[DT.FIFO_WP] = (DT.FIFO[DT.FIFO_WP] &~ mask) | (DB & mask);
|
|
DT.FIFO_WP = (DT.FIFO_WP + 1) % (sizeof(DT.FIFO) / sizeof(DT.FIFO[0]));
|
|
DT.FIFO_In++;
|
|
|
|
MDFN_en16msb(&Buffers[DT.BufList[DT.CurBufIndex]].Data[DT.InBufOffs << 1], DT.FIFO[DT.FIFO_RP]);
|
|
DT.InBufOffs++;
|
|
DT.FIFO_RP = (DT.FIFO_RP + 1) % (sizeof(DT.FIFO) / sizeof(DT.FIFO[0]));
|
|
DT.FIFO_In--;
|
|
|
|
DT.InBufCounter--;
|
|
DT.TotalCounter++;
|
|
|
|
if(!DT.InBufCounter)
|
|
{
|
|
DT.CurBufIndex++;
|
|
if(DT.CurBufIndex < DT.BufCount)
|
|
{
|
|
DT_SetIBOffsCount(NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x2:
|
|
HIRQ = HIRQ & (DB | ~mask);
|
|
RecalcIRQOut();
|
|
break;
|
|
|
|
case 0x3:
|
|
HIRQ_Mask = (HIRQ_Mask &~ mask) | (DB & mask);
|
|
RecalcIRQOut();
|
|
break;
|
|
|
|
case 0x6:
|
|
CData[0] = (CData[0] &~ mask) | (DB & mask);
|
|
break;
|
|
|
|
case 0x7:
|
|
CData[1] = (CData[1] &~ mask) | (DB & mask);
|
|
break;
|
|
|
|
case 0x8:
|
|
CData[2] = (CData[2] &~ mask) | (DB & mask);
|
|
break;
|
|
|
|
case 0x9:
|
|
CData[3] = (CData[3] &~ mask) | (DB & mask);
|
|
if(mask == 0xFFFF)
|
|
{
|
|
CommandPending = true;
|
|
nt = SH7095_mem_timestamp + 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
SS_SetEventNT(&events[SS_EVENT_CDB], nt);
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|