flycast/core/hw/maple/maple_devs.cpp

867 lines
20 KiB
C++
Executable File

#include "types.h"
#include "maple_if.h"
#include "maple_helper.h"
#include "maple_devs.h"
#include "maple_cfg.h"
#include <time.h>
#if _ANDROID
#include <android/log.h>
#include <jni.h>
#else
#define LOGW printf
#define LOGI printf
#endif
#include "deps/zlib/zlib.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.";
#define HAS_VMU
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 ? probably never used
MFID_11_Camera = 0x00080000, //DreamEye
};
enum MapleDeviceCommand
{
MDC_DeviceRequest = 0x01, //7 words.Note : Initialises device
MDC_AllStatusReq = 0x02, //7 words + device dependent ( 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 ?
//Various 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 dependent 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 any kind, 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);
//triggers
//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
u8 vmu_default[] = {
0x78,0x9c,0xed,0xd2,0x31,0x4e,0x02,0x61,0x10,0x06,0xd0,0x8f,0x04,0x28,0x4c,0x2c,
0x28,0x2d,0x0c,0xa5,0x57,0xe0,0x16,0x56,0x16,0x76,0x14,0x1e,0xc4,0x03,0x50,0x98,
0x50,0x40,0x69,0xc1,0x51,0x28,0xbc,0x8e,0x8a,0x0a,0xeb,0xc2,0xcf,0x66,0x13,0x1a,
0x13,0xa9,0x30,0x24,0xe6,0xbd,0xc9,0x57,0xcc,0x4c,0x33,0xc5,0x2c,0xb3,0x48,0x6e,
0x67,0x01,0x00,0x00,0x00,0x00,0x00,0x4e,0xaf,0xdb,0xe4,0x7a,0xd2,0xcf,0x53,0x16,
0x6d,0x46,0x99,0xb6,0xc9,0x78,0x9e,0x3c,0x5f,0x9c,0xfb,0x3c,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x80,0x5f,0xd5,0x45,0xfd,0xef,0xaa,0xca,0x6b,0xde,0xf2,0x9e,0x55,
0x3e,0xf2,0x99,0xaf,0xac,0xb3,0x49,0x95,0xef,0xd4,0xa9,0x9a,0xdd,0xdd,0x0f,0x9d,
0x52,0xca,0xc3,0x91,0x7f,0xb9,0x9a,0x0f,0x6e,0x92,0xfb,0xee,0xa1,0x2f,0x6d,0x76,
0xe9,0x64,0x9b,0xcb,0xf4,0xf2,0x92,0x61,0x33,0x79,0xfc,0xeb,0xb7,0xe5,0x44,0xf6,
0x77,0x19,0x06,0xef,
};
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");
fwrite(flash_data, sizeof(flash_data), 1, file);
fseek(file,0,SEEK_SET);
}
if (!file)
{
printf("Failed to create VMU save file \"%s\"\n",apath.c_str());
}
else
{
fread(flash_data,1,sizeof(flash_data),file);
}
u8 sum = 0;
for (int i=0;i<sizeof(flash_data);i++)
sum|=flash_data[i];
if (sum == 0) {
printf("Initialising empty vmu...\n");
uLongf dec_sz = sizeof(flash_data);
int rv=uncompress(flash_data, &dec_sz, vmu_default, sizeof(vmu_default));
verify(rv == Z_OK);
verify(dec_sz == sizeof(flash_data));
}
}
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);
#ifndef TARGET_PANDORA
push_vmu_screen(lcd_data_decoded);
#endif
#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: command 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: command 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
struct maple_microphone: maple_base
{
u8 micdata[SIZE_OF_MIC_DATA];
virtual void OnSetup()
{
memset(micdata,0,sizeof(micdata));
}
virtual u32 dma(u32 cmd)
{
//printf("maple_microphone::dma Called 0x%X;Command %d\n",this->maple_port,cmd);
//LOGD("maple_microphone::dma Called 0x%X;Command %d\n",this->maple_port,cmd);
switch (cmd)
{
case MDC_DeviceRequest:
LOGI("maple_microphone::dma MDC_DeviceRequest");
//this was copied from the controller case with just the id and name replaced!
//caps
//4
w32(MFID_4_Mic);
//struct data
//3*4
w32( 0xfe060f00);
w32( 0);
w32( 0);
//1 area code
w8(0xFF);
//1 direction
w8(0);
//30
wstr(maple_sega_mic_name,30);
//60
wstr(maple_sega_brand,60);
//2
w16(0x01AE);
//2
w16(0x01F4);
return MDRS_DeviceStatus;
case MDCF_GetCondition:
{
LOGI("maple_microphone::dma MDCF_GetCondition");
//this was copied from the controller case with just the id replaced!
//PlainJoystickState pjs;
//config->GetInput(&pjs);
//caps
//4
w32(MFID_4_Mic);
//state data
//2 key code
//w16(pjs.kcode);
//triggers
//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;
case MDC_DeviceReset:
//uhhh do nothing?
LOGI("maple_microphone::dma MDC_DeviceReset");
return MDRS_DeviceReply;
case MDCF_MICControl:
{
//LOGD("maple_microphone::dma handling MDCF_MICControl %d\n",cmd);
//MONEY
u32 function=r32();
//LOGD("maple_microphone::dma MDCF_MICControl function (1st word) %#010x\n", function);
//LOGD("maple_microphone::dma MDCF_MICControl words: %d\n", dma_count_in);
switch(function)
{
case MFID_4_Mic:
{
//MAGIC HERE
//http://dcemulation.org/phpBB/viewtopic.php?f=34&t=69600
// <3 <3 BlueCrab <3 <3
/*
2nd word What it does:
0x0000??03 Sets the amplifier gain, ?? can be from 00 to 1F
0x0f = default
0x00008002 Enables recording
0x00000001 Returns sampled data while recording is enabled
While not enabled, returns status of the mic.
0x00000002 Disables recording
*
*/
u32 secondword=r32();
//LOGD("maple_microphone::dma MDCF_MICControl subcommand (2nd word) %#010x\n", subcommand);
u32 subcommand = secondword & 0xFF; //just get last byte for now, deal with params later
//LOGD("maple_microphone::dma MDCF_MICControl (3rd word) %#010x\n", r32());
//LOGD("maple_microphone::dma MDCF_MICControl (4th word) %#010x\n", r32());
switch(subcommand)
{
case 0x01:
{
//LOGI("maple_microphone::dma MDCF_MICControl someone wants some data! (2nd word) %#010x\n", secondword);
w32(MFID_4_Mic);
//from what i can tell this is up to spec but results in transmit again
//w32(secondword);
//32 bit header
w8(0x04);//status (just the bit for recording)
w8(0x0f);//gain (default)
w8(0);//exp ?
#ifndef TARGET_PANDORA
if(get_mic_data(micdata)){
w8(240);//ct (240 samples)
wptr(micdata, SIZE_OF_MIC_DATA);
}else
#endif
{
w8(0);
}
return MDRS_DataTransfer;
}
case 0x02:
LOGI("maple_microphone::dma MDCF_MICControl toggle recording %#010x\n",secondword);
return MDRS_DeviceReply;
case 0x03:
LOGI("maple_microphone::dma MDCF_MICControl set gain %#010x\n",secondword);
return MDRS_DeviceReply;
case MDRE_TransminAgain:
LOGW("maple_microphone::dma MDCF_MICControl MDRE_TransminAgain");
//apparently this doesnt matter
//wptr(micdata, SIZE_OF_MIC_DATA);
return MDRS_DeviceReply;//MDRS_DataTransfer;
default:
LOGW("maple_microphone::dma UNHANDLED secondword %#010x\n",secondword);
break;
}
}
default:
LOGW("maple_microphone::dma UNHANDLED function %#010x\n",function);
break;
}
}
default:
LOGW("maple_microphone::dma UNHANDLED MAPLE COMMAND %d\n",cmd);
return MDRE_UnknownFunction;
}
}
};
maple_device* maple_Create(MapleDeviceType type)
{
maple_device* rv=0;
switch(type)
{
case MDT_SegaController:
rv=new maple_sega_controller();
break;
case MDT_Microphone:
rv=new maple_microphone();
break;
#ifdef HAS_VMU
case MDT_SegaVMU:
rv = new maple_sega_vmu();
break;
#endif
default:
return 0;
}
return rv;
}