project64/Source/Project64-core/N64System/Mips/Disk.cpp

323 lines
9.9 KiB
C++
Raw Normal View History

2016-01-19 18:53:18 +00:00
/****************************************************************************
* *
* Project64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
// Based from MAME's N64DD driver code by Happy_
2016-01-19 18:53:18 +00:00
#pragma once
#include "stdafx.h"
2016-01-20 13:31:29 +00:00
#include "Disk.h"
#include <Project64-core/N64System/N64DiskClass.h>
2016-01-19 18:53:18 +00:00
#include <Project64-core/N64System/SystemGlobals.h>
#include <Project64-core/N64System/Mips/RegisterClass.h>
#include <Project64-core/N64System/Mips/SystemTiming.h>
2016-01-19 18:53:18 +00:00
2016-01-20 13:31:29 +00:00
bool dd_write;
bool dd_reset_hold;
uint32_t dd_track_offset, dd_zone;
2016-01-20 16:43:23 +00:00
uint32_t dd_start_block, dd_current;
2016-01-20 13:31:29 +00:00
2016-01-19 18:53:18 +00:00
void DiskCommand()
{
if (g_Disk != NULL)
g_Reg->ASIC_STATUS |= DD_STATUS_DISK_PRES;
2016-01-20 13:31:29 +00:00
//ASIC_CMD_STATUS - Commands
uint32_t cmd = g_Reg->ASIC_CMD;
2016-01-19 18:53:18 +00:00
#ifdef _WIN32
2016-01-20 13:31:29 +00:00
SYSTEMTIME sysTime;
::GetLocalTime(&sysTime);
//BCD format needed for 64DD RTC
uint8_t year = (uint8_t)(((sysTime.wYear / 10) << 4) | (sysTime.wYear % 10));
uint8_t month = (uint8_t)(((sysTime.wMonth / 10) << 4) | (sysTime.wMonth % 10));
uint8_t day = (uint8_t)(((sysTime.wDay / 10) << 4) | (sysTime.wDay % 10));
uint8_t hour = (uint8_t)(((sysTime.wHour / 10) << 4) | (sysTime.wHour % 10));
uint8_t minute = (uint8_t)(((sysTime.wMinute / 10) << 4) | (sysTime.wMinute % 10));
uint8_t second = (uint8_t)(((sysTime.wSecond / 10) << 4) | (sysTime.wSecond % 10));
2016-01-19 18:53:18 +00:00
#else
2016-01-20 13:31:29 +00:00
time_t ltime;
ltime = time(&ltime);
struct tm result = { 0 };
localtime_r(&ltime, &result);
//BCD format needed for 64DD RTC
uint8_t year = (uint8_t)(((result.tm_year / 10) << 4) | (result.tm_year % 10));
uint8_t month = (uint8_t)(((result.tm_mon / 10) << 4) | (result.tm_mon % 10));
uint8_t day = (uint8_t)(((result.tm_mday / 10) << 4) | (result.tm_mday % 10));
uint8_t hour = (uint8_t)(((result.tm_hour / 10) << 4) | (result.tm_hour % 10));
uint8_t minute = (uint8_t)(((result.tm_min / 10) << 4) | (result.tm_min % 10));
uint8_t second = (uint8_t)(((result.tm_sec / 10) << 4) | (result.tm_sec % 10));
2016-01-19 18:53:18 +00:00
#endif
2016-01-20 13:31:29 +00:00
switch (cmd & 0xFFFF0000)
{
case 0x00010000:
//Seek Read
g_Reg->ASIC_CUR_TK = g_Reg->ASIC_DATA | 0x60000000;
2016-01-20 16:43:23 +00:00
DiskSetOffset();
2016-01-20 13:31:29 +00:00
dd_write = false;
break;
case 0x00020000:
//Seek Write
g_Reg->ASIC_CUR_TK = g_Reg->ASIC_DATA | 0x60000000;
2016-01-20 16:43:23 +00:00
DiskSetOffset();
2016-01-20 13:31:29 +00:00
dd_write = true;
break;
case 0x00080000:
//Unset Disk Changed Bit
g_Reg->ASIC_STATUS &= ~DD_STATUS_DISK_CHNG; break;
case 0x00090000:
//Unset Reset Bit
g_Reg->ASIC_STATUS &= ~DD_STATUS_RST_STATE; break;
case 0x00120000:
//RTC Get Year & Month
g_Reg->ASIC_DATA = (year << 24) | (month << 16); break;
case 0x00130000:
//RTC Get Day & Hour
g_Reg->ASIC_DATA = (day << 24) | (hour << 16); break;
case 0x00140000:
//RTC Get Minute & Second
g_Reg->ASIC_DATA = (minute << 24) | (second << 16); break;
case 0x001B0000:
//Disk Inquiry
g_Reg->ASIC_DATA = 0x00000000; break;
}
2016-01-19 18:53:18 +00:00
}
void DiskReset(void)
{
2016-01-20 13:31:29 +00:00
//ASIC_HARD_RESET 0xAAAA0000
g_Reg->ASIC_STATUS |= DD_STATUS_RST_STATE;
2016-01-19 18:53:18 +00:00
}
void DiskBMControl(void)
{
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_CUR_SECTOR = g_Reg->ASIC_BM_CTL & 0x00FF0000;
2016-01-20 16:43:23 +00:00
2016-01-20 13:31:29 +00:00
if ((g_Reg->ASIC_CUR_SECTOR >> 16) == 0x00)
{
2016-01-20 16:43:23 +00:00
dd_start_block = 0;
dd_current = 0;
2016-01-20 13:31:29 +00:00
}
else if ((g_Reg->ASIC_CUR_SECTOR >> 16) == 0x5A)
{
2016-01-20 16:43:23 +00:00
dd_start_block = 1;
dd_current = 0;
2016-01-20 13:31:29 +00:00
}
2016-01-20 16:43:23 +00:00
2016-01-20 13:31:29 +00:00
if (g_Reg->ASIC_BM_CTL & DD_BM_CTL_BLK_TRANS)
g_Reg->ASIC_BM_STATUS |= DD_BM_STATUS_BLOCK;
if (g_Reg->ASIC_BM_CTL & DD_BM_CTL_MECHA_RST)
g_Reg->ASIC_STATUS &= ~DD_STATUS_MECHA_INT;
if (g_Reg->ASIC_BM_CTL & DD_BM_CTL_RESET)
dd_reset_hold = true;
if (!(g_Reg->ASIC_BM_CTL & DD_BM_CTL_RESET) && dd_reset_hold)
{
dd_reset_hold = false;
g_Reg->ASIC_STATUS &= ~(DD_STATUS_BM_INT | DD_STATUS_BM_ERR | DD_STATUS_DATA_RQ | DD_STATUS_C2_XFER);
g_Reg->ASIC_BM_STATUS = 0;
g_Reg->ASIC_CUR_SECTOR = 0;
2016-01-20 16:43:23 +00:00
dd_start_block = 0;
dd_current = 0;
2016-01-20 13:31:29 +00:00
}
if (!(g_Reg->ASIC_STATUS & DD_STATUS_MECHA_INT) && !(g_Reg->ASIC_STATUS & DD_STATUS_BM_INT))
g_Reg->FAKE_CAUSE_REGISTER &= ~CAUSE_IP3;
if (g_Reg->ASIC_BM_CTL & DD_BM_CTL_START)
{
g_Reg->ASIC_BM_STATUS |= DD_BM_STATUS_RUNNING;
DiskBMUpdate();
}
}
void DiskGapSectorCheck()
{
if (g_Reg->ASIC_STATUS & DD_STATUS_BM_INT)
{
2016-01-20 16:43:23 +00:00
if (SECTORS_PER_BLOCK < dd_current)
2016-01-20 13:31:29 +00:00
{
g_Reg->ASIC_STATUS &= ~DD_STATUS_BM_INT;
g_Reg->FAKE_CAUSE_REGISTER &= ~CAUSE_IP3;
g_Reg->CheckInterrupts();
2016-01-20 13:31:29 +00:00
DiskBMUpdate();
}
}
}
void DiskBMUpdate()
{
if (!(g_Reg->ASIC_BM_STATUS & DD_BM_STATUS_RUNNING))
return;
2016-01-20 13:31:29 +00:00
if (dd_write)
{
//Write Data
if (dd_current < SECTORS_PER_BLOCK)
2016-01-20 13:31:29 +00:00
{
DiskBMWrite();
2016-01-20 16:43:23 +00:00
dd_current += 1;
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_STATUS |= DD_STATUS_DATA_RQ;
}
2016-01-20 16:43:23 +00:00
else if (dd_current < SECTORS_PER_BLOCK + 1)
2016-01-20 13:31:29 +00:00
{
if (g_Reg->ASIC_BM_STATUS & DD_BM_STATUS_BLOCK)
{
dd_start_block = 1 - dd_start_block;
dd_current = 0;
2016-01-20 13:31:29 +00:00
DiskBMWrite();
2016-01-20 16:43:23 +00:00
dd_current += 1;
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_BM_STATUS &= ~DD_BM_STATUS_BLOCK;
g_Reg->ASIC_STATUS |= DD_STATUS_DATA_RQ;
}
else
{
2016-01-20 16:43:23 +00:00
dd_current += 1;
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_BM_STATUS &= ~DD_BM_STATUS_RUNNING;
}
}
g_Reg->ASIC_STATUS |= DD_STATUS_BM_INT;
g_Reg->FAKE_CAUSE_REGISTER |= CAUSE_IP3;
g_Reg->CheckInterrupts();
return;
}
else
{
//Read Data
if (((g_Reg->ASIC_CUR_TK >> 16) & 0xFFF) == 6 && g_Reg->ASIC_CUR_SECTOR == 0)
{
g_Reg->ASIC_STATUS &= ~DD_STATUS_DATA_RQ;
g_Reg->ASIC_BM_STATUS |= DD_BM_STATUS_MICRO;
}
else if (dd_current < SECTORS_PER_BLOCK)
2016-01-20 13:31:29 +00:00
{
DiskBMRead();
2016-01-20 16:43:23 +00:00
dd_current += 1;
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_STATUS |= DD_STATUS_DATA_RQ;
}
2016-01-20 16:43:23 +00:00
else if (dd_current < SECTORS_PER_BLOCK + 4)
2016-01-20 13:31:29 +00:00
{
//READ C2 (00!)
2016-01-20 16:43:23 +00:00
dd_current += 1;
if (dd_current == SECTORS_PER_BLOCK + 4)
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_STATUS |= DD_STATUS_C2_XFER;
}
2016-01-20 16:43:23 +00:00
else if (dd_current == SECTORS_PER_BLOCK + 4)
2016-01-20 13:31:29 +00:00
{
if (g_Reg->ASIC_BM_STATUS & DD_BM_STATUS_BLOCK)
{
2016-01-20 16:43:23 +00:00
dd_start_block = 1 - dd_start_block;
dd_current = 0;
2016-01-20 13:31:29 +00:00
g_Reg->ASIC_BM_STATUS &= ~DD_BM_STATUS_BLOCK;
}
else
{
g_Reg->ASIC_BM_STATUS &= ~DD_BM_STATUS_RUNNING;
}
}
g_Reg->ASIC_STATUS |= DD_STATUS_BM_INT;
g_Reg->FAKE_CAUSE_REGISTER |= CAUSE_IP3;
g_Reg->CheckInterrupts();
}
}
void DiskBMRead()
{
2016-01-20 16:43:23 +00:00
uint32_t sector = 0;
2016-01-20 13:31:29 +00:00
sector += dd_track_offset;
2016-01-20 16:43:23 +00:00
sector += dd_start_block * SECTORS_PER_BLOCK * ddZoneSecSize[dd_zone];
sector += (dd_current) * (((g_Reg->ASIC_HOST_SECBYTE & 0x00FF0000) >> 16) + 1);
WriteTrace(TraceN64System, TraceDebug, "READ Block %d Sector %02X - %08X", ((g_Reg->ASIC_CUR_TK & 0x0FFF0000) >> 15) | dd_start_block, dd_current, sector);
2016-01-20 16:43:23 +00:00
g_Disk->SetDiskAddressBuffer(sector);
2016-01-20 13:31:29 +00:00
return;
}
void DiskBMWrite()
{
2016-01-20 16:43:23 +00:00
uint32_t sector = 0;
2016-01-20 13:31:29 +00:00
sector += dd_track_offset;
2016-01-20 16:43:23 +00:00
sector += dd_start_block * SECTORS_PER_BLOCK * ddZoneSecSize[dd_zone];
sector += (dd_current) * (((g_Reg->ASIC_HOST_SECBYTE & 0x00FF0000) >> 16) + 1);
WriteTrace(TraceN64System, TraceDebug, "WRITE Block %d Sector %02X - %08X", ((g_Reg->ASIC_CUR_TK & 0x0FFF0000) >> 15) | dd_start_block, dd_current, sector);
2016-01-20 16:43:23 +00:00
g_Disk->SetDiskAddressBuffer(sector);
2016-01-20 13:31:29 +00:00
return;
}
void DiskSetOffset()
{
uint16_t head = ((g_Reg->ASIC_CUR_TK >> 16) & 0x1000) >> 9; // Head * 8
uint16_t track = (g_Reg->ASIC_CUR_TK >> 16) & 0xFFF;
uint16_t tr_off = 0;
if (track >= 0x425)
{
dd_zone = 7 + head;
tr_off = track - 0x425;
}
else if (track >= 0x390)
{
dd_zone = 6 + head;
tr_off = track - 0x390;
}
else if (track >= 0x2FB)
{
dd_zone = 5 + head;
tr_off = track - 0x2FB;
}
else if (track >= 0x266)
{
dd_zone = 4 + head;
tr_off = track - 0x266;
}
else if (track >= 0x1D1)
{
dd_zone = 3 + head;
tr_off = track - 0x1D1;
}
else if (track >= 0x13C)
{
dd_zone = 2 + head;
tr_off = track - 0x13C;
}
else if (track >= 0x9E)
{
dd_zone = 1 + head;
tr_off = track - 0x9E;
}
else
{
dd_zone = 0 + head;
tr_off = track;
}
dd_track_offset = ddStartOffset[dd_zone] + tr_off * ddZoneSecSize[dd_zone] * SECTORS_PER_BLOCK * BLOCKS_PER_TRACK;
}
void DiskDMACheck(void)
{
if (g_Reg->PI_CART_ADDR_REG == 0x05000000)
{
g_Reg->ASIC_STATUS &= ~(DD_STATUS_BM_INT | DD_STATUS_BM_ERR | DD_STATUS_C2_XFER);
g_Reg->FAKE_CAUSE_REGISTER &= ~CAUSE_IP3;
g_Reg->CheckInterrupts();
}
else if (g_Reg->PI_CART_ADDR_REG == 0x05000400)
{
g_Reg->ASIC_STATUS &= ~(DD_STATUS_BM_INT | DD_STATUS_BM_ERR | DD_STATUS_DATA_RQ);
g_Reg->FAKE_CAUSE_REGISTER &= ~CAUSE_IP3;
g_Reg->CheckInterrupts();
}
2016-01-19 18:53:18 +00:00
}