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
|
# DEV9 sources
|
||||||
set(pcsx2DEV9Sources
|
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/smap.cpp
|
||||||
DEV9/DEV9.cpp
|
DEV9/DEV9.cpp
|
||||||
DEV9/flash.cpp
|
DEV9/flash.cpp
|
||||||
|
@ -327,8 +338,9 @@ set(pcsx2DEV9Sources
|
||||||
|
|
||||||
# DEV9 headers
|
# DEV9 headers
|
||||||
set(pcsx2DEV9Headers
|
set(pcsx2DEV9Headers
|
||||||
|
DEV9/ATA/ATA.h
|
||||||
|
DEV9/ATA/HddCreate.h
|
||||||
DEV9/DEV9.h
|
DEV9/DEV9.h
|
||||||
DEV9/ata.h
|
|
||||||
DEV9/net.h
|
DEV9/net.h
|
||||||
DEV9/pcap_io.h
|
DEV9/pcap_io.h
|
||||||
DEV9/smap.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
|
#pragma once
|
||||||
#include "DEV9.h"
|
|
||||||
|
|
||||||
void ata_init();
|
#include <string>
|
||||||
void ata_term();
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include "ghc/filesystem.h"
|
||||||
|
|
||||||
template <int sz>
|
class HddCreate
|
||||||
void ata_write(u32 addr, u32 value);
|
{
|
||||||
template <int sz>
|
public:
|
||||||
u8 ata_read(u32 addr);
|
ghc::filesystem::path filePath;
|
||||||
|
int neededSize;
|
||||||
|
|
||||||
EXPORT_C_(void)
|
std::atomic_bool errored{false};
|
||||||
ata_readDMA8Mem(u32* pMem, int size);
|
|
||||||
EXPORT_C_(void)
|
private:
|
||||||
ata_writeDMA8Mem(u32* pMem, int size);
|
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\MipsAssemblerTables.cpp" />
|
||||||
<ClCompile Include="..\..\DebugTools\MipsStackWalk.cpp" />
|
<ClCompile Include="..\..\DebugTools\MipsStackWalk.cpp" />
|
||||||
<ClCompile Include="..\..\DebugTools\SymbolMap.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\DEV9.cpp" />
|
||||||
<ClCompile Include="..\..\DEV9\flash.cpp" />
|
<ClCompile Include="..\..\DEV9\flash.cpp" />
|
||||||
<ClCompile Include="..\..\DEV9\pcap_io.cpp" />
|
<ClCompile Include="..\..\DEV9\pcap_io.cpp" />
|
||||||
|
@ -628,7 +639,8 @@
|
||||||
<ClInclude Include="..\..\DebugTools\MipsAssemblerTables.h" />
|
<ClInclude Include="..\..\DebugTools\MipsAssemblerTables.h" />
|
||||||
<ClInclude Include="..\..\DebugTools\MipsStackWalk.h" />
|
<ClInclude Include="..\..\DebugTools\MipsStackWalk.h" />
|
||||||
<ClInclude Include="..\..\DebugTools\SymbolMap.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\Config.h" />
|
||||||
<ClInclude Include="..\..\DEV9\DEV9.h" />
|
<ClInclude Include="..\..\DEV9\DEV9.h" />
|
||||||
<ClInclude Include="..\..\DEV9\net.h" />
|
<ClInclude Include="..\..\DEV9\net.h" />
|
||||||
|
|
|
@ -172,6 +172,12 @@
|
||||||
<Filter Include="System\Ps2\DEV9">
|
<Filter Include="System\Ps2\DEV9">
|
||||||
<UniqueIdentifier>{8d5454f9-590c-4c53-aae1-8391c6465e50}</UniqueIdentifier>
|
<UniqueIdentifier>{8d5454f9-590c-4c53-aae1-8391c6465e50}</UniqueIdentifier>
|
||||||
</Filter>
|
</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">
|
<Filter Include="System\Ps2\USB">
|
||||||
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
|
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
@ -1054,6 +1060,39 @@
|
||||||
<ClCompile Include="..\..\SPU2\SndOut_Portaudio.cpp">
|
<ClCompile Include="..\..\SPU2\SndOut_Portaudio.cpp">
|
||||||
<Filter>System\Ps2\SPU2</Filter>
|
<Filter>System\Ps2\SPU2</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\DEV9\DEV9.cpp">
|
||||||
<Filter>System\Ps2\DEV9</Filter>
|
<Filter>System\Ps2\DEV9</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1788,8 +1827,11 @@
|
||||||
<ClInclude Include="..\..\Recording\Utilities\InputRecordingLogger.h">
|
<ClInclude Include="..\..\Recording\Utilities\InputRecordingLogger.h">
|
||||||
<Filter>Recording\Utilities</Filter>
|
<Filter>Recording\Utilities</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\DEV9\ata.h">
|
<ClInclude Include="..\..\DEV9\ATA\ATA.h">
|
||||||
<Filter>System\Ps2\DEV9</Filter>
|
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\DEV9\ATA\HddCreate.h">
|
||||||
|
<Filter>System\Ps2\DEV9\ATA</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\DEV9\Config.h">
|
<ClInclude Include="..\..\DEV9\Config.h">
|
||||||
<Filter>System\Ps2\DEV9</Filter>
|
<Filter>System\Ps2\DEV9</Filter>
|
||||||
|
|
Loading…
Reference in New Issue