#include #include "types.h" #include "maple_if.h" #include "maple_helper.h" #include "maple_devs.h" #include "maple_cfg.h" #include "cfg/cfg.h" #include "hw/naomi/naomi.h" #include "hw/naomi/naomi_cart.h" #include "hw/pvr/spg.h" #include "input/gamepad.h" #include "input/gamepad_device.h" #include #include #include #include #define LOGJVS(...) DEBUG_LOG(JVS, __VA_ARGS__) 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_purupuru_name = "Puru Puru Pack"; const char* maple_sega_lightgun_name = "Dreamcast Gun"; const char* maple_sega_twinstick_name = "Twin Stick"; const char* maple_ascii_stick_name = "ASCII STICK"; 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 ? 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 ... MDC_JVSUploadFirmware = 0x80, // JVS bridge firmware MDC_JVSGetId = 0x82, MDC_JVSCommand = 0x86, // JVS I/O }; enum MapleDeviceRV { MDRS_JVSNone = 0x00, // No reply, used for multiple JVS I/O boards 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_TransmitAgain = 0xFC, //0 words MDRE_FileError = 0xFB, //1 word, bitfield MDRE_LCDError = 0xFA, //1 word, bitfield MDRE_ARGunError = 0xF9, //1 word, bitfield MDRS_JVSReply = 0x87, // JVS I/O }; #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(void* dst, u32 len) { u8* dst8=(u8*)dst; while(len--) *dst8++=r8(); } u32 r_count() { return dma_count_in; } 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; virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) { u32 command=buffer_in[0] &0xFF; //Recipient address u32 reci = (buffer_in[0] >> 8) & 0xFF; //Sender address u32 send = (buffer_in[0] >> 16) & 0xFF; u32 outlen = 0; u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen); if (reci & 0x20) reci |= maple_GetAttachedDevices(maple_GetBusId(reci)); verify(u8(outlen/4)*4==outlen); buffer_out[0] = (resp <<0 ) | (send << 8) | (reci << 16) | ((outlen / 4) << 24); return outlen + 4; } }; /* Sega Dreamcast Controller No error checking of any kind, but works just fine */ struct maple_sega_controller: maple_base { virtual u32 get_capabilities() { // byte 0: 0 0 0 0 0 0 0 0 // byte 1: 0 0 a5 a4 a3 a2 a1 a0 // byte 2: R2 L2 D2 U2 D X Y Z // byte 3: R L D U St A B C return 0xfe060f00; // 4 analog axes (0-3) X Y A B Start U D L R } virtual u32 transform_kcode(u32 kcode) { return kcode | 0xF901; // mask off DPad2, C, D and Z; } virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) { if (index == 2 || index == 3) { // Limit the magnitude of the analog axes to 128 s8 xaxis = pjs.joy[PJAI_X1] - 128; s8 yaxis = pjs.joy[PJAI_Y1] - 128; limit_joystick_magnitude<128>(xaxis, yaxis); if (index == 2) return xaxis + 128; else return yaxis + 128; } else if (index == 0) return pjs.trigger[PJTI_R]; // Right trigger else if (index == 1) return pjs.trigger[PJTI_L]; // Left trigger else return 0x80; // unused } virtual MapleDeviceType get_device_type() { return MDT_SegaController; } virtual const char *get_device_name() { return maple_sega_controller_name; } virtual const char *get_device_brand() { return maple_sega_brand; } virtual u32 dma(u32 cmd) { //printf("maple_sega_controller::dma Called 0x%X;Command %d\n", bus_id, cmd); switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_0_Input); //struct data //3*4 w32(get_capabilities()); w32(0); w32(0); //1 area code w8(0xFF); //1 direction w8(0); //30 wstr(get_device_name(), 30); //60 wstr(get_device_brand(), 60); //2 w16(0x01AE); // 43 mA //2 w16(0x01F4); // 50 mA 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(transform_kcode(pjs.kcode)); //triggers //1 R w8(get_analog_axis(0, pjs)); //1 L w8(get_analog_axis(1, pjs)); //joyx //1 w8(get_analog_axis(2, pjs)); //joyy //1 w8(get_analog_axis(3, pjs)); //not used on dreamcast //1 w8(get_analog_axis(4, pjs)); //1 w8(get_analog_axis(5, pjs)); } return MDRS_DataTransfer; default: //printf("maple_sega_controller UNKOWN MAPLE COMMAND %d\n",cmd); return MDRE_UnknownCmd; } } }; struct maple_atomiswave_controller: maple_sega_controller { virtual u32 get_capabilities() override { // byte 0: 0 0 0 0 0 0 0 0 // byte 1: 0 0 a5 a4 a3 a2 a1 a0 // byte 2: R2 L2 D2 U2 D X Y Z // byte 3: R L D U St A B C return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R } virtual u32 transform_kcode(u32 kcode) override { return kcode | AWAVE_TRIGGER_KEY; } virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) override { if (index < 2 || index > 5) return 0x80; index -= 2; if (NaomiGameInputs != NULL && NaomiGameInputs->axes[index].name != NULL && NaomiGameInputs->axes[index].inverted) return pjs.joy[index] == 0 ? 0xff : 0x100 - pjs.joy[index]; else return pjs.joy[index]; } }; /* Sega Twin Stick Controller */ struct maple_sega_twinstick: maple_sega_controller { virtual u32 get_capabilities() override { // byte 0: 0 0 0 0 0 0 0 0 // byte 1: 0 0 a5 a4 a3 a2 a1 a0 // byte 2: R2 L2 D2 U2 D X Y Z // byte 3: R L D U St A B C return 0xfefe0000; // no analog axes, X Y A B D Start U/D/L/R U2/D2/L2/R2 } virtual u32 transform_kcode(u32 kcode) override { return kcode | 0x0101; } virtual MapleDeviceType get_device_type() override { return MDT_TwinStick; } virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) override { return 0x80; } virtual const char *get_device_name() override { return maple_sega_twinstick_name; } }; /* Ascii Stick (Arcade/FT Stick) */ struct maple_ascii_stick: maple_sega_controller { virtual u32 get_capabilities() override { // byte 0: 0 0 0 0 0 0 0 0 // byte 1: 0 0 a5 a4 a3 a2 a1 a0 // byte 2: R2 L2 D2 U2 D X Y Z // byte 3: R L D U St A B C return 0xff070000; // no analog axes, X Y Z A B C Start U/D/L/R } virtual u32 transform_kcode(u32 kcode) override { return kcode | 0xF800; } virtual MapleDeviceType get_device_type() override { return MDT_AsciiStick; } virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) override { return 0x80; } virtual const char *get_device_name() override { return maple_ascii_stick_name; } }; /* Sega Dreamcast Visual Memory Unit This is pretty much done (?) */ 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 MapleDeviceType get_device_type() { return MDT_SegaVMU; } // creates an empty VMU bool init_emptyvmu() { INFO_LOG(MAPLE, "Initialising empty VMU..."); 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)); return (rv == Z_OK && dec_sz == sizeof(flash_data)); } virtual bool maple_serialize(void **data, unsigned int *total_size) { REICAST_SA(flash_data,128*1024); REICAST_SA(lcd_data,192); REICAST_SA(lcd_data_decoded,48*32); return true ; } virtual bool maple_unserialize(void **data, unsigned int *total_size) { REICAST_USA(flash_data,128*1024); REICAST_USA(lcd_data,192); REICAST_USA(lcd_data_decoded,48*32); return true ; } virtual void OnSetup() { memset(flash_data, 0, sizeof(flash_data)); memset(lcd_data, 0, sizeof(lcd_data)); char tempy[512]; sprintf(tempy, "/vmu_save_%s.bin", logical_port); string apath = get_writable_data_path(tempy); file = fopen(apath.c_str(), "rb+"); if (!file) { INFO_LOG(MAPLE, "Unable to open VMU save file \"%s\", creating new file", apath.c_str()); file = fopen(apath.c_str(), "wb"); if (file) { if (!init_emptyvmu()) INFO_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS"); fwrite(flash_data, sizeof(flash_data), 1, file); fseek(file, 0, SEEK_SET); } else { INFO_LOG(MAPLE, "Unable to create VMU!"); } } if (!file) { INFO_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str()); } else { fread(flash_data, 1, sizeof(flash_data), file); } u8 sum = 0; for (u32 i = 0; i < sizeof(flash_data); i++) sum |= flash_data[i]; if (sum == 0) { // This means the existing VMU file is completely empty and needs to be recreated if (init_emptyvmu()) { if (!file) file = fopen(apath.c_str(), "wb"); if (file) { fwrite(flash_data, sizeof(flash_data), 1, file); fseek(file, 0, SEEK_SET); } else { INFO_LOG(MAPLE, "Unable to create VMU!"); } } else { INFO_LOG(MAPLE, "Failed to initialize an empty VMU, you should reformat it using the BIOS"); } } } virtual ~maple_sega_vmu() { if (file) fclose(file); } virtual u32 dma(u32 cmd) { //printf("maple_sega_vmu::dma Called for port %d:%d, Command %d\n", bus_id, bus_port, cmd); 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); // 12.4 mA //2 w16(0x0082); // 13 mA 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); // Get data from the vmu system area (block 0xFF) wptr(flash_data + 0xFF * 512 + 0x40, 24); return MDRS_DataTransfer;//data transfer } break; case MFID_2_LCD: { u32 pt=r32(); if (pt!=0) { INFO_LOG(MAPLE, "VMU: MDCF_GetMediaInfo -> bad input |%08X|, returning MDRE_UnknownCmd", 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(2); //Padding return MDRS_DataTransfer; } } break; default: INFO_LOG(MAPLE, "VMU: MDCF_GetMediaInfo -> Bad function used |%08X|, returning -2", 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) { DEBUG_LOG(MAPLE, "Block read : %d", Block); DEBUG_LOG(MAPLE, "BLOCK READ ERROR"); 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) { INFO_LOG(MAPLE, "VMU: Block read: MFID_3_Clock : invalid params"); return MDRE_TransmitAgain; //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); DEBUG_LOG(MAPLE, "VMU: CLOCK Read-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d!", timebuf[0] + timebuf[1] * 256, timebuf[2], timebuf[3], timebuf[4], timebuf[5], timebuf[6]); return MDRS_DataTransfer;//transfer reply ... } } break; default: INFO_LOG(MAPLE, "VMU: cmd MDCF_BlockRead -> Bad function |%08X| used, returning -2", 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(); if (write_adr + write_len > sizeof(flash_data)) return MDRE_TransmitAgain; //invalid params 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 { INFO_LOG(MAPLE, "Failed to save VMU %s data", logical_port); } return MDRS_DeviceReply;//just ko } break; case MFID_2_LCD: { 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); push_vmu_screen(bus_id, bus_port, lcd_data_decoded); return MDRS_DeviceReply;//just ko } break; case MFID_3_Clock: { if (r32()!=0 || r_count()!=8) return MDRE_TransmitAgain; //invalid params ... else { u8 timebuf[8]; rptr(timebuf,8); DEBUG_LOG(MAPLE, "VMU: CLOCK Write-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d! Nothing set tho ...", timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]); return MDRS_DeviceReply;//ok ! } } break; default: { INFO_LOG(MAPLE, "VMU: command MDCF_BlockWrite -> Bad function used, returning MDRE_UnknownFunction"); 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) { INFO_LOG(MAPLE, "BEEP : %08X", bp); } return MDRS_DeviceReply;//just ko } break; default: { INFO_LOG(MAPLE, "VMU: command MDCF_SetCondition -> Bad function used, returning MDRE_UnknownFunction"); return MDRE_UnknownFunction;//bad function } break; } } default: DEBUG_LOG(MAPLE, "Unknown MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } }; struct maple_microphone: maple_base { u8 micdata[SIZE_OF_MIC_DATA]; virtual MapleDeviceType get_device_type() { return MDT_Microphone; } virtual bool maple_serialize(void **data, unsigned int *total_size) { REICAST_SA(micdata,SIZE_OF_MIC_DATA); return true ; } virtual bool maple_unserialize(void **data, unsigned int *total_size) { REICAST_USA(micdata,SIZE_OF_MIC_DATA); return true ; } 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: DEBUG_LOG(MAPLE, "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); // 43 mA //2 w16(0x01F4); // 50 mA return MDRS_DeviceStatus; case MDCF_GetCondition: { DEBUG_LOG(MAPLE, "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? DEBUG_LOG(MAPLE, "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: DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl toggle recording %#010x", secondword); return MDRS_DeviceReply; case 0x03: DEBUG_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl set gain %#010x", secondword); return MDRS_DeviceReply; case MDRE_TransmitAgain: WARN_LOG(MAPLE, "maple_microphone::dma MDCF_MICControl MDRE_TransmitAgain"); //apparently this doesnt matter //wptr(micdata, SIZE_OF_MIC_DATA); return MDRS_DeviceReply;//MDRS_DataTransfer; default: INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED secondword %#010x", secondword); return MDRE_UnknownFunction; } } default: INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED function %#010x", function); return MDRE_UnknownFunction; } } default: INFO_LOG(MAPLE, "maple_microphone::dma UNHANDLED MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } }; struct maple_sega_purupuru : maple_base { u16 AST = 19, AST_ms = 5000; u32 VIBSET; virtual MapleDeviceType get_device_type() { return MDT_PurupuruPack; } virtual bool maple_serialize(void **data, unsigned int *total_size) { REICAST_S(AST); REICAST_S(AST_ms); REICAST_S(VIBSET); return true ; } virtual bool maple_unserialize(void **data, unsigned int *total_size) { REICAST_US(AST); REICAST_US(AST_ms); REICAST_US(VIBSET); return true ; } virtual u32 dma(u32 cmd) { switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_8_Vibration); //struct data //3*4 w32(0x00000101); w32(0); w32(0); //1 area code w8(0xFF); //1 direction w8(0); //30 wstr(maple_sega_purupuru_name, 30); //60 wstr(maple_sega_brand, 60); //2 w16(0x00C8); // 20 mA //2 w16(0x0640); // 160 mA return MDRS_DeviceStatus; //get last vibration case MDCF_GetCondition: w32(MFID_8_Vibration); w32(VIBSET); return MDRS_DataTransfer; case MDCF_GetMediaInfo: w32(MFID_8_Vibration); // PuruPuru vib specs w32(0x3B07E010); return MDRS_DataTransfer; case MDCF_BlockRead: w32(MFID_8_Vibration); w32(0); w16(2); w16(AST); return MDRS_DataTransfer; case MDCF_BlockWrite: //Auto-stop time AST = dma_buffer_in[10]; AST_ms = AST * 250 + 250; return MDRS_DeviceReply; case MDCF_SetCondition: VIBSET = *(u32*)&dma_buffer_in[4]; { //Do the rumble thing! u8 POW_POS = (VIBSET >> 8) & 0x7; u8 POW_NEG = (VIBSET >> 12) & 0x7; u8 FREQ = (VIBSET >> 16) & 0xFF; s16 INC = (VIBSET >> 24) & 0xFF; if (VIBSET & 0x8000) // INH INC = -INC; else if (!(VIBSET & 0x0800)) // EXH INC = 0; bool CNT = VIBSET & 1; float power = min((POW_POS + POW_NEG) / 7.0, 1.0); u32 duration_ms; if (FREQ > 0 && (!CNT || INC)) duration_ms = min((int)(1000 * (INC ? abs(INC) * max(POW_POS, POW_NEG) : 1) / FREQ), (int)AST_ms); else duration_ms = AST_ms; float inclination; if (INC == 0 || power == 0) inclination = 0.0; else inclination = FREQ / (1000.0 * INC * max(POW_POS, POW_NEG)); config->SetVibration(power, inclination, duration_ms); } return MDRS_DeviceReply; default: INFO_LOG(MAPLE, "UNKOWN MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } }; u8 kb_shift; // shift keys pressed (bitmask) u8 kb_led; // leds currently lit u8 kb_key[6]={0}; // normal keys pressed struct maple_keyboard : maple_base { virtual MapleDeviceType get_device_type() { return MDT_Keyboard; } virtual u32 dma(u32 cmd) { switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_6_Keyboard); //struct data //3*4 w32(0x80000502); // US, 104 keys w32(0); w32(0); //1 area code w8(0xFF); //1 direction w8(0); // Product name (30) for (u32 i = 0; i < 30; i++) { w8((u8)maple_sega_kbd_name[i]); } // License (60) for (u32 i = 0; i < 60; i++) { w8((u8)maple_sega_brand[i]); } // Low-consumption standby current (2) w16(0x01AE); // 43 mA // Maximum current consumption (2) w16(0x01F5); // 50.1 mA return MDRS_DeviceStatus; case MDCF_GetCondition: w32(MFID_6_Keyboard); //struct data //int8 shift ; shift keys pressed (bitmask) //1 w8(kb_shift); //int8 led ; leds currently lit //1 w8(kb_led); //int8 key[6] ; normal keys pressed //6 for (int i = 0; i < 6; i++) { w8(kb_key[i]); } return MDRS_DataTransfer; default: INFO_LOG(MAPLE, "Keyboard: unknown MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } }; // Mouse buttons // bit 0: Button C // bit 1: Right button (B) // bit 2: Left button (A) // bit 3: Wheel button u32 mo_buttons = 0xFFFFFFFF; // Relative mouse coordinates [-512:511] f32 mo_x_delta; f32 mo_y_delta; f32 mo_wheel_delta; // Absolute mouse coordinates // Range [0:639] [0:479] // but may be outside this range if the pointer is offscreen or outside the 4:3 window. s32 mo_x_abs; s32 mo_y_abs; struct maple_mouse : maple_base { virtual MapleDeviceType get_device_type() { return MDT_Mouse; } static u16 mo_cvt(f32 delta) { delta += 0x200; if (delta <= 0) delta = 0; else if (delta > 0x3FF) delta = 0x3FF; return (u16)lroundf(delta); } virtual u32 dma(u32 cmd) { switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_9_Mouse); //struct data //3*4 w32(0x00070E00); // Mouse, 3 buttons, 3 axes w32(0); w32(0); //1 area code w8(0xFF); //1 direction w8(0); // Product name (30) for (u32 i = 0; i < 30; i++) { w8((u8)maple_sega_mouse_name[i]); } // License (60) for (u32 i = 0; i < 60; i++) { w8((u8)maple_sega_brand[i]); } // Low-consumption standby current (2) w16(0x0069); // 10.5 mA // Maximum current consumption (2) w16(0x0120); // 28.8 mA return MDRS_DeviceStatus; case MDCF_GetCondition: w32(MFID_9_Mouse); //struct data //int32 buttons ; digital buttons bitfield (little endian) w32(mo_buttons); //int16 axis1 ; horizontal movement (0-$3FF) (little endian) w16(mo_cvt(mo_x_delta)); //int16 axis2 ; vertical movement (0-$3FF) (little endian) w16(mo_cvt(mo_y_delta)); //int16 axis3 ; mouse wheel movement (0-$3FF) (little endian) w16(mo_cvt(mo_wheel_delta)); //int16 axis4 ; ? movement (0-$3FF) (little endian) w16(mo_cvt(0)); //int16 axis5 ; ? movement (0-$3FF) (little endian) w16(mo_cvt(0)); //int16 axis6 ; ? movement (0-$3FF) (little endian) w16(mo_cvt(0)); //int16 axis7 ; ? movement (0-$3FF) (little endian) w16(mo_cvt(0)); //int16 axis8 ; ? movement (0-$3FF) (little endian) w16(mo_cvt(0)); mo_x_delta=0; mo_y_delta=0; mo_wheel_delta = 0; return MDRS_DataTransfer; default: INFO_LOG(MAPLE, "Mouse: unknown MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } }; struct maple_lightgun : maple_base { virtual u32 transform_kcode(u32 kcode) { return kcode | 0xFF01; } virtual MapleDeviceType get_device_type() { return MDT_LightGun; } virtual u32 dma(u32 cmd) { switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_7_LightGun | MFID_0_Input); //struct data //3*4 w32(0); // Light gun w32(0xFE000000); // Controller w32(0); //1 area code w8(0x01); // FF: Worldwide, 01: North America //1 direction w8(0); // Product name (30) for (u32 i = 0; i < 30; i++) { w8((u8)maple_sega_lightgun_name[i]); } // License (60) for (u32 i = 0; i < 60; i++) { w8((u8)maple_sega_brand[i]); } // Low-consumption standby current (2) w16(0x0069); // 10.5 mA // Maximum current consumption (2) w16(0x0120); // 28.8 mA return MDRS_DeviceStatus; case MDCF_GetCondition: { PlainJoystickState pjs; config->GetInput(&pjs); //caps //4 w32(MFID_0_Input); //state data //2 key code w16(transform_kcode(pjs.kcode)); //not used //2 w16(0xFFFF); //not used //4 w32(0x80808080); } return MDRS_DataTransfer; default: INFO_LOG(MAPLE, "Light gun: unknown MAPLE COMMAND %d", cmd); return MDRE_UnknownCmd; } } virtual void get_lightgun_pos() { read_lightgun_position(mo_x_abs, mo_y_abs); // TODO If NAOMI, set some bits at 0x600284 http://64darksoft.blogspot.com/2013/10/atomiswage-to-naomi-update-4.html } }; struct atomiswave_lightgun : maple_lightgun { virtual u32 transform_kcode(u32 kcode) override { return (kcode & AWAVE_TRIGGER_KEY) == 0 ? ~AWAVE_BTN0_KEY : ~0; } }; char EEPROM[0x100]; bool EEPROM_loaded = false; u32 naomi_button_mapping[] = { NAOMI_SERVICE_KEY, // DC_BTN_C NAOMI_BTN1_KEY, // DC_BTN_B NAOMI_BTN0_KEY, // DC_BTN_A NAOMI_START_KEY, // DC_BTN_START NAOMI_UP_KEY, // DC_DPAD_UP NAOMI_DOWN_KEY, // DC_DPAD_DOWN NAOMI_LEFT_KEY, // DC_DPAD_LEFT NAOMI_RIGHT_KEY, // DC_DPAD_RIGHT NAOMI_TEST_KEY, // DC_BTN_Z NAOMI_BTN3_KEY, // DC_BTN_Y NAOMI_BTN2_KEY, // DC_BTN_X NAOMI_COIN_KEY, // DC_BTN_D NAOMI_BTN4_KEY, // DC_DPAD2_UP NAOMI_BTN5_KEY, // DC_DPAD2_DOWN NAOMI_BTN6_KEY, // DC_DPAD2_LEFT NAOMI_BTN7_KEY, // DC_DPAD2_RIGHT NAOMI_BTN8_KEY, }; /* * Sega JVS I/O board */ static bool old_coin_chute[4]; static int coin_count[4]; struct maple_naomi_jamma; class jvs_io_board { public: jvs_io_board(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) { this->node_id = node_id; this->parent = parent; this->first_player = first_player; } virtual ~jvs_io_board() = default; u32 handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_out); bool maple_serialize(void **data, unsigned int *total_size); bool maple_unserialize(void **data, unsigned int *total_size); bool lightgun_as_analog = false; protected: virtual const char *get_id() = 0; virtual u16 read_analog_axis(int player_num, int player_axis, bool inverted); virtual u32 read_digital_in(int player_num) { if (player_num >= (int)ARRAY_SIZE(kcode)) return 0; u32 buttons = 0; u32 keycode = ~kcode[player_num]; for (int i = 0; i < 16; i++) { if ((keycode & (1 << i)) != 0) buttons |= naomi_button_mapping[i]; } return buttons; } u32 player_count = 0; u32 digital_in_count = 0; u32 coin_input_count = 0; u32 analog_count = 0; u32 encoder_count = 0; u32 light_gun_count = 0; u32 output_count = 0; bool init_in_progress = false; private: u8 node_id; maple_naomi_jamma *parent; u8 first_player; }; // Most common JVS board class jvs_837_13551 : public jvs_io_board { public: jvs_837_13551(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 2; digital_in_count = 13; coin_input_count = 2; analog_count = 8; output_count = 6; } protected: virtual const char *get_id() override { return "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; } }; class jvs_837_13551_noanalog : public jvs_837_13551 { public: jvs_837_13551_noanalog(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_837_13551(node_id, parent, first_player) { analog_count = 0; } }; // Same in 4-player mode class jvs_837_13551_4P : public jvs_837_13551 { public: jvs_837_13551_4P(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_837_13551(node_id, parent, first_player) { player_count = 4; digital_in_count = 12; coin_input_count = 4; analog_count = 0; } }; // Rotary encoders 2nd board // Virtua Golf, Outtrigger, Shootout Pool class jvs_837_13938 : public jvs_io_board { public: jvs_837_13938(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 1; digital_in_count = 9; encoder_count = 4; output_count = 8; } protected: virtual const char *get_id() override { return "SEGA ENTERPRISES,LTD.;837-13938 ENCORDER BD ;Ver0.01;99/08"; } }; // Sega Marine Fishing class jvs_837_13844 : public jvs_io_board { public: jvs_837_13844(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 2; coin_input_count = 2; digital_in_count = 12; analog_count = 8; output_count = 16; } protected: virtual const char *get_id() override { return "SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;Ver1.00;99/07"; } }; class jvs_837_13844_encoders : public jvs_837_13844 { public: jvs_837_13844_encoders(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_837_13844(node_id, parent, first_player) { digital_in_count = 8; encoder_count = 4; } }; class jvs_837_13844_touch : public jvs_837_13844 { public: jvs_837_13844_touch(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_837_13844(node_id, parent, first_player) { light_gun_count = 1; } }; // Ninja assault class jvs_namco_jyu : public jvs_io_board { public: jvs_namco_jyu(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 2; coin_input_count = 2; digital_in_count = 12; output_count = 16; light_gun_count = 2; } protected: virtual const char *get_id() override { return "namco ltd.;JYU-PCB;Ver1.00;JPN,2Coins 2Guns"; } }; // Mazan class jvs_namco_fcb : public jvs_io_board { public: jvs_namco_fcb(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 1; coin_input_count = 2; digital_in_count = 16; output_count = 6; light_gun_count = 1; analog_count = 7; encoder_count = 2; } protected: virtual const char *get_id() override { return "namco ltd.;FCB;Ver1.0;JPN,Touch Panel & Multipurpose"; } virtual u16 read_analog_axis(int player_num, int player_axis, bool inverted) override { if (init_in_progress) return 0; if (mo_x_abs < 0 || mo_x_abs > 639 || mo_y_abs < 0 || mo_y_abs > 479) return 0; else return 0x8000; } }; // Gun Survivor class jvs_namco_fca : public jvs_io_board { public: jvs_namco_fca(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 1; coin_input_count = 1; // 2 makes bios crash digital_in_count = 16; output_count = 6; analog_count = 7; encoder_count = 2; } protected: virtual const char *get_id() override { return "namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"; } }; // World Kicks class jvs_namco_v226 : public jvs_io_board { public: jvs_namco_v226(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 1; digital_in_count = 16; coin_input_count = 1; analog_count = 12; output_count = 6; } protected: virtual const char *get_id() override { return "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; } virtual u32 read_digital_in(int player_num) override { u32 keycode1 = jvs_io_board::read_digital_in(0); u32 keycode2 = jvs_io_board::read_digital_in(1); u32 keycode3 = jvs_io_board::read_digital_in(2); u32 keycode4 = jvs_io_board::read_digital_in(3); // main button return ((keycode1 & NAOMI_BTN0_KEY) << 6) // start | ((keycode2 & NAOMI_BTN0_KEY) << 2) // left | ((keycode3 & NAOMI_BTN0_KEY) << 1) // right | ((keycode4 & NAOMI_BTN0_KEY) >> 1) // btn1 // soft kick // | ((~keycode1 & NAOMI_BTN1_KEY) >> 1) // btn2 // | ((~keycode2 & NAOMI_BTN1_KEY) >> 3) // btn4 // | ((~keycode3 & NAOMI_BTN1_KEY) >> 5) // btn6 // | ((~keycode4 & NAOMI_BTN1_KEY) >> 7) // test? // // hard kick // | ((~keycode1 & NAOMI_BTN2_KEY) >> 1) // btn3 // | ((~keycode2 & NAOMI_BTN2_KEY) >> 3) // btn5 // | ((~keycode3 & NAOMI_BTN2_KEY) >> 5) // btn7 // | ((~keycode4 & NAOMI_BTN2_KEY) >> 7) // coin? // enter | ((keycode1 & NAOMI_BTN3_KEY) << 3) // btn0 // service menu | (keycode1 & (NAOMI_TEST_KEY | NAOMI_SERVICE_KEY | NAOMI_UP_KEY | NAOMI_DOWN_KEY)); } u16 read_joystick_x(int joy_num) { s8 axis_x = joyx[joy_num]; axis_y = joyy[joy_num]; limit_joystick_magnitude<64>(axis_x, axis_y); return std::min(0xff, 0x80 - axis_x) << 8; } u16 read_joystick_y(int joy_num) { return std::min(0xff, 0x80 - axis_y) << 8; } virtual u16 read_analog_axis(int player_num, int player_axis, bool inverted) override { switch (player_axis) { case 0: return read_joystick_x(0); case 1: return read_joystick_y(0); case 2: return read_joystick_x(1); case 3: return read_joystick_y(1); case 4: return read_joystick_x(2); case 5: return read_joystick_y(2); case 6: return read_joystick_x(3); case 7: return read_joystick_y(3); case 8: return rt[0] << 8; case 9: return rt[1] << 8; case 10: return rt[2] << 8; case 11: return rt[3] << 8; default: return 0x8000; } } private: s8 axis_y = 0; }; // World Kicks PCB class jvs_namco_v226_pcb : public jvs_io_board { public: jvs_namco_v226_pcb(u8 node_id, maple_naomi_jamma *parent, int first_player = 0) : jvs_io_board(node_id, parent, first_player) { player_count = 2; digital_in_count = 16; coin_input_count = 1; analog_count = 12; output_count = 6; } protected: virtual const char *get_id() override { return "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; } virtual u32 read_digital_in(int player_num) override { u32 keycode = jvs_io_board::read_digital_in(player_num); u8 trigger = rt[player_num] >> 2; // Ball button return ((trigger & 0x20) << 3) | ((trigger & 0x10) << 5) | ((trigger & 0x08) << 7) | ((trigger & 0x04) << 9) | ((trigger & 0x02) << 11) | ((trigger & 0x01) << 13) // other buttons | (keycode & (NAOMI_SERVICE_KEY | NAOMI_TEST_KEY | NAOMI_START_KEY)) | ((keycode & NAOMI_BTN0_KEY) >> 4); // remap button4 to button0 (change button) } u16 read_joystick_x(int joy_num) { s8 axis_x = joyx[joy_num]; axis_y = joyy[joy_num]; limit_joystick_magnitude<48>(axis_x, axis_y); return (axis_x + 128) << 8; } u16 read_joystick_y(int joy_num) { return std::min(0xff, 0x80 - axis_y) << 8; } virtual u16 read_analog_axis(int player_num, int player_axis, bool inverted) override { switch (player_axis) { case 0: return read_joystick_x(0); case 1: return read_joystick_y(0); case 4: return read_joystick_x(1); case 5: return read_joystick_y(1); default: return 0x8000; } } private: s8 axis_y = 0; }; struct maple_naomi_jamma : maple_sega_controller { const u8 ALL_NODES = 0xff; std::vector> io_boards; bool crazy_mode = false; u8 jvs_repeat_request[32][256]; u8 jvs_receive_buffer[32][258]; u32 jvs_receive_length[32] = { 0 }; maple_naomi_jamma() { switch (settings.input.JammaSetup) { case JVS::Default: default: io_boards.emplace_back(new jvs_837_13551(1, this)); break; case JVS::FourPlayers: io_boards.emplace_back(new jvs_837_13551_4P(1, this)); break; case JVS::RotaryEncoders: io_boards.emplace_back(new jvs_837_13938(1, this)); io_boards.emplace_back(new jvs_837_13551(2, this)); break; case JVS::OutTrigger: io_boards.emplace_back(new jvs_837_13938(1, this)); io_boards.emplace_back(new jvs_837_13551_noanalog(2, this)); break; case JVS::SegaMarineFishing: io_boards.emplace_back(new jvs_837_13844(1, this)); break; case JVS::DualIOBoards4P: io_boards.emplace_back(new jvs_837_13551(1, this)); io_boards.emplace_back(new jvs_837_13551(2, this, 2)); break; case JVS::LightGun: io_boards.emplace_back(new jvs_namco_jyu(1, this)); break; case JVS::Mazan: io_boards.emplace_back(new jvs_namco_fcb(1, this)); io_boards.emplace_back(new jvs_namco_fcb(2, this)); break; case JVS::GunSurvivor: io_boards.emplace_back(new jvs_namco_fca(1, this)); break; case JVS::DogWalking: io_boards.emplace_back(new jvs_837_13844_encoders(1, this)); break; case JVS::TouchDeUno: io_boards.emplace_back(new jvs_837_13844_touch(1, this)); break; case JVS::WorldKicks: io_boards.emplace_back(new jvs_namco_v226(1, this)); break; case JVS::WorldKicksPCB: io_boards.emplace_back(new jvs_namco_v226_pcb(1, this)); break; } if (settings.input.JammaSetup != JVS::Mazan) for (int bus = 0; bus < MAPLE_PORTS; ++bus) if ((MapleDeviceType)settings.input.maple_devices[bus] == MDT_LightGun) { io_boards.back()->lightgun_as_analog = true; break; } } virtual ~maple_naomi_jamma() { EEPROM_loaded = false; } virtual MapleDeviceType get_device_type() { return MDT_NaomiJamma; } u8 sense_line(u32 node_id) { bool last_node = node_id == io_boards.size(); return last_node ? 0x8E : 0x8F; } void send_jvs_message(u32 node_id, u32 channel, u32 length, u8 *data) { if (node_id - 1 < io_boards.size()) { u8 temp_buffer[256]; u32 out_len = io_boards[node_id - 1]->handle_jvs_message(data, length, temp_buffer); if (out_len > 0) { u8 *pbuf = &jvs_receive_buffer[channel][jvs_receive_length[channel]]; if (jvs_receive_length[channel] + out_len + 3 <= sizeof(jvs_receive_buffer[0])) { if (crazy_mode) { pbuf[0] = 0x00; // ? 0: ok, 2: timeout, 3: dest node !=0, 4: checksum failed pbuf[1] = out_len; memcpy(&pbuf[2], temp_buffer, out_len); jvs_receive_length[channel] += out_len + 2; } else { pbuf[0] = node_id; pbuf[1] = 0x00; // 0: ok, 2: timeout, 3: dest node !=0, 4: checksum failed pbuf[2] = out_len; memcpy(&pbuf[3], temp_buffer, out_len); jvs_receive_length[channel] += out_len + 3; } } } } } void send_jvs_messages(u32 node_id, u32 channel, bool use_repeat, u32 length, u8 *data, bool repeat_first) { u8 temp_buffer[256]; if (data) { memcpy(temp_buffer, data, length); } if (node_id == ALL_NODES) { for (u32 i = 0; i < io_boards.size(); i++) send_jvs_message(i + 1, channel, length, temp_buffer); } else if (node_id >= 1 && node_id <= 32) { u32 repeat_len = jvs_repeat_request[node_id - 1][0]; if (use_repeat && repeat_len > 0) { if (repeat_first) { memmove(temp_buffer + repeat_len, temp_buffer, length); memcpy(temp_buffer, &jvs_repeat_request[node_id - 1][1], repeat_len); } else { memcpy(temp_buffer + length, &jvs_repeat_request[node_id - 1][1], repeat_len); } length += repeat_len; } send_jvs_message(node_id, channel, length, temp_buffer); } } bool receive_jvs_messages(u32 channel) { u32 dword_length = (jvs_receive_length[channel] + 0x10 + 3 - 1) / 4 + 1; w8(MDRS_JVSReply); w8(0x00); w8(0x20); if (jvs_receive_length[channel] == 0) { w8(0x05); w8(0x32); } else { w8(dword_length); w8(0x16); } w8(0xff); w8(0xff); w8(0xff); w32(0xffffff00); w32(0); w32(0); if (jvs_receive_length[channel] == 0) { w32(0); return false; } w8(0); w8(channel); if (crazy_mode) w8(0x8E); else w8(sense_line(jvs_receive_buffer[channel][0])); // bit 0 is sense line level. If set during F1 , more I/O boards need addressing memcpy(dma_buffer_out, jvs_receive_buffer[channel], jvs_receive_length[channel]); dma_buffer_out += dword_length * 4 - 0x10 - 3; *dma_count_out += dword_length * 4 - 0x10 - 3; jvs_receive_length[channel] = 0; return true; } void handle_86_subcommand() { if (dma_count_in == 0) { w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x00); return; } u32 subcode = dma_buffer_in[0]; // CT fw uses 13 as a 17, and 17 as 13 and also uses 19 if (crazy_mode) { switch (subcode) { case 0x13: subcode = 0x17; break; case 0x17: subcode = 0x13; break; } } u8 node_id = 0; u8 *cmd = NULL; u32 len = 0; u8 channel = 0; if (dma_count_in >= 3) { if (dma_buffer_in[1] > 31 && dma_buffer_in[1] != 0xff && dma_count_in >= 8) // TODO what is this? { node_id = dma_buffer_in[6]; len = dma_buffer_in[7]; cmd = &dma_buffer_in[8]; channel = dma_buffer_in[5] & 0x1f; } else { node_id = dma_buffer_in[1]; len = dma_buffer_in[2]; cmd = &dma_buffer_in[3]; } } switch (subcode) { case 0x13: // Store repeated request if (len > 0 && node_id > 0 && node_id <= 0x1f) { INFO_LOG(MAPLE, "JVS node %d: Storing %d cmd bytes", node_id, len); jvs_repeat_request[node_id - 1][0] = len; memcpy(&jvs_repeat_request[node_id - 1][1], cmd, len); } w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(dma_buffer_in[0] + 1); // subcommand + 1 w8(0); w8(len + 1); w8(0); break; case 0x15: // Receive JVS data receive_jvs_messages(dma_buffer_in[1]); break; case 0x17: // Transmit without repeat jvs_receive_length[channel] = 0; send_jvs_messages(node_id, channel, false, len, cmd, false); w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(0x18); // always w8(channel); w8(0x8E); //sense_line(node_id)); w8(0); break; case 0x19: // Transmit with repeat jvs_receive_length[channel] = 0; send_jvs_messages(node_id, channel, true, len, cmd, true); w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(0x18); // always w8(channel); w8(sense_line(node_id)); w8(0); break; case 0x21: // Transmit with repeat jvs_receive_length[channel] = 0; send_jvs_messages(node_id, channel, true, len, cmd, false); w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(0x18); // always w8(channel); w8(sense_line(node_id)); w8(0); break; case 0x35: // Receive then transmit with repeat (15 then 27) receive_jvs_messages(channel); // FALLTHROUGH case 0x27: // Transmit with repeat { jvs_receive_length[channel] = 0; u32 cmd_count = dma_buffer_in[6]; u32 idx = 7; for (u32 i = 0; i < cmd_count; i++) { node_id = dma_buffer_in[idx]; len = dma_buffer_in[idx + 1]; cmd = &dma_buffer_in[idx + 2]; idx += len + 2; send_jvs_messages(node_id, channel, true, len, cmd, false); } w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(0x26); w8(channel); w8(sense_line(node_id)); w8(0); } break; case 0x33: // Receive then transmit with repeat (15 then 21) receive_jvs_messages(channel); send_jvs_messages(node_id, channel, true, len, cmd, false); w8(MDRS_JVSReply); w8(0); w8(0x20); w8(0x01); w8(0x18); // always w8(channel); w8(sense_line(node_id)); w8(0); break; case 0x0B: //EEPROM write { int address = dma_buffer_in[1]; int size = dma_buffer_in[2]; DEBUG_LOG(MAPLE, "EEprom write %08X %08X\n", address, size); //printState(Command,buffer_in,buffer_in_len); memcpy(EEPROM + address, dma_buffer_in + 4, size); string nvmemSuffix = cfgLoadStr("net", "nvmem", ""); string eeprom_file = get_game_save_prefix() + nvmemSuffix + ".eeprom"; FILE* f = fopen(eeprom_file.c_str(), "wb"); if (f) { fwrite(EEPROM, 1, 0x80, f); fclose(f); INFO_LOG(MAPLE, "Saved EEPROM to %s", eeprom_file.c_str()); } else WARN_LOG(MAPLE, "EEPROM SAVE FAILED to %s", eeprom_file.c_str()); w8(MDRS_JVSReply); w8(0x00); w8(0x20); w8(0x01); memcpy(dma_buffer_out, EEPROM, 4); dma_buffer_out += 4; *dma_count_out += 4; } break; case 0x3: //EEPROM read { if (!EEPROM_loaded) { EEPROM_loaded = true; string nvmemSuffix = cfgLoadStr("net", "nvmem", ""); string eeprom_file = get_game_save_prefix() + nvmemSuffix + ".eeprom"; FILE* f = fopen(eeprom_file.c_str(), "rb"); if (f) { fread(EEPROM, 1, 0x80, f); fclose(f); DEBUG_LOG(MAPLE, "Loaded EEPROM from %s", eeprom_file.c_str()); } else if (naomi_default_eeprom != NULL) { DEBUG_LOG(MAPLE, "Using default EEPROM file"); memcpy(EEPROM, naomi_default_eeprom, 0x80); } else DEBUG_LOG(MAPLE, "EEPROM file not found at %s and no default found", eeprom_file.c_str()); } //printf("EEprom READ\n"); int address = dma_buffer_in[1]; //printState(Command,buffer_in,buffer_in_len); w8(MDRS_JVSReply); w8(0x00); w8(0x20); w8(0x20); memcpy(dma_buffer_out, EEPROM + address, 0x80); dma_buffer_out += 0x80; *dma_count_out += 0x80; } break; case 0x31: // DIP switches { w8(MDRS_JVSReply); w8(0x00); w8(0x20); w8(0x05); w8(0x32); w8(0xff); // in(0) w8(0xff); // in(1) w8(0xff); // in(2) w8(0x00); w8(0xff); // in(4) w8(0xff); // in(5) bit0: 1=VGA, 0=NTSCi w8(0xff); // in(6) w32(0x00); w32(0x00); w32(0x00); } break; //case 0x3: // break; case 0x1: w8(MDRS_JVSReply); w8(0x00); w8(0x20); w8(0x01); w8(0x2); w8(0x0); w8(0x0); w8(0x0); break; default: INFO_LOG(MAPLE, "JVS: Unknown 0x86 sub-command %x", subcode); w8(MDRE_UnknownCmd); w8(0x00); w8(0x20); w8(0x00); break; } } virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) { #ifdef DUMP_JVS printf("JVS IN: "); u8 *p = (u8*)buffer_in; for (int i = 0; i < buffer_in_len; i++) printf("%02x ", *p++); printf("\n"); #endif u32 out_len = 0; dma_buffer_out = (u8 *)buffer_out; dma_count_out = &out_len; dma_buffer_in = (u8 *)buffer_in + 4; dma_count_in = buffer_in_len - 4; u32 cmd = *(u8*)buffer_in; switch (cmd) { case MDC_JVSCommand: handle_86_subcommand(); break; case MDC_JVSUploadFirmware: { static u8 *ram; if (ram == NULL) ram = (u8 *)calloc(0x10000, 1); if (dma_buffer_in[1] == 0xff) { u32 hash = XXH32(ram, 0x10000, 0); LOGJVS("JVS Firmware hash %08x\n", hash); if (hash == 0xa7c50459 // CT || hash == 0xae841e36) // HOTD2 crazy_mode = true; else crazy_mode = false; #ifdef DUMP_JVS_FW FILE *fw_dump; char filename[128]; for (int i = 0; ; i++) { sprintf(filename, "z80_fw_%d.bin", i); fw_dump = fopen(filename, "r"); if (fw_dump == NULL) { fw_dump = fopen(filename, "w"); INFO_LOG(MAPLE, "Saving JVS firmware to %s", filename); break; } } if (fw_dump) { fwrite(ram, 1, 0x10000, fw_dump); fclose(fw_dump); } #endif free(ram); ram = NULL; for (int i = 0; i < 32; i++) jvs_repeat_request[i][0] = 0; return MDRS_DeviceReply; } int xfer_bytes; if (dma_buffer_in[0] == 0xff) xfer_bytes = 0x1C; else xfer_bytes = 0x18; u16 addr = (dma_buffer_in[2] << 8) + dma_buffer_in[3]; memcpy(ram + addr, &dma_buffer_in[4], xfer_bytes); u8 sum = 0; for (int i = 0; i < 0x1C; i++) sum += dma_buffer_in[i]; w8(0x80); // or 0x81 on bootrom? w8(0); w8(0x20); w8(0x01); w8(sum); w8(0); w8(0); w8(0); w8(MDRS_DeviceReply); w8(0x00); w8(0x20); w8(0x00); } break; case MDC_JVSGetId: { const char ID1[] = "315-6149 COPYRIGHT SEGA E"; const char ID2[] = "NTERPRISES CO,LTD. 1998 "; w8(0x83); w8(0x00); w8(0x20); w8(0x07); wstr(ID1, 28); w8(0x83); w8(0x00); w8(0x20); w8(0x05); wstr(ID2, 28); } break; case MDC_DeviceRequest: w8(MDRS_DeviceStatus); w8(0x00); w8(0x20); w8(0x00); break; case MDC_DeviceReset: w8(MDRS_DeviceReply); w8(0x00); w8(0x20); w8(0x00); break; case MDCF_GetCondition: w8(MDRE_UnknownCmd); w8(0x00); w8(0x00); w8(0x00); break; default: INFO_LOG(MAPLE, "Unknown Maple command %x", cmd); w8(MDRE_UnknownCmd); w8(0x00); w8(0x00); w8(0x00); break; } #ifdef DUMP_JVS printf("JVS OUT: "); p = (u8 *)buffer_out; for (int i = 0; i < out_len; i++) printf("%02x ", p[i]); printf("\n"); #endif return out_len; } virtual bool maple_serialize(void **data, unsigned int *total_size) { REICAST_S(crazy_mode); REICAST_S(jvs_repeat_request); REICAST_S(jvs_receive_length); REICAST_S(jvs_receive_buffer); size_t board_count = io_boards.size(); REICAST_S(board_count); for (u32 i = 0; i < io_boards.size(); i++) io_boards[i]->maple_serialize(data, total_size); return true ; } virtual bool maple_unserialize(void **data, unsigned int *total_size) { REICAST_US(crazy_mode); REICAST_US(jvs_repeat_request); REICAST_US(jvs_receive_length); REICAST_US(jvs_receive_buffer); size_t board_count; REICAST_US(board_count); for (u32 i = 0; i < board_count; i++) io_boards[i]->maple_unserialize(data, total_size); return true ; } }; u16 jvs_io_board::read_analog_axis(int player_num, int player_axis, bool inverted) { u16 v; switch (player_axis) { case 0: v = (joyx[player_num] + 128) << 8; break; case 1: v = (joyy[player_num] + 128) << 8; break; case 2: v = (joyrx[player_num] + 128) << 8; break; case 3: v = (joyry[player_num] + 128) << 8; break; default: return 0x8000; } return inverted ? 0xffff - v : v; } #define JVS_OUT(b) buffer_out[length++] = b #define JVS_STATUS1() JVS_OUT(1) u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_out) { u8 jvs_cmd = buffer_in[0]; if (jvs_cmd == 0xF0) // JVS reset // Nothing to do return 0; if (jvs_cmd == 0xF1 && (length_in < 2 || buffer_in[1] != node_id)) // Not for us return 0; u32 length = 0; JVS_OUT(0xE0); // sync JVS_OUT(0); // master node id u8& jvs_length = buffer_out[length++]; switch (jvs_cmd) { case 0xF1: // set board address JVS_STATUS1(); // status JVS_STATUS1(); // report JVS_OUT(5); // ? LOGJVS("JVS Node %d address assigned\n", node_id); break; case 0x10: // Read ID data JVS_STATUS1(); // status JVS_STATUS1(); // report for (const char *p = get_id(); *p != 0; ) JVS_OUT(*p++); JVS_OUT(0); break; case 0x11: // Get command format version JVS_STATUS1(); JVS_STATUS1(); JVS_OUT(0x11); // 1.1 break; case 0x12: // Get JAMMA VIDEO version JVS_STATUS1(); JVS_STATUS1(); JVS_OUT(0x20); // 2.0 break; case 0x13: // Get communication version JVS_STATUS1(); JVS_STATUS1(); JVS_OUT(0x10); // 1.0 break; case 0x14: // Get slave features JVS_STATUS1(); JVS_STATUS1(); JVS_OUT(1); // Digital inputs JVS_OUT(player_count); JVS_OUT(digital_in_count); JVS_OUT(0); if (coin_input_count > 0) { JVS_OUT(2); // Coin inputs JVS_OUT(coin_input_count); JVS_OUT(0); JVS_OUT(0); } if (analog_count > 0) { JVS_OUT(3); // Analog inputs JVS_OUT(analog_count); // channel count JVS_OUT(0x10); // 16 bits per channel, 0: unknown JVS_OUT(0); } if (encoder_count > 0) { JVS_OUT(4); // Rotary encoders JVS_OUT(encoder_count); JVS_OUT(0); JVS_OUT(0); } if (light_gun_count > 0) { JVS_OUT(6); // Light gun JVS_OUT(16); // X bits JVS_OUT(16); // Y bits JVS_OUT(light_gun_count); } JVS_OUT(0x12); // General output driver JVS_OUT(output_count); JVS_OUT(0); JVS_OUT(0); JVS_OUT(0); // End of list break; case 0x15: // Master board ID JVS_STATUS1(); JVS_STATUS1(); break; case 0x70: LOGJVS("JVS 0x70: %02x %02x %02x %02x", buffer_in[1], buffer_in[2], buffer_in[3], buffer_in[4]); init_in_progress = true; JVS_STATUS1(); JVS_STATUS1(); if (buffer_in[2] == 3) { JVS_OUT(0x10); for (int i = 0; i < 16; i++) JVS_OUT(0x7f); if (buffer_in[4] == 0x10 || buffer_in[4] == 0x11) init_in_progress = false; } else { JVS_OUT(2); JVS_OUT(3); JVS_OUT(1); } break; default: if (jvs_cmd >= 0x20 && jvs_cmd <= 0x38) // Read inputs and more { LOGJVS("JVS Node %d: ", node_id); PlainJoystickState pjs; parent->config->GetInput(&pjs); JVS_STATUS1(); // status for (u32 cmdi = 0; cmdi < length_in; ) { switch (buffer_in[cmdi]) { case 0x20: // Read digital input { JVS_STATUS1(); // report byte LOGJVS("btns "); for (int player = 0; player < buffer_in[cmdi + 1]; player++) { u16 cur_btns = read_digital_in(first_player + player); if (player == 0) JVS_OUT((cur_btns & NAOMI_TEST_KEY) ? 0x80 : 0x00); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused LOGJVS("P%d %02x ", player + 1 + first_player, cur_btns >> 8); JVS_OUT(cur_btns >> 8); if (buffer_in[cmdi + 2] == 2) { LOGJVS("%02x ", cur_btns & 0xFF); JVS_OUT(cur_btns); } } cmdi += 3; } break; case 0x21: // Read coins { JVS_STATUS1(); // report byte LOGJVS("coins "); u32 mask = 0; for (int i = 0; i < 16; i++) { if (naomi_button_mapping[i] == NAOMI_COIN_KEY) { mask = 1 << i; break; } } for (int slot = 0; slot < buffer_in[cmdi + 1]; slot++) { u16 keycode = ~kcode[first_player + slot]; bool coin_chute = false; if (keycode & mask) { coin_chute = true; if (!old_coin_chute[first_player + slot]) coin_count[first_player + slot] += 1; } old_coin_chute[first_player + slot] = coin_chute; LOGJVS("%d:%d ", slot + 1 + first_player, coin_count[first_player + slot]); // status (2 highest bits, 0: normal), coin count MSB JVS_OUT((coin_count[first_player + slot] >> 8) & 0x3F); // coin count LSB JVS_OUT(coin_count[first_player + slot]); } cmdi += 2; } break; case 0x22: // Read analog inputs { JVS_STATUS1(); // report byte u32 axis = 0; LOGJVS("ana "); if (lightgun_as_analog) { // Death Crimson / Confidential Mission u16 x = mo_x_abs * 0xFFFF / 639; u16 y = mo_y_abs * 0xFFFF / 479; if (mo_x_abs < 0 || mo_x_abs > 639 || mo_y_abs < 0 || mo_y_abs > 479) { x = 0; y = 0; } LOGJVS("x,y:%4x,%4x ", x, y); JVS_OUT(x >> 8); // X, MSB JVS_OUT(x); // X, LSB JVS_OUT(y >> 8); // Y, MSB JVS_OUT(y); // Y, LSB axis = 2; } int full_axis_count = 0; int half_axis_count = 0; for (; axis < buffer_in[cmdi + 1]; axis++) { u16 axis_value; if (NaomiGameInputs != NULL && axis < ARRAY_SIZE(NaomiGameInputs->axes) && NaomiGameInputs->axes[axis].name != NULL) { const AxisDescriptor& axisDesc = NaomiGameInputs->axes[axis]; if (axisDesc.type == Half) { if (half_axis_count == 0) axis_value = rt[first_player] << 8; else if (half_axis_count == 1) axis_value = lt[first_player] << 8; else axis_value = 0; half_axis_count++; } else { axis_value = read_analog_axis(first_player, axisDesc.axis, axisDesc.inverted); full_axis_count++; } } else { axis_value = read_analog_axis(first_player, full_axis_count, false); full_axis_count++; } LOGJVS("%d:%4x ", axis, axis_value); JVS_OUT(axis_value >> 8); JVS_OUT(axis_value); } cmdi += 2; } break; case 0x23: // Read rotary encoders { JVS_STATUS1(); // report byte static s16 rotx = 0; static s16 roty = 0; rotx += mo_x_delta * 5; roty -= mo_y_delta * 5; mo_x_delta = 0; mo_y_delta = 0; LOGJVS("rotenc "); for (int chan = 0; chan < buffer_in[cmdi + 1]; chan++) { if (chan == 0) { LOGJVS("%d:%4x ", chan, rotx & 0xFFFF); JVS_OUT(rotx >> 8); // MSB JVS_OUT(rotx); // LSB } else if (chan == 1) { LOGJVS("%d:%4x ", chan, roty & 0xFFFF); JVS_OUT(roty >> 8); // MSB JVS_OUT(roty); // LSB } else { LOGJVS("%d:%4x ", chan, 0); JVS_OUT(0x00); // MSB JVS_OUT(0x00); // LSB } } cmdi += 2; } break; case 0x25: // Read screen pos inputs { JVS_STATUS1(); // report byte // Channel number is jvs_request[channel][cmdi + 1] // specs: //u16 x = mo_x_abs * 0xFFFF / 639; //u16 y = (479 - mo_y_abs) * 0xFFFF / 479; // Ninja Assault: u32 xr = 0x19d - 0x37; u32 yr = 0x1fe - 0x40; s16 x = mo_x_abs * xr / 639 + 0x37; s16 y = mo_y_abs * yr / 479 + 0x40; LOGJVS("lightgun %4x,%4x ", x, y); JVS_OUT(x >> 8); // X, MSB JVS_OUT(x); // X, LSB JVS_OUT(y >> 8); // Y, MSB JVS_OUT(y); // Y, LSB cmdi += 2; } break; case 0x32: // switched outputs case 0x33: JVS_STATUS1(); // report byte cmdi += buffer_in[cmdi + 1] + 2; break; case 0x30: // substract coin if (buffer_in[cmdi + 1] > 0 && first_player + buffer_in[cmdi + 1] - 1 < (int)ARRAY_SIZE(coin_count)) coin_count[first_player + buffer_in[cmdi + 1] - 1] -= (buffer_in[cmdi + 2] << 8) + buffer_in[cmdi + 3]; JVS_STATUS1(); // report byte cmdi += 4; break; default: DEBUG_LOG(MAPLE, "JVS: Unknown input type %x", buffer_in[cmdi]); JVS_OUT(2); // report byte: command error cmdi = length_in; // Ignore subsequent commands break; } } LOGJVS("\n"); } else { INFO_LOG(MAPLE, "JVS: Unknown JVS command %x", jvs_cmd); JVS_OUT(2); // Unknown command } break; } jvs_length = length - 3; return length; } bool jvs_io_board::maple_serialize(void **data, unsigned int *total_size) { REICAST_S(node_id); REICAST_S(lightgun_as_analog); return true ; } bool jvs_io_board::maple_unserialize(void **data, unsigned int *total_size) { REICAST_US(node_id); REICAST_US(lightgun_as_analog); return true ; } maple_device* maple_Create(MapleDeviceType type) { maple_device* rv=0; switch(type) { case MDT_SegaController: if (settings.platform.system != DC_PLATFORM_ATOMISWAVE) rv = new maple_sega_controller(); else rv = new maple_atomiswave_controller(); break; case MDT_Microphone: rv=new maple_microphone(); break; case MDT_SegaVMU: rv = new maple_sega_vmu(); break; case MDT_PurupuruPack: rv = new maple_sega_purupuru(); break; case MDT_Keyboard: rv = new maple_keyboard(); break; case MDT_Mouse: rv = new maple_mouse(); break; case MDT_LightGun: if (settings.platform.system != DC_PLATFORM_ATOMISWAVE) rv = new maple_lightgun(); else rv = new atomiswave_lightgun(); break; case MDT_NaomiJamma: rv = new maple_naomi_jamma(); break; case MDT_TwinStick: rv = new maple_sega_twinstick(); break; case MDT_AsciiStick: rv = new maple_ascii_stick(); break; default: ERROR_LOG(MAPLE, "Invalid device type %d", type); die("Invalid maple device type"); break; } return rv; }