Supermodel/Src/Model3/DriveBoard.cpp
Nik Henson 183dca563d Committing various small updates that have been hanging around in my source tree for a while now:
- Added 'crosshairs' command line and config option.
- Added 'vsync' command line and config option (so far only tested on NVidia cards on Windows 7 - other graphics drivers, O/Ss or driver settings may simply chose to ignore this).
- Added fullscreen toggle within game using Alt+Enter key combination.
- Added framework for lamp outputs and 'outputs' command line and config option.  So far only the lamps for driving games are hooked up in the emulator (others to be added later).
- Added an initial outputs implementation for Windows that sends MAMEHooker compatible messages (-outputs=win to enable)
- Fixed fps calculation in Main.cpp that was producing incorrect results and so giving the impression that frame throttling wasn't working properly when in fact it was.
- Fixed palette indexed colours as the index was always off by one, causing incorrect colours in various games, eg drivers' suits and flashing Start sign in Daytona 2.
- Altered caching of models so that models with palette indexed colours use the dynamic cache rather than the static one.  This is so that changes in palette indexed colours appear on screen, eg the flashing Start sign on the advanced course of Daytona 2 (although currently the START message itself is not visible due to other problems with texture decoding).
- Fixed small bug in TileGen.cpp which meant both palettes were being completely recomputed pretty much with every frame.  This was a significant performance hit, particularly as palette recomputation is currently being done in SyncSnapshots (it should be moved out of here at some point, although for now it's no big deal).
- Made sure all OpenGL objects and resources are deleted in Render2D/3D destructors, in particular the deleting of the VBO buffer in DestroyModelCache.
- Made sure that GLSL uniforms are always checked to see if they are bound before using them in order to stop unecessary (but harmless) GL errors.
- Altered the default texture sheet handling to use a single large GL texture holding multiple Model3 texture sheets rather than multiple GL textures as before (if required, the old behaviour can still be selected with the mulisheet fragment shader).  I believe this fixes the disappearing crosshairs/corrupt GL state problem which the multisheet fragment shader seemed to be triggering somehow.
- Fixed a bug in debugger which meant memory watches were not triggering properly
2012-07-15 21:04:46 +00:00

842 lines
22 KiB
C++

/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* DriveBoard.cpp
*
* Implementation of the CDriveBoard class: drive board (force feedback)
* emulation.
*
* NOTE: Simulation does not yet work. Drive board ROMs are required.
*/
#include "Supermodel.h"
#include <cstdio>
#include <cmath>
#define RAM_SIZE 0x2000 // Z80 RAM
bool CDriveBoard::IsAttached(void)
{
return m_attached && !m_tmpDisabled;
}
bool CDriveBoard::IsSimulated(void)
{
return m_simulated;
}
void CDriveBoard::GetDIPSwitches(UINT8 &dip1, UINT8 &dip2)
{
dip1 = m_dip1;
dip2 = m_dip2;
}
void CDriveBoard::SetDIPSwitches(UINT8 dip1, UINT8 dip2)
{
m_dip1 = dip1;
m_dip2 = dip2;
}
unsigned CDriveBoard::GetSteeringStrength()
{
return ((~(m_dip1>>2))&7) + 1;
}
void CDriveBoard::SetSteeringStrength(unsigned steeringStrength)
{
m_dip1 = (m_dip1&0xE3) | (((~(steeringStrength - 1))&7)<<2);
}
void CDriveBoard::Get7SegDisplays(UINT8 &seg1Digit1, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2)
{
seg1Digit1 = m_seg1Digit1;
seg1Digit2 = m_seg1Digit2;
seg2Digit1 = m_seg2Digit1;
seg2Digit2 = m_seg2Digit2;
}
CZ80 *CDriveBoard::GetZ80(void)
{
return &m_z80;
}
void CDriveBoard::SaveState(CBlockFile *SaveState)
{
SaveState->NewBlock("DriveBoard", __FILE__);
// Check board is attached and not temporarily disabled
bool attached = m_attached && !m_tmpDisabled;
SaveState->Write(&attached, sizeof(attached));
if (attached)
{
// Check if simulated
SaveState->Write(&m_simulated, sizeof(m_simulated));
if (m_simulated)
{
// TODO - save board simulation state
}
else
{
// Save DIP switches and digit displays
SaveState->Write(&m_dip1, sizeof(m_dip1));
SaveState->Write(&m_dip2, sizeof(m_dip2));
//SaveState->Write(&m_seg1Digit1, sizeof(m_seg1Digit1)); // No point in saving these
//SaveState->Write(&m_seg1Digit2, sizeof(m_seg1Digit2));
//SaveState->Write(&m_seg2Digit1, sizeof(m_seg2Digit1));
//SaveState->Write(&m_seg2Digit2, sizeof(m_seg2Digit2));
// Save RAM state
SaveState->Write(m_ram, RAM_SIZE);
// Save interrupt and input/output state
SaveState->Write(&m_initialized, sizeof(m_initialized));
SaveState->Write(&m_allowInterrupts, sizeof(m_allowInterrupts));
SaveState->Write(&m_dataSent, sizeof(m_dataSent));
SaveState->Write(&m_dataReceived, sizeof(m_dataReceived));
SaveState->Write(&m_adcPortRead, sizeof(m_adcPortRead));
SaveState->Write(&m_adcPortBit, sizeof(m_adcPortBit));
SaveState->Write(&m_uncenterVal1, sizeof(m_uncenterVal1));
SaveState->Write(&m_uncenterVal2, sizeof(m_uncenterVal2));
// Save CPU state
m_z80.SaveState(SaveState, "DriveBoard Z80");
}
}
}
void CDriveBoard::LoadState(CBlockFile *SaveState)
{
if (SaveState->FindBlock("DriveBoard") != OKAY)
{
ErrorLog("Unable to load drive board state. Save state file is corrupt.");
return;
}
// Check that board was attached in saved state
bool wasAttached;
SaveState->Read(&wasAttached, sizeof(wasAttached));
if (wasAttached)
{
// Check that board configuration exactly matches current configuration
bool wasSimulated;
SaveState->Read(&wasSimulated, sizeof(wasSimulated));
if (wasAttached == m_attached && wasSimulated == m_simulated)
{
// Check if board was simulated
if (wasSimulated)
{
// TODO - load board simulation state
}
else
{
// Load DIP switches and digit displays
SaveState->Read(&m_dip1, sizeof(m_dip1));
SaveState->Read(&m_dip2, sizeof(m_dip2));
//SaveState->Read(&m_seg1Digit1, sizeof(m_seg1Digit1));
//SaveState->Read(&m_seg1Digit2, sizeof(m_seg1Digit2));
//SaveState->Read(&m_seg2Digit1, sizeof(m_seg2Digit1));
//SaveState->Read(&m_seg2Digit2, sizeof(m_seg2Digit2));
// Load RAM state
SaveState->Read(m_ram, RAM_SIZE);
// Load interrupt and input/output state
SaveState->Read(&m_initialized, sizeof(m_initialized));
SaveState->Read(&m_allowInterrupts, sizeof(m_allowInterrupts));
SaveState->Read(&m_dataSent, sizeof(m_dataSent));
SaveState->Read(&m_dataReceived, sizeof(m_dataReceived));
SaveState->Read(&m_adcPortRead, sizeof(m_adcPortRead));
SaveState->Read(&m_adcPortBit, sizeof(m_adcPortBit));
SaveState->Read(&m_uncenterVal1, sizeof(m_uncenterVal1));
SaveState->Read(&m_uncenterVal2, sizeof(m_uncenterVal2));
// Load CPU state
m_z80.LoadState(SaveState, "DriveBoard Z80");
}
// Enable board
m_tmpDisabled = false;
}
else
{
// Otherwise, disable board as it can't be used with a mismatching configuratin
m_tmpDisabled = true;
}
}
else
// Disable board if it was not attached
m_tmpDisabled = true;
if (m_attached)
{
if (m_tmpDisabled)
printf("Disabled drive board due to incompatible save state.\n");
SendStopAll();
}
}
bool CDriveBoard::Init(const UINT8 *romPtr)
{
// Assign ROM (note that the ROM data has not yet been loaded)
m_rom = romPtr;
// Check have a valid ROM
m_attached = (m_rom != NULL);
if (!m_attached)
return OKAY;
// Allocate memory for RAM
m_ram = new (std::nothrow) UINT8[RAM_SIZE];
if (NULL == m_ram)
{
float ramSizeMB = (float)RAM_SIZE/(float)0x100000;
return ErrorLog("Insufficient memoy for drive board (needs %1.1f MB).", ramSizeMB);
}
memset(m_ram, 0, RAM_SIZE);
// Initialize Z80
m_z80.Init(this, NULL);
return OKAY;
}
void CDriveBoard::AttachInputs(CInputs *inputs, unsigned gameInputFlags)
{
m_inputs = inputs;
m_inputFlags = gameInputFlags;
DebugLog("DriveBoard attached inputs\n");
}
void CDriveBoard::AttachOutputs(COutputs *outputs)
{
m_outputs = outputs;
DebugLog("DriveBoard attached outputs\n");
}
void CDriveBoard::Reset(void)
{
m_tmpDisabled = false;
m_initialized = false;
m_allowInterrupts = false;
m_seg1Digit1 = 0xFF;
m_seg1Digit2 = 0xFF;
m_seg2Digit1 = 0xFF;
m_seg2Digit2 = 0xFF;
m_dataSent = 0;
m_dataReceived = 0;
m_adcPortRead = 0;
m_adcPortBit = 0;
m_port42Out = 0;
m_port46Out = 0;
m_prev42Out = 0;
m_prev46Out = 0;
m_initState = 0;
m_boardMode = 0;
m_readMode = 0;
m_wheelCenter = 0x80;
m_uncenterVal1 = 0;
m_uncenterVal2 = 0;
m_lastConstForce = 0;
m_lastSelfCenter = 0;
m_lastFriction = 0;
m_lastVibrate = 0;
// Configure options (cannot be done in Init() because command line settings weren't yet parsed)
m_simulated = g_Config.simulateDrvBoard;
SetSteeringStrength(g_Config.steeringStrength);
m_z80.Reset(); // always reset to provide a valid Z80 state
if (!g_Config.forceFeedback)
m_attached = false;
// Stop any effects that may still be playing
if (m_attached)
SendStopAll();
}
UINT8 CDriveBoard::Read(void)
{
// TODO - simulate initialization sequence even when emulating to get rid of long pause at boot up (drive board can
// carry on booting whilst game starts)
if (m_simulated)
return SimulateRead();
else
return m_dataReceived;
}
void CDriveBoard::Write(UINT8 data)
{
//if (data >= 0x01 && data <= 0x0F ||
// data >= 0x20 && data <= 0x2F ||
// data >= 0x30 && data <= 0x3F ||
// data >= 0x40 && data <= 0x4F ||
// data >= 0x70 && data <= 0x7F)
// printf("DriveBoard.Write(%02X)\n", data);
if (m_simulated)
SimulateWrite(data);
else
{
m_dataSent = data;
if (data == 0xCB)
m_initialized = false;
}
}
UINT8 CDriveBoard::SimulateRead(void)
{
if (m_initialized)
{
switch (m_readMode)
{
case 0x0: return m_statusFlags; // Status flags
case 0x1: return m_dip1; // DIP switch 1 value
case 0x2: return m_dip2; // DIP switch 2 value
case 0x3: return m_wheelCenter; // Wheel center
case 0x4: return 0x80; // Cockpit banking center
case 0x5: return m_inputs->steering->value; // Wheel position
case 0x6: return 0x80; // Cockpit banking position
case 0x7: return m_echoVal; // Init status/echo test
default: return 0xFF;
}
}
else
{
switch (m_initState / 5)
{
case 0: return 0xCF; // Initiate start
case 1: return 0xCE;
case 2: return 0xCD;
case 3: return 0xCC; // Centering wheel
default:
m_initialized = true;
return 0x80;
}
}
}
void CDriveBoard::SimulateWrite(UINT8 cmd)
{
// Following are commands for Scud Race. Daytona 2 has a compatible command set while Sega Rally 2 is completely different
// TODO - finish for Scud Race and Daytona 2
// TODO - implement for Sega Rally 2
UINT8 type = cmd>>4;
UINT8 val = cmd&0xF;
switch (type)
{
case 0: // 0x00-0F Play sequence
/* TODO */
break;
case 1: // 0x10-1F Set centering strength
if (val == 0)
// Disable auto-centering
// TODO - is 0x10 for disable?
SendSelfCenter(0);
else
// Enable auto-centering (0x1 = weakest, 0xF = strongest)
SendSelfCenter(val * 0x11);
break;
case 2: // 0x20-2F Friction strength
if (val == 0)
// Disable friction
// TODO - is 0x20 for disable?
SendFriction(0);
else
// Enable friction (0x1 = weakest, 0xF = strongest)
SendFriction(val * 0x11);
break;
case 3: // 0x30-3F Uncentering (vibrate)
if (val == 0)
// Disable uncentering
SendVibrate(0);
else
// Enable uncentering (0x1 = weakest, 0xF = strongest)
SendVibrate(val * 0x11);
break;
case 4: // 0x40-4F Play power-slide sequence
/* TODO */
break;
case 5: // 0x50-5F Rotate wheel right
SendConstantForce((val + 1) * 0x5);
break;
case 6: // 0x60-6F Rotate wheel left
SendConstantForce(-(val + 1) * 0x5);
break;
case 7: // 0x70-7F Set steering parameters
/* TODO */
break;
case 8: // 0x80-8F Test Mode
switch (val&0x7)
{
case 0: SendStopAll(); break; // 0x80 Stop motor
case 1: SendConstantForce(20); break; // 0x81 Roll wheel right
case 2: SendConstantForce(-20); break; // 0x82 Roll wheel left
case 3: /* Ignore - no clutch */ break; // 0x83 Clutch on
case 4: /* Ignore - no clutch */ break; // 0x84 Clutch off
case 5: m_wheelCenter = m_inputs->steering->value; break; // 0x85 Set wheel center position
case 6: /* Ignore */ break; // 0x86 Set cockpit banking position
case 7: /* Ignore */ break; // 0x87 Lamp on/off
}
case 0x9: // 0x90-9F ??? Don't appear to have any effect with Scud Race ROM
/* TODO */
break;
case 0xA: // 0xA0-AF ??? Don't appear to have any effect with Scud Race ROM
/* TODO */
break;
case 0xB: // 0xB0-BF Invalid command (reserved for use by PPC to send cabinet type 0xB0 or 0xB1 during initialization)
/* Ignore */
break;
case 0xC: // 0xC0-CF Set board mode (0xCB = reset board)
SendStopAll();
if (val >= 0xB)
{
// Reset board
m_initialized = false;
m_initState = 0;
}
else
m_boardMode = val;
break;
case 0xD: // 0xD0-DF Set read mode
m_readMode = val&0x7;
break;
case 0xE: // 0xE0-EF Invalid command
/* Ignore */
break;
case 0xF: // 0xF0-FF Echo test
m_echoVal = val;
break;
}
}
void CDriveBoard::RunFrame(void)
{
if (m_simulated)
SimulateFrame();
else
EmulateFrame();
}
void CDriveBoard::SimulateFrame(void)
{
if (!m_initialized)
m_initState++;
// TODO - update m_statusFlags and play preset scripts according to board mode
}
void CDriveBoard::EmulateFrame(void)
{
// Assuming Z80 runs @ 4.0MHz and NMI triggers @ 60.0KHz
// TODO - find out if Z80 frequency is correct and exact frequency of NMI interrupts (just guesswork at the moment!)
int cycles = 4.0 * 1000000 / 60;
int loopCycles = 10000;
while (cycles > 0)
{
if (m_allowInterrupts)
m_z80.TriggerNMI();
cycles -= m_z80.Run(min<int>(loopCycles, cycles));
}
}
UINT8 CDriveBoard::Read8(UINT32 addr)
{
// TODO - shouldn't end of ROM be 0x7FFF not 0x8FFF?
if (addr < 0x9000) // ROM is 0x0000-0x8FFF
return m_rom[addr];
else if (addr >= 0xE000) // RAM is 0xE000-0xFFFF
return m_ram[(addr-0xE000)&0x1FFF];
else
{
//printf("Unhandled Z80 read of %08X (at PC = %04X)\n", addr, m_z80.GetPC());
return 0xFF;
}
}
void CDriveBoard::Write8(UINT32 addr, UINT8 data)
{
if (addr >= 0xE000) // RAM is 0xE000-0xFFFF
m_ram[(addr-0xE000)&0x1FFF] = data;
#ifdef DEBUG
else
printf("Unhandled Z80 write to %08X (at PC = %04X)\n", addr, m_z80.GetPC());
#endif
}
UINT8 CDriveBoard::IORead8(UINT32 portNum)
{
UINT8 adcVal;
switch (portNum)
{
case 32: // DIP 1 value
return m_dip1;
case 33: // 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
if (portNum == m_adcPortRead && m_adcPortBit-- > 0)
{
switch (portNum)
{
case 36: // Not connected
adcVal = 0x00;
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
break;
case 38: // Cockpit bank position for deluxe racing cabinets
adcVal = 0x80;
break;
case 39: // Not connected
adcVal = 0x00;
break;
default:
#ifdef DEBUG
printf("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
#endif
return 0xFF;
}
return (adcVal>>m_adcPortBit)&0x01;
}
else
{
#ifdef DEBUG
printf("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
#endif
return 0xFF;
}
case 40: // PPC command
return m_dataSent;
case 44: // 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
// 1 0 = encoder error 2 - overheat error
// 1 1 = encoder error 3 - encoder error, reinitializes board
return 0x00;
default:
#ifdef DEBUG
printf("Unhandled Z80 input on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
#endif
return 0xFF;
}
}
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
return;
case 17: // 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
return;
case 32: // Left digit of 7-segment display 1
m_seg1Digit1 = data;
return;
case 33: // Right digit of 7-segment display 1
m_seg1Digit2 = data;
return;
case 34: // Left digit of 7-segment display 2
m_seg2Digit1 = data;
return;
case 35: // 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
m_adcPortRead = portNum;
m_adcPortBit = 8;
return;
case 41: // Reply for PPC
m_dataReceived = data;
if (data == 0xCC)
m_initialized = true;
return;
case 42: // Encoder motor data
m_port42Out = data;
ProcessEncoderCmd();
return;
case 45: // Clutch/lamp control (deluxe cabinets)
return;
case 46: // Encoder motor control
m_port46Out = data;
return;
case 240: // Unsure? - single byte 0xBB sent at initialization only
return;
case 241: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog?
return;
default:
#ifdef DEBUG
printf("Unhandled Z80 output on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
#endif
return;
}
}
void CDriveBoard::ProcessEncoderCmd(void)
{
if (m_prev42Out != m_port42Out || m_prev46Out != m_port46Out)
{
//printf("46 [%02X] / 42 [%02X]\n", m_port46Out, m_port42Out);
switch (m_port46Out)
{
case 0xFB:
// TODO - friction? Sent during power slide. 0xFF = strongest or 0x00?
//SendFriction(m_port42Out);
break;
case 0xFC:
// Centering / uncentering (vibrate)
// Bit 2 = on for centering, off for uncentering
if (m_port42Out&0x04)
{
// Centering
// Bit 7 = on for disable, off for enable
if (m_port42Out&0x80)
{
// Disable centering
SendSelfCenter(0);
}
else
{
// Bits 3-6 = centering strength 0x0-0xF. This is scaled to range 0x0F-0xFF
UINT8 strength = ((m_port42Out&0x78)>>3) * 0x10 + 0xF;
SendSelfCenter(strength);
}
}
else
{
// Uncentering
// Bits 0-1 = data sequence number 0-3
UINT8 seqNum = m_port42Out&0x03;
// Bits 4-7 = data values
UINT16 data = (m_port42Out&0xF0)>>4;
switch (seqNum)
{
case 0: m_uncenterVal1 = data<<4; break;
case 1: m_uncenterVal1 |= data; break;
case 2: m_uncenterVal2 = data<<4; break;
case 3: m_uncenterVal2 |= data; break;
}
if (seqNum == 0 && m_uncenterVal1 == 0)
{
// Disable uncentering
SendVibrate(0);
}
else if (seqNum == 3 && m_uncenterVal1 > 0)
{
// Uncentering - unsure exactly how values sent map to strength or whether they specify some other attributes of effect
// For now just attempting to map them to a sensible value in range 0x00-0xFF
UINT8 strength = ((m_uncenterVal1>>1) - 7) * 0x50 + ((m_uncenterVal2>>1) - 5) * 0x10 + 0xF;
SendVibrate(strength);
}
}
break;
case 0xFD:
// TODO - unsure? Sent as velocity changes, similar to self-centering
break;
case 0xFE:
// Apply constant force to wheel
// Value is: 0x80 = stop motor, 0x81-0xC0 = roll wheel left, 0x40-0x7F = roll wheel right, scale to range -0x80-0x7F
// Note: seems to often output 0x7F or 0x81 for stop motor, so narrowing wheel ranges to 0x40-0x7E and 0x82-0xC0
if (m_port42Out > 0x81)
{
if (m_port42Out <= 0xC0)
SendConstantForce(2 * (0x81 - m_port42Out));
else
SendConstantForce(-0x80);
}
else if (m_port42Out < 0x7F)
{
if (m_port42Out >= 0x40)
SendConstantForce(2 * (0x7F - m_port42Out));
else
SendConstantForce(0x7F);
}
else
SendConstantForce(0);
break;
case 0xFF:
// Stop all effects
if (m_port42Out == 0)
SendStopAll();
break;
default:
//printf("Unknown = 46 [%02X] / 42 [%02X]\n", m_port46Out, m_port42Out);
break;
}
m_prev42Out = m_port42Out;
m_prev46Out = m_port46Out;
}
}
void CDriveBoard::SendStopAll(void)
{
//printf(">> Stop All Effects\n");
ForceFeedbackCmd ffCmd;
ffCmd.id = FFStop;
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
m_lastConstForce = 0;
m_lastSelfCenter = 0;
m_lastFriction = 0;
m_lastVibrate = 0;
}
void CDriveBoard::SendConstantForce(INT8 val)
{
if (val == m_lastConstForce)
return;
/*
if (val > 0)
{
printf(">> Force Right %02X [%8s", val, "");
for (unsigned i = 0; i < 8; i++)
printf(i == 0 || i <= (val + 1) / 16 ? ">" : " ");
printf("]\n");
}
else if (val < 0)
{
printf(">> Force Left %02X [", -val);
for (unsigned i = 0; i < 8; i++)
printf(i == 7 || i >= (val + 128) / 16 ? "<" : " ");
printf("%8s]\n", "");
}
else
printf(">> Stop Force [%16s]\n", "");
*/
ForceFeedbackCmd ffCmd;
ffCmd.id = FFConstantForce;
ffCmd.force = (float)val / (val >= 0 ? 127.0f : 128.0f);
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
m_lastConstForce = val;
}
void CDriveBoard::SendSelfCenter(UINT8 val)
{
if (val == m_lastSelfCenter)
return;
/*
if (val == 0)
printf(">> Stop Self-Center\n");
else
printf(">> Self-Center %02X\n", val);
*/
ForceFeedbackCmd ffCmd;
ffCmd.id = FFSelfCenter;
ffCmd.force = (float)val / 255.0f;
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
m_lastSelfCenter = val;
}
void CDriveBoard::SendFriction(UINT8 val)
{
if (val == m_lastFriction)
return;
/*
if (val == 0)
printf(">> Stop Friction\n");
else
printf(">> Friction %02X\n", val);
*/
ForceFeedbackCmd ffCmd;
ffCmd.id = FFFriction;
ffCmd.force = (float)val / 255.0f;
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
m_lastFriction = val;
}
void CDriveBoard::SendVibrate(UINT8 val)
{
if (val == m_lastVibrate)
return;
/*
if (val == 0)
printf(">> Stop Vibrate\n");
else
printf(">> Vibrate %02X\n", val);
*/
ForceFeedbackCmd ffCmd;
ffCmd.id = FFVibrate;
ffCmd.force = (float)val / 255.0f;
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
m_lastVibrate = val;
}
CDriveBoard::CDriveBoard() : m_attached(false), m_tmpDisabled(false), m_simulated(false),
m_rom(NULL), m_ram(NULL), m_inputs(NULL), m_outputs(NULL), m_dip1(0xCF), m_dip2(0xFF)
{
DebugLog("Built Drive Board\n");
}
CDriveBoard::~CDriveBoard(void)
{
if (m_ram != NULL)
{
delete[] m_ram;
m_ram = NULL;
}
m_rom = NULL;
m_inputs = NULL;
m_outputs = NULL;
DebugLog("Destroyed Drive Board\n");
}