354 lines
7.6 KiB
C++
354 lines
7.6 KiB
C++
#include "maple_if.h"
|
|
#include "maple_cfg.h"
|
|
#include "maple_helper.h"
|
|
#include "hw/holly/holly_intc.h"
|
|
#include "hw/holly/sb.h"
|
|
#include "hw/sh4/sh4_mem.h"
|
|
#include "hw/sh4/sh4_sched.h"
|
|
|
|
enum MaplePattern
|
|
{
|
|
MP_Start,
|
|
MP_SDCKBOccupy = 2,
|
|
MP_Reset,
|
|
MP_SDCKBOccupyCancel,
|
|
MP_NOP = 7
|
|
};
|
|
|
|
maple_device* MapleDevices[MAPLE_PORTS][6];
|
|
|
|
int maple_schid;
|
|
|
|
void UpdateInputState();
|
|
/*
|
|
Maple host controller
|
|
Direct processing, async interrupt handling
|
|
Device code is on maple_devs.cpp/h, config&management is on maple_cfg.cpp/h
|
|
|
|
This code is missing many of the hardware details, like proper trigger handling,
|
|
DMA continuation on suspect, etc ...
|
|
*/
|
|
|
|
static void maple_DoDma();
|
|
static void maple_handle_reconnect();
|
|
|
|
//really hackish
|
|
//misses delay , and stop/start implementation
|
|
//ddt/etc are just hacked for wince to work
|
|
//now with proper maple delayed DMA maybe its time to look into it ?
|
|
bool maple_ddt_pending_reset;
|
|
|
|
void maple_vblank()
|
|
{
|
|
if (SB_MDEN & 1)
|
|
{
|
|
if (SB_MDTSEL & 1)
|
|
{
|
|
if (maple_ddt_pending_reset)
|
|
{
|
|
DEBUG_LOG(MAPLE, "DDT vblank ; reset pending");
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG(MAPLE, "DDT vblank");
|
|
SB_MDST = 1;
|
|
maple_DoDma();
|
|
// if trigger reset is manual, mark it as pending
|
|
if ((SB_MSYS >> 12) & 1)
|
|
maple_ddt_pending_reset = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
maple_ddt_pending_reset = false;
|
|
}
|
|
}
|
|
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
|
|
maple_handle_reconnect();
|
|
}
|
|
|
|
static void maple_SB_MSHTCL_Write(u32 addr, u32 data)
|
|
{
|
|
if (data&1)
|
|
maple_ddt_pending_reset=false;
|
|
}
|
|
|
|
static void maple_SB_MDST_Write(u32 addr, u32 data)
|
|
{
|
|
if (data & 0x1)
|
|
{
|
|
if (SB_MDEN &1)
|
|
{
|
|
SB_MDST=1;
|
|
maple_DoDma();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void maple_SB_MDEN_Write(u32 addr, u32 data)
|
|
{
|
|
SB_MDEN=data&1;
|
|
|
|
if ((data & 0x1)==0 && SB_MDST)
|
|
{
|
|
INFO_LOG(MAPLE, "Maple DMA abort ?");
|
|
}
|
|
}
|
|
|
|
#ifdef STRICT_MODE
|
|
static bool check_mdapro(u32 addr)
|
|
{
|
|
u32 area = (addr >> 26) & 7;
|
|
u32 bottom = ((((SB_MDAPRO >> 8) & 0x7f) << 20) | 0x08000000);
|
|
u32 top = (((SB_MDAPRO & 0x7f) << 20) | 0x080fffe0);
|
|
|
|
if (area != 3 || addr < bottom || addr > top)
|
|
{
|
|
INFO_LOG(MAPLE, "MAPLE ERROR : Invalid address: %08x. SB_MDAPRO: %x %x", addr, (SB_MDAPRO >> 8) & 0x7f, SB_MDAPRO & 0x7f);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void maple_SB_MDSTAR_Write(u32 addr, u32 data)
|
|
{
|
|
SB_MDSTAR = data & 0x1fffffe0;
|
|
if (!check_mdapro(SB_MDSTAR))
|
|
asic_RaiseInterrupt(holly_MAPLE_ILLADDR);
|
|
}
|
|
#endif
|
|
|
|
bool IsOnSh4Ram(u32 addr)
|
|
{
|
|
if (((addr>>26)&0x7)==3)
|
|
{
|
|
if ((((addr>>29) &0x7)!=7))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void maple_DoDma()
|
|
{
|
|
verify(SB_MDEN &1);
|
|
verify(SB_MDST &1);
|
|
|
|
DEBUG_LOG(MAPLE, "Maple: DoMapleDma SB_MDSTAR=%x", SB_MDSTAR);
|
|
u32 addr = SB_MDSTAR;
|
|
#ifdef STRICT_MODE
|
|
if (!check_mdapro(addr))
|
|
{
|
|
asic_RaiseInterrupt(holly_MAPLE_ILLADDR);
|
|
SB_MDST = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
UpdateInputState();
|
|
|
|
const bool swap_msb = (SB_MMSEL == 0);
|
|
u32 xfer_count=0;
|
|
bool last = false;
|
|
bool occupy = false;
|
|
while (last != true)
|
|
{
|
|
u32 header_1 = ReadMem32_nommu(addr);
|
|
u32 header_2 = ReadMem32_nommu(addr + 4) &0x1FFFFFE0;
|
|
|
|
last = (header_1 >> 31) == 1;//is last transfer ?
|
|
u32 plen = (header_1 & 0xFF )+1;//transfer length (32-bit unit)
|
|
u32 maple_op=(header_1>>8)&7; // Pattern selection: 0 - START, 2 - SDCKB occupy permission, 3 - RESET, 4 - SDCKB occupy cancel, 7 - NOP
|
|
xfer_count+=plen*4;
|
|
|
|
//this is kinda wrong .. but meh
|
|
//really need to properly process the commands at some point
|
|
switch (maple_op)
|
|
{
|
|
case MP_Start:
|
|
{
|
|
#ifdef STRICT_MODE
|
|
if (!check_mdapro(header_2) || !check_mdapro(addr + 8 + plen * sizeof(u32) - 1))
|
|
{
|
|
asic_RaiseInterrupt(holly_MAPLE_OVERRUN);
|
|
SB_MDST = 0;
|
|
return;
|
|
}
|
|
#else
|
|
if (!IsOnSh4Ram(header_2))
|
|
{
|
|
INFO_LOG(MAPLE, "MAPLE ERROR : DESTINATION NOT ON SH4 RAM 0x%X", header_2);
|
|
header_2&=0xFFFFFF;
|
|
header_2|=(3<<26);
|
|
}
|
|
#endif
|
|
u32* p_out=(u32*)GetMemPtr(header_2,4);
|
|
|
|
u32* p_data =(u32*) GetMemPtr(addr + 8,(plen)*sizeof(u32));
|
|
if (p_data == NULL)
|
|
{
|
|
INFO_LOG(MAPLE, "MAPLE ERROR : INVALID SB_MDSTAR value 0x%X", addr);
|
|
SB_MDST=0;
|
|
return;
|
|
}
|
|
const u32 frame_header = swap_msb ? SWAP32(p_data[0]) : p_data[0];
|
|
|
|
//Command code
|
|
u32 command = frame_header & 0xFF;
|
|
//Recipient address
|
|
u32 reci = (frame_header >> 8) & 0xFF;//0-5;
|
|
//Sender address
|
|
//u32 send = (frame_header >> 16) & 0xFF;
|
|
//Number of additional words in frame
|
|
u32 inlen = (frame_header >> 24) & 0xFF;
|
|
|
|
u32 port=maple_GetPort(reci);
|
|
u32 bus=maple_GetBusId(reci);
|
|
|
|
if (MapleDevices[bus][5] && MapleDevices[bus][port])
|
|
{
|
|
if (swap_msb)
|
|
{
|
|
static u32 maple_in_buf[1024 / 4];
|
|
static u32 maple_out_buf[1024 / 4];
|
|
maple_in_buf[0] = frame_header;
|
|
for (u32 i = 1; i < inlen; i++)
|
|
maple_in_buf[i] = SWAP32(p_data[i]);
|
|
p_data = maple_in_buf;
|
|
p_out = maple_out_buf;
|
|
}
|
|
u32 outlen = MapleDevices[bus][port]->RawDma(&p_data[0], inlen * 4 + 4, &p_out[0]);
|
|
xfer_count += outlen;
|
|
#ifdef STRICT_MODE
|
|
if (!check_mdapro(header_2 + outlen - 1))
|
|
{
|
|
// TODO: This isn't correct (with SB_MMSEL=1) since the interrupt
|
|
// should be raised before the memory is written to
|
|
asic_RaiseInterrupt(holly_MAPLE_OVERRUN);
|
|
SB_MDST = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
if (swap_msb)
|
|
{
|
|
u32 *final_out = (u32 *)GetMemPtr(header_2, outlen);
|
|
for (u32 i = 0; i < outlen / 4; i++)
|
|
final_out[i] = SWAP32(p_out[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (port != 5 && command != 1)
|
|
INFO_LOG(MAPLE, "MAPLE: Unknown device bus %d port %d cmd %d reci %d", bus, port, command, reci);
|
|
p_out[0]=0xFFFFFFFF;
|
|
}
|
|
|
|
//goto next command
|
|
addr += 2 * 4 + plen * 4;
|
|
}
|
|
break;
|
|
|
|
case MP_SDCKBOccupy:
|
|
{
|
|
u32 bus = (header_1 >> 16) & 3;
|
|
if (MapleDevices[bus][5])
|
|
occupy = MapleDevices[bus][5]->get_lightgun_pos();
|
|
addr += 1 * 4;
|
|
}
|
|
break;
|
|
|
|
case MP_SDCKBOccupyCancel:
|
|
addr += 1 * 4;
|
|
break;
|
|
|
|
case MP_Reset:
|
|
addr += 1 * 4;
|
|
break;
|
|
|
|
case MP_NOP:
|
|
addr += 1 * 4;
|
|
break;
|
|
|
|
default:
|
|
INFO_LOG(MAPLE, "MAPLE: Unknown maple_op == %d length %d", maple_op, plen * 4);
|
|
addr += 1 * 4;
|
|
}
|
|
}
|
|
|
|
//printf("Maple XFER size %d bytes - %.2f ms\n",xfer_count,xfer_count*100.0f/(2*1024*1024/8));
|
|
if (!occupy)
|
|
sh4_sched_request(maple_schid, std::min((u64)xfer_count * (SH4_MAIN_CLOCK / (2 * 1024 * 1024 / 8)), (u64)SH4_MAIN_CLOCK));
|
|
}
|
|
|
|
static int maple_schd(int tag, int c, int j)
|
|
{
|
|
if (SB_MDEN&1)
|
|
{
|
|
SB_MDST=0;
|
|
asic_RaiseInterrupt(holly_MAPLE_DMA);
|
|
}
|
|
else
|
|
{
|
|
INFO_LOG(MAPLE, "WARNING: MAPLE DMA ABORT");
|
|
SB_MDST=0; //I really wonder what this means, can the DMA be continued ?
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void maple_SB_MDAPRO_Write(u32 addr, u32 data)
|
|
{
|
|
if ((data >> 16) == 0x6155)
|
|
SB_MDAPRO = data & 0x00007f7f;
|
|
}
|
|
|
|
//Init registers :)
|
|
void maple_Init()
|
|
{
|
|
sb_rio_register(SB_MDST_addr,RIO_WF,0,&maple_SB_MDST_Write);
|
|
sb_rio_register(SB_MDEN_addr,RIO_WF,0,&maple_SB_MDEN_Write);
|
|
sb_rio_register(SB_MSHTCL_addr,RIO_WF,0,&maple_SB_MSHTCL_Write);
|
|
sb_rio_register(SB_MDAPRO_addr, RIO_WO_FUNC, nullptr, &maple_SB_MDAPRO_Write);
|
|
#ifdef STRICT_MODE
|
|
sb_rio_register(SB_MDSTAR_addr, RIO_WF, nullptr, &maple_SB_MDSTAR_Write);
|
|
#endif
|
|
|
|
maple_schid=sh4_sched_register(0,&maple_schd);
|
|
}
|
|
|
|
void maple_Reset(bool hard)
|
|
{
|
|
maple_ddt_pending_reset=false;
|
|
SB_MDTSEL = 0x00000000;
|
|
SB_MDEN = 0x00000000;
|
|
SB_MDST = 0x00000000;
|
|
SB_MSYS = 0x3A980000;
|
|
SB_MSHTCL = 0x00000000;
|
|
SB_MDAPRO = 0x00007F00;
|
|
SB_MMSEL = 0x00000001;
|
|
}
|
|
|
|
void maple_Term()
|
|
{
|
|
|
|
}
|
|
|
|
static u64 reconnect_time;
|
|
|
|
void maple_ReconnectDevices()
|
|
{
|
|
mcfg_DestroyDevices();
|
|
reconnect_time = sh4_sched_now64() + SH4_MAIN_CLOCK / 10;
|
|
}
|
|
|
|
static void maple_handle_reconnect()
|
|
{
|
|
if (reconnect_time != 0 && reconnect_time <= sh4_sched_now64())
|
|
{
|
|
reconnect_time = 0;
|
|
mcfg_CreateDevices();
|
|
}
|
|
}
|