RASCSI/src/raspberrypi/devices/disk.cpp

2396 lines
48 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (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;
}