mirror of https://github.com/PCSX2/pcsx2.git
DEV9: Add ATA emulation & Hdd Creator code
This commit is contained in:
parent
d32b583b3e
commit
5640ee8a23
|
@ -315,6 +315,17 @@ compile_gresources( pcsx2DEV9UIHeaders
|
|||
|
||||
# DEV9 sources
|
||||
set(pcsx2DEV9Sources
|
||||
DEV9/ATA/Commands/ATA_Command.cpp
|
||||
DEV9/ATA/Commands/ATA_CmdDMA.cpp
|
||||
DEV9/ATA/Commands/ATA_CmdExecuteDeviceDiag.cpp
|
||||
DEV9/ATA/Commands/ATA_CmdNoData.cpp
|
||||
DEV9/ATA/Commands/ATA_CmdPIOData.cpp
|
||||
DEV9/ATA/Commands/ATA_CmdSMART.cpp
|
||||
DEV9/ATA/Commands/ATA_SCE.cpp
|
||||
DEV9/ATA/ATA_Info.cpp
|
||||
DEV9/ATA/ATA_State.cpp
|
||||
DEV9/ATA/ATA_Transfer.cpp
|
||||
DEV9/ATA/HddCreate.cpp
|
||||
DEV9/smap.cpp
|
||||
DEV9/DEV9.cpp
|
||||
DEV9/flash.cpp
|
||||
|
@ -327,8 +338,9 @@ set(pcsx2DEV9Sources
|
|||
|
||||
# DEV9 headers
|
||||
set(pcsx2DEV9Headers
|
||||
DEV9/ATA/ATA.h
|
||||
DEV9/ATA/HddCreate.h
|
||||
DEV9/DEV9.h
|
||||
DEV9/ata.h
|
||||
DEV9/net.h
|
||||
DEV9/pcap_io.h
|
||||
DEV9/smap.h
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include "ghc/filesystem.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "PS2Edefs.h"
|
||||
#include "PS2Eext.h"
|
||||
|
||||
class ATA
|
||||
{
|
||||
public:
|
||||
//Transfer
|
||||
bool dmaReady = false;
|
||||
int nsector = 0; //sector count
|
||||
int nsectorLeft = 0; //sectors left to transfer
|
||||
private:
|
||||
const bool lba48Supported = false;
|
||||
|
||||
std::fstream hddImage;
|
||||
u64 hddImageSize;
|
||||
|
||||
int pioMode;
|
||||
int sdmaMode;
|
||||
int mdmaMode;
|
||||
int udmaMode;
|
||||
|
||||
//Info
|
||||
u8 curHeads = 16;
|
||||
u8 curSectors = 63;
|
||||
u16 curCylinders = 0;
|
||||
|
||||
u8 curMultipleSectorsSetting = 128;
|
||||
|
||||
u8 identifyData[512] = { 0 };
|
||||
|
||||
//LBA48 in use?
|
||||
bool lba48 = false;
|
||||
|
||||
//Enable/disable features
|
||||
bool fetSmartEnabled = true;
|
||||
bool fetSecurityEnabled = false;
|
||||
bool fetWriteCacheEnabled = true;
|
||||
bool fetHostProtectedAreaEnabled = false;
|
||||
|
||||
//Regs
|
||||
u16 regCommand; //WriteOnly, Only to be written BSY and DRQ are cleared, DMACK is not set and device is not sleeping, except for DEVICE RESET
|
||||
//PIO Read/Write, Only to be written DMACK is not set and DRQ is 1
|
||||
//COMMAND REG (WriteOnly) Only to be written DMACK is not set
|
||||
//Bit 0 = 0
|
||||
bool regControlEnableIRQ = false; //Bit 1 = 1 Disable Interrupt
|
||||
//Bit 2 = 1 Software Reset
|
||||
bool regControlHOBRead = false; //Bit 7 = HOB (cleared by any write to RegCommand, Sets if Low order or High order bytes are read in ATAread16)
|
||||
//End COMMAND REG
|
||||
u8 regError; //ReadOnly
|
||||
|
||||
//DEVICE REG (Read/Write)
|
||||
u8 regSelect;
|
||||
//Bit 0-3: LBA Bits 24-27 (Unused in 48bit) or Command Dependent
|
||||
//Bit 4: Selected Device
|
||||
//Bit 5: Obsolete (All?)
|
||||
//Bit 6: Command Dependent
|
||||
//Bit 7: Obsolete (All?)
|
||||
//End COMMAND REG
|
||||
u8 regFeature; //WriteOnly, Only to be written BSY and DRQ are cleared and DMACK is not set
|
||||
u8 regFeatureHOB;
|
||||
|
||||
//Following regs are Read/Write, Only to be written BSY and DRQ are cleared and DMACK is not set
|
||||
u8 regSector; //Sector Number or LBA Low
|
||||
u8 regSectorHOB;
|
||||
u8 regLcyl; //LBA Mid
|
||||
u8 regLcylHOB;
|
||||
u8 regHcyl; //LBA High
|
||||
u8 regHcylHOB;
|
||||
//TODO handle nsector code
|
||||
u8 regNsector;
|
||||
u8 regNsectorHOB;
|
||||
|
||||
u8 regStatus; //ReadOnly. When read via AlternateStatus pending interrupts are not cleared
|
||||
|
||||
//Transfer
|
||||
//Write Buffer(s)
|
||||
bool awaitFlush = false;
|
||||
u8* currentWrite; //array
|
||||
u32 currentWriteLength;
|
||||
u64 currentWriteSectors;
|
||||
|
||||
struct WriteQueueEntry
|
||||
{
|
||||
std::atomic_bool ready{false};
|
||||
WriteQueueEntry* next;
|
||||
u8* data;
|
||||
u32 length;
|
||||
u64 sector;
|
||||
};
|
||||
|
||||
WriteQueueEntry* head = nullptr;
|
||||
WriteQueueEntry* tail = nullptr;
|
||||
|
||||
std::thread ioThread;
|
||||
bool ioRunning = false;
|
||||
std::mutex ioMutex;
|
||||
|
||||
std::condition_variable ioThreadIdle_cv;
|
||||
bool ioThreadIdle_bool = false;
|
||||
|
||||
std::condition_variable ioReady;
|
||||
std::atomic_bool ioClose{false};
|
||||
bool ioWrite;
|
||||
bool ioRead;
|
||||
void (ATA::*waitingCmd)() = nullptr;
|
||||
//Write Buffer(s)
|
||||
|
||||
//Read Buffer
|
||||
int rdTransferred = 0;
|
||||
int wrTransferred = 0;
|
||||
//Max tranfer on 24bit is 256*512 = 128KB
|
||||
//Max tranfer on 48bit is 65536*512 = 32MB
|
||||
int readBufferLen;
|
||||
u8* readBuffer = nullptr;
|
||||
//Read Buffer
|
||||
|
||||
//PIO Buffer
|
||||
int pioPtr;
|
||||
int pioEnd;
|
||||
u8 pioBuffer[512];
|
||||
|
||||
int sectorsPerInterrupt;
|
||||
void (ATA::*pioDRQEndTransferFunc)() = nullptr;
|
||||
//PIO Buffer
|
||||
|
||||
//Smart
|
||||
bool smartAutosave = true;
|
||||
bool smartErrors = false;
|
||||
u8 smartSelfTestCount = 0;
|
||||
//Smart
|
||||
|
||||
u8 sceSec[256 * 2] = {0};
|
||||
|
||||
public:
|
||||
ATA();
|
||||
|
||||
int Open(ghc::filesystem::path hddPath);
|
||||
void Close();
|
||||
|
||||
void ATA_HardReset();
|
||||
|
||||
u16 Read16(u32 addr);
|
||||
void Write16(u32 addr, u16 value);
|
||||
|
||||
void Async(u32 cycles);
|
||||
|
||||
void ATAreadDMA8Mem(u8* pMem, int size);
|
||||
void ATAwriteDMA8Mem(u8* pMem, int size);
|
||||
|
||||
u16 ATAreadPIO();
|
||||
//ATAwritePIO;
|
||||
|
||||
private:
|
||||
//Info
|
||||
void CreateHDDinfo(int sizeMb);
|
||||
void CreateHDDinfoCsum();
|
||||
|
||||
//State
|
||||
void ResetBegin();
|
||||
void ResetEnd(bool hard);
|
||||
|
||||
u8 GetSelectedDevice()
|
||||
{
|
||||
return (regSelect >> 4) & 1;
|
||||
}
|
||||
void SetSelectedDevice(u8 value)
|
||||
{
|
||||
if (value == 1)
|
||||
regSelect |= (1 << 4);
|
||||
else
|
||||
regSelect &= ~(1 << 4);
|
||||
}
|
||||
|
||||
s64 HDD_GetLBA();
|
||||
void HDD_SetLBA(s64 sectorNum);
|
||||
|
||||
bool HDD_CanSeek();
|
||||
bool HDD_CanAccess(int* sectors);
|
||||
|
||||
void ClearHOB();
|
||||
|
||||
//Transfer
|
||||
void IO_Thread();
|
||||
void IO_Read();
|
||||
bool IO_Write();
|
||||
void HDD_ReadAsync(void (ATA::*drqCMD)());
|
||||
void HDD_ReadSync(void (ATA::*drqCMD)());
|
||||
bool HDD_CanAssessOrSetError();
|
||||
void HDD_SetErrorAtTransferEnd();
|
||||
|
||||
void QueueWrite(u64 sector, u8* data, u32 length);
|
||||
bool DequeueWrite(u64* sector, u8** data, u32* length);
|
||||
bool IsQueueEmpty();
|
||||
|
||||
//Commands
|
||||
void IDE_ExecCmd(u16 value);
|
||||
|
||||
bool PreCmd();
|
||||
void HDD_Unk();
|
||||
|
||||
void IDE_CmdLBA48Transform(bool islba48);
|
||||
|
||||
void DRQCmdDMADataToHost();
|
||||
void PostCmdDMADataToHost();
|
||||
void DRQCmdDMADataFromHost();
|
||||
void PostCmdDMADataFromHost();
|
||||
void HDD_ReadDMA(bool isLBA48);
|
||||
void HDD_WriteDMA(bool isLBA48);
|
||||
|
||||
void PreCmdExecuteDeviceDiag();
|
||||
void PostCmdExecuteDeviceDiag();
|
||||
void HDD_ExecuteDeviceDiag();
|
||||
|
||||
void PostCmdNoData();
|
||||
void CmdNoDataAbort();
|
||||
void HDD_FlushCache();
|
||||
void HDD_InitDevParameters();
|
||||
void HDD_ReadVerifySectors(bool isLBA48);
|
||||
void HDD_SeekCmd();
|
||||
void HDD_SetFeatures();
|
||||
void HDD_SetMultipleMode();
|
||||
void HDD_Nop();
|
||||
void HDD_Idle();
|
||||
|
||||
void DRQCmdPIODataToHost(u8* buff, int buffLen, int buffIndex, int size, bool sendIRQ);
|
||||
void PostCmdPIODataToHost();
|
||||
void HDD_IdentifyDevice();
|
||||
|
||||
void HDD_ReadMultiple(bool isLBA48);
|
||||
void HDD_ReadSectors(bool isLBA48);
|
||||
void HDD_ReadPIO(bool isLBA48);
|
||||
void HDD_ReadPIOS2();
|
||||
void HDD_ReadPIOEndBlock();
|
||||
//HDD_Write*
|
||||
|
||||
void HDD_Smart();
|
||||
void SMART_SetAutoSaveAttribute();
|
||||
void SMART_ExecuteOfflineImmediate();
|
||||
void SMART_EnableOps(bool enable);
|
||||
void SMART_ReturnStatus();
|
||||
|
||||
void HDD_SCE();
|
||||
void SCE_IDENTIFY_DRIVE();
|
||||
|
||||
//In here temporally
|
||||
static void WriteUInt16(u8* data, int* index, u16 value);
|
||||
static void WriteUInt32(u8* data, int* index, u32 value);
|
||||
static void WriteUInt64(u8* data, int* index, u64 value);
|
||||
static void WritePaddedString(u8* data, int* index, std::string value, u32 len);
|
||||
};
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "ATA.h"
|
||||
#include "../DEV9.h"
|
||||
|
||||
void ATA::WriteUInt16(u8* data, int* index, u16 value)
|
||||
{
|
||||
*(u16*)&data[*index] = value;
|
||||
*index += sizeof(value);
|
||||
}
|
||||
|
||||
void ATA::WriteUInt32(u8* data, int* index, u32 value)
|
||||
{
|
||||
*(u32*)&data[*index] = value;
|
||||
*index += sizeof(value);
|
||||
}
|
||||
|
||||
void ATA::WriteUInt64(u8* data, int* index, u64 value)
|
||||
{
|
||||
*(u64*)&data[*index] = value;
|
||||
*index += sizeof(value);
|
||||
}
|
||||
|
||||
//No null char
|
||||
void ATA::WritePaddedString(u8* data, int* index, std::string value, u32 len)
|
||||
{
|
||||
memset(&data[*index], (u8)' ', len);
|
||||
memcpy(&data[*index], value.c_str(), value.length() < len ? value.length() : len);
|
||||
*index += len;
|
||||
}
|
||||
|
||||
void ATA::CreateHDDinfo(int sizeMb)
|
||||
{
|
||||
const u16 sectorSize = 512;
|
||||
DEV9_LOG_VERB("HddSize : %i\n", config.HddSize);
|
||||
const u64 nbSectors = ((u64)(sizeMb / sectorSize) * 1024 * 1024);
|
||||
DEV9_LOG_VERB("nbSectors : %i\n", nbSectors);
|
||||
|
||||
memset(&identifyData, 0, sizeof(identifyData));
|
||||
//Defualt CHS translation
|
||||
const u16 defHeads = 16;
|
||||
const u16 defSectors = 63;
|
||||
u64 cylinderslong = std::min<u64>(nbSectors, 16514064) / defHeads / defSectors;
|
||||
const u16 defCylinders = (u16)std::min<u64>(cylinderslong, UINT16_MAX);
|
||||
|
||||
//Curent CHS translation
|
||||
cylinderslong = std::min<u64>(nbSectors, 16514064) / curHeads / curSectors;
|
||||
curCylinders = (u16)std::min<u64>(cylinderslong, UINT16_MAX);
|
||||
|
||||
const int curOldsize = curCylinders * curHeads * curSectors;
|
||||
//SET MAX ADDRESS will set the nbSectors reported
|
||||
|
||||
//General configuration bit-significant information:
|
||||
/*
|
||||
* 0x848A is for CFA devices
|
||||
* bit 0: Resv (all?)
|
||||
* bit 1: Hard Sectored (ATA-1)
|
||||
* bit 2: Soft Sectored (ATA-1) / Response incomplete (ATA-5,6,7,8)
|
||||
* bit 3: Not MFM encoded (ATA-1)
|
||||
* bit 4: Head switch time > 15 usec (ATA-1)
|
||||
* bit 5: Spindle motor control option implemented (ATA-1)
|
||||
* bit 6: Non-Removable (Obsolete) (ATA-1,2,3,4,5)
|
||||
* bit 7: Removable (ATA-1,2,3,4,5,6,7,8)
|
||||
* bit 8: disk transfer rate > 10Mbs (ATA-1)
|
||||
* bit 9: disk transfer rate > 5Mbs but <= 10Mbs (ATA-1)
|
||||
* bit 10: disk transfer rate <= 5Mbs (ATA-1)
|
||||
* bit 11: rotational speed tolerance is > 0.5% (ATA-1)
|
||||
* bit 12: data strobe offset option available (ATA-1)
|
||||
* bit 13: track offset option available (ATA-1)
|
||||
* bit 14: format speed tolerance gap required (ATA-1)
|
||||
* bit 15: 0 = ATA dev (All?)
|
||||
*/
|
||||
int index = 0;
|
||||
WriteUInt16(identifyData, &index, 0x0040); //word 0
|
||||
//Default Num of cylinders
|
||||
WriteUInt16(identifyData, &index, defCylinders); //word 1
|
||||
//Specific configuration
|
||||
index += 1 * 2; //word 2
|
||||
//Default Num of heads (Retired)
|
||||
WriteUInt16(identifyData, &index, defHeads); //word 3
|
||||
//Number of unformatted bytes per track (Retired)
|
||||
WriteUInt16(identifyData, &index, (u16)(sectorSize * defSectors)); //word 4
|
||||
//Number of unformatted bytes per sector (Retired)
|
||||
WriteUInt16(identifyData, &index, sectorSize); //word 5
|
||||
//Default Number of sectors per track (Retired)
|
||||
WriteUInt16(identifyData, &index, defSectors); //word 6
|
||||
//Reserved for assignment by the CompactFlash™ Association
|
||||
index += 2 * 2; //word 7-8
|
||||
//Retired
|
||||
index += 1 * 2; //word 9
|
||||
//Serial number (20 ASCII characters)
|
||||
WritePaddedString(identifyData, &index, "PCSX2-DEV9-ATA-HDD", 20); //word 10-19
|
||||
//Buffer(cache) type (Retired)
|
||||
WriteUInt16(identifyData, &index, /*3*/ 0); //word 20
|
||||
//Buffer(cache) size in sectors (Retired)
|
||||
WriteUInt16(identifyData, &index, /*512*/ 0); //word 21
|
||||
//Number of ECC bytes available on read / write long commands (Obsolete)
|
||||
WriteUInt16(identifyData, &index, /*4*/ 0); //word 22
|
||||
//Firmware revision (8 ASCII characters)
|
||||
WritePaddedString(identifyData, &index, "FIRM100", 8); //word 23-26
|
||||
//Model number (40 ASCII characters)
|
||||
WritePaddedString(identifyData, &index, "PCSX2-DEV9-ATA-HDD", 40); //word 27-46
|
||||
//READ/WRITE MULI max sectors
|
||||
WriteUInt16(identifyData, &index, 128 & (0x80 << 8)); //word 47
|
||||
//Dword IO supported
|
||||
WriteUInt16(identifyData, &index, 1); //word 48
|
||||
//Capabilities
|
||||
/*
|
||||
* bits 7-0: Retired
|
||||
* bit 8: DMA supported
|
||||
* bit 9: LBA supported
|
||||
* bit 10:IORDY may be disabled
|
||||
* bit 11:IORDY supported
|
||||
* bit 12:Reserved
|
||||
* bit 13:Standby timer values as specified in this standard are supported
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, ((1 << 11) | (1 << 9) | (1 << 8))); //word 49
|
||||
//Capabilities (0-Shall be set to one to indicate a device specific Standby timer value minimum)
|
||||
index += 1 * 2; //word 50
|
||||
//PIO data transfer cycle timing mode (Obsolete)
|
||||
WriteUInt16(identifyData, &index, (u8)((pioMode > 2 ? pioMode : 2) << 8)); //word 51
|
||||
//DMA data transfer cycle timing mode (Obsolete)
|
||||
WriteUInt16(identifyData, &index, 0); //word 52
|
||||
//
|
||||
/*
|
||||
* bit 0: Fields in 54:58 are valid (CHS sizes)(Obsolete)
|
||||
* bit 1: Fields in 64:70 are valid (pio3,4 and MWDMA info)
|
||||
* bit 2: Fields in 88 are valid (UDMA modes)
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, (1 | (1 << 1) | (1 << 2))); //word 53
|
||||
//Number of current cylinders
|
||||
WriteUInt16(identifyData, &index, curCylinders); //word 54
|
||||
//Number of current heads
|
||||
WriteUInt16(identifyData, &index, curHeads); //word 55
|
||||
//Number of current sectors per track
|
||||
WriteUInt16(identifyData, &index, curSectors); //word 56
|
||||
//Current capacity in sectors
|
||||
WriteUInt32(identifyData, &index, (u32)curOldsize); //word 57-58
|
||||
//PIO READ/WRITE Multiple setting
|
||||
/*
|
||||
* bit 7-0: Current setting for number of logical sectors that shall be transferred per DRQ
|
||||
* data block on READ/WRITE Multiple commands
|
||||
* bit 8: Multiple sector setting is valid
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, (u16)(curMultipleSectorsSetting | (1 << 8))); //word 59
|
||||
//Total number of user addressable logical sectors
|
||||
WriteUInt32(identifyData, &index, (u32)(nbSectors < 268435456 ? nbSectors : 268435456)); //word 60-61
|
||||
//DMA modes
|
||||
/*
|
||||
* bits 0-7: Singleword modes supported (0,1,2)
|
||||
* bits 8-15: Transfer mode active
|
||||
*/
|
||||
if (sdmaMode > 0)
|
||||
WriteUInt16(identifyData, &index, (u16)(0x07 | (1 << (sdmaMode + 8)))); //word 62
|
||||
else
|
||||
WriteUInt16(identifyData, &index, 0x07); //word 62
|
||||
//DMA Modes
|
||||
/*
|
||||
* bits 0-7: Multiword modes supported (0,1,2)
|
||||
* bits 8-15: Transfer mode active
|
||||
*/
|
||||
if (mdmaMode > 0)
|
||||
WriteUInt16(identifyData, &index, (u16)(0x07 | (1 << (mdmaMode + 8)))); //word 63
|
||||
else
|
||||
WriteUInt16(identifyData, &index, 0x07); //word 63
|
||||
//Bit 0-7-PIO modes supported (0,1,2,3,4)
|
||||
WriteUInt16(identifyData, &index, 0x1F); //word 64 (pio3,4 supported) selection not reported here
|
||||
//Minimum Multiword DMA transfer cycle time per word
|
||||
WriteUInt16(identifyData, &index, 80); //word 65
|
||||
//Manufacturer’s recommended Multiword DMA transfer cycle time
|
||||
WriteUInt16(identifyData, &index, 80); //word 66
|
||||
//Minimum PIO transfer cycle time without flow control
|
||||
WriteUInt16(identifyData, &index, 120); //word 67
|
||||
//Minimum PIO transfer cycle time with IORDY flow control
|
||||
WriteUInt16(identifyData, &index, 120); //word 68
|
||||
//Reserved
|
||||
//69-70
|
||||
//Reserved
|
||||
//71-74
|
||||
//Queue depth (4bit, Maximum queue depth - 1)
|
||||
//75
|
||||
//Reserved
|
||||
//76-79
|
||||
index = 80 * 2;
|
||||
//Major revision number (1-3-Obsolete, 4-7-ATA4-7 supported)
|
||||
WriteUInt16(identifyData, &index, 0x70); //word 80
|
||||
//Minor revision number
|
||||
WriteUInt16(identifyData, &index, 0); //word 81
|
||||
//Supported Feature Sets (82)
|
||||
/*
|
||||
* bit 0: Smart
|
||||
* bit 1: Security Mode
|
||||
* bit 2: Removable media feature set
|
||||
* bit 3: Power management
|
||||
* bit 4: Packet (the CD features)
|
||||
* bit 5: Write cache
|
||||
* bit 6: Look-ahead
|
||||
* bit 7: Release interrupt
|
||||
* bit 8: SERVICE interrupt
|
||||
* bit 9: DEVICE RESET interrupt
|
||||
* bit 10: Host Protected Area
|
||||
* bit 11: (Obsolete)
|
||||
* bit 12: WRITE BUFFER command
|
||||
* bit 13: READ BUFFER command
|
||||
* bit 14: NOP
|
||||
* bit 15: (Obsolete)
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, (u16)((1 << 14) | (1 << 5) | /*(1 << 1) | (1 << 10) |*/ 1)); //word 82
|
||||
//Supported Feature Sets (83)
|
||||
/*
|
||||
* bit 0: DOWNLOAD MICROCODE
|
||||
* bit 1: READ/WRITE DMA QUEUED
|
||||
* bit 2: CFA (Card reader)
|
||||
* bit 3: Advanced Power Management
|
||||
* bit 4: Removable Media Status Notifications
|
||||
* bit 5: Power-Up Standby
|
||||
* bit 6: SET FEATURES required to spin up after power-up
|
||||
* bit 7: ??
|
||||
* bit 8: SET MAX security extension
|
||||
* bit 9: Automatic Acoustic Management
|
||||
* bit 10: 48bit LBA
|
||||
* bit 11: Device Configuration Overlay
|
||||
* bit 12: FLUSH CACHE
|
||||
* bit 13: FLUSH CACHE EXT
|
||||
* bit 14: 1
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, (u16)((1 << 14) | (1 << 13) | (1 << 12) /*| (1 << 8)*/ | ((lba48Supported ? 1 : 0) << 10))); //word 83
|
||||
//Supported Feature Sets (84)
|
||||
/*
|
||||
* bit 0: Smart error logging
|
||||
* bit 1: smart self-test
|
||||
* bit 2: Media serial number
|
||||
* bit 3: Media Card Pass Though
|
||||
* bit 4: Streaming feature set
|
||||
* bit 5: General Purpose Logging
|
||||
* bit 6: WRITE DMA FUA EXT & WRITE MULTIPLE FUA EXT
|
||||
* bit 7: WRITE DMA QUEUED FUA EXT
|
||||
* bit 8: 64bit World Wide Name
|
||||
* bit 9: URG bit supported for WRITE STREAM DMA EXT amd WRITE STREAM EXT
|
||||
* bit 10: URG bit supported for READ STREAM DMA EXT amd READ STREAM EXT
|
||||
* bit 13: IDLE IMMEDIATE with UNLOAD FEATURE
|
||||
* bit 14: 1
|
||||
*/
|
||||
WriteUInt16(identifyData, &index, (u16)((1 << 14) | (1 << 1) | 1)); //word 84
|
||||
//Command set/feature enabled/supported (See word 82)
|
||||
WriteUInt16(identifyData, &index, (u16)((fetSmartEnabled << 0) | (fetSecurityEnabled << 1) | (fetWriteCacheEnabled << 5) | (fetHostProtectedAreaEnabled << 10) | (1 << 14))); //Fixed //word 85
|
||||
//Command set/feature enabled/supported (See word 83)
|
||||
WriteUInt16(identifyData, &index, (u16)(
|
||||
/*(1 << 8) | //SET MAX */
|
||||
((lba48Supported ? 1 : 0) << 10) | //Fixed
|
||||
(1 << 12) | //Fixed
|
||||
(1 << 13))); //Fixed //word 86
|
||||
//Command set/feature enabled/supported (See word 84)
|
||||
WriteUInt16(identifyData, &index, (u16)((1 << 14) | (1 << 1) | 1));
|
||||
WriteUInt16(identifyData, &index, (u16)(
|
||||
(1) | //Fixed
|
||||
((1) << 1))); //Fixed //word 87
|
||||
//UDMA modes
|
||||
/*
|
||||
* bits 0-7: ultraword modes supported (0,1,2,4,5,6,7)
|
||||
* bits 8-15: Transfer mode active
|
||||
*/
|
||||
if (udmaMode > 0)
|
||||
WriteUInt16(identifyData, &index, (u16)(0x7f | (1 << (udmaMode + 8)))); //word 88
|
||||
else
|
||||
WriteUInt16(identifyData, &index, 0x7f); //word 88
|
||||
//Time required for security erase unit completion
|
||||
//89
|
||||
//Time required for Enhanced security erase completion
|
||||
//90
|
||||
//Current advanced power management value
|
||||
//91
|
||||
//Master Password Identifier
|
||||
//92
|
||||
//Hardware reset result. The contents of bits (12:0) of this word shall change only during the execution of a hardware reset.
|
||||
/*
|
||||
* bit 0: 1
|
||||
* bit 1-2: How Dev0 determined Dev number (11 = unk)
|
||||
* bit 3: Dev 0 Passes Diag
|
||||
* bit 4: Dev 0 Detected assertion of PDIAG
|
||||
* bit 5: Dev 0 Detected assertion of DSAP
|
||||
* bit 6: Dev 0 Responds when Dev1 is selected
|
||||
* bit 7: Reserved
|
||||
* bit 8: 1
|
||||
* bit 9-10: How Dev1 determined Dev number
|
||||
* bit 11: Dev1 asserted 1
|
||||
* bit 12: Reserved
|
||||
* bit 13: Dev detected CBLID above Vih
|
||||
* bit 14: 1
|
||||
*/
|
||||
index = 93 * 2;
|
||||
WriteUInt16(identifyData, &index, (u16)(1 | (1 << 14) | 0x2000)); //word 93
|
||||
//Vendor’s recommended acoustic management value.
|
||||
//94
|
||||
//Stream Minimum Request Size
|
||||
//95
|
||||
//Streaming Transfer Time - DMA
|
||||
//96
|
||||
//Streaming Access Latency - DMA and PIO
|
||||
//97
|
||||
//Streaming Performance Granularity
|
||||
//98-99
|
||||
//Total Number of User Addressable Sectors for the 48-bit Address feature set.
|
||||
index = 100 * 2;
|
||||
WriteUInt64(identifyData, &index, (u16)nbSectors);
|
||||
index -= 2;
|
||||
WriteUInt16(identifyData, &index, 0); //truncate to 48bits
|
||||
//Streaming Transfer Time - PIO
|
||||
//104
|
||||
//Reserved
|
||||
//105
|
||||
//Physical sector size / Logical Sector Size
|
||||
/*
|
||||
* bit 0-3: 2^X logical sectors per physical sector
|
||||
* bit 12: Logical sector longer than 512 bytes
|
||||
* bit 13: multiple logical sectors per physical sector
|
||||
* bit 14: 1
|
||||
*/
|
||||
index = 106 * 2;
|
||||
WriteUInt16(identifyData, &index, (u16)((1 << 14) | 0));
|
||||
//Inter-seek delay for ISO-7779acoustic testing in microseconds
|
||||
//107
|
||||
//WNN
|
||||
//108-111
|
||||
//Reserved
|
||||
//112-115
|
||||
//Reserved
|
||||
//116
|
||||
//Words per Logical Sector
|
||||
//117-118
|
||||
//Reserved
|
||||
//119-126
|
||||
//Removable Media Status Notification feature support
|
||||
//127
|
||||
//Security status
|
||||
/*
|
||||
* bit 0: Security supported
|
||||
* bit 1: Security enabled
|
||||
* bit 2: Security locked
|
||||
* bit 3: Security frozen
|
||||
* bit 4: Security count expired
|
||||
* bit 5: Enhanced erase supported
|
||||
* bit 6-7: reserved
|
||||
* bit 8: is Maximum Security
|
||||
*/
|
||||
//Vendor Specific
|
||||
//129-159
|
||||
//CFA power mode 1
|
||||
//160
|
||||
//Reserved for CFA
|
||||
//161-175
|
||||
//Current media serial number (60 ASCII characters)
|
||||
//176-205
|
||||
//Reserved
|
||||
//206-254
|
||||
//Integrity word
|
||||
//15:8 Checksum, 7:0 Signature
|
||||
CreateHDDinfoCsum();
|
||||
}
|
||||
void ATA::CreateHDDinfoCsum() //Is this correct?
|
||||
{
|
||||
u8 counter = 0;
|
||||
|
||||
for (int i = 0; i < (512 - 1); i++)
|
||||
counter += identifyData[i];
|
||||
|
||||
counter += 0xA5;
|
||||
|
||||
identifyData[510] = 0xA5;
|
||||
identifyData[511] = (u8)(255 - counter + 1);
|
||||
counter = 0;
|
||||
|
||||
for (int i = 0; i < (512); i++)
|
||||
counter += identifyData[i];
|
||||
|
||||
DEV9_LOG_VERB("%s\n", counter);
|
||||
}
|
|
@ -0,0 +1,446 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "ATA.h"
|
||||
#include "../DEV9.h"
|
||||
#include "HddCreate.h"
|
||||
|
||||
ATA::ATA()
|
||||
{
|
||||
//Power on, Would do self-Diag + Hardware Init
|
||||
ResetBegin();
|
||||
ResetEnd(true);
|
||||
}
|
||||
|
||||
int ATA::Open(ghc::filesystem::path hddPath)
|
||||
{
|
||||
readBufferLen = 256 * 512;
|
||||
readBuffer = new u8[readBufferLen];
|
||||
|
||||
CreateHDDinfo(config.HddSize);
|
||||
|
||||
//Open File
|
||||
if (ghc::filesystem::exists(hddPath))
|
||||
hddImage = ghc::filesystem::fstream(hddPath, std::ios::in | std::ios::out | std::ios::binary);
|
||||
else
|
||||
{
|
||||
HddCreate hddCreator;
|
||||
hddCreator.filePath = hddPath;
|
||||
hddCreator.neededSize = config.HddSize;
|
||||
hddCreator.Start();
|
||||
|
||||
if (hddCreator.errored)
|
||||
return -1;
|
||||
|
||||
hddImage = ghc::filesystem::fstream(hddPath);
|
||||
}
|
||||
|
||||
//Store HddImage size for later check
|
||||
hddImage.seekg(0, std::ios::end);
|
||||
hddImageSize = hddImage.tellg();
|
||||
|
||||
//Setup Write queue
|
||||
tail = new WriteQueueEntry();
|
||||
head = tail;
|
||||
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioRead = false;
|
||||
ioWrite = false;
|
||||
}
|
||||
|
||||
ioThread = std::thread(&ATA::IO_Thread, this);
|
||||
ioRunning = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ATA::Close()
|
||||
{
|
||||
//Wait for async code to finish
|
||||
if (ioRunning)
|
||||
{
|
||||
ioClose.store(true);
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioWrite = true;
|
||||
}
|
||||
ioReady.notify_all();
|
||||
|
||||
ioThread.join();
|
||||
ioRunning = false;
|
||||
}
|
||||
//Delete queue
|
||||
if (head != nullptr)
|
||||
{
|
||||
if (!IsQueueEmpty())
|
||||
{
|
||||
DEV9_LOG_ERROR("Write queue not empty");
|
||||
pxAssert(false);
|
||||
abort(); //All data must be written at this point
|
||||
}
|
||||
|
||||
delete head;
|
||||
head = nullptr;
|
||||
tail = nullptr;
|
||||
}
|
||||
//Close File Handle
|
||||
if (hddImage.is_open())
|
||||
hddImage.close();
|
||||
|
||||
delete[] readBuffer;
|
||||
readBuffer = nullptr;
|
||||
}
|
||||
|
||||
void ATA::ResetBegin()
|
||||
{
|
||||
PreCmdExecuteDeviceDiag();
|
||||
}
|
||||
void ATA::ResetEnd(bool hard)
|
||||
{
|
||||
curHeads = 16;
|
||||
curSectors = 63;
|
||||
curCylinders = 0;
|
||||
curMultipleSectorsSetting = 128;
|
||||
|
||||
//UDMA Mode setting is preserved
|
||||
//across SRST
|
||||
if (hard)
|
||||
{
|
||||
pioMode = 4;
|
||||
sdmaMode = -1;
|
||||
mdmaMode = 2;
|
||||
udmaMode = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pioMode = 4;
|
||||
if (udmaMode == -1)
|
||||
{
|
||||
sdmaMode = -1;
|
||||
mdmaMode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
regControlEnableIRQ = false;
|
||||
HDD_ExecuteDeviceDiag();
|
||||
regControlEnableIRQ = true;
|
||||
}
|
||||
|
||||
void ATA::ATA_HardReset()
|
||||
{
|
||||
DEV9_LOG_VERB("*ATA_HARD RESET\n");
|
||||
ResetBegin();
|
||||
ResetEnd(true);
|
||||
}
|
||||
|
||||
u16 ATA::Read16(u32 addr)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case ATA_R_DATA:
|
||||
return ATAreadPIO();
|
||||
case ATA_R_ERROR:
|
||||
DEV9_LOG_VERB("*ATA_R_ERROR 16bit read at address %x, value %x, Active %s\n", addr, regError, (GetSelectedDevice() == 0) ? "True" : "False");
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
return regError;
|
||||
case ATA_R_NSECTOR:
|
||||
DEV9_LOG_VERB("*ATA_R_NSECTOR 16bit read at address %x, value %x, Active %s\n", addr, nsector, (GetSelectedDevice() == 0) ? "True" : "False");
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
if (!regControlHOBRead)
|
||||
return regNsector;
|
||||
else
|
||||
return regNsectorHOB;
|
||||
case ATA_R_SECTOR:
|
||||
DEV9_LOG_VERB("*ATA_R_NSECTOR 16bit read at address %x, value %x, Active %s\n", addr, regSector, (GetSelectedDevice() == 0) ? "True" : "False");
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
if (!regControlHOBRead)
|
||||
return regSector;
|
||||
else
|
||||
return regSectorHOB;
|
||||
case ATA_R_LCYL:
|
||||
DEV9_LOG_VERB("*ATA_R_LCYL 16bit read at address %x, value %x, Active %s\n", addr, regLcyl, (GetSelectedDevice() == 0) ? "True" : "False");
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
if (!regControlHOBRead)
|
||||
return regLcyl;
|
||||
else
|
||||
return regLcylHOB;
|
||||
case ATA_R_HCYL:
|
||||
DEV9_LOG_VERB("*ATA_R_HCYL 16bit read at address % x, value % x, Active %s\n", addr, regHcyl, (GetSelectedDevice() == 0) ? " True " : " False ");
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
if (!regControlHOBRead)
|
||||
return regHcyl;
|
||||
else
|
||||
return regHcylHOB;
|
||||
case ATA_R_SELECT:
|
||||
DEV9_LOG_VERB("*ATA_R_SELECT 16bit read at address % x, value % x, Active %s\n", addr, regSelect, (GetSelectedDevice() == 0) ? " True " : " False ");
|
||||
return regSelect;
|
||||
case ATA_R_STATUS:
|
||||
DEV9_LOG_VERB("*ATA_R_STATUS (Fallthough to ATA_R_ALT_STATUS)\n");
|
||||
//Clear irqcause
|
||||
dev9.irqcause &= ~ATA_INTR_INTRQ;
|
||||
[[fallthrough]];
|
||||
case ATA_R_ALT_STATUS:
|
||||
DEV9_LOG_VERB("*ATA_R_ALT_STATUS 16bit read at address % x, value % x, Active %s\n", addr, regStatus, (GetSelectedDevice() == 0) ? " True " : " False ");
|
||||
//raise IRQ?
|
||||
if (GetSelectedDevice() != 0)
|
||||
return 0;
|
||||
return regStatus;
|
||||
default:
|
||||
DEV9_LOG_ERROR("*Unknown 16bit read at address %x\n", addr);
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::Write16(u32 addr, u16 value)
|
||||
{
|
||||
if (addr != ATA_R_CMD && (regStatus & (ATA_STAT_BUSY | ATA_STAT_DRQ)) != 0)
|
||||
{
|
||||
DEV9_LOG_ERROR("*DEVICE BUSY, DROPPING WRITE\n");
|
||||
return;
|
||||
}
|
||||
switch (addr)
|
||||
{
|
||||
case ATA_R_FEATURE:
|
||||
DEV9_LOG_VERB("*ATA_R_FEATURE 16bit write at address %x, value %x\n", addr, value);
|
||||
ClearHOB();
|
||||
regFeatureHOB = regFeature;
|
||||
regFeature = (u8)value;
|
||||
break;
|
||||
case ATA_R_NSECTOR:
|
||||
DEV9_LOG_VERB("*ATA_R_NSECTOR 16bit write at address %x, value %x\n", addr, value);
|
||||
ClearHOB();
|
||||
regNsectorHOB = regNsector;
|
||||
regNsector = (u8)value;
|
||||
break;
|
||||
case ATA_R_SECTOR:
|
||||
DEV9_LOG_VERB("*ATA_R_SECTOR 16bit write at address %x, value %x\n", addr, value);
|
||||
ClearHOB();
|
||||
regSectorHOB = regSector;
|
||||
regSector = (u8)value;
|
||||
break;
|
||||
case ATA_R_LCYL:
|
||||
DEV9_LOG_VERB("*ATA_R_LCYL 16bit write at address %x, value %x\n", addr, value);
|
||||
ClearHOB();
|
||||
regLcylHOB = regLcyl;
|
||||
regLcyl = (u8)value;
|
||||
break;
|
||||
case ATA_R_HCYL:
|
||||
DEV9_LOG_VERB("*ATA_R_HCYL 16bit write at address %x, value %x\n", addr, value);
|
||||
ClearHOB();
|
||||
regHcylHOB = regHcyl;
|
||||
regHcyl = (u8)value;
|
||||
break;
|
||||
case ATA_R_SELECT:
|
||||
DEV9_LOG_VERB("*ATA_R_SELECT 16bit write at address %x, value %x\n", addr, value);
|
||||
regSelect = (u8)value;
|
||||
//bus->ifs[0].select = (val & ~0x10) | 0xa0;
|
||||
//bus->ifs[1].select = (val | 0x10) | 0xa0;
|
||||
break;
|
||||
case ATA_R_CONTROL:
|
||||
DEV9_LOG_VERB("*ATA_R_CONTROL 16bit write at address %x, value %x\n", addr, value);
|
||||
//dev9Ru16(ATA_R_CONTROL) = value;
|
||||
if ((value & 0x2) != 0)
|
||||
{
|
||||
//Supress all IRQ
|
||||
dev9.irqcause &= ~ATA_INTR_INTRQ;
|
||||
regControlEnableIRQ = false;
|
||||
}
|
||||
else
|
||||
regControlEnableIRQ = true;
|
||||
|
||||
if ((value & 0x4) != 0)
|
||||
{
|
||||
DEV9_LOG_VERB("*ATA_R_CONTROL RESET\n");
|
||||
ResetBegin();
|
||||
ResetEnd(false);
|
||||
}
|
||||
if ((value & 0x80) != 0)
|
||||
regControlHOBRead = true;
|
||||
|
||||
break;
|
||||
case ATA_R_CMD:
|
||||
DEV9_LOG_VERB("*ATA_R_CMD 16bit write at address %x, value %x\n", addr, value);
|
||||
regCommand = value;
|
||||
regControlHOBRead = false;
|
||||
dev9.irqcause &= ~ATA_INTR_INTRQ;
|
||||
IDE_ExecCmd(value);
|
||||
break;
|
||||
default:
|
||||
DEV9_LOG_ERROR("*UNKOWN 16bit write at address %x, value %x\n", addr, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::Async(uint cycles)
|
||||
{
|
||||
if (!hddImage.is_open())
|
||||
return;
|
||||
|
||||
if ((regStatus & (ATA_STAT_BUSY | ATA_STAT_DRQ)) == 0 ||
|
||||
awaitFlush || (waitingCmd != nullptr))
|
||||
{
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
if (ioRead || ioWrite)
|
||||
//IO Running
|
||||
return;
|
||||
}
|
||||
|
||||
//Note, ioThread may still be working.
|
||||
if (waitingCmd != nullptr) //Are we waiting to continue a command?
|
||||
{
|
||||
//Log_Info("Running waiting command");
|
||||
void(ATA::*cmd)() = waitingCmd;
|
||||
waitingCmd = nullptr;
|
||||
(this->*cmd)();
|
||||
}
|
||||
else if (!IsQueueEmpty()) //Flush cache
|
||||
{
|
||||
//Log_Info("Starting async write");
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioWrite = true;
|
||||
}
|
||||
ioReady.notify_all();
|
||||
}
|
||||
else if (awaitFlush) //Fire IRQ on flush completion?
|
||||
{
|
||||
//Log_Info("Flush done, raise IRQ");
|
||||
awaitFlush = false;
|
||||
PostCmdNoData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s64 ATA::HDD_GetLBA()
|
||||
{
|
||||
if ((regSelect & 0x40) != 0)
|
||||
{
|
||||
if (!lba48)
|
||||
{
|
||||
return (regSector |
|
||||
(regLcyl << 8) |
|
||||
(regHcyl << 16) |
|
||||
((regSelect & 0x0f) << 24));
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((s64)regHcylHOB << 40) |
|
||||
((s64)regLcylHOB << 32) |
|
||||
((s64)regSectorHOB << 24) |
|
||||
((s64)regHcyl << 16) |
|
||||
((s64)regLcyl << 8) |
|
||||
regSector;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regStatus |= (u8)ATA_STAT_ERR;
|
||||
regError |= (u8)ATA_ERR_ABORT;
|
||||
|
||||
DEV9_LOG_ERROR("DEV9 ERROR : tried to get LBA address while LBA mode disabled\n");
|
||||
//(c.Nh + h).Ns+(s-1)
|
||||
//s64 CHSasLBA = ((regLcyl + (regHcyl << 8)) * curHeads + (regSelect & 0x0F)) * curSectors + (regSector - 1);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::HDD_SetLBA(s64 sectorNum)
|
||||
{
|
||||
if ((regSelect & 0x40) != 0)
|
||||
{
|
||||
if (!lba48)
|
||||
{
|
||||
regSelect = (u8)((regSelect & 0xf0) | (int)((sectorNum >> 24) & 0x0f));
|
||||
regHcyl = (u8)(sectorNum >> 16);
|
||||
regLcyl = (u8)(sectorNum >> 8);
|
||||
regSector = (u8)(sectorNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
regSector = (u8)sectorNum;
|
||||
regLcyl = (u8)(sectorNum >> 8);
|
||||
regHcyl = (u8)(sectorNum >> 16);
|
||||
regSectorHOB = (u8)(sectorNum >> 24);
|
||||
regLcylHOB = (u8)(sectorNum >> 32);
|
||||
regHcylHOB = (u8)(sectorNum >> 40);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
regError |= ATA_ERR_ABORT;
|
||||
|
||||
DEV9_LOG_ERROR("DEV9 ERROR : tried to get LBA address while LBA mode disabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool ATA::HDD_CanSeek()
|
||||
{
|
||||
int sectors = 0;
|
||||
return HDD_CanAccess(§ors);
|
||||
}
|
||||
|
||||
bool ATA::HDD_CanAccess(int* sectors)
|
||||
{
|
||||
s64 lba;
|
||||
s64 posStart;
|
||||
s64 posEnd;
|
||||
s64 maxLBA;
|
||||
|
||||
maxLBA = std::min<s64>((s64)config.HddSize * 1024 * 1024 / 512, hddImageSize);
|
||||
if ((regSelect & 0x40) == 0) //CHS mode
|
||||
maxLBA = std::min<s64>(maxLBA, curCylinders * curHeads * curSectors);
|
||||
|
||||
lba = HDD_GetLBA();
|
||||
if (lba == -1)
|
||||
return false;
|
||||
|
||||
DEV9_LOG_VERB("LBA :%i\n", lba);
|
||||
posStart = lba;
|
||||
|
||||
if (posStart > maxLBA)
|
||||
{
|
||||
*sectors = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
posEnd = posStart + *sectors;
|
||||
|
||||
if (posEnd > maxLBA)
|
||||
{
|
||||
const s64 overshoot = posEnd - maxLBA;
|
||||
s64 space = *sectors - overshoot;
|
||||
*sectors = (int)space;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//QEMU stuff
|
||||
void ATA::ClearHOB()
|
||||
{
|
||||
/* any write clears HOB high bit of device control register */
|
||||
regControlHOBRead = false;
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "ATA.h"
|
||||
#include "../DEV9.h"
|
||||
|
||||
void ATA::IO_Thread()
|
||||
{
|
||||
std::unique_lock ioWaitHandle(ioMutex);
|
||||
ioThreadIdle_bool = false;
|
||||
ioWaitHandle.unlock();
|
||||
|
||||
while (true)
|
||||
{
|
||||
ioWaitHandle.lock();
|
||||
ioThreadIdle_bool = true;
|
||||
ioThreadIdle_cv.notify_all();
|
||||
|
||||
ioReady.wait(ioWaitHandle, [&] { return ioRead | ioWrite; });
|
||||
ioThreadIdle_bool = false;
|
||||
|
||||
int ioType = -1;
|
||||
if (ioRead)
|
||||
ioType = 0;
|
||||
else if (ioWrite)
|
||||
ioType = 1;
|
||||
|
||||
ioWaitHandle.unlock();
|
||||
|
||||
//Read or Write
|
||||
if (ioType == 0)
|
||||
IO_Read();
|
||||
else if (ioType == 1)
|
||||
{
|
||||
if (!IO_Write())
|
||||
{
|
||||
if (ioClose.load())
|
||||
{
|
||||
ioClose.store(false);
|
||||
ioWaitHandle.lock();
|
||||
ioThreadIdle_bool = true;
|
||||
ioWaitHandle.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::IO_Read()
|
||||
{
|
||||
const s64 lba = HDD_GetLBA();
|
||||
|
||||
if (lba == -1)
|
||||
{
|
||||
DEV9_LOG_ERROR("Invalid LBA");
|
||||
pxAssert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
const u64 pos = lba * 512;
|
||||
hddImage.seekg(pos, std::ios::beg);
|
||||
if (hddImage.fail())
|
||||
{
|
||||
DEV9_LOG_ERROR("Read error");
|
||||
pxAssert(false);
|
||||
abort();
|
||||
}
|
||||
hddImage.read((char*)readBuffer, (u64)nsector * 512);
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioRead = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ATA::IO_Write()
|
||||
{
|
||||
u64 sector = 0;
|
||||
u8* data = nullptr;
|
||||
u32 len = 0;
|
||||
if (!DequeueWrite(§or, &data, &len))
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioWrite = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
hddImage.seekp(sector * 512, std::ios::beg);
|
||||
hddImage.write((char*)data, len);
|
||||
if (hddImage.fail())
|
||||
{
|
||||
DEV9_LOG_ERROR("Write error");
|
||||
pxAssert(false);
|
||||
abort();
|
||||
}
|
||||
hddImage.flush();
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadAsync(void (ATA::*drqCMD)())
|
||||
{
|
||||
nsectorLeft = 0;
|
||||
|
||||
if (!HDD_CanAssessOrSetError())
|
||||
return;
|
||||
|
||||
nsectorLeft = nsector;
|
||||
if (readBufferLen < nsector * 512)
|
||||
{
|
||||
delete readBuffer;
|
||||
readBuffer = new u8[nsector * 512];
|
||||
readBufferLen = nsector * 512;
|
||||
}
|
||||
waitingCmd = drqCMD;
|
||||
|
||||
{
|
||||
std::lock_guard ioSignallock(ioMutex);
|
||||
ioRead = true;
|
||||
}
|
||||
ioReady.notify_all();
|
||||
}
|
||||
|
||||
//Note, we don't expect both Async & Sync Reads
|
||||
//Do one of the other
|
||||
void ATA::HDD_ReadSync(void (ATA::*drqCMD)())
|
||||
{
|
||||
//unique_lock instead of lock_guard as also used for cv
|
||||
std::unique_lock ioWaitHandle(ioMutex);
|
||||
//Set ioWrite false to prevent reading & writing at the same time
|
||||
const bool ioWritePaused = ioWrite;
|
||||
ioWrite = false;
|
||||
|
||||
//wait until thread waiting
|
||||
ioThreadIdle_cv.wait(ioWaitHandle, [&] { return ioThreadIdle_bool; });
|
||||
ioWaitHandle.unlock();
|
||||
|
||||
nsectorLeft = 0;
|
||||
|
||||
if (!HDD_CanAssessOrSetError())
|
||||
{
|
||||
if (ioWritePaused)
|
||||
{
|
||||
ioWaitHandle.lock();
|
||||
ioWrite = true;
|
||||
ioWaitHandle.unlock();
|
||||
ioReady.notify_all();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nsectorLeft = nsector;
|
||||
if (readBufferLen < nsector * 512)
|
||||
{
|
||||
delete[] readBuffer;
|
||||
readBuffer = new u8[nsector * 512];
|
||||
readBufferLen = nsector * 512;
|
||||
}
|
||||
|
||||
IO_Read();
|
||||
|
||||
if (ioWritePaused)
|
||||
{
|
||||
ioWaitHandle.lock();
|
||||
ioWrite = true;
|
||||
ioWaitHandle.unlock();
|
||||
ioReady.notify_all();
|
||||
}
|
||||
|
||||
(this->*drqCMD)();
|
||||
}
|
||||
|
||||
bool ATA::HDD_CanAssessOrSetError()
|
||||
{
|
||||
if (!HDD_CanAccess(&nsector))
|
||||
{
|
||||
//Read what we can
|
||||
regStatus |= (u8)ATA_STAT_ERR;
|
||||
regError |= (u8)ATA_ERR_ID;
|
||||
if (nsector == -1)
|
||||
{
|
||||
PostCmdNoData();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void ATA::HDD_SetErrorAtTransferEnd()
|
||||
{
|
||||
u64 currSect = HDD_GetLBA();
|
||||
currSect += nsector;
|
||||
if ((regStatus & ATA_STAT_ERR) != 0)
|
||||
{
|
||||
//Error condition
|
||||
//Write errored sector to LBA
|
||||
currSect++;
|
||||
HDD_SetLBA(currSect);
|
||||
}
|
||||
}
|
||||
|
||||
//Used by EE thread only
|
||||
void ATA::QueueWrite(u64 sector, u8* data, u32 length)
|
||||
{
|
||||
WriteQueueEntry* newEntry = head;
|
||||
newEntry->data = data;
|
||||
newEntry->length = length;
|
||||
newEntry->sector = sector;
|
||||
|
||||
//Allocate Next entry
|
||||
newEntry->next = new WriteQueueEntry();
|
||||
head = newEntry->next;
|
||||
//Set ready
|
||||
newEntry->ready.store(true);
|
||||
}
|
||||
|
||||
//Used by IO thread only
|
||||
bool ATA::DequeueWrite(u64* sector, u8** data, u32* length)
|
||||
{
|
||||
if (!tail->ready.load())
|
||||
return false;
|
||||
|
||||
WriteQueueEntry* entry = tail;
|
||||
tail = entry->next;
|
||||
|
||||
*sector = entry->sector;
|
||||
*data = entry->data;
|
||||
*length = entry->length;
|
||||
delete entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Used by EE thread only
|
||||
bool ATA::IsQueueEmpty()
|
||||
{
|
||||
return !tail->ready.load();
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::DRQCmdDMADataToHost()
|
||||
{
|
||||
//Ready to Start DMA
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
regStatus |= ATA_STAT_DRQ;
|
||||
dmaReady = true;
|
||||
_DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1);
|
||||
//PCSX2 will Start DMA
|
||||
}
|
||||
void ATA::PostCmdDMADataToHost()
|
||||
{
|
||||
//readBuffer = null;
|
||||
nsectorLeft = 0;
|
||||
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
dmaReady = false;
|
||||
|
||||
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA;
|
||||
if (regControlEnableIRQ)
|
||||
_DEV9irq(ATA_INTR_INTRQ, 1);
|
||||
//PCSX2 Will Start DMA
|
||||
}
|
||||
|
||||
void ATA::DRQCmdDMADataFromHost()
|
||||
{
|
||||
//Ready to Start DMA
|
||||
if (!HDD_CanAssessOrSetError())
|
||||
return;
|
||||
|
||||
nsectorLeft = nsector;
|
||||
currentWrite = new u8[nsector * 512];
|
||||
currentWriteLength = nsector * 512;
|
||||
currentWriteSectors = HDD_GetLBA();
|
||||
|
||||
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
regStatus |= ATA_STAT_DRQ;
|
||||
dmaReady = true;
|
||||
_DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1);
|
||||
//PCSX2 will Start DMA
|
||||
}
|
||||
void ATA::PostCmdDMADataFromHost()
|
||||
{
|
||||
QueueWrite(currentWriteSectors, currentWrite, currentWriteLength);
|
||||
currentWrite = nullptr;
|
||||
currentWriteLength = 0;
|
||||
currentWriteSectors = 0;
|
||||
nsectorLeft = 0;
|
||||
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
dmaReady = false;
|
||||
|
||||
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA;
|
||||
|
||||
if (fetWriteCacheEnabled)
|
||||
{
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
if (regControlEnableIRQ)
|
||||
_DEV9irq(ATA_INTR_INTRQ, 1); //0x6C
|
||||
}
|
||||
else
|
||||
awaitFlush = true;
|
||||
|
||||
Async(-1);
|
||||
}
|
||||
|
||||
void ATA::ATAreadDMA8Mem(u8* pMem, int size)
|
||||
{
|
||||
if ((udmaMode >= 0) &&
|
||||
(dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
DEV9_LOG_VERB("DMA read, size %i, transferred %i, total size %i\n", size, rdTransferred, nsector * 512);
|
||||
|
||||
//read
|
||||
memcpy(pMem, &readBuffer[rdTransferred], size);
|
||||
|
||||
rdTransferred += size;
|
||||
|
||||
if (rdTransferred >= nsector * 512)
|
||||
{
|
||||
HDD_SetErrorAtTransferEnd();
|
||||
|
||||
nsector = 0;
|
||||
rdTransferred = 0;
|
||||
PostCmdDMADataToHost();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::ATAwriteDMA8Mem(u8* pMem, int size)
|
||||
{
|
||||
if ((udmaMode >= 0) &&
|
||||
(dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0)
|
||||
{
|
||||
DEV9_LOG_VERB("DMA write, size %i, transferred %i, total size %i\n", size, wrTransferred, nsector * 512);
|
||||
|
||||
//write
|
||||
memcpy(¤tWrite[wrTransferred], pMem, size);
|
||||
|
||||
wrTransferred += size;
|
||||
|
||||
if (wrTransferred >= nsector * 512)
|
||||
{
|
||||
HDD_SetErrorAtTransferEnd();
|
||||
|
||||
nsector = 0;
|
||||
wrTransferred = 0;
|
||||
PostCmdDMADataFromHost();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//GENRAL FEATURE SET
|
||||
|
||||
void ATA::HDD_ReadDMA(bool isLBA48)
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HDD_ReadDMA\n");
|
||||
|
||||
IDE_CmdLBA48Transform(isLBA48);
|
||||
|
||||
if (!HDD_CanSeek())
|
||||
{
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
regError |= ATA_ERR_ID;
|
||||
PostCmdNoData();
|
||||
return;
|
||||
}
|
||||
|
||||
//Do Sync Read
|
||||
HDD_ReadSync(&ATA::DRQCmdDMADataToHost);
|
||||
}
|
||||
|
||||
void ATA::HDD_WriteDMA(bool isLBA48)
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HDD_WriteDMA\n");
|
||||
|
||||
IDE_CmdLBA48Transform(isLBA48);
|
||||
|
||||
if (!HDD_CanSeek())
|
||||
{
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
regError |= ATA_ERR_ID;
|
||||
PostCmdNoData();
|
||||
return;
|
||||
}
|
||||
|
||||
//Do Async write
|
||||
DRQCmdDMADataFromHost();
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::PreCmdExecuteDeviceDiag()
|
||||
{
|
||||
regStatus |= ATA_STAT_BUSY;
|
||||
regStatus &= ~ATA_STAT_READY;
|
||||
dev9.irqcause &= ~ATA_INTR_INTRQ;
|
||||
//dev9.spd.regIntStat &= unchecked((UInt16)~DEV9Header.ATA_INTR_DMA_RDY); //Is this correct?
|
||||
}
|
||||
|
||||
void ATA::PostCmdExecuteDeviceDiag()
|
||||
{
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
regStatus |= ATA_STAT_READY;
|
||||
|
||||
SetSelectedDevice(0);
|
||||
|
||||
if (regControlEnableIRQ)
|
||||
_DEV9irq(ATA_INTR_INTRQ, 1);
|
||||
}
|
||||
|
||||
//GENRAL FEATURE SET
|
||||
|
||||
void ATA::HDD_ExecuteDeviceDiag()
|
||||
{
|
||||
PreCmdExecuteDeviceDiag();
|
||||
//Perform Self Diag
|
||||
//Log_Error("ExecuteDeviceDiag");
|
||||
//Would check both drives, but the PS2 would only have 1
|
||||
regError &= ~ATA_ERR_ICRC;
|
||||
//Passed self-Diag
|
||||
regError = (0x01 | (regError & ATA_ERR_ICRC));
|
||||
|
||||
regNsector = 1;
|
||||
regSector = 1;
|
||||
regLcyl = 0;
|
||||
regHcyl = 0;
|
||||
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
regStatus &= ~ATA_STAT_ECC;
|
||||
regStatus &= ~ATA_STAT_ERR;
|
||||
|
||||
PostCmdExecuteDeviceDiag();
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::PostCmdNoData()
|
||||
{
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
|
||||
if (regControlEnableIRQ)
|
||||
_DEV9irq(ATA_INTR_INTRQ, 1);
|
||||
}
|
||||
|
||||
void ATA::CmdNoDataAbort()
|
||||
{
|
||||
PreCmd();
|
||||
|
||||
regError |= ATA_ERR_ABORT;
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
//GENRAL FEATURE SET
|
||||
|
||||
void ATA::HDD_FlushCache() //Can't when DRQ set
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HDD_FlushCache\n");
|
||||
|
||||
awaitFlush = true;
|
||||
Async(-1);
|
||||
}
|
||||
|
||||
void ATA::HDD_InitDevParameters()
|
||||
{
|
||||
PreCmd(); //Ignore DRDY bit
|
||||
DEV9_LOG_INFO("HDD_InitDevParameters\n");
|
||||
|
||||
curSectors = regNsector;
|
||||
curHeads = (u8)((regSelect & 0x7) + 1);
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadVerifySectors(bool isLBA48)
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HDD_ReadVerifySectors\n");
|
||||
|
||||
IDE_CmdLBA48Transform(isLBA48);
|
||||
|
||||
HDD_CanAssessOrSetError();
|
||||
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::HDD_SeekCmd()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_INFO("HDD_SeekCmd\n");
|
||||
|
||||
regStatus &= ~ATA_STAT_SEEK;
|
||||
|
||||
if (HDD_CanSeek())
|
||||
{
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
regError |= ATA_ERR_ID;
|
||||
}
|
||||
else
|
||||
regStatus |= ATA_STAT_SEEK;
|
||||
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::HDD_SetFeatures()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HDD_SetFeatures\n");
|
||||
|
||||
switch (regFeature)
|
||||
{
|
||||
case 0x02:
|
||||
fetWriteCacheEnabled = true;
|
||||
break;
|
||||
case 0x82:
|
||||
fetWriteCacheEnabled = false;
|
||||
awaitFlush = true; //Flush Cache
|
||||
return;
|
||||
case 0x03: //Set transfer mode
|
||||
{
|
||||
const u16 xferMode = (u16)regNsector; //Set Transfer mode
|
||||
|
||||
const int mode = xferMode & 0x07;
|
||||
switch ((xferMode) >> 3)
|
||||
{
|
||||
case 0x00: //pio default
|
||||
//if mode = 1, disable IORDY
|
||||
DEV9_LOG_ERROR("PIO Default\n");
|
||||
pioMode = 4;
|
||||
sdmaMode = -1;
|
||||
mdmaMode = -1;
|
||||
udmaMode = -1;
|
||||
break;
|
||||
case 0x01: //pio mode (3,4)
|
||||
DEV9_LOG_ERROR("PIO Mode %i\n", mode);
|
||||
pioMode = mode;
|
||||
sdmaMode = -1;
|
||||
mdmaMode = -1;
|
||||
udmaMode = -1;
|
||||
break;
|
||||
case 0x02: //Single word dma mode (0,1,2)
|
||||
DEV9_LOG_ERROR("SDMA Mode %i\n", mode);
|
||||
//pioMode = -1;
|
||||
sdmaMode = mode;
|
||||
mdmaMode = -1;
|
||||
udmaMode = -1;
|
||||
break;
|
||||
case 0x04: //Multi word dma mode (0,1,2)
|
||||
DEV9_LOG_ERROR("MDMA Mode %i\n", mode);
|
||||
//pioMode = -1;
|
||||
sdmaMode = -1;
|
||||
mdmaMode = mode;
|
||||
udmaMode = -1;
|
||||
break;
|
||||
case 0x08: //Ulta dma mode (0,1,2,3,4,5,6)
|
||||
DEV9_LOG_ERROR("UDMA Mode %i\n", mode);
|
||||
//pioMode = -1;
|
||||
sdmaMode = -1;
|
||||
mdmaMode = -1;
|
||||
udmaMode = mode;
|
||||
break;
|
||||
default:
|
||||
DEV9_LOG_ERROR("Unkown transfer mode\n");
|
||||
CmdNoDataAbort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEV9_LOG_ERROR("Unkown feature mode\n");
|
||||
break;
|
||||
}
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::HDD_SetMultipleMode()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_INFO("HDD_SetMultipleMode\n");
|
||||
|
||||
curMultipleSectorsSetting = regNsector;
|
||||
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::HDD_Nop()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
|
||||
DEV9_LOG_INFO("HDD_Nop\n");
|
||||
|
||||
if (regFeature == 0)
|
||||
{
|
||||
//This would abort queues if the
|
||||
//PS2 HDD supported them.
|
||||
}
|
||||
//Always ends in error
|
||||
regError |= ATA_ERR_ABORT;
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
//Other Feature Sets
|
||||
|
||||
void ATA::HDD_Idle()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
|
||||
DEV9_LOG_VERB("HDD_Idle\n");
|
||||
|
||||
long idleTime = 0; //in seconds
|
||||
if (regNsector >= 1 && regNsector <= 240)
|
||||
idleTime = 5 * regNsector;
|
||||
else if (regNsector >= 241 && regNsector <= 251)
|
||||
idleTime = 30 * (regNsector - 240) * 60;
|
||||
else
|
||||
{
|
||||
switch (regNsector)
|
||||
{
|
||||
case 0:
|
||||
idleTime = 0;
|
||||
break;
|
||||
case 252:
|
||||
idleTime = 21 * 60;
|
||||
break;
|
||||
case 253: //bettween 8 and 12 hrs
|
||||
idleTime = 10 * 60 * 60;
|
||||
break;
|
||||
case 254: //reserved
|
||||
idleTime = -1;
|
||||
break;
|
||||
case 255:
|
||||
idleTime = 21 * 60 + 15;
|
||||
break;
|
||||
default:
|
||||
idleTime = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEV9_LOG_VERB("HDD_Idle for %is\n", idleTime);
|
||||
PostCmdNoData();
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::DRQCmdPIODataToHost(u8* buff, int buffLen, int buffIndex, int size, bool sendIRQ)
|
||||
{
|
||||
//Data in PIO ready to be sent
|
||||
pioPtr = 0;
|
||||
pioEnd = size >> 1;
|
||||
|
||||
memcpy(pioBuffer, &buff[buffIndex], size < (buffLen - buffIndex) ? size : (buffLen - buffIndex));
|
||||
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
regStatus |= ATA_STAT_DRQ;
|
||||
|
||||
if (regControlEnableIRQ && sendIRQ)
|
||||
_DEV9irq(ATA_INTR_INTRQ, 1); //0x6c cycles before
|
||||
}
|
||||
void ATA::PostCmdPIODataToHost()
|
||||
{
|
||||
pioPtr = 0;
|
||||
pioEnd = 0;
|
||||
//AnyMoreData?
|
||||
if (pioDRQEndTransferFunc != nullptr)
|
||||
{
|
||||
regStatus |= ATA_STAT_BUSY;
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
//Call cmd to retrive more data
|
||||
(this->*pioDRQEndTransferFunc)();
|
||||
}
|
||||
else
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
}
|
||||
|
||||
//FromHost
|
||||
u16 ATA::ATAreadPIO()
|
||||
{
|
||||
DEV9_LOG_VERB("*ATA_R_DATA 16bit read, pio_count %i, pio_size %i\n", pioPtr, pioEnd);
|
||||
if (pioPtr < pioEnd)
|
||||
{
|
||||
const u16 ret = *(u16*)&pioBuffer[pioPtr * 2];
|
||||
DEV9_LOG_VERB("*ATA_R_DATA returned value is %x\n", ret);
|
||||
pioPtr++;
|
||||
if (pioPtr >= pioEnd) //Fnished transfer (Changed from MegaDev9)
|
||||
PostCmdPIODataToHost();
|
||||
|
||||
return ret;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
//ATAwritePIO
|
||||
|
||||
void ATA::HDD_IdentifyDevice()
|
||||
{
|
||||
if (!PreCmd())
|
||||
return;
|
||||
DEV9_LOG_VERB("HddidentifyDevice\n");
|
||||
|
||||
//IDE transfer start
|
||||
CreateHDDinfo(config.HddSize);
|
||||
|
||||
pioDRQEndTransferFunc = nullptr;
|
||||
DRQCmdPIODataToHost(identifyData, 256 * 2, 0, 256 * 2, true);
|
||||
}
|
||||
|
||||
//Read Buffer
|
||||
|
||||
void ATA::HDD_ReadMultiple(bool isLBA48)
|
||||
{
|
||||
sectorsPerInterrupt = curMultipleSectorsSetting;
|
||||
HDD_ReadPIO(isLBA48);
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadSectors(bool isLBA48)
|
||||
{
|
||||
sectorsPerInterrupt = 1;
|
||||
HDD_ReadPIO(isLBA48);
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadPIO(bool isLBA48)
|
||||
{
|
||||
//Log_Info("HDD_ReadPIO");
|
||||
if (!PreCmd())
|
||||
return;
|
||||
|
||||
if (sectorsPerInterrupt == 0)
|
||||
{
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
IDE_CmdLBA48Transform(isLBA48);
|
||||
|
||||
if (!HDD_CanSeek())
|
||||
{
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
regError |= ATA_ERR_ID;
|
||||
PostCmdNoData();
|
||||
return;
|
||||
}
|
||||
|
||||
HDD_ReadSync(&ATA::HDD_ReadPIOS2);
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadPIOS2()
|
||||
{
|
||||
//Log_Info("HDD_ReadPIO Stage 2");
|
||||
pioDRQEndTransferFunc = &ATA::HDD_ReadPIOEndBlock;
|
||||
DRQCmdPIODataToHost(readBuffer, readBufferLen, 0, 256 * 2, true);
|
||||
}
|
||||
|
||||
void ATA::HDD_ReadPIOEndBlock()
|
||||
{
|
||||
//Log_Info("HDD_ReadPIO End Block");
|
||||
rdTransferred += 512;
|
||||
if (rdTransferred >= nsector * 512)
|
||||
{
|
||||
//Log_Info("HDD_ReadPIO Done");
|
||||
HDD_SetErrorAtTransferEnd();
|
||||
regStatus &= ~ATA_STAT_BUSY;
|
||||
pioDRQEndTransferFunc = nullptr;
|
||||
rdTransferred = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((rdTransferred / 512) % sectorsPerInterrupt == 0)
|
||||
DRQCmdPIODataToHost(readBuffer, readBufferLen, rdTransferred, 256 * 2, true);
|
||||
else
|
||||
DRQCmdPIODataToHost(readBuffer, readBufferLen, rdTransferred, 256 * 2, false);
|
||||
}
|
||||
}
|
||||
|
||||
//Write Buffer
|
||||
|
||||
//Write Multiple
|
||||
|
||||
//Write Sectors
|
||||
|
||||
//Download Microcode (Used for FW updates)
|
|
@ -0,0 +1,157 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::HDD_Smart()
|
||||
{
|
||||
DEV9_LOG_VERB("HDD_Smart\n");
|
||||
|
||||
if ((regStatus & ATA_STAT_READY) == 0)
|
||||
return;
|
||||
|
||||
if (regHcyl != 0xC2 || regLcyl != 0x4F)
|
||||
{
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fetSmartEnabled && regFeature != 0xD8)
|
||||
{
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (regFeature)
|
||||
{
|
||||
case 0xD9: //SMART_DISABLE
|
||||
SMART_EnableOps(false);
|
||||
return;
|
||||
case 0xD8: //SMART_ENABLE
|
||||
SMART_EnableOps(true);
|
||||
return;
|
||||
case 0xD2: //SMART_ATTR_AUTOSAVE
|
||||
SMART_SetAutoSaveAttribute();
|
||||
return;
|
||||
case 0xD3: //SMART_ATTR_SAVE
|
||||
return;
|
||||
case 0xDA: //SMART_STATUS (is fault in disk?)
|
||||
SMART_ReturnStatus();
|
||||
return;
|
||||
case 0xD1: //SMART_READ_THRESH
|
||||
DEV9_LOG_ERROR("DEV9 : SMART_READ_THRESH Not Impemented\n");
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
case 0xD0: //SMART_READ_DATA
|
||||
DEV9_LOG_ERROR("DEV9 : SMART_READ_DATA Not Impemented\n");
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
case 0xD5: //SMART_READ_LOG
|
||||
DEV9_LOG_ERROR("DEV9 : SMART_READ_LOG Not Impemented\n");
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
case 0xD4: //SMART_EXECUTE_OFFLINE
|
||||
SMART_ExecuteOfflineImmediate();
|
||||
return;
|
||||
default:
|
||||
DEV9_LOG_ERROR("DEV9 : Unknown SMART command %x\n", regFeature);
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::SMART_SetAutoSaveAttribute()
|
||||
{
|
||||
PreCmd();
|
||||
switch (regSector)
|
||||
{
|
||||
case 0x00:
|
||||
smartAutosave = false;
|
||||
break;
|
||||
case 0xF1:
|
||||
smartAutosave = true;
|
||||
break;
|
||||
default:
|
||||
DEV9_LOG_ERROR("DEV9 : Unknown SMART_ATTR_AUTOSAVE command %s\n", regSector);
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::SMART_ExecuteOfflineImmediate()
|
||||
{
|
||||
PreCmd();
|
||||
int n = 0;
|
||||
switch (regSector)
|
||||
{
|
||||
case 0: /* off-line routine */
|
||||
case 1: /* short self test */
|
||||
case 2: /* extended self test */
|
||||
smartSelfTestCount++;
|
||||
if (smartSelfTestCount > 21)
|
||||
smartSelfTestCount = 1;
|
||||
|
||||
n = 2 + (smartSelfTestCount - 1) * 24;
|
||||
//s->smart_selftest_data[n] = s->sector;
|
||||
//s->smart_selftest_data[n + 1] = 0x00; /* OK and finished */
|
||||
//s->smart_selftest_data[n + 2] = 0x34; /* hour count lsb */
|
||||
//s->smart_selftest_data[n + 3] = 0x12; /* hour count msb */
|
||||
break;
|
||||
case 127: /* abort off-line routine */
|
||||
break;
|
||||
case 129: /* short self test, which holds BSY until complete */
|
||||
case 130: /* extended self test, which holds BSY until complete */
|
||||
smartSelfTestCount++;
|
||||
if (smartSelfTestCount > 21)
|
||||
{
|
||||
smartSelfTestCount = 1;
|
||||
}
|
||||
n = 2 + (smartSelfTestCount - 1) * 24;
|
||||
|
||||
SMART_ReturnStatus();
|
||||
return;
|
||||
default:
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::SMART_EnableOps(bool enable)
|
||||
{
|
||||
PreCmd();
|
||||
fetSmartEnabled = enable;
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
void ATA::SMART_ReturnStatus()
|
||||
{
|
||||
PreCmd();
|
||||
if (!smartErrors)
|
||||
{
|
||||
regHcyl = 0xC2;
|
||||
regLcyl = 0x4F;
|
||||
}
|
||||
else
|
||||
{
|
||||
regHcyl = 0x2C;
|
||||
regLcyl = 0xF4;
|
||||
}
|
||||
PostCmdNoData();
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::IDE_ExecCmd(u16 value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0x00:
|
||||
HDD_Nop();
|
||||
break;
|
||||
case 0x20:
|
||||
HDD_ReadSectors(false);
|
||||
break;
|
||||
//0x21
|
||||
case 0x40:
|
||||
HDD_ReadVerifySectors(false);
|
||||
break;
|
||||
//0x41
|
||||
case 0x70:
|
||||
HDD_SeekCmd();
|
||||
break;
|
||||
case 0x90:
|
||||
HDD_ExecuteDeviceDiag();
|
||||
break;
|
||||
case 0x91:
|
||||
HDD_InitDevParameters();
|
||||
break;
|
||||
case 0xB0:
|
||||
HDD_Smart();
|
||||
break;
|
||||
case 0xC4:
|
||||
HDD_ReadMultiple(false);
|
||||
break;
|
||||
case 0xC8:
|
||||
HDD_ReadDMA(false);
|
||||
break;
|
||||
//0xC9
|
||||
case 0xCA:
|
||||
HDD_WriteDMA(false);
|
||||
break;
|
||||
//0xCB
|
||||
//0x25 = HDDreadDMA48;
|
||||
//0x35 = HDDwriteDMA48;*/
|
||||
case 0xE3:
|
||||
HDD_Idle();
|
||||
break;
|
||||
case 0xE7:
|
||||
HDD_FlushCache();
|
||||
break;
|
||||
//0xEA = HDDflushCache48
|
||||
case 0xEC:
|
||||
HDD_IdentifyDevice();
|
||||
break;
|
||||
//0xA1 = HDDidentifyPktDevice
|
||||
case 0xEF:
|
||||
HDD_SetFeatures();
|
||||
break;
|
||||
|
||||
//0xF1 = HDDsecSetPassword
|
||||
//0xF2 = HDDsecUnlock
|
||||
//0xF3 = HDDsecErasePrepare;
|
||||
//0xF4 = HDDsecEraseUnit;
|
||||
|
||||
/* This command is Sony-specific and isn't part of the IDE standard */
|
||||
/* The Sony HDD has a modified firmware that supports this command */
|
||||
/* Sending this command to a standard HDD will give an error */
|
||||
/* We roughly emulate it to make programs think the HDD is a Sony one */
|
||||
/* However, we only send null, if anyting checks the returned data */
|
||||
/* it will fail */
|
||||
case 0x8E:
|
||||
HDD_SCE();
|
||||
break;
|
||||
|
||||
default:
|
||||
HDD_Unk();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ATA::HDD_Unk()
|
||||
{
|
||||
DEV9_LOG_ERROR("DEV9 HDD error : unknown cmd %x\n", regCommand);
|
||||
|
||||
PreCmd();
|
||||
|
||||
regError |= ATA_ERR_ABORT;
|
||||
regStatus |= ATA_STAT_ERR;
|
||||
PostCmdNoData();
|
||||
}
|
||||
|
||||
bool ATA::PreCmd()
|
||||
{
|
||||
if ((regStatus & ATA_STAT_READY) == 0)
|
||||
{
|
||||
//Ignore CMD write except for EXECUTE DEVICE DIAG and INITIALIZE DEVICE PARAMETERS
|
||||
return false;
|
||||
}
|
||||
regStatus |= ATA_STAT_BUSY;
|
||||
|
||||
regStatus &= ~ATA_STAT_WRERR;
|
||||
regStatus &= ~ATA_STAT_DRQ;
|
||||
regStatus &= ~ATA_STAT_ERR;
|
||||
|
||||
regStatus &= ~ATA_STAT_SEEK;
|
||||
|
||||
regError = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ATA::IDE_CmdLBA48Transform(bool islba48)
|
||||
{
|
||||
lba48 = islba48;
|
||||
//TODO
|
||||
/* handle the 'magic' 0 nsector count conversion here. to avoid
|
||||
* fiddling with the rest of the read logic, we just store the
|
||||
* full sector count in ->nsector
|
||||
*/
|
||||
if (!lba48)
|
||||
{
|
||||
if (regNsector == 0)
|
||||
nsector = 256;
|
||||
else
|
||||
nsector = regNsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regNsector == 0 && regNsectorHOB == 0)
|
||||
nsector = 65536;
|
||||
else
|
||||
{
|
||||
const int lo = regNsector;
|
||||
const int hi = regNsectorHOB;
|
||||
|
||||
nsector = (hi << 8) | lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//OTHER FEATURE SETS BELOW (TODO?)
|
||||
|
||||
//CFA ERASE SECTORS
|
||||
//WRITE MULTIPLE
|
||||
//SET MULTIPLE
|
||||
|
||||
//CFA WRITE MULTIPLE WITHOUT ERASE
|
||||
//GET MEDIA STATUS
|
||||
//MEDIA LOCK
|
||||
//MEDIA UNLOCK
|
||||
//STANDBY IMMEDIAYTE
|
||||
//IDLE IMMEDIATE
|
||||
//STANBY
|
||||
|
||||
//CHECK POWER MODE
|
||||
//SLEEP
|
||||
|
||||
//MEDIA EJECT
|
||||
|
||||
//SECURITY SET PASSWORD
|
||||
//SECURITY UNLOCK
|
||||
//SECUTIRY ERASE PREPARE
|
||||
//SECURITY ERASE UNIT
|
||||
//SECURITY FREEZE LOCK
|
||||
//SECURITY DIABLE PASSWORD
|
||||
//READ NATIVE MAX ADDRESS
|
||||
//SET MAX ADDRESS
|
|
@ -0,0 +1,56 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "../ATA.h"
|
||||
#include "../../DEV9.h"
|
||||
|
||||
void ATA::HDD_SCE()
|
||||
{
|
||||
DEV9_LOG_INFO("DEV9 : SONY-SPECIFIC SECURITY CONTROL COMMAND %x\n", regFeature);
|
||||
|
||||
switch (regFeature)
|
||||
{
|
||||
case 0xEC:
|
||||
SCE_IDENTIFY_DRIVE();
|
||||
break;
|
||||
default:
|
||||
DEV9_LOG_ERROR("DEV9 : Unknown SCE command %x\n", regFeature);
|
||||
CmdNoDataAbort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
//Has
|
||||
//ATA_SCE_IDENTIFY_DRIVE @ 0xEC
|
||||
|
||||
//ATA_SCE_SECURITY_ERASE_PREPARE @ 0xF1
|
||||
//ATA_SCE_SECURITY_ERASE_UNIT
|
||||
//ATA_SCE_SECURITY_FREEZE_LOCK
|
||||
//ATA_SCE_SECURITY_SET_PASSWORD
|
||||
//ATA_SCE_SECURITY_UNLOCK
|
||||
|
||||
//ATA_SCE_SECURITY_WRITE_ID @ 0x20
|
||||
//ATA_SCE_SECURITY_READ_ID @ 0x30
|
||||
|
||||
void ATA::SCE_IDENTIFY_DRIVE()
|
||||
{
|
||||
PreCmd();
|
||||
|
||||
//HDD_IdentifyDevice(); //Maybe?
|
||||
|
||||
pioDRQEndTransferFunc = nullptr;
|
||||
DRQCmdPIODataToHost(sceSec, 256 * 2, 0, 256 * 2, true);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <fstream>
|
||||
#include "HddCreate.h"
|
||||
|
||||
void HddCreate::Start()
|
||||
{
|
||||
fileThread = std::thread(&HddCreate::WriteImage, this, filePath, neededSize);
|
||||
fileThread.join();
|
||||
}
|
||||
|
||||
void HddCreate::WriteImage(ghc::filesystem::path hddPath, int reqSizeMiB)
|
||||
{
|
||||
constexpr int buffsize = 4 * 1024;
|
||||
u8 buff[buffsize] = {0}; //4kb
|
||||
|
||||
if (ghc::filesystem::exists(hddPath))
|
||||
{
|
||||
SetError();
|
||||
return;
|
||||
}
|
||||
|
||||
std::fstream newImage = ghc::filesystem::fstream(hddPath, std::ios::out | std::ios::binary);
|
||||
|
||||
if (newImage.fail())
|
||||
{
|
||||
SetError();
|
||||
return;
|
||||
}
|
||||
|
||||
//Size file
|
||||
newImage.seekp(((u64)reqSizeMiB) * 1024 * 1024 - 1, std::ios::beg);
|
||||
const char zero = 0;
|
||||
newImage.write(&zero, 1);
|
||||
|
||||
if (newImage.fail())
|
||||
{
|
||||
newImage.close();
|
||||
ghc::filesystem::remove(filePath);
|
||||
SetError();
|
||||
return;
|
||||
}
|
||||
|
||||
lastUpdate = std::chrono::steady_clock::now();
|
||||
|
||||
newImage.seekp(0, std::ios::beg);
|
||||
|
||||
for (int iMiB = 0; iMiB < reqSizeMiB; iMiB++)
|
||||
{
|
||||
for (int i4kb = 0; i4kb < 256; i4kb++)
|
||||
{
|
||||
newImage.write((char*)buff, buffsize);
|
||||
if (newImage.fail())
|
||||
{
|
||||
newImage.close();
|
||||
ghc::filesystem::remove(filePath);
|
||||
SetError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
SetFileProgress(iMiB + 1);
|
||||
}
|
||||
newImage.flush();
|
||||
newImage.close();
|
||||
|
||||
SetDone();
|
||||
}
|
||||
|
||||
void HddCreate::SetFileProgress(int currentSize)
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(now - lastUpdate).count() >= 1)
|
||||
{
|
||||
lastUpdate = now;
|
||||
fprintf(stdout, "%i / %i MiB\n", currentSize, neededSize);
|
||||
}
|
||||
}
|
||||
|
||||
void HddCreate::SetError()
|
||||
{
|
||||
fprintf(stderr, "Unable to create file\n");
|
||||
errored.store(true);
|
||||
completed.store(true);
|
||||
}
|
||||
|
||||
void HddCreate::SetDone()
|
||||
{
|
||||
fprintf(stdout, "%i / %i MiB\n", neededSize, neededSize);
|
||||
fprintf(stdout, "Done\n");
|
||||
completed.store(true);
|
||||
}
|
|
@ -14,17 +14,33 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "DEV9.h"
|
||||
|
||||
void ata_init();
|
||||
void ata_term();
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include "ghc/filesystem.h"
|
||||
|
||||
template <int sz>
|
||||
void ata_write(u32 addr, u32 value);
|
||||
template <int sz>
|
||||
u8 ata_read(u32 addr);
|
||||
class HddCreate
|
||||
{
|
||||
public:
|
||||
ghc::filesystem::path filePath;
|
||||
int neededSize;
|
||||
|
||||
EXPORT_C_(void)
|
||||
ata_readDMA8Mem(u32* pMem, int size);
|
||||
EXPORT_C_(void)
|
||||
ata_writeDMA8Mem(u32* pMem, int size);
|
||||
std::atomic_bool errored{false};
|
||||
|
||||
private:
|
||||
std::thread fileThread;
|
||||
std::atomic_bool completed{false};
|
||||
|
||||
std::chrono::steady_clock::time_point lastUpdate;
|
||||
|
||||
public:
|
||||
void Start();
|
||||
|
||||
private:
|
||||
void SetFileProgress(int currentSize);
|
||||
void SetError();
|
||||
void SetDone();
|
||||
void WriteImage(ghc::filesystem::path hddPath, int reqSizeMB);
|
||||
};
|
|
@ -267,6 +267,17 @@
|
|||
<ClCompile Include="..\..\DebugTools\MipsAssemblerTables.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\MipsStackWalk.cpp" />
|
||||
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_Command.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdDMA.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdExecuteDeviceDiag.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdNoData.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdPIOData.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdSMART.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_SCE.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_Info.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_State.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_Transfer.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\ATA\HddCreate.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\DEV9.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\flash.cpp" />
|
||||
<ClCompile Include="..\..\DEV9\pcap_io.cpp" />
|
||||
|
@ -628,7 +639,8 @@
|
|||
<ClInclude Include="..\..\DebugTools\MipsAssemblerTables.h" />
|
||||
<ClInclude Include="..\..\DebugTools\MipsStackWalk.h" />
|
||||
<ClInclude Include="..\..\DebugTools\SymbolMap.h" />
|
||||
<ClInclude Include="..\..\DEV9\ata.h" />
|
||||
<ClInclude Include="..\..\DEV9\ATA\ATA.h" />
|
||||
<ClInclude Include="..\..\DEV9\ATA\HddCreate.h" />
|
||||
<ClInclude Include="..\..\DEV9\Config.h" />
|
||||
<ClInclude Include="..\..\DEV9\DEV9.h" />
|
||||
<ClInclude Include="..\..\DEV9\net.h" />
|
||||
|
|
|
@ -172,6 +172,12 @@
|
|||
<Filter Include="System\Ps2\DEV9">
|
||||
<UniqueIdentifier>{8d5454f9-590c-4c53-aae1-8391c6465e50}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\ATA">
|
||||
<UniqueIdentifier>{bc20f567-851d-4440-a3fd-ef470241962e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\DEV9\ATA\Commands">
|
||||
<UniqueIdentifier>{9e9b52d7-7b1c-44b2-82d8-1e0d8085e7e0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="System\Ps2\USB">
|
||||
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -1054,6 +1060,39 @@
|
|||
<ClCompile Include="..\..\SPU2\SndOut_Portaudio.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_Command.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdDMA.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdExecuteDeviceDiag.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdNoData.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdPIOData.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdSMART.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_SCE.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_Info.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_State.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\ATA_Transfer.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\ATA\HddCreate.cpp">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\DEV9\DEV9.cpp">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1788,8 +1827,11 @@
|
|||
<ClInclude Include="..\..\Recording\Utilities\InputRecordingLogger.h">
|
||||
<Filter>Recording\Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DEV9\ata.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
<ClInclude Include="..\..\DEV9\ATA\ATA.h">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DEV9\ATA\HddCreate.h">
|
||||
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\DEV9\Config.h">
|
||||
<Filter>System\Ps2\DEV9</Filter>
|
||||
|
|
Loading…
Reference in New Issue