// $Id: AbstractIDEDevice.hh 5854 2006-11-06 18:24:21Z m9710797 $ #ifndef ABSTRACTIDEDEVICE_HH #define ABSTRACTIDEDEVICE_HH #include "IDEDevice.hh" #include namespace openmsx { class MSXMotherBoard; class AbstractIDEDevice : public IDEDevice { public: virtual void reset(const EmuTime& time); virtual word readData(const EmuTime& time); virtual byte readReg(nibble reg, const EmuTime& time); virtual void writeData(word value, const EmuTime& time); virtual void writeReg(nibble reg, byte value, const EmuTime& time); protected: // Bit flags for the status register: static const byte DRDY = 0x40; static const byte DSC = 0x10; static const byte DRQ = 0x08; static const byte ERR = 0x01; // Bit flags for the error register: static const byte UNC = 0x40; static const byte IDNF = 0x10; static const byte ABORT = 0x04; explicit AbstractIDEDevice(MSXMotherBoard& motherBoard); virtual ~AbstractIDEDevice(); /** Is this device a packet (ATAPI) device? * @return True iff this device supports the packet commands. */ virtual bool isPacketDevice() = 0; /** Gets the device name to insert as "model number" into the identify * block. * @return An ASCII string, up to 40 characters long. */ virtual const std::string& getDeviceName() = 0; /** Tells a subclass to fill the device specific parts of the identify * block located in the buffer. * The generic part is already written there. * @param buffer Array of 512 bytes. */ virtual void fillIdentifyBlock(byte* buffer) = 0; /** Called when a block of read data should be buffered by the controller: * when the buffer is empty or at the start of the transfer. * @param buffer Pointer to the start of a byte array. * @param count Number of bytes to be filled by this method. * This number will not exceed the array size nor the transfer length. * @return The number of bytes that was added to the array, * or 0 if the transfer was aborted (the implementation of this method * must set the relevant error flags as well). */ virtual unsigned readBlockStart(byte* buffer, unsigned count) = 0; /** Called when a read transfer completes. * The default implementation does nothing. */ virtual void readEnd(); /** Called when a block of written data has been buffered by the controller: * when the buffer is full or at the end of the transfer. * @param buffer Pointer to the start of a byte array. * @param count Number of data bytes in the array. */ virtual void writeBlockComplete(byte* buffer, unsigned count) = 0; /** Starts execution of an IDE command. * Override this to implement additional commands and make sure you call * the superclass implementation for all commands that you don't handle. */ virtual void executeCommand(byte cmd); /** Indicates an error: sets error register, error flag, aborts transfers. * @param error Value to be written to the error register. */ void setError(byte error); /** Creates an LBA sector address from the contents of the sectorNumReg, * cylinderLowReg, cylinderHighReg and devHeadReg registers. */ unsigned getSectorNumber() const; /** Gets the number of sectors indicated by the sector count register. */ unsigned getNumSectors() const; /** Writes the interrupt reason register. * This is the same as register as sector count, but serves a different * purpose. */ void setInterruptReason(byte value); /** Reads the byte count limit of a packet transfer in the registers. * The cylinder low/high registers are used for this. */ unsigned getByteCount(); /** Writes the byte count of a packet transfer in the registers. * The cylinder low/high registers are used for this. */ void setByteCount(unsigned count); /** Writes a 28-bit LBA sector number in the registers. * The cylinder low/high registers are used for this. */ void setSectorNumber(unsigned lba); /** Indicates the start of a read data transfer which uses blocks. * The readBlockStart() method is called at the start of each block. * The first block will be read immediately, so make sure you initialise * all variables needed by readBlockStart() before calling this method. * @param count Total number of bytes to transfer. */ void startLongReadTransfer(unsigned count); /** Indicates the start of a read data transfer where all data fits * into the buffer at once. * @param count Total number of bytes to transfer. * @return Pointer to the start of the buffer. * The caller should write the data there. * The relevant part of the buffer contains zeroes. */ byte* startShortReadTransfer(unsigned count); /** Aborts the read transfer in progress. */ void abortReadTransfer(byte error); /** Indicates the start of a write data transfer. * @param count Total number of bytes to transfer. */ void startWriteTransfer(unsigned count); /** Aborts the write transfer in progress. */ void abortWriteTransfer(byte error); byte getFeatureReg() const { return featureReg; } void setLBALow (byte value) { sectorNumReg = value; } void setLBAMid (byte value) { cylinderLowReg = value; } void setLBAHigh(byte value) { cylinderHighReg = value; } private: /** Perform diagnostic and return result. * Actually, just return success, because we don't emulate faulty hardware. */ byte diagnostic(); /** Puts special values in the sector address, sector count and device * registers to identify the type of device. * @param preserveDevice If true, preserve the value of the DEV bit; * if false, set the DEV bit to 0. */ void createSignature(bool preserveDevice = false); /** Puts the output for the IDENTIFY DEVICE command in the buffer. * @param buffer Pointer to the start of the buffer. * The buffer must be at least 512 bytes in size. */ void createIdentifyBlock(byte* buffer); /** Initialises registers for a data transfer. */ void startReadTransfer(); /** Initialises buffer related variables for the next data block. * Calls readBlockStart() to deliver the actual data. */ void readNextBlock(); /** Indicates that a read transfer starts. */ void setTransferRead(bool status); /** Initialises buffer related variables for the next data block. * Make sure transferCount is initialised before calling this method. */ void writeNextBlock(); /** Indicates that a write transfer starts. */ void setTransferWrite(bool status); // ATA registers: byte errorReg; byte sectorCountReg; byte sectorNumReg; byte cylinderLowReg; byte cylinderHighReg; byte devHeadReg; byte statusReg; byte featureReg; /** Data buffer shared by all transfers. * The size must be a multiple of 512. * Right now I don't see any reason to make it larger than the minimum * size of 1 * 512. */ byte buffer[512]; /** Pointer to current read/write position in the buffer. */ byte* transferPntr; /** Number of bytes remaining in the buffer. */ unsigned bufferLeft; /** Number of bytes remaining in the transfer after this buffer. * (total bytes remaining == transferCount + bufferLeft) */ unsigned transferCount; /** True iff the current transfer is part of an IDENTIFY DEVICE command. */ bool transferIdentifyBlock; bool transferRead; bool transferWrite; MSXMotherBoard& motherBoard; }; } // namespace openmsx #endif // ABSTRACTIDEDEVICE_HH