632 lines
14 KiB
C++
632 lines
14 KiB
C++
#include "types.h"
|
|
#include "maple_if.h"
|
|
#include "maple_helper.h"
|
|
#include "maple_devs.h"
|
|
#include "maple_cfg.h"
|
|
#include <time.h>
|
|
|
|
const char* maple_sega_controller_name = "Dreamcast Controller";
|
|
const char* maple_sega_vmu_name = "Visual Memory";
|
|
const char* maple_sega_kbd_name = "Emulated Dreamcast Keyboard";
|
|
const char* maple_sega_mouse_name = "Emulated Dreamcast Mouse";
|
|
const char* maple_sega_dreameye_name_1 = "Dreamcast Camera Flash Devic";
|
|
const char* maple_sega_dreameye_name_2 = "Dreamcast Camera Flash LDevic";
|
|
const char* maple_sega_mic_name = "MicDevice for Dreameye";
|
|
|
|
const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
|
|
|
|
|
|
enum MapleFunctionID
|
|
{
|
|
MFID_0_Input = 0x01000000, //DC Controller, Lightgun buttons, arcade stick .. stuff like that
|
|
MFID_1_Storage = 0x02000000, //VMU , VMS
|
|
MFID_2_LCD = 0x04000000, //VMU
|
|
MFID_3_Clock = 0x08000000, //VMU
|
|
MFID_4_Mic = 0x10000000, //DC Mic (, dreameye too ?)
|
|
MFID_5_ARGun = 0x20000000, //Artificial Retina gun ? seems like this one was never developed or smth -- i only remember of lightguns
|
|
MFID_6_Keyboard = 0x40000000, //DC Keyboard
|
|
MFID_7_LightGun = 0x80000000, //DC Lightgun
|
|
MFID_8_Vibration = 0x00010000, //Puru Puru Puur~~~
|
|
MFID_9_Mouse = 0x00020000, //DC Mouse
|
|
MFID_10_StorageExt = 0x00040000, //Storage ? propably never used
|
|
MFID_11_Camera = 0x00080000, //DreamEye
|
|
};
|
|
|
|
enum MapleDeviceCommand
|
|
{
|
|
MDC_DeviceRequest =0x01, //7 words.Note : Initialises device
|
|
MDC_AllStatusReq =0x02, //7 words + device depedant ( seems to be 8 words)
|
|
MDC_DeviceReset =0x03, //0 words
|
|
MDC_DeviceKill =0x04, //0 words
|
|
MDC_DeviceStatus =0x05, //Same as MDC_DeviceRequest ?
|
|
MDC_DeviceAllStatus =0x06, //Same as MDC_AllStatusReq ?
|
|
|
|
//Varius Functions
|
|
MDCF_GetCondition=0x09, //FT
|
|
MDCF_GetMediaInfo=0x0A, //FT,PT,3 pad
|
|
MDCF_BlockRead =0x0B, //FT,PT,Phase,Block #
|
|
MDCF_BlockWrite =0x0C, //FT,PT,Phase,Block #,data ...
|
|
MDCF_GetLastError=0x0D, //FT,PT,Phase,Block #
|
|
MDCF_SetCondition=0x0E, //FT,data ...
|
|
MDCF_MICControl =0x0F, //FT,MIC data ...
|
|
MDCF_ARGunControl=0x10, //FT,AR-Gun data ...
|
|
};
|
|
|
|
enum MapleDeviceRV
|
|
{
|
|
MDRS_DeviceStatus=0x05, //28 words
|
|
MDRS_DeviceStatusAll=0x06, //28 words + device depedant data
|
|
MDRS_DeviceReply=0x07, //0 words
|
|
MDRS_DataTransfer=0x08, //FT,depends on the command
|
|
|
|
MDRE_UnknownFunction=0xFE, //0 words
|
|
MDRE_UnknownCmd=0xFD, //0 words
|
|
MDRE_TransminAgain=0xFC, //0 words
|
|
MDRE_FileError=0xFB, //1 word, bitfield
|
|
MDRE_LCDError=0xFA, //1 word, bitfield
|
|
MDRE_ARGunError=0xF9, //1 word, bitfield
|
|
};
|
|
|
|
|
|
|
|
#define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
|
|
|
|
//fill in the info
|
|
void maple_device::Setup(u32 prt)
|
|
{
|
|
maple_port = prt;
|
|
bus_port = maple_GetPort(prt);
|
|
bus_id = maple_GetBusId(prt);
|
|
logical_port[0] = 'A' + bus_id;
|
|
logical_port[1] = bus_port == 5 ? 'x' : '1' + bus_port;
|
|
logical_port[2] = 0;
|
|
}
|
|
maple_device::~maple_device()
|
|
{
|
|
if (config)
|
|
delete config;
|
|
}
|
|
|
|
/*
|
|
Base class with dma helpers and stuff
|
|
*/
|
|
struct maple_base: maple_device
|
|
{
|
|
u8* dma_buffer_out;
|
|
u32* dma_count_out;
|
|
|
|
u8* dma_buffer_in;
|
|
u32 dma_count_in;
|
|
|
|
void w8(u8 data) { *((u8*)dma_buffer_out)=data;dma_buffer_out+=1;dma_count_out[0]+=1; }
|
|
void w16(u16 data) { *((u16*)dma_buffer_out)=data;dma_buffer_out+=2;dma_count_out[0]+=2; }
|
|
void w32(u32 data) { *(u32*)dma_buffer_out=data;dma_buffer_out+=4;dma_count_out[0]+=4; }
|
|
|
|
void wptr(const void* src,u32 len)
|
|
{
|
|
u8* src8=(u8*)src;
|
|
while(len--)
|
|
w8(*src8++);
|
|
}
|
|
void wstr(const char* str,u32 len)
|
|
{
|
|
size_t ln=strlen(str);
|
|
verify(len>=ln);
|
|
len-=ln;
|
|
while(ln--)
|
|
w8(*str++);
|
|
|
|
while(len--)
|
|
w8(0x20);
|
|
}
|
|
|
|
u8 r8() { u8 rv=*((u8*)dma_buffer_in);dma_buffer_in+=1;dma_count_in-=1; return rv; }
|
|
u16 r16() { u16 rv=*((u16*)dma_buffer_in);dma_buffer_in+=2;dma_count_in-=2; return rv; }
|
|
u32 r32() { u32 rv=*(u32*)dma_buffer_in;dma_buffer_in+=4;dma_count_in-=4; return rv; }
|
|
void rptr(const void* dst,u32 len)
|
|
{
|
|
u8* dst8=(u8*)dst;
|
|
while(len--)
|
|
*dst8++=r8();
|
|
}
|
|
u32 r_count() { return dma_count_in; }
|
|
|
|
virtual u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len)
|
|
{
|
|
dma_buffer_out=(u8*)buffer_out;
|
|
dma_count_out=&buffer_out_len;
|
|
|
|
dma_buffer_in=(u8*)buffer_in;
|
|
dma_count_in=buffer_in_len;
|
|
|
|
return dma(Command);
|
|
}
|
|
virtual u32 dma(u32 cmd)=0;
|
|
};
|
|
|
|
/*
|
|
Sega Dreamcast Controller
|
|
No error checking of anykind, but works just fine
|
|
*/
|
|
struct maple_sega_controller: maple_base
|
|
{
|
|
virtual u32 dma(u32 cmd)
|
|
{
|
|
//printf("maple_sega_controller::dma Called 0x%X;Command %d\n",device_instance->port,Command);
|
|
switch (cmd)
|
|
{
|
|
case MDC_DeviceRequest:
|
|
//caps
|
|
//4
|
|
w32(MFID_0_Input);
|
|
|
|
//struct data
|
|
//3*4
|
|
w32( 0xfe060f00);
|
|
w32( 0);
|
|
w32( 0);
|
|
|
|
//1 area code
|
|
w8(0xFF);
|
|
|
|
//1 direction
|
|
w8(0);
|
|
|
|
//30
|
|
wstr(maple_sega_controller_name,30);
|
|
|
|
//60
|
|
wstr(maple_sega_brand,60);
|
|
|
|
//2
|
|
w16(0x01AE);
|
|
|
|
//2
|
|
w16(0x01F4);
|
|
|
|
return MDRS_DeviceStatus;
|
|
|
|
//controller condition
|
|
case MDCF_GetCondition:
|
|
{
|
|
PlainJoystickState pjs;
|
|
config->GetInput(&pjs);
|
|
//caps
|
|
//4
|
|
w32(MFID_0_Input);
|
|
|
|
//state data
|
|
//2 key code
|
|
w16(pjs.kcode);
|
|
|
|
//trigers
|
|
//1 R
|
|
w8(pjs.trigger[PJTI_R]);
|
|
//1 L
|
|
w8(pjs.trigger[PJTI_L]);
|
|
|
|
//joyx
|
|
//1
|
|
w8(pjs.joy[PJAI_X1]);
|
|
//joyy
|
|
//1
|
|
w8(pjs.joy[PJAI_Y1]);
|
|
|
|
//not used
|
|
//1
|
|
w8(0x80);
|
|
//1
|
|
w8(0x80);
|
|
}
|
|
|
|
return MDRS_DataTransfer;
|
|
|
|
default:
|
|
printf("UNKOWN MAPLE COMMAND %d\n",cmd);
|
|
return MDRE_UnknownFunction;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
Sega Dreamcast Visual Memory Unit
|
|
This is pretty much done (?)
|
|
*/
|
|
#ifdef HAS_VMU
|
|
struct maple_sega_vmu: maple_base
|
|
{
|
|
FILE* file;
|
|
u8 flash_data[128*1024];
|
|
u8 lcd_data[192];
|
|
u8 lcd_data_decoded[48*32];
|
|
|
|
virtual void OnSetup()
|
|
{
|
|
memset(flash_data,0,sizeof(flash_data));
|
|
memset(lcd_data,0,sizeof(lcd_data));
|
|
wchar tempy[512];
|
|
sprintf(tempy,"/vmu_save_%s.bin",logical_port);
|
|
string apath=GetPath(tempy);
|
|
|
|
file=fopen(apath.c_str(),"rb+");
|
|
if (!file)
|
|
{
|
|
printf("Unable to open vmu save file \"%s\", creating new file\n",apath.c_str());
|
|
file=fopen(apath.c_str(),"wb");
|
|
}
|
|
|
|
if (!file)
|
|
{
|
|
printf("Failed to create vmu save file \"%s\"\n",apath.c_str());
|
|
}
|
|
else
|
|
{
|
|
fread(flash_data,1,sizeof(flash_data),file);
|
|
}
|
|
}
|
|
virtual ~maple_sega_vmu()
|
|
{
|
|
if (file) fclose(file);
|
|
}
|
|
virtual u32 dma(u32 cmd)
|
|
{
|
|
//printf("maple_sega_vmu::dma Called for port 0x%X, Command %d\n",device_instance->port,Command);
|
|
switch (cmd)
|
|
{
|
|
case MDC_DeviceRequest:
|
|
{
|
|
//caps
|
|
//4
|
|
w32(MFID_1_Storage | MFID_2_LCD | MFID_3_Clock);
|
|
|
|
//struct data
|
|
//3*4
|
|
w32( 0x403f7e7e); //for clock
|
|
w32( 0x00100500); //for LCD
|
|
w32( 0x00410f00); //for storage
|
|
//1 area code
|
|
w8(0xFF);
|
|
//1 direction
|
|
w8(0);
|
|
//30
|
|
wstr(maple_sega_vmu_name,30);
|
|
|
|
//60
|
|
wstr(maple_sega_brand,60);
|
|
|
|
//2
|
|
w16(0x007c);
|
|
|
|
//2
|
|
w16(0x0082);
|
|
|
|
return MDRS_DeviceStatus;
|
|
}
|
|
|
|
//in[0] is function used
|
|
//out[0] is function used
|
|
case MDCF_GetMediaInfo:
|
|
{
|
|
u32 function=r32();
|
|
switch(function)
|
|
{
|
|
case MFID_1_Storage:
|
|
{
|
|
w32(MFID_1_Storage);
|
|
|
|
//total_size;
|
|
w16(0xff);
|
|
//partition_number;
|
|
w16(0);
|
|
//system_area_block;
|
|
w16(0xFF);
|
|
//fat_area_block;
|
|
w16(0xfe);
|
|
//number_fat_areas_block;
|
|
w16(1);
|
|
//file_info_block;
|
|
w16(0xfd);
|
|
//number_info_blocks;
|
|
w16(0xd);
|
|
//volume_icon;
|
|
w8(0);
|
|
//reserved1;
|
|
w8(0);
|
|
//save_area_block;
|
|
w16(0xc8);
|
|
//number_of_save_blocks;
|
|
w16(0x1f);
|
|
//reserverd0 (something for execution files?)
|
|
w32(0);
|
|
|
|
return MDRS_DataTransfer;//data transfer
|
|
}
|
|
break;
|
|
|
|
case MFID_2_LCD:
|
|
{
|
|
u32 pt=r32();
|
|
if (pt!=0)
|
|
{
|
|
printf("VMU: MDCF_GetMediaInfo -> bad input |%08X|, returning MDRE_UnknownCmd\n",pt);
|
|
return MDRE_UnknownCmd;
|
|
}
|
|
else
|
|
{
|
|
w32(MFID_2_LCD);
|
|
|
|
w8(47); //X dots -1
|
|
w8(31); //Y dots -1
|
|
w8(((1)<<4) | (0)); //1 Color, 0 contrast levels
|
|
w8(0); //Padding
|
|
|
|
return MDRS_DataTransfer;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("VMU: MDCF_GetMediaInfo -> Bad function used |%08X|, returning -2\n",function);
|
|
return MDRE_UnknownFunction;//bad function
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MDCF_BlockRead:
|
|
{
|
|
u32 function=r32();
|
|
switch(function)
|
|
{
|
|
case MFID_1_Storage:
|
|
{
|
|
w32(MFID_1_Storage);
|
|
u32 xo=r32();
|
|
u32 Block = (SWAP32(xo))&0xffff;
|
|
w32(xo);
|
|
|
|
if (Block>255)
|
|
{
|
|
printf("Block read : %d\n",Block);
|
|
printf("BLOCK READ ERROR\n");
|
|
Block&=255;
|
|
}
|
|
wptr(flash_data+Block*512,512);
|
|
|
|
return MDRS_DataTransfer;//data transfer
|
|
}
|
|
break;
|
|
|
|
case MFID_2_LCD:
|
|
{
|
|
w32(MFID_2_LCD);
|
|
w32(r32()); // mnn ?
|
|
wptr(flash_data,192);
|
|
|
|
return MDRS_DataTransfer;//data transfer
|
|
}
|
|
break;
|
|
|
|
case MFID_3_Clock:
|
|
{
|
|
if (r32()!=0)
|
|
{
|
|
printf("VMU: Block read: MFID_3_Clock : invalid params \n");
|
|
return MDRE_TransminAgain; //invalid params
|
|
}
|
|
else
|
|
{
|
|
w32(MFID_3_Clock);
|
|
|
|
time_t now;
|
|
time(&now);
|
|
tm* timenow=localtime(&now);
|
|
|
|
u8* timebuf=dma_buffer_out;
|
|
|
|
w8((timenow->tm_year+1900)%256);
|
|
w8((timenow->tm_year+1900)/256);
|
|
|
|
w8(timenow->tm_mon+1);
|
|
w8(timenow->tm_mday);
|
|
|
|
w8(timenow->tm_hour);
|
|
w8(timenow->tm_min);
|
|
w8(timenow->tm_sec);
|
|
w8(0);
|
|
|
|
printf("VMU: CLOCK Read-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d!\n",timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]);
|
|
|
|
return MDRS_DataTransfer;//transfer reply ...
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("VMU: cmd MDCF_BlockRead -> Bad function |%08X| used, returning -2\n",function);
|
|
return MDRE_UnknownFunction;//bad function
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MDCF_BlockWrite:
|
|
{
|
|
switch(r32())
|
|
{
|
|
case MFID_1_Storage:
|
|
{
|
|
u32 bph=r32();
|
|
u32 Block = (SWAP32(bph))&0xffff;
|
|
u32 Phase = ((SWAP32(bph))>>16)&0xff;
|
|
u32 write_adr=Block*512+Phase*(512/4);
|
|
u32 write_len=r_count();
|
|
rptr(&flash_data[write_adr],write_len);
|
|
|
|
if (file)
|
|
{
|
|
fseek(file,write_adr,SEEK_SET);
|
|
fwrite(&flash_data[write_adr],1,write_len,file);
|
|
fflush(file);
|
|
}
|
|
else
|
|
{
|
|
printf("Failed to save vmu %s data\n",logical_port);
|
|
}
|
|
return MDRS_DeviceReply;//just ko
|
|
}
|
|
break;
|
|
|
|
|
|
case MFID_2_LCD:
|
|
{
|
|
u32 wat=r32();
|
|
rptr(lcd_data,192);
|
|
|
|
u8 white=0xff,black=0x00;
|
|
|
|
for(int y=0;y<32;++y)
|
|
{
|
|
u8* dst=lcd_data_decoded+y*48;
|
|
u8* src=lcd_data+6*y+5;
|
|
for(int x=0;x<6;++x)
|
|
{
|
|
u8 col=*src--;
|
|
for(int l=0;l<8;l++)
|
|
{
|
|
*dst++=col&1?black:white;
|
|
col>>=1;
|
|
}
|
|
}
|
|
}
|
|
config->SetImage(lcd_data_decoded);
|
|
#if 0
|
|
//Update lcd window
|
|
if (!dev->lcd.visible)
|
|
{
|
|
dev->lcd.visible=true;
|
|
ShowWindow(dev->lcd.handle,SHOW_OPENNOACTIVATE);
|
|
}
|
|
|
|
|
|
InvalidateRect(dev->lcd.handle,NULL,FALSE);
|
|
}
|
|
|
|
//Logitech G series stuff start
|
|
#ifdef _HAS_LGLCD_
|
|
{
|
|
lgLcdBitmap160x43x1 bmp;
|
|
bmp.hdr.Format = LGLCD_BMP_FORMAT_160x43x1;
|
|
|
|
const BYTE white=0x00;
|
|
const BYTE black=0xFF;
|
|
|
|
//make it all black...
|
|
memset(bmp.pixels,black,sizeof(bmp.pixels));
|
|
|
|
//decode from the vmu
|
|
for(int y=0;y<32;++y)
|
|
{
|
|
BYTE *dst=bmp.pixels+5816+((-y)*(48+112)); //ugly way to make things look right :p
|
|
BYTE *src=dev->lcd.data+6*y+5;
|
|
for(int x=0;x<48/8;++x)
|
|
{
|
|
BYTE val=*src;
|
|
for(int m=0;m<8;++m)
|
|
{
|
|
if(val&(1<<(m)))
|
|
*dst++=black;
|
|
else
|
|
*dst++=white;
|
|
}
|
|
--src;
|
|
}
|
|
}
|
|
|
|
//Set the damned bits
|
|
res = lgLcdUpdateBitmap(openContext.device, &bmp.hdr, LGLCD_ASYNC_UPDATE(LGLCD_PRIORITY_NORMAL));
|
|
}
|
|
#endif
|
|
//Logitech G series stuff end
|
|
#endif
|
|
return MDRS_DeviceReply;//just ko
|
|
}
|
|
break;
|
|
|
|
case MFID_3_Clock:
|
|
{
|
|
if (r32()!=0 || r_count()!=8)
|
|
return MDRE_TransminAgain; //invalid params ...
|
|
else
|
|
{
|
|
u8 timebuf[8];
|
|
rptr(timebuf,8);
|
|
printf("VMU: CLOCK Write-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d! Nothing set tho ...\n",timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]);
|
|
return MDRS_DeviceReply;//ok !
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
printf("VMU: cmd MDCF_BlockWrite -> Bad function used, returning MDRE_UnknownFunction\n");
|
|
return MDRE_UnknownFunction;//bad function
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MDCF_GetLastError:
|
|
return MDRS_DeviceReply;//just ko
|
|
|
|
case MDCF_SetCondition:
|
|
{
|
|
switch(r32())
|
|
{
|
|
case MFID_3_Clock:
|
|
{
|
|
u32 bp=r32();
|
|
if (bp)
|
|
{
|
|
printf("BEEP : %08X\n",bp);
|
|
}
|
|
return MDRS_DeviceReply;//just ko
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
printf("VMU: cmd MDCF_SetCondition -> Bad function used, returning MDRE_UnknownFunction\n");
|
|
return MDRE_UnknownFunction;//bad function
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
default:
|
|
printf("unknown MAPLE COMMAND %d\n",cmd);
|
|
return MDRE_UnknownCmd;
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
maple_device* maple_Create(MapleDeviceType type)
|
|
{
|
|
maple_device* rv=0;
|
|
switch(type)
|
|
{
|
|
case MDT_SegaController:
|
|
rv=new maple_sega_controller();
|
|
break;
|
|
#ifdef HAS_VMU
|
|
case MDT_SegaVMU:
|
|
rv = new maple_sega_vmu();
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return rv;
|
|
} |