Supermodel/Src/Model3/DriveBoard/SkiBoard.cpp
SpinDizzy 08d4735ee8 Huge refactor of the Driveboard:
-Separate each possible boards (wheel, joystick, skipad, billboard).
-Defined a Driveboard type in Games.xml for each games.
-Due to the refactoring, Driveboard Savestates have changed (a common base data + a specific board data are saved).
-Backwards compatibility with previous save states is maintained.
-Driveboard rom section is no longer required anymore. This disables Driveboard emulation in case the rom is not found.
-Added Billboard emulation (vf3, vs2, fvipers2, von2). 7 segments and lamps Outputs are redirected to Supermodel outputs.
-Changes project to C++ 17 standard.
2021-02-18 10:29:15 +00:00

267 lines
5.9 KiB
C++

/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
** Harry Tuttle, and Spindizzi
**
** 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/>.
**/
/*
* SkiBoard.cpp
*
* Implementation of the CSkiBoard class: rumble ski pad emulation
* emulation.
*
* NOTE: Simulation does not yet work. Drive board ROMs are required.
*/
#include "Supermodel.h"
#include <cstdio>
#include <cmath>
#include <algorithm>
Game::DriveBoardType CSkiBoard::GetType(void)
{
return Game::DRIVE_BOARD_SKI;
}
unsigned CSkiBoard::GetForceFeedbackStrength()
{
return 0;
}
void CSkiBoard::SetForceFeedbackStrength(unsigned strength)
{
}
void CSkiBoard::LoadState(CBlockFile *SaveState)
{
CDriveBoard::LoadState(SaveState);
if (!IsDisabled())
SendVibrate(0);
}
void CSkiBoard::Disable(void)
{
SendVibrate(0);
CDriveBoard::Disable();
}
bool CSkiBoard::Init(const UINT8 *romPtr)
{
bool result = CDriveBoard::Init(romPtr);
m_simulated = true;
return result;
}
void CSkiBoard::Reset(void)
{
CDriveBoard::Reset();
m_lastVibrate = 0;
if (!m_config["ForceFeedback"].ValueAsDefault<bool>(false))
Disable();
// Stop any effects that may still be playing
if (!IsDisabled())
SendVibrate(0);
}
UINT8 CSkiBoard::Read(void)
{
if (IsDisabled())
{
return 0xFF;
}
// 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)
return SimulateRead();
}
void CSkiBoard::Write(UINT8 data)
{
if (IsDisabled())
{
return;
}
//if (data >= 0x01 && data <= 0x0F ||
// data >= 0x20 && data <= 0x2F ||
// data >= 0x30 && data <= 0x3F ||
// data >= 0x40 && data <= 0x4F ||
// data >= 0x70 && data <= 0x7F)
// DebugLog("DriveBoard.Write(%02X)\n", data);
if (m_simulated)
SimulateWrite(data);
}
UINT8 CSkiBoard::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->skiX->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 CSkiBoard::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;
// Ski Champ vibration pad
switch (type)
{
case 0x00: // nothing to do ?
break;
case 0x01: // full stop ?
if (val == 0)
SendVibrate(0);
break;
case 0x04:
SendVibrate(val*0x11);
break;
case 0x08:
if (val == 0x08)
{
// test driveboard passed ?
}
break;
case 0x0A: // only when test in service menu
switch (val)
{
case 3: // clutch
break;
case 5: // motor test
SendVibrate(val * 0x11);
break;
case 6: // end motor test
SendVibrate(0);
break;
}
break;
case 0x0C: // game state or reset driveboard ?
switch (val)
{
case 1:
// in game
break;
case 2:
// game ready
break;
case 3:
// test mode
break;
}
break;
case 0x0D: // 0xD0-DF Set read mode
m_readMode = val & 0x7;
break;
default:
//DebugLog("Skipad unknown command %02X , val= %02X\n",type,val);
break;
}
}
void CSkiBoard::RunFrame(void)
{
if (m_simulated)
{
if (!m_initialized)
m_initState++;
}
}
UINT8 CSkiBoard::Read8(UINT32 addr)
{
return 0xFF;
}
void CSkiBoard::Write8(UINT32 addr, UINT8 data)
{
}
void CSkiBoard::SendVibrate(UINT8 val)
{
if (val == m_lastVibrate)
return;
/*
if (val == 0)
DebugLog(">> Stop Vibrate\n");
else
DebugLog(">> Vibrate %02X\n", val);
*/
ForceFeedbackCmd ffCmd;
ffCmd.id = FFVibrate;
ffCmd.force = (float)val / 255.0f;
m_inputs->skiX->SendForceFeedbackCmd(ffCmd);
m_lastVibrate = val;
}
CSkiBoard::CSkiBoard(const Util::Config::Node& config)
: CDriveBoard(config),
m_lastVibrate(0)
{
m_attached = false;
m_simulated = false;
m_initialized = false;
m_dip1 = 0xCF;
m_dip2 = 0xFF;
DebugLog("Built Drive Board (ski pad)\n");
}
CSkiBoard::~CSkiBoard(void)
{
}