mirror of
https://github.com/akuker/RASCSI.git
synced 2025-01-10 17:30:47 +00:00
2396 lines
48 KiB
C++
2396 lines
48 KiB
C++
//---------------------------------------------------------------------------
|
||
//
|
||
// X68000 EMULATOR "XM6"
|
||
//
|
||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||
// Copyright (C) 2014-2020 GIMONS
|
||
//
|
||
// XM6i
|
||
// Copyright (C) 2010-2015 isaki@NetBSD.org
|
||
// Copyright (C) 2010 Y.Sugahara
|
||
//
|
||
// Imported sava's Anex86/T98Next image and MO format support patch.
|
||
// Imported NetBSD support and some optimisation patch by Rin Okuyama.
|
||
// Comments translated to english by akuker.
|
||
//
|
||
// [ Disk ]
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
#include "os.h"
|
||
#include "xm6.h"
|
||
#include "filepath.h"
|
||
#include "fileio.h"
|
||
#ifdef RASCSI
|
||
#include "gpiobus.h"
|
||
#ifndef BAREMETAL
|
||
#include "ctapdriver.h"
|
||
#endif // BAREMETAL
|
||
#include "cfilesystem.h"
|
||
#include "disk.h"
|
||
#else
|
||
#include "vm.h"
|
||
#include "disk.h"
|
||
#include "windrv.h"
|
||
#include "ctapdriver.h"
|
||
#include "mfc_com.h"
|
||
#include "mfc_host.h"
|
||
#endif // RASCSI
|
||
|
||
//===========================================================================
|
||
//
|
||
// Disk
|
||
//
|
||
//===========================================================================
|
||
|
||
|
||
//===========================================================================
|
||
//
|
||
// Disk Track
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Constructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DiskTrack::DiskTrack()
|
||
{
|
||
// Initialization of internal information
|
||
dt.track = 0;
|
||
dt.size = 0;
|
||
dt.sectors = 0;
|
||
dt.raw = FALSE;
|
||
dt.init = FALSE;
|
||
dt.changed = FALSE;
|
||
dt.length = 0;
|
||
dt.buffer = NULL;
|
||
dt.maplen = 0;
|
||
dt.changemap = NULL;
|
||
dt.imgoffset = 0;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Destructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DiskTrack::~DiskTrack()
|
||
{
|
||
// Release memory, but do not save automatically
|
||
if (dt.buffer) {
|
||
free(dt.buffer);
|
||
dt.buffer = NULL;
|
||
}
|
||
if (dt.changemap) {
|
||
free(dt.changemap);
|
||
dt.changemap = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Initialization
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL DiskTrack::Init(
|
||
int track, int size, int sectors, BOOL raw, off64_t imgoff)
|
||
{
|
||
ASSERT(track >= 0);
|
||
ASSERT((size >= 8) && (size <= 11));
|
||
ASSERT((sectors > 0) && (sectors <= 0x100));
|
||
ASSERT(imgoff >= 0);
|
||
|
||
// Set Parameters
|
||
dt.track = track;
|
||
dt.size = size;
|
||
dt.sectors = sectors;
|
||
dt.raw = raw;
|
||
|
||
// Not initialized (needs to be loaded)
|
||
dt.init = FALSE;
|
||
|
||
// Not Changed
|
||
dt.changed = FALSE;
|
||
|
||
// Offset to actual data
|
||
dt.imgoffset = imgoff;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Load
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskTrack::Load(const Filepath& path)
|
||
{
|
||
Fileio fio;
|
||
off64_t offset;
|
||
int i;
|
||
int length;
|
||
|
||
ASSERT(this);
|
||
|
||
// Not needed if already loaded
|
||
if (dt.init) {
|
||
ASSERT(dt.buffer);
|
||
ASSERT(dt.changemap);
|
||
return TRUE;
|
||
}
|
||
|
||
// Calculate offset (previous tracks are considered to
|
||
// hold 256 sectors)
|
||
offset = ((off64_t)dt.track << 8);
|
||
if (dt.raw) {
|
||
ASSERT(dt.size == 11);
|
||
offset *= 0x930;
|
||
offset += 0x10;
|
||
} else {
|
||
offset <<= dt.size;
|
||
}
|
||
|
||
// Add offset to real image
|
||
offset += dt.imgoffset;
|
||
|
||
// Calculate length (data size of this track)
|
||
length = dt.sectors << dt.size;
|
||
|
||
// Allocate buffer memory
|
||
ASSERT((dt.size >= 8) && (dt.size <= 11));
|
||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||
|
||
if (dt.buffer == NULL) {
|
||
#if defined(RASCSI) && !defined(BAREMETAL)
|
||
posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512);
|
||
#else
|
||
dt.buffer = (BYTE *)malloc(length * sizeof(BYTE));
|
||
#endif // RASCSI && !BAREMETAL
|
||
dt.length = length;
|
||
}
|
||
|
||
if (!dt.buffer) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Reallocate if the buffer length is different
|
||
if (dt.length != (DWORD)length) {
|
||
free(dt.buffer);
|
||
#if defined(RASCSI) && !defined(BAREMETAL)
|
||
posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512);
|
||
#else
|
||
dt.buffer = (BYTE *)malloc(length * sizeof(BYTE));
|
||
#endif // RASCSI && !BAREMETAL
|
||
dt.length = length;
|
||
}
|
||
|
||
// Reserve change map memory
|
||
if (dt.changemap == NULL) {
|
||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||
dt.maplen = dt.sectors;
|
||
}
|
||
|
||
if (!dt.changemap) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Reallocate if the buffer length is different
|
||
if (dt.maplen != (DWORD)dt.sectors) {
|
||
free(dt.changemap);
|
||
dt.changemap = (BOOL *)malloc(dt.sectors * sizeof(BOOL));
|
||
dt.maplen = dt.sectors;
|
||
}
|
||
|
||
// Clear changemap
|
||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||
|
||
// Read from File
|
||
#if defined(RASCSI) && !defined(BAREMETAL)
|
||
if (!fio.OpenDIO(path, Fileio::ReadOnly)) {
|
||
#else
|
||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||
#endif // RASCSI && !BAREMETAL
|
||
return FALSE;
|
||
}
|
||
if (dt.raw) {
|
||
// Split Reading
|
||
for (i = 0; i < dt.sectors; i++) {
|
||
// Seek
|
||
if (!fio.Seek(offset)) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
|
||
// Read
|
||
if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
|
||
// Next offset
|
||
offset += 0x930;
|
||
}
|
||
} else {
|
||
// Continuous reading
|
||
if (!fio.Seek(offset)) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
if (!fio.Read(dt.buffer, length)) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
}
|
||
fio.Close();
|
||
|
||
// Set a flag and end normally
|
||
dt.init = TRUE;
|
||
dt.changed = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Save
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskTrack::Save(const Filepath& path)
|
||
{
|
||
off64_t offset;
|
||
int i;
|
||
int j;
|
||
Fileio fio;
|
||
int length;
|
||
int total;
|
||
|
||
ASSERT(this);
|
||
|
||
// Not needed if not initialized
|
||
if (!dt.init) {
|
||
return TRUE;
|
||
}
|
||
|
||
// Not needed unless changed
|
||
if (!dt.changed) {
|
||
return TRUE;
|
||
}
|
||
|
||
// Need to write
|
||
ASSERT(dt.buffer);
|
||
ASSERT(dt.changemap);
|
||
ASSERT((dt.size >= 8) && (dt.size <= 11));
|
||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||
|
||
// Writing in RAW mode is not allowed
|
||
ASSERT(!dt.raw);
|
||
|
||
// Calculate offset (previous tracks are considered to hold 256
|
||
offset = ((off64_t)dt.track << 8);
|
||
offset <<= dt.size;
|
||
|
||
// Add offset to real image
|
||
offset += dt.imgoffset;
|
||
|
||
// Calculate length per sector
|
||
length = 1 << dt.size;
|
||
|
||
// Open file
|
||
if (!fio.Open(path, Fileio::ReadWrite)) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Partial write loop
|
||
for (i = 0; i < dt.sectors;) {
|
||
// If changed
|
||
if (dt.changemap[i]) {
|
||
// Initialize write size
|
||
total = 0;
|
||
|
||
// Seek
|
||
if (!fio.Seek(offset + ((off64_t)i << dt.size))) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
|
||
// Consectutive sector length
|
||
for (j = i; j < dt.sectors; j++) {
|
||
// end when interrupted
|
||
if (!dt.changemap[j]) {
|
||
break;
|
||
}
|
||
|
||
// Add one sector
|
||
total += length;
|
||
}
|
||
|
||
// Write
|
||
if (!fio.Write(&dt.buffer[i << dt.size], total)) {
|
||
fio.Close();
|
||
return FALSE;
|
||
}
|
||
|
||
// To unmodified sector
|
||
i = j;
|
||
} else {
|
||
// Next Sector
|
||
i++;
|
||
}
|
||
}
|
||
|
||
// Close
|
||
fio.Close();
|
||
|
||
// Drop the change flag and exit
|
||
memset(dt.changemap, 0x00, dt.sectors * sizeof(BOOL));
|
||
dt.changed = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Read Sector
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskTrack::Read(BYTE *buf, int sec) const
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
ASSERT((sec >= 0) & (sec < 0x100));
|
||
|
||
LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec);
|
||
// Error if not initialized
|
||
if (!dt.init) {
|
||
return FALSE;
|
||
}
|
||
|
||
// // Error if the number of sectors exceeds the valid number
|
||
if (sec >= dt.sectors) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Copy
|
||
ASSERT(dt.buffer);
|
||
ASSERT((dt.size >= 8) && (dt.size <= 11));
|
||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||
memcpy(buf, &dt.buffer[(off64_t)sec << dt.size], (off64_t)1 << dt.size);
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Write Sector
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskTrack::Write(const BYTE *buf, int sec)
|
||
{
|
||
int offset;
|
||
int length;
|
||
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
ASSERT((sec >= 0) & (sec < 0x100));
|
||
ASSERT(!dt.raw);
|
||
|
||
// Error if not initialized
|
||
if (!dt.init) {
|
||
return FALSE;
|
||
}
|
||
|
||
// // Error if the number of sectors exceeds the valid number
|
||
if (sec >= dt.sectors) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Calculate offset and length
|
||
offset = sec << dt.size;
|
||
length = 1 << dt.size;
|
||
|
||
// Compare
|
||
ASSERT(dt.buffer);
|
||
ASSERT((dt.size >= 8) && (dt.size <= 11));
|
||
ASSERT((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||
if (memcmp(buf, &dt.buffer[offset], length) == 0) {
|
||
// 同じものを書き込もうとしているので、正常終了
|
||
return TRUE;
|
||
}
|
||
|
||
// Copy, change
|
||
memcpy(&dt.buffer[offset], buf, length);
|
||
dt.changemap[sec] = TRUE;
|
||
dt.changed = TRUE;
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//===========================================================================
|
||
//
|
||
// Disk Cache
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Constructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DiskCache::DiskCache(
|
||
const Filepath& path, int size, int blocks, off64_t imgoff)
|
||
{
|
||
int i;
|
||
|
||
ASSERT((size >= 8) && (size <= 11));
|
||
ASSERT(blocks > 0);
|
||
ASSERT(imgoff >= 0);
|
||
|
||
// Cache work
|
||
for (i = 0; i < CacheMax; i++) {
|
||
cache[i].disktrk = NULL;
|
||
cache[i].serial = 0;
|
||
}
|
||
|
||
// Other
|
||
serial = 0;
|
||
sec_path = path;
|
||
sec_size = size;
|
||
sec_blocks = blocks;
|
||
cd_raw = FALSE;
|
||
imgoffset = imgoff;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Destructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DiskCache::~DiskCache()
|
||
{
|
||
// Clear the track
|
||
Clear();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// RAW Mode Setting
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL DiskCache::SetRawMode(BOOL raw)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(sec_size == 11);
|
||
|
||
// Configuration
|
||
cd_raw = raw;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Save
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskCache::Save()
|
||
{
|
||
int i;
|
||
|
||
ASSERT(this);
|
||
|
||
// Save track
|
||
for (i = 0; i < CacheMax; i++) {
|
||
// Is it a valid track?
|
||
if (cache[i].disktrk) {
|
||
// Save
|
||
if (!cache[i].disktrk->Save(sec_path)) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get disk cache information
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskCache::GetCache(int index, int& track, DWORD& aserial) const
|
||
{
|
||
ASSERT(this);
|
||
ASSERT((index >= 0) && (index < CacheMax));
|
||
|
||
// FALSE if unused
|
||
if (!cache[index].disktrk) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Set track and serial
|
||
track = cache[index].disktrk->GetTrack();
|
||
aserial = cache[index].serial;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Clear
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL DiskCache::Clear()
|
||
{
|
||
int i;
|
||
|
||
ASSERT(this);
|
||
|
||
// Free the cache
|
||
for (i = 0; i < CacheMax; i++) {
|
||
if (cache[i].disktrk) {
|
||
delete cache[i].disktrk;
|
||
cache[i].disktrk = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Sector Read
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskCache::Read(BYTE *buf, int block)
|
||
{
|
||
int track;
|
||
DiskTrack *disktrk;
|
||
|
||
ASSERT(this);
|
||
ASSERT(sec_size != 0);
|
||
|
||
// Update first
|
||
Update();
|
||
|
||
// Calculate track (fixed to 256 sectors/track)
|
||
track = block >> 8;
|
||
|
||
// Get the track data
|
||
disktrk = Assign(track);
|
||
if (!disktrk) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Read the track data to the cache
|
||
return disktrk->Read(buf, (BYTE)block);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Sector write
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskCache::Write(const BYTE *buf, int block)
|
||
{
|
||
int track;
|
||
DiskTrack *disktrk;
|
||
|
||
ASSERT(this);
|
||
ASSERT(sec_size != 0);
|
||
|
||
// Update first
|
||
Update();
|
||
|
||
// Calculate track (fixed to 256 sectors/track)
|
||
track = block >> 8;
|
||
|
||
// Get that track data
|
||
disktrk = Assign(track);
|
||
if (!disktrk) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Write the data to the cache
|
||
return disktrk->Write(buf, (BYTE)block);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Track Assignment
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DiskTrack* FASTCALL DiskCache::Assign(int track)
|
||
{
|
||
int i;
|
||
int c;
|
||
DWORD s;
|
||
DiskTrack *disktrk;
|
||
|
||
ASSERT(this);
|
||
ASSERT(sec_size != 0);
|
||
ASSERT(track >= 0);
|
||
|
||
// First, check if it is already assigned
|
||
for (i = 0; i < CacheMax; i++) {
|
||
if (cache[i].disktrk) {
|
||
if (cache[i].disktrk->GetTrack() == track) {
|
||
// Track match
|
||
cache[i].serial = serial;
|
||
return cache[i].disktrk;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Next, check for empty
|
||
for (i = 0; i < CacheMax; i++) {
|
||
if (!cache[i].disktrk) {
|
||
// Try loading
|
||
if (Load(i, track)) {
|
||
// Success loading
|
||
cache[i].serial = serial;
|
||
return cache[i].disktrk;
|
||
}
|
||
|
||
// Load failed
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
// Finally, find the youngest serial number and delete it
|
||
|
||
// Set index 0 as candidate c
|
||
s = cache[0].serial;
|
||
c = 0;
|
||
|
||
// Compare candidate with serial and update to smaller one
|
||
for (i = 0; i < CacheMax; i++) {
|
||
ASSERT(cache[i].disktrk);
|
||
|
||
// Compare and update the existing serial
|
||
if (cache[i].serial < s) {
|
||
s = cache[i].serial;
|
||
c = i;
|
||
}
|
||
}
|
||
|
||
// Save this track
|
||
if (!cache[c].disktrk->Save(sec_path)) {
|
||
return NULL;
|
||
}
|
||
|
||
// Delete this track
|
||
disktrk = cache[c].disktrk;
|
||
cache[c].disktrk = NULL;
|
||
|
||
// Load
|
||
if (Load(c, track, disktrk)) {
|
||
// Successful loading
|
||
cache[c].serial = serial;
|
||
return cache[c].disktrk;
|
||
}
|
||
|
||
// Load failed
|
||
return NULL;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Load cache
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL DiskCache::Load(int index, int track, DiskTrack *disktrk)
|
||
{
|
||
int sectors;
|
||
|
||
ASSERT(this);
|
||
ASSERT((index >= 0) && (index < CacheMax));
|
||
ASSERT(track >= 0);
|
||
ASSERT(!cache[index].disktrk);
|
||
|
||
// Get the number of sectors on this track
|
||
sectors = sec_blocks - (track << 8);
|
||
ASSERT(sectors > 0);
|
||
if (sectors > 0x100) {
|
||
sectors = 0x100;
|
||
}
|
||
|
||
// Create a disk track
|
||
if (disktrk == NULL) {
|
||
disktrk = new DiskTrack();
|
||
}
|
||
|
||
// Initialize disk track
|
||
disktrk->Init(track, sec_size, sectors, cd_raw, imgoffset);
|
||
|
||
// Try loading
|
||
if (!disktrk->Load(sec_path)) {
|
||
// 失敗
|
||
delete disktrk;
|
||
return FALSE;
|
||
}
|
||
|
||
// Allocation successful, work set
|
||
cache[index].disktrk = disktrk;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Update serial number
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL DiskCache::Update()
|
||
{
|
||
int i;
|
||
|
||
ASSERT(this);
|
||
|
||
// Update and do nothing except 0
|
||
serial++;
|
||
if (serial != 0) {
|
||
return;
|
||
}
|
||
|
||
// Clear serial of all caches (loop in 32bit)
|
||
for (i = 0; i < CacheMax; i++) {
|
||
cache[i].serial = 0;
|
||
}
|
||
}
|
||
|
||
|
||
//===========================================================================
|
||
//
|
||
// Disk
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Constructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
Disk::Disk()
|
||
{
|
||
// Work initialization
|
||
disk.id = MAKEID('N', 'U', 'L', 'L');
|
||
disk.ready = FALSE;
|
||
disk.writep = FALSE;
|
||
disk.readonly = FALSE;
|
||
disk.removable = FALSE;
|
||
disk.lock = FALSE;
|
||
disk.attn = FALSE;
|
||
disk.reset = FALSE;
|
||
disk.size = 0;
|
||
disk.blocks = 0;
|
||
disk.lun = 0;
|
||
disk.code = 0;
|
||
disk.dcache = NULL;
|
||
disk.imgoffset = 0;
|
||
|
||
// Other
|
||
cache_wb = TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Destructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
Disk::~Disk()
|
||
{
|
||
// Save disk cache
|
||
if (disk.ready) {
|
||
// Only if ready...
|
||
if (disk.dcache) {
|
||
disk.dcache->Save();
|
||
}
|
||
}
|
||
|
||
// Clear disk cache
|
||
if (disk.dcache) {
|
||
delete disk.dcache;
|
||
disk.dcache = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Reset
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::Reset()
|
||
{
|
||
ASSERT(this);
|
||
|
||
// no lock, no attention, reset
|
||
disk.lock = FALSE;
|
||
disk.attn = FALSE;
|
||
disk.reset = TRUE;
|
||
}
|
||
|
||
#ifndef RASCSI
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Save
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Save(Fileio *fio, int ver)
|
||
{
|
||
DWORD sz;
|
||
DWORD padding;
|
||
|
||
ASSERT(this);
|
||
ASSERT(fio);
|
||
|
||
// Save size
|
||
sz = 52;
|
||
if (!fio->Write(&sz, sizeof(sz))) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Save entity
|
||
PROP_EXPORT(fio, disk.id);
|
||
PROP_EXPORT(fio, disk.ready);
|
||
PROP_EXPORT(fio, disk.writep);
|
||
PROP_EXPORT(fio, disk.readonly);
|
||
PROP_EXPORT(fio, disk.removable);
|
||
PROP_EXPORT(fio, disk.lock);
|
||
PROP_EXPORT(fio, disk.attn);
|
||
PROP_EXPORT(fio, disk.reset);
|
||
PROP_EXPORT(fio, disk.size);
|
||
PROP_EXPORT(fio, disk.blocks);
|
||
PROP_EXPORT(fio, disk.lun);
|
||
PROP_EXPORT(fio, disk.code);
|
||
PROP_EXPORT(fio, padding);
|
||
|
||
// Save the path
|
||
if (!diskpath.Save(fio, ver)) {
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Load
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Load(Fileio *fio, int ver)
|
||
{
|
||
DWORD sz;
|
||
disk_t buf;
|
||
DWORD padding;
|
||
Filepath path;
|
||
|
||
ASSERT(this);
|
||
ASSERT(fio);
|
||
|
||
// Prior to version 2.03, the disk was not saved
|
||
if (ver <= 0x0202) {
|
||
return TRUE;
|
||
}
|
||
|
||
// Delete the current disk cache
|
||
if (disk.dcache) {
|
||
disk.dcache->Save();
|
||
delete disk.dcache;
|
||
disk.dcache = NULL;
|
||
}
|
||
|
||
// Load size
|
||
if (!fio->Read(&sz, sizeof(sz))) {
|
||
return FALSE;
|
||
}
|
||
if (sz != 52) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Load into buffer
|
||
PROP_IMPORT(fio, buf.id);
|
||
PROP_IMPORT(fio, buf.ready);
|
||
PROP_IMPORT(fio, buf.writep);
|
||
PROP_IMPORT(fio, buf.readonly);
|
||
PROP_IMPORT(fio, buf.removable);
|
||
PROP_IMPORT(fio, buf.lock);
|
||
PROP_IMPORT(fio, buf.attn);
|
||
PROP_IMPORT(fio, buf.reset);
|
||
PROP_IMPORT(fio, buf.size);
|
||
PROP_IMPORT(fio, buf.blocks);
|
||
PROP_IMPORT(fio, buf.lun);
|
||
PROP_IMPORT(fio, buf.code);
|
||
PROP_IMPORT(fio, padding);
|
||
|
||
// Load path
|
||
if (!path.Load(fio, ver)) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Move only if IDs match
|
||
if (disk.id == buf.id) {
|
||
// Do nothing if null
|
||
if (IsNULL()) {
|
||
return TRUE;
|
||
}
|
||
|
||
// Same type of device as when saving
|
||
disk.ready = FALSE;
|
||
if (Open(path)) {
|
||
// Disk cache is created in Open
|
||
// move only properties
|
||
if (!disk.readonly) {
|
||
disk.writep = buf.writep;
|
||
}
|
||
disk.lock = buf.lock;
|
||
disk.attn = buf.attn;
|
||
disk.reset = buf.reset;
|
||
disk.lun = buf.lun;
|
||
disk.code = buf.code;
|
||
|
||
// Loaded successfully
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
// Disk cache rebuild
|
||
if (!IsReady()) {
|
||
disk.dcache = NULL;
|
||
} else {
|
||
disk.dcache = new DiskCache(diskpath, disk.size, disk.blocks);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
#endif // RASCSI
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// NULL Check
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::IsNULL() const
|
||
{
|
||
ASSERT(this);
|
||
|
||
if (disk.id == MAKEID('N', 'U', 'L', 'L')) {
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Retrieve the disk's ID
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DWORD FASTCALL Disk::GetID() const
|
||
{
|
||
return disk.id;
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get cache writeback mode
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::IsCacheWB()
|
||
{
|
||
return cache_wb;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Set cache writeback mode
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::SetCacheWB(BOOL enable)
|
||
{
|
||
cache_wb = enable;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Set unsupported command
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::InvalidCmd()
|
||
{
|
||
disk.code = DISK_INVALIDCMD;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SASI Check
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::IsSASI() const
|
||
{
|
||
ASSERT(this);
|
||
|
||
if (disk.id == MAKEID('S', 'A', 'H', 'D')) {
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SCSI Check
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::IsSCSI() const
|
||
{
|
||
ASSERT(this);
|
||
// If this isn't SASI, then it must be SCSI.
|
||
return (this->IsSASI()) ? FALSE : TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Open
|
||
// * Call as a post-process after successful opening in a derived class
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Open(const Filepath& path, BOOL /*attn*/)
|
||
{
|
||
Fileio fio;
|
||
|
||
ASSERT(this);
|
||
ASSERT((disk.size >= 8) && (disk.size <= 11));
|
||
ASSERT(disk.blocks > 0);
|
||
|
||
// Ready
|
||
disk.ready = TRUE;
|
||
|
||
// Cache initialization
|
||
ASSERT(!disk.dcache);
|
||
disk.dcache =
|
||
new DiskCache(path, disk.size, disk.blocks, disk.imgoffset);
|
||
|
||
// Can read/write open
|
||
if (fio.Open(path, Fileio::ReadWrite)) {
|
||
// Write permission, not read only
|
||
disk.writep = FALSE;
|
||
disk.readonly = FALSE;
|
||
fio.Close();
|
||
} else {
|
||
// Write protected, read only
|
||
disk.writep = TRUE;
|
||
disk.readonly = TRUE;
|
||
}
|
||
|
||
// Not locked
|
||
disk.lock = FALSE;
|
||
|
||
// Save path
|
||
diskpath = path;
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Eject
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::Eject(BOOL force)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Can only be ejected if it is removable
|
||
if (!disk.removable) {
|
||
return;
|
||
}
|
||
|
||
// If you're not ready, you don't need to eject
|
||
if (!disk.ready) {
|
||
return;
|
||
}
|
||
|
||
// Must be unlocked if there is no force flag
|
||
if (!force) {
|
||
if (disk.lock) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Remove disk cache
|
||
disk.dcache->Save();
|
||
delete disk.dcache;
|
||
disk.dcache = NULL;
|
||
|
||
// Not ready, no attention
|
||
disk.ready = FALSE;
|
||
disk.writep = FALSE;
|
||
disk.readonly = FALSE;
|
||
disk.attn = FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Write Protected
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::WriteP(BOOL writep)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// be ready
|
||
if (!disk.ready) {
|
||
return;
|
||
}
|
||
|
||
// Read Only, protect only
|
||
if (disk.readonly) {
|
||
ASSERT(disk.writep);
|
||
return;
|
||
}
|
||
|
||
// Write protect flag setting
|
||
disk.writep = writep;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get Disk
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::GetDisk(disk_t *buffer) const
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buffer);
|
||
|
||
// Assign internal buffer
|
||
*buffer = disk;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get Path
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL Disk::GetPath(Filepath& path) const
|
||
{
|
||
path = diskpath;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Flush
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Flush()
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Do nothing if there's nothing cached
|
||
if (!disk.dcache) {
|
||
return TRUE;
|
||
}
|
||
|
||
// Save cache
|
||
return disk.dcache->Save();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Check Ready
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::CheckReady()
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Not ready if reset
|
||
if (disk.reset) {
|
||
disk.code = DISK_DEVRESET;
|
||
disk.reset = FALSE;
|
||
LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__);
|
||
return FALSE;
|
||
}
|
||
|
||
// Not ready if it needs attention
|
||
if (disk.attn) {
|
||
disk.code = DISK_ATTENTION;
|
||
disk.attn = FALSE;
|
||
LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__);
|
||
return FALSE;
|
||
}
|
||
|
||
// Return status if not ready
|
||
if (!disk.ready) {
|
||
disk.code = DISK_NOTREADY;
|
||
LOGTRACE("%s Disk not ready", __PRETTY_FUNCTION__);
|
||
return FALSE;
|
||
}
|
||
|
||
// Initialization with no error
|
||
disk.code = DISK_NOERROR;
|
||
LOGTRACE("%s Disk is ready!", __PRETTY_FUNCTION__);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// INQUIRY
|
||
// *You need to be successful at all times
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::Inquiry(
|
||
const DWORD* /*cdb*/, BYTE* /*buf*/, DWORD /*major*/, DWORD /*minor*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// default is INQUIRY failure
|
||
disk.code = DISK_INVALIDCMD;
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REQUEST SENSE
|
||
// *SASI is a separate process
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::RequestSense(const DWORD *cdb, BYTE *buf)
|
||
{
|
||
int size;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(buf);
|
||
|
||
// Return not ready only if there are no errors
|
||
if (disk.code == DISK_NOERROR) {
|
||
if (!disk.ready) {
|
||
disk.code = DISK_NOTREADY;
|
||
}
|
||
}
|
||
|
||
// Size determination (according to allocation length)
|
||
size = (int)cdb[4];
|
||
LOGTRACE("%s size of data = %d", __PRETTY_FUNCTION__, size);
|
||
ASSERT((size >= 0) && (size < 0x100));
|
||
|
||
// For SCSI-1, transfer 4 bytes when the size is 0
|
||
// (Deleted this specification for SCSI-2)
|
||
if (size == 0) {
|
||
size = 4;
|
||
}
|
||
|
||
// Clear the buffer
|
||
memset(buf, 0, size);
|
||
|
||
// Set 18 bytes including extended sense data
|
||
buf[0] = 0x70;
|
||
buf[2] = (BYTE)(disk.code >> 16);
|
||
buf[7] = 10;
|
||
buf[12] = (BYTE)(disk.code >> 8);
|
||
buf[13] = (BYTE)disk.code;
|
||
|
||
// Clear the code
|
||
disk.code = 0x00;
|
||
|
||
return size;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// MODE SELECT check
|
||
// *Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::SelectCheck(const DWORD *cdb)
|
||
{
|
||
int length;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
|
||
// Error if save parameters are set instead of SCSIHD
|
||
if (disk.id != MAKEID('S', 'C', 'H', 'D')) {
|
||
// Error if save parameters are set
|
||
if (cdb[1] & 0x01) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// Receive the data specified by the parameter length
|
||
length = (int)cdb[4];
|
||
return length;
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// MODE SELECT(10) check
|
||
// * Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::SelectCheck10(const DWORD *cdb)
|
||
{
|
||
DWORD length;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
|
||
// Error if save parameters are set instead of SCSIHD
|
||
if (disk.id != MAKEID('S', 'C', 'H', 'D')) {
|
||
if (cdb[1] & 0x01) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// Receive the data specified by the parameter length
|
||
length = cdb[7];
|
||
length <<= 8;
|
||
length |= cdb[8];
|
||
if (length > 0x800) {
|
||
length = 0x800;
|
||
}
|
||
|
||
return (int)length;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// MODE SELECT
|
||
// * Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::ModeSelect(
|
||
const DWORD* /*cdb*/, const BYTE *buf, int length)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
ASSERT(length >= 0);
|
||
|
||
// cannot be set
|
||
disk.code = DISK_INVALIDPRM;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// MODE SENSE
|
||
// *Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::ModeSense(const DWORD *cdb, BYTE *buf)
|
||
{
|
||
int page;
|
||
int length;
|
||
int size;
|
||
BOOL valid;
|
||
BOOL change;
|
||
int ret;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(buf);
|
||
ASSERT(cdb[0] == 0x1a);
|
||
|
||
// Get length, clear buffer
|
||
length = (int)cdb[4];
|
||
ASSERT((length >= 0) && (length < 0x100));
|
||
memset(buf, 0, length);
|
||
|
||
// Get changeable flag
|
||
if ((cdb[2] & 0xc0) == 0x40) {
|
||
change = TRUE;
|
||
} else {
|
||
change = FALSE;
|
||
}
|
||
|
||
// Get page code (0x00 is valid from the beginning)
|
||
page = cdb[2] & 0x3f;
|
||
if (page == 0x00) {
|
||
valid = TRUE;
|
||
} else {
|
||
valid = FALSE;
|
||
}
|
||
|
||
// Basic information
|
||
size = 4;
|
||
|
||
// MEDIUM TYPE
|
||
if (disk.id == MAKEID('S', 'C', 'M', 'O')) {
|
||
buf[1] = 0x03; // optical reversible or erasable
|
||
}
|
||
|
||
// DEVICE SPECIFIC PARAMETER
|
||
if (disk.writep) {
|
||
buf[2] = 0x80;
|
||
}
|
||
|
||
// add block descriptor if DBD is 0
|
||
if ((cdb[1] & 0x08) == 0) {
|
||
// Mode parameter header
|
||
buf[3] = 0x08;
|
||
|
||
// Only if ready
|
||
if (disk.ready) {
|
||
// Block descriptor (number of blocks)
|
||
buf[5] = (BYTE)(disk.blocks >> 16);
|
||
buf[6] = (BYTE)(disk.blocks >> 8);
|
||
buf[7] = (BYTE)disk.blocks;
|
||
|
||
// Block descriptor (block length)
|
||
size = 1 << disk.size;
|
||
buf[9] = (BYTE)(size >> 16);
|
||
buf[10] = (BYTE)(size >> 8);
|
||
buf[11] = (BYTE)size;
|
||
}
|
||
|
||
// size
|
||
size = 12;
|
||
}
|
||
|
||
// Page code 1(read-write error recovery)
|
||
if ((page == 0x01) || (page == 0x3f)) {
|
||
size += AddError(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 3(format device)
|
||
if ((page == 0x03) || (page == 0x3f)) {
|
||
size += AddFormat(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 4(drive parameter)
|
||
if ((page == 0x04) || (page == 0x3f)) {
|
||
size += AddDrive(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 6(optical)
|
||
if (disk.id == MAKEID('S', 'C', 'M', 'O')) {
|
||
if ((page == 0x06) || (page == 0x3f)) {
|
||
size += AddOpt(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page code 8(caching)
|
||
if ((page == 0x08) || (page == 0x3f)) {
|
||
size += AddCache(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 13(CD-ROM)
|
||
if (disk.id == MAKEID('S', 'C', 'C', 'D')) {
|
||
if ((page == 0x0d) || (page == 0x3f)) {
|
||
size += AddCDROM(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page code 14(CD-DA)
|
||
if (disk.id == MAKEID('S', 'C', 'C', 'D')) {
|
||
if ((page == 0x0e) || (page == 0x3f)) {
|
||
size += AddCDDA(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page (vendor special)
|
||
ret = AddVendor(page, change, &buf[size]);
|
||
if (ret > 0) {
|
||
size += ret;
|
||
valid = TRUE;
|
||
}
|
||
|
||
// final setting of mode data length
|
||
buf[0] = (BYTE)(size - 1);
|
||
|
||
// Unsupported page
|
||
if (!valid) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return 0;
|
||
}
|
||
|
||
// MODE SENSE success
|
||
disk.code = DISK_NOERROR;
|
||
return length;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// MODE SENSE(10)
|
||
// *Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::ModeSense10(const DWORD *cdb, BYTE *buf)
|
||
{
|
||
int page;
|
||
int length;
|
||
int size;
|
||
BOOL valid;
|
||
BOOL change;
|
||
int ret;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(buf);
|
||
ASSERT(cdb[0] == 0x5a);
|
||
|
||
// Get length, clear buffer
|
||
length = cdb[7];
|
||
length <<= 8;
|
||
length |= cdb[8];
|
||
if (length > 0x800) {
|
||
length = 0x800;
|
||
}
|
||
ASSERT((length >= 0) && (length < 0x800));
|
||
memset(buf, 0, length);
|
||
|
||
// Get changeable flag
|
||
if ((cdb[2] & 0xc0) == 0x40) {
|
||
change = TRUE;
|
||
} else {
|
||
change = FALSE;
|
||
}
|
||
|
||
// Get page code (0x00 is valid from the beginning)
|
||
page = cdb[2] & 0x3f;
|
||
if (page == 0x00) {
|
||
valid = TRUE;
|
||
} else {
|
||
valid = FALSE;
|
||
}
|
||
|
||
// Basic Information
|
||
size = 4;
|
||
if (disk.writep) {
|
||
buf[2] = 0x80;
|
||
}
|
||
|
||
// add block descriptor if DBD is 0
|
||
if ((cdb[1] & 0x08) == 0) {
|
||
// Mode parameter header
|
||
buf[3] = 0x08;
|
||
|
||
// Only if ready
|
||
if (disk.ready) {
|
||
// Block descriptor (number of blocks)
|
||
buf[5] = (BYTE)(disk.blocks >> 16);
|
||
buf[6] = (BYTE)(disk.blocks >> 8);
|
||
buf[7] = (BYTE)disk.blocks;
|
||
|
||
// Block descriptor (block length)
|
||
size = 1 << disk.size;
|
||
buf[9] = (BYTE)(size >> 16);
|
||
buf[10] = (BYTE)(size >> 8);
|
||
buf[11] = (BYTE)size;
|
||
}
|
||
|
||
// Size
|
||
size = 12;
|
||
}
|
||
|
||
// Page code 1(read-write error recovery)
|
||
if ((page == 0x01) || (page == 0x3f)) {
|
||
size += AddError(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 3(format device)
|
||
if ((page == 0x03) || (page == 0x3f)) {
|
||
size += AddFormat(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 4(drive parameter)
|
||
if ((page == 0x04) || (page == 0x3f)) {
|
||
size += AddDrive(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// ペPage code 6(optical)
|
||
if (disk.id == MAKEID('S', 'C', 'M', 'O')) {
|
||
if ((page == 0x06) || (page == 0x3f)) {
|
||
size += AddOpt(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page code 8(caching)
|
||
if ((page == 0x08) || (page == 0x3f)) {
|
||
size += AddCache(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
|
||
// Page code 13(CD-ROM)
|
||
if (disk.id == MAKEID('S', 'C', 'C', 'D')) {
|
||
if ((page == 0x0d) || (page == 0x3f)) {
|
||
size += AddCDROM(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page code 14(CD-DA)
|
||
if (disk.id == MAKEID('S', 'C', 'C', 'D')) {
|
||
if ((page == 0x0e) || (page == 0x3f)) {
|
||
size += AddCDDA(change, &buf[size]);
|
||
valid = TRUE;
|
||
}
|
||
}
|
||
|
||
// Page (vendor special)
|
||
ret = AddVendor(page, change, &buf[size]);
|
||
if (ret > 0) {
|
||
size += ret;
|
||
valid = TRUE;
|
||
}
|
||
|
||
// final setting of mode data length
|
||
buf[0] = (BYTE)(size - 1);
|
||
|
||
// Unsupported page
|
||
if (!valid) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return 0;
|
||
}
|
||
|
||
// MODE SENSE success
|
||
disk.code = DISK_NOERROR;
|
||
return length;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add error page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddError(BOOL change, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x01;
|
||
buf[1] = 0x0a;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 12;
|
||
}
|
||
|
||
// Retry count is 0, limit time uses internal default value
|
||
return 12;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add format page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddFormat(BOOL change, BYTE *buf)
|
||
{
|
||
int size;
|
||
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x80 | 0x03;
|
||
buf[1] = 0x16;
|
||
|
||
// Show the number of bytes in the physical sector as changeable
|
||
// (though it cannot be changed in practice)
|
||
if (change) {
|
||
buf[0xc] = 0xff;
|
||
buf[0xd] = 0xff;
|
||
return 24;
|
||
}
|
||
|
||
if (disk.ready) {
|
||
// Set the number of tracks in one zone to 8 (TODO)
|
||
buf[0x3] = 0x08;
|
||
|
||
// Set sector/track to 25 (TODO)
|
||
buf[0xa] = 0x00;
|
||
buf[0xb] = 0x19;
|
||
|
||
// Set the number of bytes in the physical sector
|
||
size = 1 << disk.size;
|
||
buf[0xc] = (BYTE)(size >> 8);
|
||
buf[0xd] = (BYTE)size;
|
||
}
|
||
|
||
// Set removable attribute
|
||
if (disk.removable) {
|
||
buf[20] = 0x20;
|
||
}
|
||
|
||
return 24;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add drive page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddDrive(BOOL change, BYTE *buf)
|
||
{
|
||
DWORD cylinder;
|
||
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x04;
|
||
buf[1] = 0x16;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 24;
|
||
}
|
||
|
||
if (disk.ready) {
|
||
// Set the number of cylinders (total number of blocks
|
||
// divided by 25 sectors/track and 8 heads)
|
||
cylinder = disk.blocks;
|
||
cylinder >>= 3;
|
||
cylinder /= 25;
|
||
buf[0x2] = (BYTE)(cylinder >> 16);
|
||
buf[0x3] = (BYTE)(cylinder >> 8);
|
||
buf[0x4] = (BYTE)cylinder;
|
||
|
||
// Fix the head at 8
|
||
buf[0x5] = 0x8;
|
||
}
|
||
|
||
return 24;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add option
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddOpt(BOOL change, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x06;
|
||
buf[1] = 0x02;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 4;
|
||
}
|
||
|
||
// Do not report update blocks
|
||
return 4;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add Cache Page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddCache(BOOL change, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x08;
|
||
buf[1] = 0x0a;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 12;
|
||
}
|
||
|
||
// Only read cache is valid, no prefetch
|
||
return 12;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add CDROM Page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddCDROM(BOOL change, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x0d;
|
||
buf[1] = 0x06;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 8;
|
||
}
|
||
|
||
// 2 seconds for inactive timer
|
||
buf[3] = 0x05;
|
||
|
||
// MSF multiples are 60 and 75 respectively
|
||
buf[5] = 60;
|
||
buf[7] = 75;
|
||
|
||
return 8;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// CD-DAページ追加
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddCDDA(BOOL change, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Set the message length
|
||
buf[0] = 0x0e;
|
||
buf[1] = 0x0e;
|
||
|
||
// No changeable area
|
||
if (change) {
|
||
return 16;
|
||
}
|
||
|
||
// Audio waits for operation completion and allows
|
||
// PLAY across multiple tracks
|
||
return 16;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Add special vendor page
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::AddVendor(int /*page*/, BOOL /*change*/, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ DEFECT DATA(10)
|
||
// *Not affected by disk.code
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::ReadDefectData10(const DWORD *cdb, BYTE *buf)
|
||
{
|
||
DWORD length;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(buf);
|
||
ASSERT(cdb[0] == 0x37);
|
||
|
||
// Get length, clear buffer
|
||
length = cdb[7];
|
||
length <<= 8;
|
||
length |= cdb[8];
|
||
if (length > 0x800) {
|
||
length = 0x800;
|
||
}
|
||
ASSERT((length >= 0) && (length < 0x800));
|
||
memset(buf, 0, length);
|
||
|
||
// P/G/FORMAT
|
||
buf[1] = (cdb[1] & 0x18) | 5;
|
||
buf[3] = 8;
|
||
|
||
buf[4] = 0xff;
|
||
buf[5] = 0xff;
|
||
buf[6] = 0xff;
|
||
buf[7] = 0xff;
|
||
|
||
buf[8] = 0xff;
|
||
buf[9] = 0xff;
|
||
buf[10] = 0xff;
|
||
buf[11] = 0xff;
|
||
|
||
// no list
|
||
disk.code = DISK_NODEFECT;
|
||
return 4;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Command
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// TEST UNIT READY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::TestUnitReady(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// TEST UNIT READY Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REZERO UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Rezero(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// REZERO Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// FORMAT UNIT
|
||
// *Opcode $06 for SASI, Opcode $04 for SCSI
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Format(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
|
||
if ((cdb[1] & 0x10) != 0 && cdb[4] != 0) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return FALSE;
|
||
}
|
||
|
||
// FORMAT Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REASSIGN BLOCKS
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Reassign(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// REASSIGN BLOCKS Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::Read(const DWORD *cdb, BYTE *buf, DWORD block)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return 0;
|
||
}
|
||
|
||
// Error if the total number of blocks is exceeded
|
||
if (block >= disk.blocks) {
|
||
disk.code = DISK_INVALIDLBA;
|
||
return 0;
|
||
}
|
||
|
||
// leave it to the cache
|
||
if (!disk.dcache->Read(buf, block)) {
|
||
disk.code = DISK_READFAULT;
|
||
return 0;
|
||
}
|
||
|
||
// Success
|
||
return (1 << disk.size);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// WRITE check
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::WriteCheck(DWORD block)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return 0;
|
||
}
|
||
|
||
// Error if the total number of blocks is exceeded
|
||
if (block >= disk.blocks) {
|
||
return 0;
|
||
}
|
||
|
||
// Error if write protected
|
||
if (disk.writep) {
|
||
disk.code = DISK_WRITEPROTECT;
|
||
return 0;
|
||
}
|
||
|
||
// Success
|
||
return (1 << disk.size);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// WRITE
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Write(const DWORD *cdb, const BYTE *buf, DWORD block)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
LOGTRACE("%s", __PRETTY_FUNCTION__);
|
||
// Error if not ready
|
||
if (!disk.ready) {
|
||
disk.code = DISK_NOTREADY;
|
||
return FALSE;
|
||
}
|
||
|
||
// Error if the total number of blocks is exceeded
|
||
if (block >= disk.blocks) {
|
||
disk.code = DISK_INVALIDLBA;
|
||
return FALSE;
|
||
}
|
||
|
||
// Error if write protected
|
||
if (disk.writep) {
|
||
disk.code = DISK_WRITEPROTECT;
|
||
return FALSE;
|
||
}
|
||
|
||
// Leave it to the cache
|
||
if (!disk.dcache->Write(buf, block)) {
|
||
disk.code = DISK_WRITEFAULT;
|
||
return FALSE;
|
||
}
|
||
|
||
// Success
|
||
disk.code = DISK_NOERROR;
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SEEK
|
||
// *Does not check LBA (SASI IOCS)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Seek(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// SEEK Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// ASSIGN
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Assign(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SPECIFY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Specify(const DWORD* /*cdb*/)
|
||
{
|
||
ASSERT(this);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// START STOP UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::StartStop(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x1b);
|
||
|
||
// Look at the eject bit and eject if necessary
|
||
if (cdb[4] & 0x02) {
|
||
if (disk.lock) {
|
||
// Cannot be ejected because it is locked
|
||
disk.code = DISK_PREVENT;
|
||
return FALSE;
|
||
}
|
||
|
||
// Eject
|
||
Eject(FALSE);
|
||
}
|
||
|
||
// OK
|
||
disk.code = DISK_NOERROR;
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SEND DIAGNOSTIC
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::SendDiag(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x1d);
|
||
|
||
// Do not support PF bit
|
||
if (cdb[1] & 0x10) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return FALSE;
|
||
}
|
||
|
||
// Do not support parameter list
|
||
if ((cdb[3] != 0) || (cdb[4] != 0)) {
|
||
disk.code = DISK_INVALIDCDB;
|
||
return FALSE;
|
||
}
|
||
|
||
// Always successful
|
||
disk.code = DISK_NOERROR;
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// PREVENT/ALLOW MEDIUM REMOVAL
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Removal(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x1e);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Set Lock flag
|
||
if (cdb[4] & 0x01) {
|
||
disk.lock = TRUE;
|
||
} else {
|
||
disk.lock = FALSE;
|
||
}
|
||
|
||
// REMOVAL Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ CAPACITY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::ReadCapacity(const DWORD* /*cdb*/, BYTE *buf)
|
||
{
|
||
DWORD blocks;
|
||
DWORD length;
|
||
|
||
ASSERT(this);
|
||
ASSERT(buf);
|
||
|
||
// Buffer clear
|
||
memset(buf, 0, 8);
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return 0;
|
||
}
|
||
|
||
// Create end of logical block address (disk.blocks-1)
|
||
ASSERT(disk.blocks > 0);
|
||
blocks = disk.blocks - 1;
|
||
buf[0] = (BYTE)(blocks >> 24);
|
||
buf[1] = (BYTE)(blocks >> 16);
|
||
buf[2] = (BYTE)(blocks >> 8);
|
||
buf[3] = (BYTE)blocks;
|
||
|
||
// Create block length (1 << disk.size)
|
||
length = 1 << disk.size;
|
||
buf[4] = (BYTE)(length >> 24);
|
||
buf[5] = (BYTE)(length >> 16);
|
||
buf[6] = (BYTE)(length >> 8);
|
||
buf[7] = (BYTE)length;
|
||
|
||
// return the size
|
||
return 8;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// VERIFY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::Verify(const DWORD *cdb)
|
||
{
|
||
DWORD record;
|
||
DWORD blocks;
|
||
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x2f);
|
||
|
||
// Get parameters
|
||
record = cdb[2];
|
||
record <<= 8;
|
||
record |= cdb[3];
|
||
record <<= 8;
|
||
record |= cdb[4];
|
||
record <<= 8;
|
||
record |= cdb[5];
|
||
blocks = cdb[7];
|
||
blocks <<= 8;
|
||
blocks |= cdb[8];
|
||
|
||
// Status check
|
||
if (!CheckReady()) {
|
||
return 0;
|
||
}
|
||
|
||
// Parameter check
|
||
if (disk.blocks < (record + blocks)) {
|
||
disk.code = DISK_INVALIDLBA;
|
||
return FALSE;
|
||
}
|
||
|
||
// Success
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ TOC
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
int FASTCALL Disk::ReadToc(const DWORD *cdb, BYTE *buf)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x43);
|
||
ASSERT(buf);
|
||
|
||
// This command is not supported
|
||
disk.code = DISK_INVALIDCMD;
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// PLAY AUDIO
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::PlayAudio(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x45);
|
||
|
||
// This command is not supported
|
||
disk.code = DISK_INVALIDCMD;
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// PLAY AUDIO MSF
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::PlayAudioMSF(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x47);
|
||
|
||
// This command is not supported
|
||
disk.code = DISK_INVALIDCMD;
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// PLAY AUDIO TRACK
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL Disk::PlayAudioTrack(const DWORD *cdb)
|
||
{
|
||
ASSERT(this);
|
||
ASSERT(cdb);
|
||
ASSERT(cdb[0] == 0x48);
|
||
|
||
// This command is not supported
|
||
disk.code = DISK_INVALIDCMD;
|
||
return FALSE;
|
||
}
|
||
|