diff --git a/Config/Games.xml b/Config/Games.xml index 9d88017..723e919 100644 --- a/Config/Games.xml +++ b/Config/Games.xml @@ -2086,8 +2086,8 @@ - - + + @@ -2153,11 +2153,11 @@ - + @@ -2184,10 +2184,14 @@ - + - + + + + + diff --git a/Src/Model3/DriveBoard.cpp b/Src/Model3/DriveBoard.cpp index 46eec91..bd1ebea 100644 --- a/Src/Model3/DriveBoard.cpp +++ b/Src/Model3/DriveBoard.cpp @@ -254,8 +254,10 @@ void CDriveBoard::Reset(void) m_adcPortRead = 0; m_adcPortBit = 0; m_port42Out = 0; + m_port45Out = 0; m_port46Out = 0; m_prev42Out = 0; + m_prev45Out = 0; m_prev46Out = 0; m_initState = 0; @@ -266,6 +268,7 @@ void CDriveBoard::Reset(void) m_uncenterVal2 = 0; m_lastConstForce = 0; + m_lastConstForceY = 0; m_lastSelfCenter = 0; m_lastFriction = 0; m_lastVibrate = 0; @@ -496,32 +499,29 @@ UINT8 CDriveBoard::IORead8(UINT32 portNum) UINT8 adcVal; switch (portNum) { - case 32: // DIP 1 value + case 0x20: // DIP 1 value return m_dip1; - case 33: // DIP 2 value + case 0x21: // DIP 2 value return m_dip2; - case 36: // ADC channel 1 - not connected - case 37: // ADC channel 2 - steering wheel position (0x00 = full left, 0x80 = center, 0xFF = full right) - case 38: // ADC channel 3 - cockpit bank position (deluxe cabinets) (0x00 = full left, 0x80 = center, 0xFF = full right) - case 39: // ADC channel 4 - not connected + case 0x24: // ADC channel 1 - Y analog axis for joystick + case 0x25: // ADC channel 2 - steering wheel position (0x00 = full left, 0x80 = center, 0xFF = full right) and X analog axis for joystick + case 0x26: // ADC channel 3 - cockpit bank position (deluxe cabinets) (0x00 = full left, 0x80 = center, 0xFF = full right) + case 0x27: // ADC channel 4 - not connected if (portNum == m_adcPortRead && m_adcPortBit-- > 0) { switch (portNum) { - case 36: // Not connected - adcVal = 0x00; + case 0x24: // Y analog axis for joystick + adcVal = ReadADCChannel1(); break; - case 37: // Steering wheel for twin racing cabinets - TODO - check actual range of steering, suspect it is not really 0x00-0xFF - if (m_initialized) - adcVal = (UINT8)m_inputs->steering->value; - else - adcVal = 0x80; // If not initialized, return 0x80 so that wheel centering test does not fail + case 0x25: // Steering wheel for twin racing cabinets - TODO - check actual range of steering, suspect it is not really 0x00-0xFF + adcVal = ReadADCChannel2(); break; - case 38: // Cockpit bank position for deluxe racing cabinets - adcVal = 0x80; + case 0x26: // Cockpit bank position for deluxe racing cabinets + adcVal = ReadADCChannel3(); break; - case 39: // Not connected - adcVal = 0x00; + case 0x27: // Not connected + adcVal = ReadADCChannel4(); break; default: #ifdef DEBUG @@ -538,9 +538,9 @@ UINT8 CDriveBoard::IORead8(UINT32 portNum) #endif return 0xFF; } - case 40: // PPC command + case 0x28: // PPC command return m_dataSent; - case 44: // Encoder error reporting (kept at 0x00 for no error) + case 0x2c: // Encoder error reporting (kept at 0x00 for no error) // Bit 1 0 // 0 0 = encoder okay, no error // 0 1 = encoder error 1 - overcurrent error @@ -559,55 +559,66 @@ void CDriveBoard::IOWrite8(UINT32 portNum, UINT8 data) { switch (portNum) { - case 16: // Unsure? - single byte 0x03 sent at initialization, then occasionally writes 0x07 & 0xFA to port + case 0x10: // Unsure? - single byte 0x03 sent at initialization, then occasionally writes 0x07 & 0xFA to port return; - case 17: // Interrupt control + case 0x11: // Interrupt control if (data == 0x57) m_allowInterrupts = true; else if (data == 0x53) // Strictly speaking 0x53 then 0x04 m_allowInterrupts = false; return; - case 28: // Unsure? - two bytes 0xFF, 0xFF sent at initialization only - case 29: // Unsure? - two bytes 0x0F, 0x17 sent at initialization only - case 30: // Unsure? - same as port 28 - case 31: // Unsure? - same as port 31 + case 0x1c: // Unsure? - two bytes 0xFF, 0xFF sent at initialization only + case 0x1d: // Unsure? - two bytes 0x0F, 0x17 sent at initialization only + case 0x1e: // Unsure? - same as port 28 + case 0x1f: // Unsure? - same as port 31 return; - case 32: // Left digit of 7-segment display 1 + case 0x20: // Left digit of 7-segment display 1 m_seg1Digit1 = data; return; - case 33: // Right digit of 7-segment display 1 + case 0x21: // Right digit of 7-segment display 1 m_seg1Digit2 = data; return; - case 34: // Left digit of 7-segment display 2 + case 0x22: // Left digit of 7-segment display 2 m_seg2Digit1 = data; return; - case 35: // Right digit of 7-segment display 2 + case 0x23: // Right digit of 7-segment display 2 m_seg2Digit2 = data; return; - case 36: // ADC channel 1 control - case 37: // ADC channel 2 control - case 38: // ADC channel 3 control - case 39: // ADC channel 4 control + case 0x24: // ADC channel 1 control + case 0x25: // ADC channel 2 control + case 0x26: // ADC channel 3 control + case 0x27: // ADC channel 4 control m_adcPortRead = portNum; m_adcPortBit = 8; return; - case 41: // Reply for PPC + case 0x29: // Reply for PPC m_dataReceived = data; - if (data == 0xCC) + if ((data == 0xCC && m_boardType == Wheel) || (data==0xCB && m_boardType == Joystick)) m_initialized = true; return; - case 42: // Encoder motor data + case 0x2a: // Encoder motor data (x axis) m_port42Out = data; - ProcessEncoderCmd(); + switch (m_boardType) + { + case Wheel: + ProcessEncoderCmd(); + break; + case Joystick: + ProcessEncoderCmdJoystick(); + break; + } return; - case 45: // Clutch/lamp control (deluxe cabinets) + case 0x2d: // Clutch/lamp control (deluxe cabinets) ( or y axis) + m_port45Out = data; + if (m_boardType==Joystick) + ProcessEncoderCmdJoystick(); return; - case 46: // Encoder motor control + case 0x2e: // Encoder motor control m_port46Out = data; return; - case 240: // Unsure? - single byte 0xBB sent at initialization only + case 0xf0: // Unsure? - single byte 0xBB sent at initialization only return; - case 241: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog? + case 0xf1: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog? return; default: #ifdef DEBUG @@ -719,18 +730,148 @@ void CDriveBoard::ProcessEncoderCmd(void) } } +void CDriveBoard::ProcessEncoderCmdJoystick(void) +{ + if (m_prev42Out != m_port42Out || m_prev46Out != m_port46Out || m_prev45Out != m_port45Out) + { + switch (m_port46Out) + { + case 0xEE: + // Apply constant force + + if (m_port42Out > 0x7f) // X + { + SendConstantForce(2 * (m_port42Out - 0x7f)); + } + else if (m_port42Out < 0x7f) + { + SendConstantForce(2 * (m_port42Out - 0x7f)); + } + else + { + SendConstantForce(0); + } + + if (m_port45Out > 0x7f) // Y + { + SendConstantForceY(-2 * (m_port45Out - 0x7f)); + } + else if (m_port45Out < 0x7f) + { + SendConstantForceY(-2 * (m_port45Out - 0x7f)); + } + else + { + SendConstantForceY(0); + } + + if (m_port42Out == 0x7f && m_port45Out == 0x81) + { + SendSelfCenter(255); + } + else SendSelfCenter(0); + + break; + + case 0xFF: + // Stop all effects + if (m_port42Out == 0 || m_port45Out == 0) + SendStopAll(); + break; + + case 0xcc: // init + //42[0B] / 45[0A] + //42[0B] / 45[0B] + //42[FF] / 45[0B] + //42[FF] / 45[FF] + break; + + case 0xdd: // init + // 42[FF] / 45[00] + // 42[FF] / 45[FF] + + // 42[0A] / 45[00] + // 42[0A] / 45[0A] + break; + + case 0xce: + // 42[7F] / 45[08] + // 42[7F] / 45[09] + // 42[7F] / 45[0A] + // 42[7F] / 45[0B] + // 42[7F] / 45[81] + if (m_port42Out == 0x7f && m_port45Out != 0x81) // X + { + SendConstantForce(2 * m_port45Out); + } + + if (m_port42Out == 0x7f && m_port45Out == 0x81) + { + SendSelfCenter(255); + } + break; + + case 0xec: + // 42[09] / 45[81] + // 42[2A] / 45[81] + // 42[1B] / 45[81] + // 42[7F] / 45[81] + if (m_port45Out == 0x81 && m_port42Out != 0x7f) // Y + { + SendConstantForceY(2 * m_port42Out); + } + + if (m_port42Out == 0x7f && m_port45Out == 0x81) + { + SendSelfCenter(255); + } + break; + + case 0x00: // init + // 42[FF] / 45[00] + // 42[FF] / 45[FF] + break; + + case 0x99: // init + // 42[B0] / 45[B0] + // 42[80] / 45[B0] + // 42[80] / 45[80] + break; + + default: + //printf("Unknown = 46 [%02X] / 42 [%02X] / 45 [%02X]\n", m_port46Out, m_port42Out, m_port45Out); + break; + } + + m_prev42Out = m_port42Out; + m_prev46Out = m_port46Out; + m_prev45Out = m_port45Out; + } +} + void CDriveBoard::SendStopAll(void) { //printf(">> Stop All Effects\n"); ForceFeedbackCmd ffCmd; ffCmd.id = FFStop; - m_inputs->steering->SendForceFeedbackCmd(ffCmd); + switch (m_boardType) + { + case Wheel: + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + break; + case Joystick: + m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd); + m_inputs->analogJoyY->SendForceFeedbackCmd(ffCmd); + break; + } + m_lastConstForce = 0; m_lastSelfCenter = 0; m_lastFriction = 0; m_lastVibrate = 0; + m_lastConstForceY = 0; } void CDriveBoard::SendConstantForce(INT8 val) @@ -759,11 +900,32 @@ void CDriveBoard::SendConstantForce(INT8 val) ForceFeedbackCmd ffCmd; ffCmd.id = FFConstantForce; ffCmd.force = (float)val / (val >= 0 ? 127.0f : 128.0f); - m_inputs->steering->SendForceFeedbackCmd(ffCmd); - + + switch (m_boardType) + { + case Wheel: + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + break; + case Joystick: + m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd); + break; + } + m_lastConstForce = val; } +void CDriveBoard::SendConstantForceY(INT8 val) +{ + if (val == m_lastConstForceY) + return; + + ForceFeedbackCmd ffCmd; + ffCmd.id = FFConstantForce; + ffCmd.force = (float)val / (val >= 0 ? 127.0f : 128.0f); + m_inputs->analogJoyY->SendForceFeedbackCmd(ffCmd); + m_lastConstForceY = val; +} + void CDriveBoard::SendSelfCenter(UINT8 val) { if (val == m_lastSelfCenter) @@ -778,11 +940,22 @@ void CDriveBoard::SendSelfCenter(UINT8 val) ForceFeedbackCmd ffCmd; ffCmd.id = FFSelfCenter; ffCmd.force = (float)val / 255.0f; - m_inputs->steering->SendForceFeedbackCmd(ffCmd); + + switch (m_boardType) + { + case Wheel: + m_inputs->steering->SendForceFeedbackCmd(ffCmd); + break; + case Joystick: + m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd); + m_inputs->analogJoyY->SendForceFeedbackCmd(ffCmd); + break; + } m_lastSelfCenter = val; } + void CDriveBoard::SendFriction(UINT8 val) { if (val == m_lastFriction) @@ -821,6 +994,59 @@ void CDriveBoard::SendVibrate(UINT8 val) m_lastVibrate = val; } +uint8_t CDriveBoard::ReadADCChannel1() +{ + switch (m_boardType) + { + case Wheel: + return 0x00; + break; + case Joystick: + if (m_initialized) + return (UINT8)m_inputs->analogJoyY->value; + else + return 0x80; // If not initialized, return 0x80 so that ffb centering test does not fail + break; + } +} + +uint8_t CDriveBoard::ReadADCChannel2() +{ + switch (m_boardType) + { + case Wheel: + if (m_initialized) + return (UINT8)m_inputs->steering->value; + else + return 0x80; // If not initialized, return 0x80 so that wheel centering test does not fail + break; + case Joystick: + if (m_initialized) + return (UINT8)m_inputs->analogJoyX->value; + else + return 0x80; // If not initialized, return 0x80 so that ffb centering test does not fail + break; + } +} + +uint8_t CDriveBoard::ReadADCChannel3() +{ + return 0x80; +} + +uint8_t CDriveBoard::ReadADCChannel4() +{ + switch (m_boardType) + { + case Wheel: + return 0x00; + break; + case Joystick: + return 0x80; + break; + } +} + CDriveBoard::CDriveBoard(const Util::Config::Node &config) : m_config(config), m_attached(false), diff --git a/Src/Model3/DriveBoard.h b/Src/Model3/DriveBoard.h index 385cd6a..2a0d517 100644 --- a/Src/Model3/DriveBoard.h +++ b/Src/Model3/DriveBoard.h @@ -36,6 +36,14 @@ class CDriveBoard : public IBus { public: + enum BoardType + { + Wheel=0, + Joystick + }; + + BoardType m_boardType; + /* * IsAttached(void): * @@ -244,7 +252,7 @@ public: */ void Write8(UINT32 addr, UINT8 data); void IOWrite8(UINT32 portNum, UINT8 data); - + private: const Util::Config::Node &m_config; bool m_attached; // True if drive board is attached @@ -281,9 +289,11 @@ private: UINT8 m_port42Out; // Last value sent to Z80 I/O port 42 (encoder motor data) UINT8 m_port46Out; // Last value sent to Z80 I/O port 46 (encoder motor control) + UINT8 m_port45Out; // Last value sent to Z80 I/O port 42 (up-down motor data) UINT8 m_prev42Out; // Previous value sent to Z80 I/O port 42 UINT8 m_prev46Out; // Previous value sent to Z80 I/O port 46 + UINT8 m_prev45Out; // Previous value sent to Z80 I/O port 45 (up-down) UINT8 m_uncenterVal1; // First part of pending uncenter command UINT8 m_uncenterVal2; // Second part of pending uncenter command @@ -299,6 +309,7 @@ private: // Feedback state INT8 m_lastConstForce; // Last constant force command sent + INT8 m_lastConstForceY; // Last constant force command sent y axis UINT8 m_lastSelfCenter; // Last self center command sent UINT8 m_lastFriction; // Last friction command sent UINT8 m_lastVibrate; // Last vibrate command sent @@ -313,15 +324,27 @@ private: void ProcessEncoderCmd(void); + void ProcessEncoderCmdJoystick(void); + void SendStopAll(void); void SendConstantForce(INT8 val); + void SendConstantForceY(INT8 val); + void SendSelfCenter(UINT8 val); void SendFriction(UINT8 val); void SendVibrate(UINT8 val); + + uint8_t ReadADCChannel1(); + + uint8_t ReadADCChannel2(); + + uint8_t ReadADCChannel3(); + + uint8_t ReadADCChannel4(); }; #endif // INCLUDED_DRIVEBOARD_H diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp index d0d2af8..c8cd399 100644 --- a/Src/Model3/Model3.cpp +++ b/Src/Model3/Model3.cpp @@ -2932,6 +2932,7 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) rom_set.get_rom("mpeg_program").CopyTo(dsbROM, 128*1024); rom_set.get_rom("mpeg_music").CopyTo(mpegROM, 16*0x100000); rom_set.get_rom("driveboard_program").CopyTo(driveROM, 64*1024); + rom_set.get_rom("ffb_program").CopyTo(driveROM, 64 * 1024); // Convert PowerPC and 68K ROMs to little endian words Util::FlipEndian32(crom, 8*0x100000 + 128*0x100000); @@ -3030,6 +3031,15 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) { if (DriveBoard.Init(driveROM)) return FAIL; + else + DriveBoard.m_boardType = DriveBoard.Wheel; + } + else if (rom_set.get_rom("ffb_program").size) + { + if (DriveBoard.Init(driveROM)) + return FAIL; + else + DriveBoard.m_boardType = DriveBoard.Joystick; } else DriveBoard.Init(NULL); @@ -3046,10 +3056,12 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set) extra_hw.insert(Util::Format() << "Digital Sound Board (Type " << game.mpeg_board << ")"); if (rom_set.get_rom("driveboard_program").size) extra_hw.insert("Drive Board"); + if (rom_set.get_rom("ffb_program").size) + extra_hw.insert("Joystick FFB Board"); if (game.encryption_key) extra_hw.insert("Security Board"); if (netboard_present.compare("true")==0) - extra_hw.insert("Net Board"); + extra_hw.insert("Net Board"); if (!game.version.empty()) std::cout << " Title: " << game.title << " (" << game.version << ")" << std::endl; else