//---------------------------------------------------------------------------
//
//	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
//
//  Imported sava's Anex86/T98Next image and MO format support patch.
//  Comments translated to english by akuker.
//
//	[ Disk ]
//
//---------------------------------------------------------------------------

#pragma once

#include "xm6.h"
#include "log.h"
#include "scsi.h"
#include "filepath.h"

//---------------------------------------------------------------------------
//
//	Error definition (sense code returned by REQUEST SENSE)
//
//	MSB		Reserved (0x00)
//			Sense Key
//			Additional Sense Code (ASC)
//	LSB		Additional Sense Code Qualifier(ASCQ)
//
//---------------------------------------------------------------------------
#define DISK_NOERROR		0x00000000	// NO ADDITIONAL SENSE INFO.
#define DISK_DEVRESET		0x00062900	// POWER ON OR RESET OCCURED
#define DISK_NOTREADY		0x00023a00	// MEDIUM NOT PRESENT
#define DISK_ATTENTION		0x00062800	// MEDIUM MAY HAVE CHANGED
#define DISK_PREVENT		0x00045302	// MEDIUM REMOVAL PREVENTED
#define DISK_READFAULT		0x00031100	// UNRECOVERED READ ERROR
#define DISK_WRITEFAULT		0x00030300	// PERIPHERAL DEVICE WRITE FAULT
#define DISK_WRITEPROTECT	0x00042700	// WRITE PROTECTED
#define DISK_MISCOMPARE		0x000e1d00	// MISCOMPARE DURING VERIFY
#define DISK_INVALIDCMD		0x00052000	// INVALID COMMAND OPERATION CODE
#define DISK_INVALIDLBA		0x00052100	// LOGICAL BLOCK ADDR. OUT OF RANGE
#define DISK_INVALIDCDB		0x00052400	// INVALID FIELD IN CDB
#define DISK_INVALIDLUN		0x00052500	// LOGICAL UNIT NOT SUPPORTED
#define DISK_INVALIDPRM		0x00052600	// INVALID FIELD IN PARAMETER LIST
#define DISK_INVALIDMSG		0x00054900	// INVALID MESSAGE ERROR
#define DISK_PARAMLEN		0x00051a00	// PARAMETERS LIST LENGTH ERROR
#define DISK_PARAMNOT		0x00052601	// PARAMETERS NOT SUPPORTED
#define DISK_PARAMVALUE		0x00052602	// PARAMETERS VALUE INVALID
#define DISK_PARAMSAVE		0x00053900	// SAVING PARAMETERS NOT SUPPORTED
#define DISK_NODEFECT		0x00010000	// DEFECT LIST NOT FOUND

#if 0
#define DISK_AUDIOPROGRESS	0x00??0011	// AUDIO PLAY IN PROGRESS
#define DISK_AUDIOPAUSED	0x00??0012	// AUDIO PLAY PAUSED
#define DISK_AUDIOSTOPPED	0x00??0014	// AUDIO PLAY STOPPED DUE TO ERROR
#define DISK_AUDIOCOMPLETE	0x00??0013	// AUDIO PLAY SUCCESSFULLY COMPLETED
#endif


#ifdef RASCSI
#define BENDER_SIGNATURE "RaSCSI"
// The following line was to mimic Apple's CDROM ID
// #define BENDER_SIGNATURE "SONY    "
#else
#define BENDER_SIGNATURE "XM6"
#endif

//===========================================================================
//
//	Disk Track
//
//===========================================================================
class DiskTrack
{
public:
	// Internal data definition
	typedef struct {
		int track;						// Track Number
		int size;						// Sector Size(8 or 9)
		int sectors;					// Number of sectors(<=0x100)
		DWORD length;					// Data buffer length
		BYTE *buffer;					// Data buffer
		BOOL init;						// Is it initilized?
		BOOL changed;					// Changed flag
		DWORD maplen;					// Changed map length
		BOOL *changemap;				// Changed map
		BOOL raw;						// RAW mode flag
		off64_t imgoffset;				// Offset to actual data
	} disktrk_t;

public:
	// Basic Functions
	DiskTrack();
										// Constructor
	virtual ~DiskTrack();
										// Destructor
	void FASTCALL Init(int track, int size, int sectors, BOOL raw = FALSE,
														 off64_t imgoff = 0);
										// Initialization
	BOOL FASTCALL Load(const Filepath& path);
										// Load
	BOOL FASTCALL Save(const Filepath& path);
										// Save

