flycast/core/hw/gdrom/gdromv3.cpp

1150 lines
27 KiB
C++
Raw Normal View History

2018-09-20 17:48:46 +00:00
/*
2013-12-19 17:10:14 +00:00
gdrom, v3
Overly complex implementation of a very ugly device
*/
#include "gdromv3.h"
2020-03-28 16:58:01 +00:00
#include "hw/holly/holly_intc.h"
2013-12-19 17:10:14 +00:00
#include "hw/holly/sb.h"
#include "hw/sh4/modules/dmac.h"
#include "hw/sh4/sh4_interpreter.h"
2020-03-28 16:58:01 +00:00
#include "hw/sh4/sh4_mem.h"
#include "hw/sh4/sh4_sched.h"
2013-12-19 17:10:14 +00:00
int gdrom_schid;
//Sense: ASC - ASCQ - Key
signed int sns_asc=0;
signed int sns_ascq=0;
signed int sns_key=0;
2013-12-19 17:10:14 +00:00
u32 set_mode_offset;
2018-09-02 13:49:23 +00:00
read_params_t read_params ;
packet_cmd_t packet_cmd ;
read_buff_t read_buff ;
pio_buff_t pio_buff ;
ata_cmd_t ata_cmd ;
cdda_t cdda ;
2013-12-19 17:10:14 +00:00
gd_states gd_state;
DiscType gd_disk_type;
/*
GD rom reset -> GDS_WAITCMD
GDS_WAITCMD -> ATA/SPI command [Command code is on ata_cmd]
SPI Command -> GDS_WAITPACKET -> GDS_SPI_* , depending on input
GDS_SPI_READSECTOR -> Depending on features , it can do quite a few things
*/
u32 data_write_mode=0;
//Registers
u32 DriveSel;
GD_ErrRegT Error;
GD_InterruptReasonT IntReason;
GD_FeaturesT Features;
GD_SecCountT SecCount;
GD_SecNumbT SecNumber;
GD_StatusT GDStatus;
2018-09-02 13:49:23 +00:00
ByteCount_t ByteCount;
2013-12-19 17:10:14 +00:00
//end
2019-07-30 17:04:51 +00:00
GD_HardwareInfo_t GD_HardwareInfo;
2013-12-19 17:10:14 +00:00
2019-06-30 20:41:55 +00:00
#define printf_rm(...) DEBUG_LOG(GDROM, __VA_ARGS__)
#define printf_ata(...) DEBUG_LOG(GDROM, __VA_ARGS__)
#define printf_spi(...) DEBUG_LOG(GDROM, __VA_ARGS__)
#define printf_spicmd(...) DEBUG_LOG(GDROM, __VA_ARGS__)
#define printf_subcode(...) DEBUG_LOG(GDROM, __VA_ARGS__)
2013-12-19 17:10:14 +00:00
void libCore_CDDA_Sector(s16* sector)
{
//silence ! :p
if (cdda.playing)
{
libGDR_ReadSector((u8*)sector,cdda.CurrAddr.FAD,1,2352);
cdda.CurrAddr.FAD++;
if (cdda.CurrAddr.FAD >= cdda.EndAddr.FAD)
2013-12-19 17:10:14 +00:00
{
if (cdda.repeats==0)
{
//stop
cdda.playing=false;
SecNumber.Status=GD_STANDBY;
}
else
{
//Repeat ;)
if (cdda.repeats!=0xf)
cdda.repeats--;
2013-12-19 17:10:14 +00:00
cdda.CurrAddr.FAD=cdda.StartAddr.FAD;
}
}
}
else
{
memset(sector,0,2352);
}
}
void gd_spi_pio_end(const u8* buffer, u32 len, gd_states next_state = gds_pio_end);
2013-12-19 17:10:14 +00:00
void gd_process_spi_cmd();
void gd_process_ata_cmd();
2019-08-31 15:36:34 +00:00
static void FillReadBuffer()
2013-12-19 17:10:14 +00:00
{
read_buff.cache_index=0;
u32 count = read_params.remaining_sectors;
u32 hint=0;
if (count > 32)
2013-12-19 17:10:14 +00:00
{
2020-03-29 17:29:14 +00:00
hint = std::max(count - 32, (u32)32);
count = 32;
2013-12-19 17:10:14 +00:00
}
read_buff.cache_size=count*read_params.sector_type;
libGDR_ReadSector(read_buff.cache,read_params.start_sector,count,read_params.sector_type);
read_params.start_sector+=count;
read_params.remaining_sectors-=count;
}
2018-09-02 13:49:23 +00:00
2013-12-19 17:10:14 +00:00
void gd_set_state(gd_states state)
{
gd_states prev=gd_state;
gd_state=state;
switch(state)
{
case gds_waitcmd:
GDStatus.DRDY=1; // Can accept ATA command :)
GDStatus.BSY=0; // Does not access command block
2013-12-19 17:10:14 +00:00
break;
case gds_procata:
//verify(prev==gds_waitcmd); // Validate the previous command ;)
2013-12-19 17:10:14 +00:00
GDStatus.DRDY=0; // Can't accept ATA command
GDStatus.BSY=1; // Accessing command block to process command
2013-12-19 17:10:14 +00:00
gd_process_ata_cmd();
break;
case gds_waitpacket:
verify(prev==gds_procata); // Validate the previous command ;)
2013-12-19 17:10:14 +00:00
// Prepare for packet command
2013-12-19 17:10:14 +00:00
packet_cmd.index=0;
// Set CoD, clear BSY and IO
2013-12-19 17:10:14 +00:00
IntReason.CoD=1;
GDStatus.BSY = 0;
IntReason.IO=0;
// Make DRQ valid
2013-12-19 17:10:14 +00:00
GDStatus.DRQ = 1;
// ATA can optionally raise the interrupt ...
// RaiseInterrupt(holly_GDROM_CMD);
2013-12-19 17:10:14 +00:00
break;
case gds_procpacket:
verify(prev==gds_waitpacket); // Validate the previous state ;)
2013-12-19 17:10:14 +00:00
GDStatus.DRQ=0; // Can't accept ATA command
GDStatus.BSY=1; // Accessing command block to process command
2013-12-19 17:10:14 +00:00
gd_process_spi_cmd();
break;
//yep , get/set are the same !
case gds_pio_get_data:
case gds_pio_send_data:
// When preparations are complete, the following steps are carried out at the device.
//(1) Number of bytes to be read is set in "Byte Count" register.
2013-12-19 17:10:14 +00:00
ByteCount.full =(u16)(pio_buff.size<<1);
//(2) IO bit is set and CoD bit is cleared.
2013-12-19 17:10:14 +00:00
IntReason.IO=1;
IntReason.CoD=0;
//(3) DRQ bit is set, BSY bit is cleared.
2013-12-19 17:10:14 +00:00
GDStatus.DRQ=1;
GDStatus.BSY=0;
//(4) INTRQ is set, and a host interrupt is issued.
2013-12-19 17:10:14 +00:00
asic_RaiseInterrupt(holly_GDROM_CMD);
/*
The number of bytes normally is the byte number in the register at the time of receiving
the command, but it may also be the total of several devices handled by the buffer at that point.
*/
break;
case gds_readsector_pio:
{
/*
If more data are to be sent, the device sets the BSY bit and repeats the above sequence
from step 7.
*/
GDStatus.BSY=1;
u32 sector_count = read_params.remaining_sectors;
gd_states next_state=gds_pio_end;
if (sector_count > 27)
2013-12-19 17:10:14 +00:00
{
sector_count = 27;
next_state = gds_readsector_pio;
2013-12-19 17:10:14 +00:00
}
libGDR_ReadSector((u8*)&pio_buff.data[0],read_params.start_sector,sector_count, read_params.sector_type);
2013-12-19 17:10:14 +00:00
read_params.start_sector+=sector_count;
read_params.remaining_sectors-=sector_count;
gd_spi_pio_end(0,sector_count*read_params.sector_type,next_state);
}
break;
case gds_readsector_dma:
FillReadBuffer();
break;
case gds_pio_end:
GDStatus.DRQ=0;//all data is sent !
gd_set_state(gds_procpacketdone);
break;
case gds_procpacketdone:
/*
7. When the device is ready to send the status, it writes the
2013-12-19 17:10:14 +00:00
final status (IO, CoD, DRDY set, BSY, DRQ cleared) to the "Status" register before making INTRQ valid.
After checking INTRQ, the host reads the "Status" register to check the completion status.
*/
//Set IO, CoD, DRDY
GDStatus.DRDY=1;
IntReason.CoD=1;
IntReason.IO=1;
//Clear DRQ,BSY
GDStatus.DRQ=0;
GDStatus.BSY=0;
//Make INTRQ valid
asic_RaiseInterrupt(holly_GDROM_CMD);
//command finished !
gd_set_state(gds_waitcmd);
break;
case gds_process_set_mode:
2019-07-30 17:04:51 +00:00
memcpy((u8 *)&GD_HardwareInfo + set_mode_offset, pio_buff.data, pio_buff.size << 1);
2013-12-19 17:10:14 +00:00
//end pio transfer ;)
gd_set_state(gds_pio_end);
break;
2013-12-19 17:10:14 +00:00
default :
die("Unhandled GDROM state ...");
2013-12-19 17:10:14 +00:00
break;
}
}
void gd_setdisc()
{
cdda.playing = false;
2013-12-19 17:10:14 +00:00
DiscType newd = (DiscType)libGDR_GetDiscType();
switch(newd)
{
case NoDisk:
SecNumber.Status = GD_NODISC;
//GDStatus.BSY=0;
//GDStatus.DRDY=1;
break;
2013-12-19 17:10:14 +00:00
case Open:
SecNumber.Status = GD_OPEN;
//GDStatus.BSY=0;
//GDStatus.DRDY=1;
break;
2013-12-19 17:10:14 +00:00
case Busy:
SecNumber.Status = GD_BUSY;
GDStatus.BSY=1;
GDStatus.DRDY=0;
break;
2013-12-19 17:10:14 +00:00
default :
if (SecNumber.Status==GD_BUSY)
SecNumber.Status = GD_PAUSE;
else
SecNumber.Status = GD_STANDBY;
//GDStatus.BSY=0;
//GDStatus.DRDY=1;
break;
}
2013-12-19 17:10:14 +00:00
if (gd_disk_type==Busy && newd!=Busy)
{
GDStatus.BSY=0;
GDStatus.DRDY=1;
}
gd_disk_type=newd;
SecNumber.DiscFormat=gd_disk_type>>4;
}
void gd_reset()
{
//Reset the drive
gd_setdisc();
gd_set_state(gds_waitcmd);
}
u32 GetFAD(u8* data, bool msf)
2013-12-19 17:10:14 +00:00
{
if(msf)
2013-12-19 17:10:14 +00:00
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: MSF FORMAT");
2013-12-19 17:10:14 +00:00
return ((data[0]*60*75) + (data[1]*75) + (data[2]));
}
else
{
return (data[0]<<16) | (data[1]<<8) | (data[2]);
}
}
//disk changes etc
2013-12-19 17:10:14 +00:00
void libCore_gdrom_disc_change()
{
gd_setdisc();
2019-05-19 19:36:08 +00:00
read_params = { 0 };
set_mode_offset = 0;
packet_cmd = { 0 };
2019-05-21 09:34:51 +00:00
memset(&read_buff, 0, sizeof(read_buff));
2019-05-19 19:36:08 +00:00
pio_buff = { gds_waitcmd, 0 };
ata_cmd = { 0 };
cdda = { 0 };
2013-12-19 17:10:14 +00:00
}
//This handles the work of setting up the pio regs/state :)
void gd_spi_pio_end(const u8* buffer, u32 len, gd_states next_state)
2013-12-19 17:10:14 +00:00
{
verify(len<0xFFFF);
pio_buff.index=0;
pio_buff.size=len>>1;
pio_buff.next_state=next_state;
2013-12-19 17:10:14 +00:00
if (buffer!=0)
memcpy(pio_buff.data,buffer,len);
2013-12-19 17:10:14 +00:00
if (len==0)
gd_set_state(next_state);
else
gd_set_state(gds_pio_send_data);
}
void gd_spi_pio_read_end(u32 len, gd_states next_state)
2013-12-19 17:10:14 +00:00
{
verify(len<0xFFFF);
pio_buff.index=0;
pio_buff.size=len>>1;
pio_buff.next_state=next_state;
2013-12-19 17:10:14 +00:00
if (len==0)
gd_set_state(next_state);
else
gd_set_state(gds_pio_get_data);
}
void gd_process_ata_cmd()
{
//Any ATA command clears these bits, unless aborted/error :p
2013-12-19 17:10:14 +00:00
Error.ABRT=0;
if (sns_key==0x0 || sns_key==0xB)
GDStatus.CHECK=0;
else
GDStatus.CHECK=1;
2013-12-19 17:10:14 +00:00
switch(ata_cmd.command)
{
case ATA_NOP:
2019-06-30 20:41:55 +00:00
printf_ata("ATA_NOP");
2013-12-19 17:10:14 +00:00
/*
Setting "abort" in the error register
Setting an error in the status register
Clearing "busy" in the status register
Asserting the INTRQ signal
*/
//this is all very hacky, I don't know if the abort is correct actually
2013-12-19 17:10:14 +00:00
//the above comment is from a wrong place in the docs ...
Error.ABRT=1;
Error.Sense=sns_key;
2013-12-19 17:10:14 +00:00
GDStatus.BSY=0;
GDStatus.CHECK=1;
asic_RaiseInterrupt(holly_GDROM_CMD);
gd_set_state(gds_waitcmd);
break;
case ATA_SOFT_RESET:
{
2019-06-30 20:41:55 +00:00
printf_ata("ATA_SOFT_RESET");
2013-12-19 17:10:14 +00:00
//DRV -> preserved -> wtf is it anyway ?
gd_reset();
}
break;
case ATA_EXEC_DIAG:
2019-07-01 14:10:28 +00:00
printf_ata("ATA_EXEC_DIAG -- not implemented");
2013-12-19 17:10:14 +00:00
break;
case ATA_SPI_PACKET:
2019-06-30 20:41:55 +00:00
printf_ata("ATA_SPI_PACKET");
2013-12-19 17:10:14 +00:00
gd_set_state(gds_waitpacket);
break;
case ATA_IDENTIFY_DEV:
2019-06-30 20:41:55 +00:00
printf_ata("ATA_IDENTIFY_DEV");
gd_spi_pio_end((const u8*)&reply_a1[packet_cmd.data_8[2] >> 1], packet_cmd.data_8[4]);
2013-12-19 17:10:14 +00:00
break;
case ATA_SET_FEATURES:
2019-06-30 20:41:55 +00:00
printf_ata("ATA_SET_FEATURES");
2013-12-19 17:10:14 +00:00
//Set features sets :
//Error : ABRT
Error.ABRT=0; // Command was not aborted ;) [hopefully ...]
2013-12-19 17:10:14 +00:00
//status : DRDY , DSC , DF , CHECK
//DRDY is set on state change
GDStatus.DSC=0;
GDStatus.DF=0;
GDStatus.CHECK=0;
asic_RaiseInterrupt(holly_GDROM_CMD); //???
gd_set_state(gds_waitcmd);
break;
2020-02-11 20:25:01 +00:00
case ATA_IDENTIFY:
printf_ata("ATA_IDENTIFY\n");
// Set Signature
DriveSel &= 0xf0;
SecCount.full = 1;
SecNumber.full = 1;
ByteCount.low = 0x14;
ByteCount.hi = 0xeb;
// where did this come from?
//GDStatus.DRQ = 0;
// ABORT command
Error.full = 0x4;
GDStatus.full = 0;
GDStatus.DRDY = 1;
GDStatus.CHECK = 1;
asic_RaiseInterrupt(holly_GDROM_CMD);
gd_set_state(gds_waitcmd);
break;
2013-12-19 17:10:14 +00:00
default:
2020-02-02 20:15:53 +00:00
ERROR_LOG(GDROM, "Unknown ATA command %x", ata_cmd.command);
2013-12-19 17:10:14 +00:00
break;
};
}
2019-07-30 17:04:51 +00:00
u32 gd_get_subcode(u32 format, u32 fad, u8 *subc_info)
{
subc_info[0] = 0;
subc_info[1] = 0x15; // no audio status info
if (format == 0)
{
subc_info[2] = 0;
subc_info[3] = 100;
libGDR_ReadSubChannel(subc_info + 4, 0, 100 - 4);
}
else
{
u32 elapsed;
u32 tracknum = libGDR_GetTrackNumber(fad, elapsed);
//2 DATA Length MSB (0 = 0h)
subc_info[2] = 0;
//3 DATA Length LSB (14 = Eh)
subc_info[3] = 0xE;
//4 Control ADR
subc_info[4] = (SecNumber.DiscFormat == 0 ? 0 : 0x40) | 1; // Control = 4 for data track
//5-13 DATA-Q
u8* data_q = &subc_info[5 - 1];
//-When ADR = 1
//1 TNO - track number
data_q[1] = tracknum;
//2 X - index within track
data_q[2] = 1;
//3-5 Elapsed FAD within track
data_q[3] = elapsed >> 16;
data_q[4] = elapsed >> 8;
data_q[5] = elapsed;
//6 ZERO
data_q[6] = 0;
//7-9 FAD
data_q[7] = fad >> 16;
data_q[8] = fad >> 8;
data_q[9] = fad;
DEBUG_LOG(GDROM, "gd_get_subcode: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
subc_info[0], subc_info[1], subc_info[2], subc_info[3],
subc_info[4], subc_info[5], subc_info[6], subc_info[7],
subc_info[8], subc_info[9], subc_info[10], subc_info[11],
subc_info[12], subc_info[13]);
}
return subc_info[3];
}
2013-12-19 17:10:14 +00:00
void gd_process_spi_cmd()
{
2019-06-30 20:41:55 +00:00
printf_spi("Sense: %02x %02x %02x", sns_asc, sns_ascq, sns_key);
printf_spi("SPI command %02x;",packet_cmd.data_8[0]);
2019-06-30 20:41:55 +00:00
printf_spi("Params: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
2013-12-19 17:10:14 +00:00
packet_cmd.data_8[0], packet_cmd.data_8[1], packet_cmd.data_8[2], packet_cmd.data_8[3], packet_cmd.data_8[4], packet_cmd.data_8[5],
packet_cmd.data_8[6], packet_cmd.data_8[7], packet_cmd.data_8[8], packet_cmd.data_8[9], packet_cmd.data_8[10], packet_cmd.data_8[11] );
if (sns_key==0x0 || sns_key==0xB)
GDStatus.CHECK=0;
else
GDStatus.CHECK=1;
2013-12-19 17:10:14 +00:00
switch(packet_cmd.data_8[0])
{
case SPI_TEST_UNIT:
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_TEST_UNIT");
2013-12-19 17:10:14 +00:00
GDStatus.CHECK=SecNumber.Status==GD_BUSY; // Drive is ready ;)
2020-02-02 20:15:53 +00:00
cdda.playing = false;
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
break;
case SPI_REQ_MODE:
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_REQ_MODE");
2019-07-30 17:04:51 +00:00
gd_spi_pio_end((u8*)&GD_HardwareInfo + packet_cmd.data_8[2], packet_cmd.data_8[4]);
2013-12-19 17:10:14 +00:00
break;
/////////////////////////////////////////////////
// *FIXME* CHECK FOR DMA, Diff Settings !?!@$#!@%
case SPI_CD_READ:
{
#define readcmd packet_cmd.GDReadBlock
2020-02-02 20:15:53 +00:00
cdda.playing = false;
2013-12-19 17:10:14 +00:00
u32 sector_type=2048;
if (readcmd.head ==1 && readcmd.subh==1 && readcmd.data==1 && readcmd.expdtype==3 && readcmd.other==0)
sector_type=2340;
else if(readcmd.head ||readcmd.subh || readcmd.other || (!readcmd.data)) // assert
2019-07-01 14:10:28 +00:00
WARN_LOG(GDROM, "GDROM: *FIXME* ADD MORE CD READ SETTINGS %d %d %d %d 0x%01X",readcmd.head,readcmd.subh,readcmd.other,readcmd.data,readcmd.expdtype);
2013-12-19 17:10:14 +00:00
read_params.start_sector = GetFAD(&readcmd.b[2], readcmd.prmtype);
read_params.remaining_sectors = (readcmd.b[8] << 16) | (readcmd.b[9] << 8) | (readcmd.b[10]);
read_params.sector_type = sector_type;//yeah i know , not really many types supported...
2013-12-19 17:10:14 +00:00
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_CD_READ - Sector=%d Size=%d/%d DMA=%d",read_params.start_sector,read_params.remaining_sectors,read_params.sector_type,Features.CDRead.DMA);
if (Features.CDRead.DMA == 1)
2013-12-19 17:10:14 +00:00
{
gd_set_state(gds_readsector_dma);
}
else
{
gd_set_state(gds_readsector_pio);
}
}
break;
case SPI_GET_TOC:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_GET_TOC");
2013-12-19 17:10:14 +00:00
//printf("SPI_GET_TOC - %d\n",(packet_cmd.data_8[4]) | (packet_cmd.data_8[3]<<8) );
u32 toc_gd[102];
//toc - dd/sd
libGDR_GetToc(&toc_gd[0],packet_cmd.data_8[1]&0x1);
gd_spi_pio_end((u8*)&toc_gd[0], (packet_cmd.data_8[4]) | (packet_cmd.data_8[3]<<8) );
}
break;
//mount/map drive ? some kind of reset/unlock ??
//seems like a non data command :)
case 0x70:
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI : unknown ? [0x70]");
//printf("SPI : unknown ? [0x70]\n");
2013-12-19 17:10:14 +00:00
/*GDStatus.full=0x50; //FIXME
RaiseInterrupt(holly_GDROM_CMD);*/
gd_set_state(gds_procpacketdone);
break;
// Command 71 seems to trigger some sort of authentication check(?).
// Update Sept 1st 2010: It looks like after a sequence of events the drive ends up having a specific state.
// If the drive is fed with a "bootable" disc it ends up in "PAUSE" state. On all other cases it ends up in "STANDBY".
// Cmd 70 and Error Handling / Sense also seem to take part in the above mentioned sequence of events.
// This is more or less a hack until more info about this command becomes available. ~Psy
case 0x71:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI : unknown ? [0x71]");
//printf("SPI : unknown ? [0x71]\n");
extern const u32 reply_71_sz;
gd_spi_pio_end((const u8*)&reply_71[0], reply_71_sz);//uCount
2013-12-19 17:10:14 +00:00
if (libGDR_GetDiscType()==GdRom || libGDR_GetDiscType()==CdRom_XA)
SecNumber.Status=GD_PAUSE;
else
SecNumber.Status=GD_STANDBY;
}
break;
case SPI_SET_MODE:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_SET_MODE");
2013-12-19 17:10:14 +00:00
u32 Offset = packet_cmd.data_8[2];
u32 Count = packet_cmd.data_8[4];
verify((Offset+Count)<11); //cant set write olny things :P
set_mode_offset=Offset;
gd_spi_pio_read_end(Count,gds_process_set_mode);
}
break;
case SPI_CD_READ2:
2019-07-01 14:10:28 +00:00
printf_spicmd("SPI_CD_READ2 Unhandled");
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
break;
case SPI_REQ_STAT:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_REQ_STAT");
2013-12-19 17:10:14 +00:00
u8 stat[10];
//0 0 0 0 0 STATUS
stat[0]=SecNumber.Status; //low nibble
//1 Disc Format Repeat Count
2013-12-19 17:10:14 +00:00
stat[1]=(u8)(SecNumber.DiscFormat<<4) | (cdda.repeats);
//2 Address Control
2013-12-19 17:10:14 +00:00
stat[2]=0x4;
//3 TNO
2013-12-19 17:10:14 +00:00
stat[3]=2;
//4 X
2013-12-19 17:10:14 +00:00
stat[4]=0;
//5 FAD
2013-12-19 17:10:14 +00:00
stat[5]=cdda.CurrAddr.B0;
//6 FAD
2013-12-19 17:10:14 +00:00
stat[6]=cdda.CurrAddr.B1;
//7 FAD
2013-12-19 17:10:14 +00:00
stat[7]=cdda.CurrAddr.B2;
//8 Max Read Error Retry Times
2013-12-19 17:10:14 +00:00
stat[8]=0;
//9 0 0 0 0 0 0 0 0
2013-12-19 17:10:14 +00:00
stat[9]=0;
verify((packet_cmd.data_8[2]+packet_cmd.data_8[4])<11);
gd_spi_pio_end(&stat[packet_cmd.data_8[2]],packet_cmd.data_8[4]);
}
break;
case SPI_REQ_ERROR:
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_REQ_ERROR");
2013-12-19 17:10:14 +00:00
u8 resp[10];
resp[0]=0xF0;
resp[1]=0;
resp[2]=sns_key;//sense
2013-12-19 17:10:14 +00:00
resp[3]=0;
resp[4]=resp[5]=resp[6]=resp[7]=0; //Command Specific Information
resp[8]=sns_asc;//Additional Sense Code
resp[9]=sns_ascq;//Additional Sense Code Qualifier
2013-12-19 17:10:14 +00:00
gd_spi_pio_end(resp,packet_cmd.data_8[4]);
sns_key=0;
sns_asc=0;
sns_ascq=0;
//GDStatus.CHECK=0;
2013-12-19 17:10:14 +00:00
break;
case SPI_REQ_SES:
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_REQ_SES");
2013-12-19 17:10:14 +00:00
u8 ses_inf[6];
libGDR_GetSessionInfo(ses_inf,packet_cmd.data_8[2]);
ses_inf[0]=SecNumber.Status;
gd_spi_pio_end((u8*)&ses_inf[0],packet_cmd.data_8[4]);
break;
case SPI_CD_OPEN:
2019-07-01 14:10:28 +00:00
printf_spicmd("SPI_CD_OPEN Unhandled");
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
break;
case SPI_CD_PLAY:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_CD_PLAY");
2013-12-19 17:10:14 +00:00
//cdda.CurrAddr.FAD=60000;
cdda.playing=true;
SecNumber.Status=GD_PLAY;
u32 param_type=packet_cmd.data_8[1]&0x7;
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "param_type=%d", param_type);
2013-12-19 17:10:14 +00:00
if (param_type==1)
{
cdda.StartAddr.FAD=cdda.CurrAddr.FAD=GetFAD(&packet_cmd.data_8[2],0);
cdda.EndAddr.FAD=GetFAD(&packet_cmd.data_8[8],0);
GDStatus.DSC=1; //we did the seek xD lol
}
else if (param_type==2)
{
cdda.StartAddr.FAD=cdda.CurrAddr.FAD=GetFAD(&packet_cmd.data_8[2],1);
cdda.EndAddr.FAD=GetFAD(&packet_cmd.data_8[8],1);
GDStatus.DSC=1; //we did the seek xD lol
}
else if (param_type==7)
{
// Resume from previous pos unless we're at the end
if (cdda.CurrAddr.FAD > cdda.EndAddr.FAD)
{
cdda.playing = false;
SecNumber.Status = GD_STANDBY;
}
2013-12-19 17:10:14 +00:00
}
else
{
die("SPI_CD_PLAY : not known parameter..");
2013-12-19 17:10:14 +00:00
}
cdda.repeats=packet_cmd.data_8[6]&0xF;
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "cdda.StartAddr=%d",cdda.StartAddr.FAD);
DEBUG_LOG(GDROM, "cdda.EndAddr=%d",cdda.EndAddr.FAD);
DEBUG_LOG(GDROM, "cdda.repeats=%d",cdda.repeats);
DEBUG_LOG(GDROM, "cdda.playing=%d",cdda.playing);
DEBUG_LOG(GDROM, "cdda.CurrAddr=%d",cdda.CurrAddr.FAD);
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
}
break;
case SPI_CD_SEEK:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_CD_SEEK");
2013-12-19 17:10:14 +00:00
SecNumber.Status=GD_PAUSE;
cdda.playing=false;
u32 param_type=packet_cmd.data_8[1]&0x7;
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "param_type=%d",param_type);
2013-12-19 17:10:14 +00:00
if (param_type==1)
{
cdda.StartAddr.FAD=cdda.CurrAddr.FAD=GetFAD(&packet_cmd.data_8[2],0);
GDStatus.DSC=1; //we did the seek xD lol
}
else if (param_type==2)
{
cdda.StartAddr.FAD=cdda.CurrAddr.FAD=GetFAD(&packet_cmd.data_8[2],1);
GDStatus.DSC=1; //we did the seek xD lol
}
else if (param_type==3)
{
//stop audio , goto home
SecNumber.Status=GD_STANDBY;
cdda.StartAddr.FAD=cdda.CurrAddr.FAD=150;
GDStatus.DSC=1; //we did the seek xD lol
}
else if (param_type==4)
{
//pause audio -- nothing more
}
else
{
die("SPI_CD_SEEK : not known parameter..");
}
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "cdda.StartAddr=%d",cdda.StartAddr.FAD);
DEBUG_LOG(GDROM, "cdda.EndAddr=%d",cdda.EndAddr.FAD);
DEBUG_LOG(GDROM, "cdda.repeats=%d",cdda.repeats);
DEBUG_LOG(GDROM, "cdda.playing=%d",cdda.playing);
DEBUG_LOG(GDROM, "cdda.CurrAddr=%d",cdda.CurrAddr.FAD);
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
}
break;
case SPI_CD_SCAN:
2019-07-01 14:10:28 +00:00
printf_spicmd("SPI_CD_SCAN Unhandled");
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
break;
case SPI_GET_SCD:
{
2019-06-30 20:41:55 +00:00
printf_spicmd("SPI_GET_SCD");
2013-12-19 17:10:14 +00:00
2019-07-30 17:04:51 +00:00
u32 format = packet_cmd.data_8[1] & 0xF;
2013-12-19 17:10:14 +00:00
u8 subc_info[100];
2019-07-30 17:04:51 +00:00
u32 size = gd_get_subcode(format, read_params.start_sector - 1, subc_info);
gd_spi_pio_end(subc_info, size);
2013-12-19 17:10:14 +00:00
}
break;
default:
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Unhandled Sega SPI frame: %X", packet_cmd.data_8[0]);
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
break;
}
}
//Read handler
u32 ReadMem_gdrom(u32 Addr, u32 sz)
{
switch (Addr)
{
//cancel interrupt
case GD_STATUS_Read :
asic_CancelInterrupt(holly_GDROM_CMD); //Clear INTRQ signal
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: STATUS [cancel int](v=%X)",GDStatus.full);
2013-12-19 17:10:14 +00:00
return GDStatus.full | (1<<4);
case GD_ALTSTAT_Read:
// printf_rm("GDROM: Read From AltStatus (v=%X)",GDStatus.full);
2013-12-19 17:10:14 +00:00
return GDStatus.full | (1<<4);
case GD_BYCTLLO :
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Read From GD_BYCTLLO");
2013-12-19 17:10:14 +00:00
return ByteCount.low;
case GD_BYCTLHI :
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Read From GD_BYCTLHI");
2013-12-19 17:10:14 +00:00
return ByteCount.hi;
case GD_DATA:
if(2!=sz)
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Bad size on DATA REG Read");
2013-12-19 17:10:14 +00:00
//if (gd_state == gds_pio_send_data)
//{
if (pio_buff.index == pio_buff.size)
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Illegal Read From DATA (underflow)");
}
2013-12-19 17:10:14 +00:00
else
{
u32 rv= pio_buff.data[pio_buff.index];
pio_buff.index+=1;
ByteCount.full-=2;
if (pio_buff.index==pio_buff.size)
{
verify(pio_buff.next_state != gds_pio_send_data);
//end of pio transfer !
gd_set_state(pio_buff.next_state);
}
return rv;
}
//}
//else
// printf("GDROM: Illegal Read From DATA (wrong mode)\n");
2013-12-19 17:10:14 +00:00
return 0;
case GD_DRVSEL:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Read From DriveSel");
2013-12-19 17:10:14 +00:00
return DriveSel;
case GD_ERROR_Read:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Read from ERROR Register");
Error.Sense=sns_key;
2013-12-19 17:10:14 +00:00
return Error.full;
case GD_IREASON_Read:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Read from INTREASON Register");
2013-12-19 17:10:14 +00:00
return IntReason.full;
case GD_SECTNUM:
// printf_rm("GDROM: Read from SecNumber Register (v=%X)", SecNumber.full);
2013-12-19 17:10:14 +00:00
return SecNumber.full;
default:
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Unhandled read from address %X, Size:%X",Addr,sz);
2013-12-19 17:10:14 +00:00
return 0;
}
}
//Write Handler
void WriteMem_gdrom(u32 Addr, u32 data, u32 sz)
{
switch(Addr)
{
2020-02-11 20:25:01 +00:00
//ATA_IOPORT_WR_CYLINDER_LOW
2013-12-19 17:10:14 +00:00
case GD_BYCTLLO:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Write to GD_BYCTLLO = %X, Size:%X",data,sz);
2013-12-19 17:10:14 +00:00
ByteCount.low =(u8) data;
break;
2020-02-11 20:25:01 +00:00
//ATA_IOPORT_WR_CYLINDER_HIGH
2013-12-19 17:10:14 +00:00
case GD_BYCTLHI:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Write to GD_BYCTLHI = %X, Size:%X",data,sz);
2013-12-19 17:10:14 +00:00
ByteCount.hi =(u8) data;
break;
case GD_DATA:
{
if(2!=sz)
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Bad size on DATA REG");
2013-12-19 17:10:14 +00:00
if (gd_state == gds_waitpacket)
{
packet_cmd.data_16[packet_cmd.index]=(u16)data;
packet_cmd.index+=1;
if (packet_cmd.index==6)
gd_set_state(gds_procpacket);
}
else if (gd_state == gds_pio_get_data)
{
pio_buff.data[pio_buff.index]=(u16)data;
pio_buff.index+=1;
if (pio_buff.size==pio_buff.index)
{
verify(pio_buff.next_state!=gds_pio_get_data);
gd_set_state(pio_buff.next_state);
}
}
else
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Illegal Write to DATA");
}
2013-12-19 17:10:14 +00:00
return;
}
2013-12-19 17:10:14 +00:00
case GD_DEVCTRL_Write:
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Write GD_DEVCTRL (Not implemented on Dreamcast)");
2013-12-19 17:10:14 +00:00
break;
2020-02-11 20:25:01 +00:00
//ATA_IOPORT_WR_DEVICE_HEAD
2013-12-19 17:10:14 +00:00
case GD_DRVSEL:
2015-03-22 00:16:28 +00:00
if (data != 0) {
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Write to GD_DRVSEL, !=0. Value is: %02X", data);
2015-03-22 00:16:28 +00:00
}
2013-12-19 17:10:14 +00:00
DriveSel = data;
break;
// By writing "3" as Feature Number and issuing the Set Feature command,
// the PIO or DMA transfer mode set in the Sector Count register can be selected.
// The actual transfer mode is specified by the Sector Counter Register.
2013-12-19 17:10:14 +00:00
case GD_FEATURES_Write:
2019-06-30 20:41:55 +00:00
printf_rm("GDROM: Write to GD_FEATURES");
2013-12-19 17:10:14 +00:00
Features.full =(u8) data;
break;
case GD_SECTCNT_Write:
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "GDROM: Write to SecCount = %X", data);
2013-12-19 17:10:14 +00:00
SecCount.full =(u8) data;
break;
case GD_SECTNUM:
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Write to SecNum; not possible = %X", data);
2013-12-19 17:10:14 +00:00
break;
case GD_COMMAND_Write:
verify(sz==1);
if ((data !=ATA_NOP) && (data != ATA_SOFT_RESET))
verify(gd_state==gds_waitcmd);
//printf("\nGDROM:\tCOMMAND: %X !\n", data);
ata_cmd.command=(u8)data;
gd_set_state(gds_procata);
break;
default:
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Unhandled write to address %X <= %X, Size:%X",Addr,data,sz);
2013-12-19 17:10:14 +00:00
break;
}
}
2018-09-21 15:56:29 +00:00
static int getGDROMTicks()
{
if (SB_GDST & 1)
{
if (SB_GDLEN - SB_GDLEND > 10240)
return 1000000; // Large transfers: GD-ROM transfer rate 1.8 MB/s
else
2020-03-29 17:29:14 +00:00
return std::min((u32)10240, SB_GDLEN - SB_GDLEND) * 2; // Small transfers: Max G1 bus rate: 50 MHz x 16 bits
2018-09-21 15:56:29 +00:00
}
else
return 0;
}
2013-12-19 17:10:14 +00:00
//is this needed ?
int GDRomschd(int i, int c, int j)
{
if(!(SB_GDST&1) || !(SB_GDEN &1) || (read_buff.cache_size==0 && read_params.remaining_sectors==0))
return 0;
//SB_GDST=0;
//TODO : Fix dmaor
u32 dmaor = DMAC_DMAOR.full;
2013-12-19 17:10:14 +00:00
u32 src = SB_GDSTARD,
len = SB_GDLEN-SB_GDLEND ;
2013-12-19 17:10:14 +00:00
if(SB_GDLEN & 0x1F)
{
die("\n!\tGDROM: SB_GDLEN has invalid size !\n");
2018-09-21 15:56:29 +00:00
return 0;
2013-12-19 17:10:14 +00:00
}
//if we don't have any more sectors to read
if (read_params.remaining_sectors == 0)
2013-12-19 17:10:14 +00:00
//make sure we don't underrun the cache :)
2020-03-29 17:29:14 +00:00
len = std::min(len, read_buff.cache_size);
2013-12-19 17:10:14 +00:00
2020-03-29 17:29:14 +00:00
len = std::min(len, (u32)10240);
// do we need to do this for GDROM DMA?
if(0x8201 != (dmaor &DMAOR_MASK))
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: DMAOR has invalid settings (%X)", dmaor);
2013-12-19 17:10:14 +00:00
//return;
}
if(len == 0)
2013-12-19 17:10:14 +00:00
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "GDROM: Len: %X, Abnormal Termination !", len);
2013-12-19 17:10:14 +00:00
}
u32 len_backup = len;
if(1 == SB_GDDIR)
2013-12-19 17:10:14 +00:00
{
while(len)
{
u32 buff_size =read_buff.cache_size;
if (buff_size==0)
{
verify(read_params.remaining_sectors>0);
//buffer is empty , fill it :)
FillReadBuffer();
}
//transfer up to len bytes
if (buff_size>len)
buff_size=len;
WriteMemBlock_nommu_ptr(src,(u32*)&read_buff.cache[read_buff.cache_index], buff_size);
read_buff.cache_index+=buff_size;
read_buff.cache_size-=buff_size;
src+=buff_size;
len-=buff_size;
}
}
else
{
2019-07-01 14:10:28 +00:00
WARN_LOG(GDROM, "GDROM: SB_GDDIR %X (TO AICA WAVE MEM?)", src);
}
2013-12-19 17:10:14 +00:00
//SB_GDLEN = 0x00000000; //13/5/2k7 -> according to docs these regs are not updated by hardware
2013-12-19 17:10:14 +00:00
//SB_GDSTAR = (src + len_backup);
SB_GDLEND+= len_backup;
SB_GDSTARD+= len_backup;//(src + len_backup)&0x1FFFFFFF;
if (SB_GDLEND==SB_GDLEN)
{
//printf("Streamed GDMA end - %d bytes transferred\n",SB_GDLEND);
2013-12-19 17:10:14 +00:00
SB_GDST=0;//done
// The DMA end interrupt flag
asic_RaiseInterrupt(holly_GDROM_DMA);
}
//Read ALL sectors
2013-12-19 17:10:14 +00:00
if (read_params.remaining_sectors==0)
{
//And all buffer :p
if (read_buff.cache_size==0)
{
//verify(!SB_GDST&1) -> dc can do multi read dma
2013-12-19 17:10:14 +00:00
gd_set_state(gds_procpacketdone);
}
}
2018-09-21 15:56:29 +00:00
return getGDROMTicks();
2013-12-19 17:10:14 +00:00
}
//DMA Start
2013-12-19 17:10:14 +00:00
void GDROM_DmaStart(u32 addr, u32 data)
{
if (SB_GDEN==0)
{
2019-07-01 14:10:28 +00:00
INFO_LOG(GDROM, "Invalid GD-DMA start, SB_GDEN=0.Ingoring it.");
2013-12-19 17:10:14 +00:00
return;
}
SB_GDST|=data&1;
if (SB_GDST==1)
{
SB_GDSTARD=SB_GDSTAR;
SB_GDLEND=0;
2019-07-01 14:10:28 +00:00
DEBUG_LOG(GDROM, "GDROM-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
2018-09-21 15:56:29 +00:00
int ticks = getGDROMTicks();
if (ticks < SH4_TIMESLICE)
2018-09-21 15:56:29 +00:00
{
ticks = GDRomschd(0,0,0);
}
if (ticks)
sh4_sched_request(gdrom_schid, ticks);
2013-12-19 17:10:14 +00:00
}
}
void GDROM_DmaEnable(u32 addr, u32 data)
{
SB_GDEN = (data & 1);
if (SB_GDEN == 0 && SB_GDST == 1)
2013-12-19 17:10:14 +00:00
{
2019-06-30 20:41:55 +00:00
printf_spi("GD-DMA aborted");
SB_GDST = 0;
2013-12-19 17:10:14 +00:00
}
}
//Init/Term/Res
void gdrom_reg_Init()
{
gdrom_schid = sh4_sched_register(0, &GDRomschd);
2013-12-19 17:10:14 +00:00
}
2013-12-19 17:10:14 +00:00
void gdrom_reg_Term()
{
}
2019-07-10 15:25:11 +00:00
void gdrom_reg_Reset(bool hard)
2013-12-19 17:10:14 +00:00
{
sb_rio_register(SB_GDST_addr, RIO_WF, 0, &GDROM_DmaStart);
sb_rio_register(SB_GDEN_addr, RIO_WF, 0, &GDROM_DmaEnable);
SB_GDST = 0;
SB_GDEN = 0;
2019-07-30 17:04:51 +00:00
// set default hardware information
memset(&GD_HardwareInfo, 0, sizeof(GD_HardwareInfo));
GD_HardwareInfo.speed = 0x0;
GD_HardwareInfo.standby_hi = 0x00;
GD_HardwareInfo.standby_lo = 0xb4;
GD_HardwareInfo.read_flags = 0x19;
GD_HardwareInfo.read_retry = 0x08;
memcpy(GD_HardwareInfo.drive_info, "SE ", sizeof(GD_HardwareInfo.drive_info));
memcpy(GD_HardwareInfo.system_version, "Rev 6.43", sizeof(GD_HardwareInfo.system_version));
memcpy(GD_HardwareInfo.system_date, "990408", sizeof(GD_HardwareInfo.system_date));
2013-12-19 17:10:14 +00:00
}