DEV9: Add ATA emulation & Hdd Creator code

This commit is contained in:
TheLastRar 2020-11-16 20:15:03 +00:00 committed by refractionpcsx2
parent d32b583b3e
commit 5640ee8a23
16 changed files with 2592 additions and 15 deletions

View File

@ -315,6 +315,17 @@ compile_gresources( pcsx2DEV9UIHeaders
# DEV9 sources
set(pcsx2DEV9Sources
DEV9/ATA/Commands/ATA_Command.cpp
DEV9/ATA/Commands/ATA_CmdDMA.cpp
DEV9/ATA/Commands/ATA_CmdExecuteDeviceDiag.cpp
DEV9/ATA/Commands/ATA_CmdNoData.cpp
DEV9/ATA/Commands/ATA_CmdPIOData.cpp
DEV9/ATA/Commands/ATA_CmdSMART.cpp
DEV9/ATA/Commands/ATA_SCE.cpp
DEV9/ATA/ATA_Info.cpp
DEV9/ATA/ATA_State.cpp
DEV9/ATA/ATA_Transfer.cpp
DEV9/ATA/HddCreate.cpp
DEV9/smap.cpp
DEV9/DEV9.cpp
DEV9/flash.cpp
@ -327,8 +338,9 @@ set(pcsx2DEV9Sources
# DEV9 headers
set(pcsx2DEV9Headers
DEV9/ATA/ATA.h
DEV9/ATA/HddCreate.h
DEV9/DEV9.h
DEV9/ata.h
DEV9/net.h
DEV9/pcap_io.h
DEV9/smap.h

276
pcsx2/DEV9/ATA/ATA.h Normal file
View File

@ -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);
};

392
pcsx2/DEV9/ATA/ATA_Info.cpp Normal file
View File

@ -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
//Manufacturers 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
//Vendors 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);
}

View File

@ -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(&sectors);
}
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;
}

View File

@ -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(&sector, &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();
}

View File

@ -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(&currentWrite[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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -14,17 +14,33 @@
*/
#pragma once
#include "DEV9.h"
void ata_init();
void ata_term();
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include "ghc/filesystem.h"
template <int sz>
void ata_write(u32 addr, u32 value);
template <int sz>
u8 ata_read(u32 addr);
class HddCreate
{
public:
ghc::filesystem::path filePath;
int neededSize;
EXPORT_C_(void)
ata_readDMA8Mem(u32* pMem, int size);
EXPORT_C_(void)
ata_writeDMA8Mem(u32* pMem, int size);
std::atomic_bool errored{false};
private:
std::thread fileThread;
std::atomic_bool completed{false};
std::chrono::steady_clock::time_point lastUpdate;
public:
void Start();
private:
void SetFileProgress(int currentSize);
void SetError();
void SetDone();
void WriteImage(ghc::filesystem::path hddPath, int reqSizeMB);
};

View File

@ -267,6 +267,17 @@
<ClCompile Include="..\..\DebugTools\MipsAssemblerTables.cpp" />
<ClCompile Include="..\..\DebugTools\MipsStackWalk.cpp" />
<ClCompile Include="..\..\DebugTools\SymbolMap.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_Command.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdDMA.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdExecuteDeviceDiag.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdNoData.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdPIOData.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdSMART.cpp" />
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_SCE.cpp" />
<ClCompile Include="..\..\DEV9\ATA\ATA_Info.cpp" />
<ClCompile Include="..\..\DEV9\ATA\ATA_State.cpp" />
<ClCompile Include="..\..\DEV9\ATA\ATA_Transfer.cpp" />
<ClCompile Include="..\..\DEV9\ATA\HddCreate.cpp" />
<ClCompile Include="..\..\DEV9\DEV9.cpp" />
<ClCompile Include="..\..\DEV9\flash.cpp" />
<ClCompile Include="..\..\DEV9\pcap_io.cpp" />
@ -628,7 +639,8 @@
<ClInclude Include="..\..\DebugTools\MipsAssemblerTables.h" />
<ClInclude Include="..\..\DebugTools\MipsStackWalk.h" />
<ClInclude Include="..\..\DebugTools\SymbolMap.h" />
<ClInclude Include="..\..\DEV9\ata.h" />
<ClInclude Include="..\..\DEV9\ATA\ATA.h" />
<ClInclude Include="..\..\DEV9\ATA\HddCreate.h" />
<ClInclude Include="..\..\DEV9\Config.h" />
<ClInclude Include="..\..\DEV9\DEV9.h" />
<ClInclude Include="..\..\DEV9\net.h" />

View File

@ -172,6 +172,12 @@
<Filter Include="System\Ps2\DEV9">
<UniqueIdentifier>{8d5454f9-590c-4c53-aae1-8391c6465e50}</UniqueIdentifier>
</Filter>
<Filter Include="System\Ps2\DEV9\ATA">
<UniqueIdentifier>{bc20f567-851d-4440-a3fd-ef470241962e}</UniqueIdentifier>
</Filter>
<Filter Include="System\Ps2\DEV9\ATA\Commands">
<UniqueIdentifier>{9e9b52d7-7b1c-44b2-82d8-1e0d8085e7e0}</UniqueIdentifier>
</Filter>
<Filter Include="System\Ps2\USB">
<UniqueIdentifier>{df9de75c-2272-4f73-b2a0-4f9f492ba1e9}</UniqueIdentifier>
</Filter>
@ -1054,6 +1060,39 @@
<ClCompile Include="..\..\SPU2\SndOut_Portaudio.cpp">
<Filter>System\Ps2\SPU2</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_Command.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdDMA.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdExecuteDeviceDiag.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdNoData.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdPIOData.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_CmdSMART.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\Commands\ATA_SCE.cpp">
<Filter>System\Ps2\DEV9\ATA\Commands</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\ATA_Info.cpp">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\ATA_State.cpp">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\ATA_Transfer.cpp">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\ATA\HddCreate.cpp">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClCompile>
<ClCompile Include="..\..\DEV9\DEV9.cpp">
<Filter>System\Ps2\DEV9</Filter>
</ClCompile>
@ -1788,8 +1827,11 @@
<ClInclude Include="..\..\Recording\Utilities\InputRecordingLogger.h">
<Filter>Recording\Utilities</Filter>
</ClInclude>
<ClInclude Include="..\..\DEV9\ata.h">
<Filter>System\Ps2\DEV9</Filter>
<ClInclude Include="..\..\DEV9\ATA\ATA.h">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClInclude>
<ClInclude Include="..\..\DEV9\ATA\HddCreate.h">
<Filter>System\Ps2\DEV9\ATA</Filter>
</ClInclude>
<ClInclude Include="..\..\DEV9\Config.h">
<Filter>System\Ps2\DEV9</Filter>