	// Read / Write
	BOOL FASTCALL Read(BYTE *buf, int sec) const;
										// Sector Read
	BOOL FASTCALL Write(const BYTE *buf, int sec);
										// Sector Write

	// Other
	int FASTCALL GetTrack() const		{ return dt.track; }
										// Get track
	BOOL FASTCALL IsChanged() const		{ return dt.changed; }
										// Changed flag check

private:
	// Internal data
	disktrk_t dt;
										// Internal data
};

//===========================================================================
//
//	Disk Cache
//
//===========================================================================
class DiskCache
{
public:
	// Internal data definition
	typedef struct {
		DiskTrack *disktrk;				// Disk Track
		DWORD serial;					// Serial
	} cache_t;

	// Number of caches
	enum {
		CacheMax = 16					// Number of tracks to cache
	};

public:
	// Basic Functions
	DiskCache(const Filepath& path, int size, int blocks,
														off64_t imgoff = 0);
										// Constructor
	virtual ~DiskCache();
										// Destructor
	void FASTCALL SetRawMode(BOOL raw);
										// CD-ROM raw mode setting

	// Access
	BOOL FASTCALL Save();
										// Save and release all
	BOOL FASTCALL Read(BYTE *buf, int block);
										// Sector Read
	BOOL FASTCALL Write(const BYTE *buf, int block);
										// Sector Write
	BOOL FASTCALL GetCache(int index, int& track, DWORD& serial) const;
										// Get cache information

private:
	// Internal Management
	void FASTCALL Clear();
										// Clear all tracks
	DiskTrack* FASTCALL Assign(int track);
										// Load track
	BOOL FASTCALL Load(int index, int track, DiskTrack *disktrk = NULL);
										// Load track
	void FASTCALL Update();
										// Update serial number

	// Internal data
	cache_t cache[CacheMax];
										// Cache management
	DWORD serial;
										// Last serial number
	Filepath sec_path;
										// Path
	int sec_size;
										// Sector size (8 or 9 or 11)
	int sec_blocks;
										// Blocks per sector
	BOOL cd_raw;
										// CD-ROM RAW mode
	off64_t imgoffset;
										// Offset to actual data
};

//===========================================================================
//
//	Disk
//
//===========================================================================
class Disk
{
public:
	// Internal data structure
	typedef struct {
		DWORD id;						// Media ID
		BOOL ready;						// Valid Disk
		BOOL writep;					// Write protected
		BOOL readonly;					// Read only
		BOOL removable;					// Removable
		BOOL lock;						// Locked
		BOOL attn;						// Attention
		BOOL reset;						// Reset
		int size;						// Sector Size
		DWORD blocks;					// Total number of sectors
		DWORD lun;						// LUN
		DWORD code;						// Status code
		DiskCache *dcache;				// Disk cache
		off64_t imgoffset;				// Offset to actual data
	} disk_t;

public:
	// Basic Functions
	Disk();
										// Constructor
	virtual ~Disk();
										// Destructor
	virtual void FASTCALL Reset();
										// Device Reset
#ifndef RASCSI
	virtual BOOL FASTCALL Save(Fileio *fio, int ver);
										// Save
	virtual BOOL FASTCALL Load(Fileio *fio, int ver);
										// Load
#endif	// RASCSI

	// ID
	DWORD FASTCALL GetID() const		{ return disk.id; }
										// Get media ID
	BOOL FASTCALL IsNULL() const;
										// NULL check
	BOOL FASTCALL IsSASI() const;
										// SASI Check
	BOOL FASTCALL IsSCSI() const;
										// SASI Check

	// Media Operations
	virtual BOOL FASTCALL Open(const Filepath& path, BOOL attn = TRUE);
										// Open
	void FASTCALL GetPath(Filepath& path) const;
										// Get the path
	void FASTCALL Eject(BOOL force);
										// Eject
	BOOL FASTCALL IsReady() const		{ return disk.ready; }
										// Ready check
	void FASTCALL WriteP(BOOL flag);
										// Set Write Protect flag
	BOOL FASTCALL IsWriteP() const		{ return disk.writep; }
										// Get write protect flag
	BOOL FASTCALL IsReadOnly() const	{ return disk.readonly; }
										// Get read only flag
	BOOL FASTCALL IsRemovable() const	{ return disk.removable; }
										// Get is removable flag
	BOOL FASTCALL IsLocked() const		{ return disk.lock; }
										// Get locked status
	BOOL FASTCALL IsAttn() const		{ return disk.attn; }
										// Get attention flag
	BOOL FASTCALL Flush();
										// Flush the cache
	void FASTCALL GetDisk(disk_t *buffer) const;
										// Get the internal data struct

	// Properties
	void FASTCALL SetLUN(DWORD lun)		{ disk.lun = lun; }
										// LUN set
	DWORD FASTCALL GetLUN()				{ return disk.lun; }
										// LUN get
	// commands
	virtual int FASTCALL Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor);
										// INQUIRY command
	virtual int FASTCALL RequestSense(const DWORD *cdb, BYTE *buf);
										// REQUEST SENSE command
	int FASTCALL SelectCheck(const DWORD *cdb);
										// SELECT check
	int FASTCALL SelectCheck10(const DWORD *cdb);
										// SELECT(10) check
	virtual BOOL FASTCALL ModeSelect(const DWORD *cdb, const BYTE *buf, int length);
										// MODE SELECT command
	virtual int FASTCALL ModeSense(const DWORD *cdb, BYTE *buf);
										// MODE SENSE command
	virtual int FASTCALL ModeSense10(const DWORD *cdb, BYTE *buf);
										// MODE SENSE(10) command
	int FASTCALL ReadDefectData10(const DWORD *cdb, BYTE *buf);
										// READ DEFECT DATA(10) command
	virtual BOOL FASTCALL TestUnitReady(const DWORD *cdb);
										// TEST UNIT READY command
	BOOL FASTCALL Rezero(const DWORD *cdb);
										// REZERO command
	BOOL FASTCALL Format(const DWORD *cdb);
										// FORMAT UNIT command
	BOOL FASTCALL Reassign(const DWORD *cdb);
										// REASSIGN UNIT command
	virtual int FASTCALL Read(BYTE *buf, DWORD block);
										// READ command
	int FASTCALL WriteCheck(DWORD block);
										// WRITE check
	BOOL FASTCALL Write(const BYTE *buf, DWORD block);
										// WRITE command
	BOOL FASTCALL Seek(const DWORD *cdb);
										// SEEK command
	BOOL FASTCALL Assign(const DWORD *cdb);
										// ASSIGN command
	BOOL FASTCALL Specify(const DWORD *cdb);
										// SPECIFY command
	BOOL FASTCALL StartStop(const DWORD *cdb);
										// START STOP UNIT command
	BOOL FASTCALL SendDiag(const DWORD *cdb);
										// SEND DIAGNOSTIC command
	BOOL FASTCALL Removal(const DWORD *cdb);
										// PREVENT/ALLOW MEDIUM REMOVAL command
	int FASTCALL ReadCapacity(const DWORD *cdb, BYTE *buf);
										// READ CAPACITY command
	BOOL FASTCALL Verify(const DWORD *cdb);
										// VERIFY command
	virtual int FASTCALL ReadToc(const DWORD *cdb, BYTE *buf);
										// READ TOC command
	virtual BOOL FASTCALL PlayAudio(const DWORD *cdb);
										// PLAY AUDIO command
	virtual BOOL FASTCALL PlayAudioMSF(const DWORD *cdb);
										// PLAY AUDIO MSF command
	virtual BOOL FASTCALL PlayAudioTrack(const DWORD *cdb);
										// PLAY AUDIO TRACK command
	void FASTCALL InvalidCmd()			{ disk.code = DISK_INVALIDCMD; }
										// Unsupported command

	// Other
	BOOL IsCacheWB() { return cache_wb; }
										// Get cache writeback mode
	void SetCacheWB(BOOL enable) { cache_wb = enable; }
										// Set cache writeback mode

protected:
	// Internal processing
	virtual int FASTCALL AddError(BOOL change, BYTE *buf);
										// Add error
	virtual int FASTCALL AddFormat(BOOL change, BYTE *buf);
										// Add format
	virtual int FASTCALL AddDrive(BOOL change, BYTE *buf);
										// Add drive
	int FASTCALL AddOpt(BOOL change, BYTE *buf);
										// Add optical
	int FASTCALL AddCache(BOOL change, BYTE *buf);
										// Add cache
	int FASTCALL AddCDROM(BOOL change, BYTE *buf);
										// Add CD-ROM
	int FASTCALL AddCDDA(BOOL change, BYTE *buf);
										// Add CD_DA
	virtual int FASTCALL AddVendor(int page, BOOL change, BYTE *buf);
										// Add vendor special info
	BOOL FASTCALL CheckReady();
										// Check if ready

	// Internal data
	disk_t disk;
										// Internal disk data
	Filepath diskpath;
										// File path (for GetPath)
	BOOL cache_wb;
										// Cache mode
};
