mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 07:05:40 +00:00
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.
This commit is contained in:
parent
ab367774d3
commit
08d4735ee8
122
Config/Games.xml
122
Config/Games.xml
|
@ -2,9 +2,9 @@
|
||||||
Supermodel
|
Supermodel
|
||||||
A Sega Model 3 Arcade Emulator.
|
A Sega Model 3 Arcade Emulator.
|
||||||
Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
|
Copyright 2011-2017 Bart Trzynadlowski, Nik Henson, Ian Curtis
|
||||||
|
|
||||||
Games.xml
|
Games.xml
|
||||||
|
|
||||||
This file defines ROM sets and is required in order to recognize and properly
|
This file defines ROM sets and is required in order to recognize and properly
|
||||||
load them. Do not modify this unless you really know what you're doing!
|
load them. Do not modify this unless you really know what you're doing!
|
||||||
-->
|
-->
|
||||||
|
@ -171,6 +171,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame>
|
<real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -246,7 +247,7 @@
|
||||||
<file offset="0x800000" name="mpr-20889.ic22" crc32="0x18EEC79E" />
|
<file offset="0x800000" name="mpr-20889.ic22" crc32="0x18EEC79E" />
|
||||||
<file offset="0xC00000" name="mpr-20890.ic24" crc32="0xAAC96FA2" />
|
<file offset="0xC00000" name="mpr-20890.ic24" crc32="0xAAC96FA2" />
|
||||||
</region>
|
</region>
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-20985.bin" crc32="0xB139481D" />
|
<file offset="0" name="epr-20985.bin" crc32="0xB139481D" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -263,6 +264,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -295,7 +297,7 @@
|
||||||
<file offset="0x4000002" name="mpr-21192.11" crc32="0x60CBB1FA" />
|
<file offset="0x4000002" name="mpr-21192.11" crc32="0x60CBB1FA" />
|
||||||
<file offset="0x4000004" name="mpr-21191.10" crc32="0xA2BDCFE0" />
|
<file offset="0x4000004" name="mpr-21191.10" crc32="0xA2BDCFE0" />
|
||||||
<file offset="0x4000006" name="mpr-21190.9" crc32="0x984D56EB" />
|
<file offset="0x4000006" name="mpr-21190.9" crc32="0x984D56EB" />
|
||||||
<!-- CROM3 -->
|
<!-- CROM3 -->
|
||||||
<file offset="0x6000000" name="mpr-21197.16" crc32="0x04015247" />
|
<file offset="0x6000000" name="mpr-21197.16" crc32="0x04015247" />
|
||||||
<file offset="0x6000002" name="mpr-21196.15" crc32="0x0AB46DB5" />
|
<file offset="0x6000002" name="mpr-21196.15" crc32="0x0AB46DB5" />
|
||||||
<file offset="0x6000004" name="mpr-21195.14" crc32="0x7F39761C" />
|
<file offset="0x6000004" name="mpr-21195.14" crc32="0x7F39761C" />
|
||||||
|
@ -342,6 +344,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -399,7 +402,7 @@
|
||||||
<file offset="0x800000" name="mpr-21032.23" crc32="0x3D3FF407" />
|
<file offset="0x800000" name="mpr-21032.23" crc32="0x3D3FF407" />
|
||||||
</region>
|
</region>
|
||||||
<!-- Spindizzi notes : apparently it uses the same rom from scud race-->
|
<!-- Spindizzi notes : apparently it uses the same rom from scud race-->
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -416,6 +419,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -448,6 +452,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -468,7 +473,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="dirtdvlsj" parent="dirtdvls">
|
<game name="dirtdvlsj" parent="dirtdvls">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Dirt Devils</title>
|
<title>Dirt Devils</title>
|
||||||
|
@ -480,6 +485,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -512,6 +518,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -543,6 +550,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -607,7 +615,7 @@
|
||||||
<file offset="0xC00000" name="mpr-22890.25" crc32="0xB638BD7C" />
|
<file offset="0xC00000" name="mpr-22890.25" crc32="0xB638BD7C" />
|
||||||
</region>
|
</region>
|
||||||
<!-- Spindizzi notes : apparently it uses the same rom from scud race-->
|
<!-- Spindizzi notes : apparently it uses the same rom from scud race-->
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -623,6 +631,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -668,6 +677,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -689,7 +699,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="ecaj" parent="eca">
|
<game name="ecaj" parent="eca">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Emergency Call Ambulance</title>
|
<title>Emergency Call Ambulance</title>
|
||||||
|
@ -700,6 +710,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -732,6 +743,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame>
|
<real3d_status_bit_set_percent_of_frame>24</real3d_status_bit_set_percent_of_frame>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -797,9 +809,12 @@
|
||||||
<file offset="0x800000" name="mpr-20577" crc32="0x3B236187" />
|
<file offset="0x800000" name="mpr-20577" crc32="0x3B236187" />
|
||||||
<file offset="0xC00000" name="mpr-20579" crc32="0x08788436" />
|
<file offset="0xC00000" name="mpr-20579" crc32="0x08788436" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="fvipers2o" parent="fvipers2">
|
<game name="fvipers2o" parent="fvipers2">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Fighting Vipers 2</title>
|
<title>Fighting Vipers 2</title>
|
||||||
|
@ -1013,6 +1028,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1077,7 +1093,7 @@
|
||||||
</region>
|
</region>
|
||||||
<!-- Spindizzi notes : original Driveboard from model2 hardware -->
|
<!-- Spindizzi notes : original Driveboard from model2 hardware -->
|
||||||
<!-- Not working ATM - Commands don't correspond -->
|
<!-- Not working ATM - Commands don't correspond -->
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-18261.bin" crc32="0x0C7FAC58" />
|
<file offset="0" name="epr-18261.bin" crc32="0x0C7FAC58" />
|
||||||
<!-- Driveboard program from scud - Can be a replacement from original model2 z80 program -->
|
<!-- Driveboard program from scud - Can be a replacement from original model2 z80 program -->
|
||||||
<!-- I think Model3 driveboard hardware acts exactly like Model2 driveboard hardware (same irq, same memory mapping, same commands etc...) -->
|
<!-- I think Model3 driveboard hardware acts exactly like Model2 driveboard hardware (same irq, same memory mapping, same commands etc...) -->
|
||||||
|
@ -1272,7 +1288,7 @@
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="analog_gun1" />
|
<input type="analog_gun1" />
|
||||||
<input type="analog_gun2" />
|
<input type="analog_gun2" />
|
||||||
</inputs>
|
</inputs>
|
||||||
<encryption_key>0x292B6A01</encryption_key>
|
<encryption_key>0x292B6A01</encryption_key>
|
||||||
</hardware>
|
</hardware>
|
||||||
<roms>
|
<roms>
|
||||||
|
@ -1340,6 +1356,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
<mpeg_board>DSB1</mpeg_board>
|
<mpeg_board>DSB1</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1406,7 +1423,7 @@
|
||||||
<file offset="0x400000" name="mpr-19605.59" crc32="0xBEC891EB" />
|
<file offset="0x400000" name="mpr-19605.59" crc32="0xBEC891EB" />
|
||||||
<file offset="0x600000" name="mpr-19606.60" crc32="0xADAD46B2" />
|
<file offset="0x600000" name="mpr-19606.60" crc32="0xADAD46B2" />
|
||||||
</region>
|
</region>
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
<file offset="0" name="epr-19338a.bin" crc32="0xC9FAC464" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -1423,6 +1440,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
<mpeg_board>DSB1</mpeg_board>
|
<mpeg_board>DSB1</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1456,6 +1474,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
<mpeg_board>DSB1</mpeg_board>
|
<mpeg_board>DSB1</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1527,6 +1546,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
<mpeg_board>DSB1</mpeg_board>
|
<mpeg_board>DSB1</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1555,7 +1575,7 @@
|
||||||
<region name="sound_samples" stride="1" chunk_size="1" byte_swap="true">
|
<region name="sound_samples" stride="1" chunk_size="1" byte_swap="true">
|
||||||
<file offset="0x400000" name="mpr-20101.24" crc32="0x66D1E31F" />
|
<file offset="0x400000" name="mpr-20101.24" crc32="0x66D1E31F" />
|
||||||
</region>
|
</region>
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-19338.bin" crc32="0xDBF88DE6" />
|
<file offset="0" name="epr-19338.bin" crc32="0xDBF88DE6" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -1572,6 +1592,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
<mpeg_board>DSB1</mpeg_board>
|
<mpeg_board>DSB1</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<pci_bridge>MPC106</pci_bridge>
|
<pci_bridge>MPC106</pci_bridge>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -1601,7 +1622,7 @@
|
||||||
<region name="sound_samples" stride="1" chunk_size="1" byte_swap="true">
|
<region name="sound_samples" stride="1" chunk_size="1" byte_swap="true">
|
||||||
<file offset="0x400000" name="mpr-20101.24" crc32="0x66D1E31F" />
|
<file offset="0x400000" name="mpr-20101.24" crc32="0x66D1E31F" />
|
||||||
</region>
|
</region>
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-19338.bin" crc32="0xDBF88DE6" />
|
<file offset="0" name="epr-19338.bin" crc32="0xDBF88DE6" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -1617,6 +1638,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Ski</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1650,7 +1672,7 @@
|
||||||
<file offset="0x2000002" name="mpr-20328.11" crc32="0x5FA5E9F5" />
|
<file offset="0x2000002" name="mpr-20328.11" crc32="0x5FA5E9F5" />
|
||||||
<file offset="0x2000004" name="mpr-20327.10" crc32="0xF55F51B2" />
|
<file offset="0x2000004" name="mpr-20327.10" crc32="0xF55F51B2" />
|
||||||
<file offset="0x2000006" name="mpr-20326.9" crc32="0xB63E1CB4" />
|
<file offset="0x2000006" name="mpr-20326.9" crc32="0xB63E1CB4" />
|
||||||
<!-- CROM3 -->
|
<!-- CROM3 -->
|
||||||
<file offset="0x3000000" name="mpr-20333.16" crc32="0x76B8E0FA" />
|
<file offset="0x3000000" name="mpr-20333.16" crc32="0x76B8E0FA" />
|
||||||
<file offset="0x3000002" name="mpr-20332.15" crc32="0x500DB1EE" />
|
<file offset="0x3000002" name="mpr-20332.15" crc32="0x500DB1EE" />
|
||||||
<file offset="0x3000004" name="mpr-20331.14" crc32="0xC4C45FB1" />
|
<file offset="0x3000004" name="mpr-20331.14" crc32="0xC4C45FB1" />
|
||||||
|
@ -1869,6 +1891,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -1942,7 +1965,7 @@
|
||||||
<file offset="0x800000" name="mpr-20639.59" crc32="0xF6603B7B" />
|
<file offset="0x800000" name="mpr-20639.59" crc32="0xF6603B7B" />
|
||||||
<file offset="0xC00000" name="mpr-20640.60" crc32="0x9EEA07B7" />
|
<file offset="0xC00000" name="mpr-20640.60" crc32="0x9EEA07B7" />
|
||||||
</region>
|
</region>
|
||||||
<region name="driveboard_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-20512.bin" crc32="0xCF64350D" />
|
<file offset="0" name="epr-20512.bin" crc32="0xCF64350D" />
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
|
@ -1959,6 +1982,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -2001,7 +2025,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="srally2pa" parent="srally2">
|
<game name="srally2pa" parent="srally2">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Sega Rally 2</title>
|
<title>Sega Rally 2</title>
|
||||||
|
@ -2013,6 +2037,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="vehicle" />
|
<input type="vehicle" />
|
||||||
|
@ -2049,7 +2074,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="srally2dx" parent="srally2">
|
<game name="srally2dx" parent="srally2">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Sega Rally 2</title>
|
<title>Sega Rally 2</title>
|
||||||
|
@ -2065,6 +2090,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Wheel</drive_board>
|
||||||
<real3d_status_bit_set_percent_of_frame>48</real3d_status_bit_set_percent_of_frame>
|
<real3d_status_bit_set_percent_of_frame>48</real3d_status_bit_set_percent_of_frame>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -2076,14 +2102,14 @@
|
||||||
</hardware>
|
</hardware>
|
||||||
<roms>
|
<roms>
|
||||||
<patches>
|
<patches>
|
||||||
<!--
|
<!--
|
||||||
Base offset of program in CROM space: 0 (lines up perfectly with RAM
|
Base offset of program in CROM space: 0 (lines up perfectly with RAM
|
||||||
offsets). The game gets stuck in a region of code that appears to
|
offsets). The game gets stuck in a region of code that appears to
|
||||||
wait on some exception to occur. It also appears to access the
|
wait on some exception to occur. It also appears to access the
|
||||||
tilegen space, indicating that this is perhaps related to frame
|
tilegen space, indicating that this is perhaps related to frame
|
||||||
timing and IRQs (pure speculation; I haven't really looked at it).
|
timing and IRQs (pure speculation; I haven't really looked at it).
|
||||||
Multiple places can be patched out - another one that works is
|
Multiple places can be patched out - another one that works is
|
||||||
0x66ae0.
|
0x66ae0.
|
||||||
-->
|
-->
|
||||||
<patch region="crom" bits="32" offset="0x66aa8" value="0x60000000" />
|
<patch region="crom" bits="32" offset="0x66aa8" value="0x60000000" />
|
||||||
</patches>
|
</patches>
|
||||||
|
@ -2149,6 +2175,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Joystick</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="analog_joystick" />
|
<input type="analog_joystick" />
|
||||||
|
@ -2226,11 +2253,11 @@
|
||||||
<file offset="0xC00000" name="mpr-21378.24" crc32="0x1FCF715E" />
|
<file offset="0xC00000" name="mpr-21378.24" crc32="0x1FCF715E" />
|
||||||
</region>
|
</region>
|
||||||
<!-- Force feedback controller prg -->
|
<!-- Force feedback controller prg -->
|
||||||
|
|
||||||
<region name="ffb_program" stride="1" chunk_size="1">
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
<file offset="0" name="epr-21119.ic8" crc32="0x65082B14" />
|
<file offset="0" name="epr-21119.ic8" crc32="0x65082B14" />
|
||||||
</region>
|
</region>
|
||||||
|
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -2245,6 +2272,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Joystick</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="analog_joystick" />
|
<input type="analog_joystick" />
|
||||||
|
@ -2274,7 +2302,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="swtrilgyp" parent="swtrilgy">
|
<game name="swtrilgyp" parent="swtrilgy">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Star Wars Trilogy Arcade</title>
|
<title>Star Wars Trilogy Arcade</title>
|
||||||
|
@ -2286,6 +2314,7 @@
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
<mpeg_board>DSB2</mpeg_board>
|
<mpeg_board>DSB2</mpeg_board>
|
||||||
|
<drive_board>Joystick</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="analog_joystick" />
|
<input type="analog_joystick" />
|
||||||
|
@ -2362,6 +2391,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.0</stepping>
|
<stepping>1.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2423,6 +2453,9 @@
|
||||||
<file offset="0x000000" name="mpr-19209.22" crc32="0x3715E38C" />
|
<file offset="0x000000" name="mpr-19209.22" crc32="0x3715E38C" />
|
||||||
<file offset="0x400000" name="mpr-19210.24" crc32="0xC03D6502" />
|
<file offset="0x400000" name="mpr-19210.24" crc32="0xC03D6502" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -2436,6 +2469,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.0</stepping>
|
<stepping>1.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2463,6 +2497,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.0</stepping>
|
<stepping>1.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2490,6 +2525,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.0</stepping>
|
<stepping>1.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2524,6 +2560,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -2588,6 +2625,9 @@
|
||||||
<file offset="0x800000" name="mpr-20664.23" crc32="0x89220782" />
|
<file offset="0x800000" name="mpr-20664.23" crc32="0x89220782" />
|
||||||
<file offset="0xC00000" name="mpr-20666.25" crc32="0x3ECB2606" />
|
<file offset="0xC00000" name="mpr-20666.25" crc32="0x3ECB2606" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -2601,6 +2641,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -2629,6 +2670,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -2646,7 +2688,7 @@
|
||||||
</region>
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
<game name="von2o" parent="von2">
|
<game name="von2o" parent="von2">
|
||||||
<identity>
|
<identity>
|
||||||
<title>Virtual On 2: Oratorio Tangram</title>
|
<title>Virtual On 2: Oratorio Tangram</title>
|
||||||
|
@ -2657,6 +2699,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
<real3d_pci_id>0x16C311DB</real3d_pci_id>
|
||||||
<netboard>true</netboard>
|
<netboard>true</netboard>
|
||||||
<inputs>
|
<inputs>
|
||||||
|
@ -2685,6 +2728,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2746,6 +2790,9 @@
|
||||||
<file offset="0x000000" name="mpr-19785.22" crc32="0xE7D190E3" />
|
<file offset="0x000000" name="mpr-19785.22" crc32="0xE7D190E3" />
|
||||||
<file offset="0x400000" name="mpr-19786.24" crc32="0xB08D889B" />
|
<file offset="0x400000" name="mpr-19786.24" crc32="0xB08D889B" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -2759,6 +2806,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<pci_bridge>MPC106</pci_bridge>
|
<pci_bridge>MPC106</pci_bridge>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -2787,6 +2835,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<pci_bridge>MPC106</pci_bridge>
|
<pci_bridge>MPC106</pci_bridge>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -2815,6 +2864,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.0</stepping>
|
<stepping>2.0</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2825,10 +2875,10 @@
|
||||||
</hardware>
|
</hardware>
|
||||||
<roms>
|
<roms>
|
||||||
<patches>
|
<patches>
|
||||||
<!--
|
<!--
|
||||||
Offset of program relative to CROM base: 0x600000 (0x200000 in the
|
Offset of program relative to CROM base: 0x600000 (0x200000 in the
|
||||||
ROM itself). Inexplicably, at PC=AFC1C, a call is made to FC78, which
|
ROM itself). Inexplicably, at PC=AFC1C, a call is made to FC78, which
|
||||||
is right in the middle of some totally unrelated initialization code
|
is right in the middle of some totally unrelated initialization code
|
||||||
(ASIC checks). This causes an invalid pointer to be fetched. Perhaps
|
(ASIC checks). This causes an invalid pointer to be fetched. Perhaps
|
||||||
FC78 should be overwritten with other program data by then? Why is it
|
FC78 should be overwritten with other program data by then? Why is it
|
||||||
not? Or, 300138 needs to be written with a non-zero value, it is
|
not? Or, 300138 needs to be written with a non-zero value, it is
|
||||||
|
@ -2889,6 +2939,9 @@
|
||||||
<file offset="0x000000" name="mpr-20903.22" crc32="0xE343E131" />
|
<file offset="0x000000" name="mpr-20903.22" crc32="0xE343E131" />
|
||||||
<file offset="0x400000" name="mpr-20904.24" crc32="0x21A91B84" />
|
<file offset="0x400000" name="mpr-20904.24" crc32="0x21A91B84" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -2902,6 +2955,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<pci_bridge>MPC106</pci_bridge>
|
<pci_bridge>MPC106</pci_bridge>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -2930,6 +2984,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -2992,6 +3047,9 @@
|
||||||
<file offset="0x000000" name="mpr-21513.22" crc32="0xCCA1CC00" />
|
<file offset="0x000000" name="mpr-21513.22" crc32="0xCCA1CC00" />
|
||||||
<file offset="0x400000" name="mpr-21514.24" crc32="0x6CEDD292" />
|
<file offset="0x400000" name="mpr-21514.24" crc32="0x6CEDD292" />
|
||||||
</region>
|
</region>
|
||||||
|
<region name="driveboard_program" stride="1" chunk_size="1" required="false">
|
||||||
|
<file offset="0" name="epr-18022.ic2" crc32="0x0CA70F80" />
|
||||||
|
</region>
|
||||||
</roms>
|
</roms>
|
||||||
</game>
|
</game>
|
||||||
|
|
||||||
|
@ -3005,6 +3063,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -3033,6 +3092,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>1.5</stepping>
|
<stepping>1.5</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<pci_bridge>MPC106</pci_bridge>
|
<pci_bridge>MPC106</pci_bridge>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
|
@ -3061,6 +3121,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
@ -3089,6 +3150,7 @@
|
||||||
<hardware>
|
<hardware>
|
||||||
<platform>Sega Model 3</platform>
|
<platform>Sega Model 3</platform>
|
||||||
<stepping>2.1</stepping>
|
<stepping>2.1</stepping>
|
||||||
|
<drive_board>Billboard</drive_board>
|
||||||
<inputs>
|
<inputs>
|
||||||
<input type="common" />
|
<input type="common" />
|
||||||
<input type="joystick1" />
|
<input type="joystick1" />
|
||||||
|
|
|
@ -73,7 +73,7 @@ ARCH =
|
||||||
OPT = -O3
|
OPT = -O3
|
||||||
WARN = -Wall
|
WARN = -Wall
|
||||||
CSTD = -std=iso9899:2011
|
CSTD = -std=iso9899:2011
|
||||||
CXXSTD = -std=c++14
|
CXXSTD = -std=c++17
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -139,7 +139,11 @@ SRC_FILES = \
|
||||||
Src/Model3/53C810.cpp \
|
Src/Model3/53C810.cpp \
|
||||||
Src/Model3/PCI.cpp \
|
Src/Model3/PCI.cpp \
|
||||||
Src/Model3/RTC72421.cpp \
|
Src/Model3/RTC72421.cpp \
|
||||||
Src/Model3/DriveBoard.cpp \
|
Src/Model3/DriveBoard/DriveBoard.cpp \
|
||||||
|
Src/Model3/DriveBoard/WheelBoard.cpp \
|
||||||
|
Src/Model3/DriveBoard/JoystickBoard.cpp \
|
||||||
|
Src/Model3/DriveBoard/SkiBoard.cpp \
|
||||||
|
Src/Model3/DriveBoard/BillBoard.cpp \
|
||||||
Src/Model3/MPC10x.cpp \
|
Src/Model3/MPC10x.cpp \
|
||||||
Src/Inputs/Input.cpp \
|
Src/Inputs/Input.cpp \
|
||||||
Src/Inputs/Inputs.cpp \
|
Src/Inputs/Inputs.cpp \
|
||||||
|
|
12
Src/Game.h
12
Src/Game.h
|
@ -25,7 +25,7 @@ struct Game
|
||||||
INPUT_UI = 0, // special code reserved for Supermodel UI inputs
|
INPUT_UI = 0, // special code reserved for Supermodel UI inputs
|
||||||
INPUT_COMMON = 0x00000001, // common controls (coins, service, test)
|
INPUT_COMMON = 0x00000001, // common controls (coins, service, test)
|
||||||
INPUT_VEHICLE = 0x00000002, // vehicle controls
|
INPUT_VEHICLE = 0x00000002, // vehicle controls
|
||||||
INPUT_JOYSTICK1 = 0x00000004, // joystick 1
|
INPUT_JOYSTICK1 = 0x00000004, // joystick 1
|
||||||
INPUT_JOYSTICK2 = 0x00000008, // joystick 2
|
INPUT_JOYSTICK2 = 0x00000008, // joystick 2
|
||||||
INPUT_FIGHTING = 0x00000010, // fighting game controls
|
INPUT_FIGHTING = 0x00000010, // fighting game controls
|
||||||
INPUT_VR4 = 0x00000020, // four VR view buttons
|
INPUT_VR4 = 0x00000020, // four VR view buttons
|
||||||
|
@ -48,6 +48,16 @@ struct Game
|
||||||
INPUT_ALL = 0x003FFFFF
|
INPUT_ALL = 0x003FFFFF
|
||||||
};
|
};
|
||||||
uint32_t inputs = 0;
|
uint32_t inputs = 0;
|
||||||
|
|
||||||
|
enum DriveBoardType
|
||||||
|
{
|
||||||
|
DRIVE_BOARD_NONE = 0,
|
||||||
|
DRIVE_BOARD_WHEEL,
|
||||||
|
DRIVE_BOARD_JOYSTICK,
|
||||||
|
DRIVE_BOARD_SKI,
|
||||||
|
DRIVE_BOARD_BILLBOARD
|
||||||
|
};
|
||||||
|
DriveBoardType driveboard_type = DriveBoardType::DRIVE_BOARD_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDED_GAME_H
|
#endif // INCLUDED_GAME_H
|
||||||
|
|
|
@ -166,6 +166,7 @@ GameLoader::Region::ptr_t GameLoader::Region::Create(const GameLoader &loader, c
|
||||||
region->stride = region_node["stride"].ValueAs<size_t>();
|
region->stride = region_node["stride"].ValueAs<size_t>();
|
||||||
region->chunk_size = region_node["chunk_size"].ValueAs<size_t>();
|
region->chunk_size = region_node["chunk_size"].ValueAs<size_t>();
|
||||||
region->byte_swap = region_node["byte_swap"].ValueAsDefault<bool>(false);
|
region->byte_swap = region_node["byte_swap"].ValueAsDefault<bool>(false);
|
||||||
|
region->required = region_node["required"].ValueAsDefault<bool>(true);
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +237,16 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
|
||||||
game->inputs |= input_flags[input_type];
|
game->inputs |= input_flags[input_type];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string, Game::DriveBoardType> drive_board_types
|
||||||
|
{
|
||||||
|
{ "Wheel", Game::DRIVE_BOARD_WHEEL },
|
||||||
|
{ "Joystick", Game::DRIVE_BOARD_JOYSTICK },
|
||||||
|
{ "Ski", Game::DRIVE_BOARD_SKI },
|
||||||
|
{ "Billboard", Game::DRIVE_BOARD_BILLBOARD}
|
||||||
|
};
|
||||||
|
std::string drive_board_type = game_node["hardware/drive_board"].ValueAsDefault<std::string>(std::string());
|
||||||
|
game->driveboard_type = drive_board_types[drive_board_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
|
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
|
||||||
|
@ -500,7 +511,8 @@ void GameLoader::IdentifyGamesInZipArchive(
|
||||||
std::map<std::string, std::set<File::ptr_t>> files_found_by_game;
|
std::map<std::string, std::set<File::ptr_t>> files_found_by_game;
|
||||||
|
|
||||||
// Determine which files each game requires and which files are present in
|
// Determine which files each game requires and which files are present in
|
||||||
// the zip archive
|
// the zip archive. Files belonging to optional regions cannot be used to
|
||||||
|
// identify games.
|
||||||
for (auto &v1: regions_by_game)
|
for (auto &v1: regions_by_game)
|
||||||
{
|
{
|
||||||
const std::string &game_name = v1.first;
|
const std::string &game_name = v1.first;
|
||||||
|
@ -508,6 +520,8 @@ void GameLoader::IdentifyGamesInZipArchive(
|
||||||
for (auto &v2: regions_by_name)
|
for (auto &v2: regions_by_name)
|
||||||
{
|
{
|
||||||
Region::ptr_t region = v2.second;
|
Region::ptr_t region = v2.second;
|
||||||
|
if (!region->required)
|
||||||
|
continue;
|
||||||
for (auto file: region->files)
|
for (auto file: region->files)
|
||||||
{
|
{
|
||||||
// Add each file to the set of required files per game
|
// Add each file to the set of required files per game
|
||||||
|
@ -764,15 +778,31 @@ bool GameLoader::LoadROMs(ROMSet *rom_set, const std::string &game_name, const Z
|
||||||
{
|
{
|
||||||
auto ®ion = v.second;
|
auto ®ion = v.second;
|
||||||
uint32_t region_size = 0;
|
uint32_t region_size = 0;
|
||||||
|
bool error_loading_region = false;
|
||||||
|
|
||||||
|
// Attempt to load the region
|
||||||
if (ComputeRegionSize(®ion_size, region, zip))
|
if (ComputeRegionSize(®ion_size, region, zip))
|
||||||
error |= true;
|
error_loading_region = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Load up the ROM region
|
// Load up the ROM region
|
||||||
auto &rom = rom_set->rom_by_region[region->region_name];
|
auto &rom = rom_set->rom_by_region[region->region_name];
|
||||||
rom.data.reset(new uint8_t[region_size], std::default_delete<uint8_t[]>());
|
rom.data.reset(new uint8_t[region_size], std::default_delete<uint8_t[]>());
|
||||||
rom.size = region_size;
|
rom.size = region_size;
|
||||||
error |= LoadRegion(&rom, region, zip);
|
error_loading_region = LoadRegion(&rom, region, zip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error_loading_region && !region->required)
|
||||||
|
{
|
||||||
|
// Failed to load the region but it wasn't required anyway, so remove it
|
||||||
|
// and proceed
|
||||||
|
rom_set->rom_by_region.erase(region->region_name);
|
||||||
|
ErrorLog("Optional ROM region '%s' in '%s' could not be loaded.", region->region_name.c_str(), game_name.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Proceed normally: accumulate errors
|
||||||
|
error |= error_loading_region;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,13 @@ private:
|
||||||
size_t stride;
|
size_t stride;
|
||||||
size_t chunk_size;
|
size_t chunk_size;
|
||||||
bool byte_swap;
|
bool byte_swap;
|
||||||
|
bool required;
|
||||||
std::vector<File::ptr_t> files;
|
std::vector<File::ptr_t> files;
|
||||||
static ptr_t Create(const GameLoader &loader, const Util::Config::Node ®ion_node);
|
static ptr_t Create(const GameLoader &loader, const Util::Config::Node ®ion_node);
|
||||||
bool AttribsMatch(const ptr_t &other) const;
|
bool AttribsMatch(const ptr_t &other) const;
|
||||||
bool FindFileIndexByOffset(size_t *idx, uint32_t offset) const;
|
bool FindFileIndexByOffset(size_t *idx, uint32_t offset) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Game information from XML
|
// Game information from XML
|
||||||
std::map<std::string, Game> m_game_info_by_game;
|
std::map<std::string, Game> m_game_info_by_game;
|
||||||
|
|
||||||
|
@ -45,18 +46,18 @@ private:
|
||||||
// sets do not inherit parent patches, which may complicate merging.
|
// sets do not inherit parent patches, which may complicate merging.
|
||||||
typedef std::map<std::string, std::vector<ROM::BigEndianPatch>> PatchesByRegion_t;
|
typedef std::map<std::string, std::vector<ROM::BigEndianPatch>> PatchesByRegion_t;
|
||||||
std::map<std::string, PatchesByRegion_t> m_patches_by_game;
|
std::map<std::string, PatchesByRegion_t> m_patches_by_game;
|
||||||
|
|
||||||
// Parsed XML
|
// Parsed XML
|
||||||
typedef std::map<std::string, Region::ptr_t> RegionsByName_t;
|
typedef std::map<std::string, Region::ptr_t> RegionsByName_t;
|
||||||
std::map<std::string, RegionsByName_t> m_regions_by_game; // all games as defined in XML
|
std::map<std::string, RegionsByName_t> m_regions_by_game; // all games as defined in XML
|
||||||
std::map<std::string, RegionsByName_t> m_regions_by_merged_game; // only child sets merged w/ parents
|
std::map<std::string, RegionsByName_t> m_regions_by_merged_game; // only child sets merged w/ parents
|
||||||
std::string m_xml_filename;
|
std::string m_xml_filename;
|
||||||
|
|
||||||
// Single compressed file inside of a zip archive
|
// Single compressed file inside of a zip archive
|
||||||
struct ZippedFile
|
struct ZippedFile
|
||||||
{
|
{
|
||||||
unzFile zf = nullptr;
|
unzFile zf = nullptr;
|
||||||
std::string zipfilename; // zip archive
|
std::string zipfilename; // zip archive
|
||||||
std::string filename; // file inside the zip archive
|
std::string filename; // file inside the zip archive
|
||||||
size_t uncompressed_size = 0;
|
size_t uncompressed_size = 0;
|
||||||
uint32_t crc32 = 0;
|
uint32_t crc32 = 0;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
192
Src/Model3/DriveBoard/BillBoard.cpp
Normal file
192
Src/Model3/DriveBoard/BillBoard.cpp
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BillBoard.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CBillBoard class
|
||||||
|
* emulation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** digits format
|
||||||
|
**
|
||||||
|
** a
|
||||||
|
** -------
|
||||||
|
** | |
|
||||||
|
** f | | b
|
||||||
|
** | g |
|
||||||
|
** -------
|
||||||
|
** | |
|
||||||
|
** e | | c
|
||||||
|
** | |
|
||||||
|
** -------
|
||||||
|
** d (O) h
|
||||||
|
**
|
||||||
|
** h g f e d c b a
|
||||||
|
** msb lsb
|
||||||
|
**
|
||||||
|
** 0 switch on
|
||||||
|
** 1 switch off
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** lamps
|
||||||
|
** x x x x x x P2 P1
|
||||||
|
** 0 switch on
|
||||||
|
** 1 switch off
|
||||||
|
**
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
Game::DriveBoardType CBillBoard::GetType(void)
|
||||||
|
{
|
||||||
|
return Game::DRIVE_BOARD_BILLBOARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CBillBoard::GetForceFeedbackStrength()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBillBoard::SetForceFeedbackStrength(unsigned strength)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBillBoard::SaveState(CBlockFile* SaveState)
|
||||||
|
{
|
||||||
|
CDriveBoard::SaveState(SaveState);
|
||||||
|
SaveState->NewBlock("BillBoard", __FILE__);
|
||||||
|
SaveState->Write(&m_dip1, sizeof(m_dip1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBillBoard::LoadState(CBlockFile* SaveState)
|
||||||
|
{
|
||||||
|
CDriveBoard::LoadState(SaveState);
|
||||||
|
if (SaveState->FindBlock("BillBoard") != OKAY)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load billboard state. Save state file is corrupt.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveState->Read(&m_dip1, sizeof(m_dip1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBillBoard::AttachInputs(CInputs* inputs, unsigned gameInputFlags)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CBillBoard::IORead8(UINT32 portNum)
|
||||||
|
{
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x20:
|
||||||
|
// return the dipswitch
|
||||||
|
// 0x80 : test all segments
|
||||||
|
return m_dip1;
|
||||||
|
case 0x21:
|
||||||
|
//DebugLog(" Bill R portnum=%X m_dataSent=%X\n", portNum, m_dataSent);
|
||||||
|
return m_dataSent;
|
||||||
|
case 0x26:
|
||||||
|
//DebugLog(" Bill R portnum=%X m_dataSent=%X\n", portNum, m_dataSent);
|
||||||
|
// 0xf0 or 0x0f = no more test lamp
|
||||||
|
return 0xff;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 input on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBillBoard::IOWrite8(UINT32 portNum, UINT8 data)
|
||||||
|
{
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x22: // P1 Digit 1
|
||||||
|
//DebugLog("Bill W 0x22 <- %X\n", data);
|
||||||
|
if (m_outputs != NULL)
|
||||||
|
m_outputs->SetValue(OutputBill1, data);
|
||||||
|
m_dataReceived = data;
|
||||||
|
break;
|
||||||
|
case 0x23: // P1 Digit 2
|
||||||
|
//DebugLog("Bill W 0x23 <- %X\n", data);
|
||||||
|
if (m_outputs != NULL)
|
||||||
|
m_outputs->SetValue(OutputBill2, data);
|
||||||
|
m_dataReceived = data;
|
||||||
|
break;
|
||||||
|
case 0x24: // P2 Digit 1
|
||||||
|
//DebugLog("Bill W 0x24 <- %X\n", data);
|
||||||
|
if (m_outputs != NULL)
|
||||||
|
m_outputs->SetValue(OutputBill3, data);
|
||||||
|
m_dataReceived = data;
|
||||||
|
break;
|
||||||
|
case 0x25: // P2 Digit 2
|
||||||
|
//DebugLog("Bill W 0x25 <- %X\n", data);
|
||||||
|
if (m_outputs != NULL)
|
||||||
|
m_outputs->SetValue(OutputBill4, data);
|
||||||
|
m_dataReceived = data;
|
||||||
|
break;
|
||||||
|
case 0x26: // lamp P1 P2
|
||||||
|
//DebugLog("Bill W 0x26 <- %X\n", data);
|
||||||
|
if (m_outputs != NULL)
|
||||||
|
m_outputs->SetValue(OutputBill5, data);
|
||||||
|
m_dataReceived = data;
|
||||||
|
break;
|
||||||
|
case 0x28:
|
||||||
|
//DebugLog("Bill W 0x28 <- %X\n", data);
|
||||||
|
if (data == 0x03)
|
||||||
|
m_allowInterrupts = true;
|
||||||
|
break;
|
||||||
|
case 0x2e:
|
||||||
|
//DebugLog("Bill W 0x2e <- %X\n", data);
|
||||||
|
if (data == 0x00)
|
||||||
|
m_initialized = true;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 output on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CBillBoard::CBillBoard(const Util::Config::Node& config)
|
||||||
|
: CDriveBoard(config)
|
||||||
|
{
|
||||||
|
m_dip1 = 0x0f;
|
||||||
|
m_simulated = false;
|
||||||
|
m_z80Clock = 8.0;
|
||||||
|
m_z80NMI = false;
|
||||||
|
|
||||||
|
DebugLog("Built Drive Board (billboard)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CBillBoard::~CBillBoard(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
117
Src/Model3/DriveBoard/BillBoard.h
Normal file
117
Src/Model3/DriveBoard/BillBoard.h
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BillBoard.h
|
||||||
|
*
|
||||||
|
* Header for the CBillBoard class (BillBoard emulation).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_BILLBOARD_H
|
||||||
|
#define INCLUDED_BILLBOARD_H
|
||||||
|
|
||||||
|
#include "Util/NewConfig.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBillBoard
|
||||||
|
*/
|
||||||
|
class CBillBoard : public CDriveBoard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* GetType(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Drive board type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Game::DriveBoardType GetType(void);
|
||||||
|
|
||||||
|
unsigned GetForceFeedbackStrength(void);
|
||||||
|
void SetForceFeedbackStrength(unsigned strength);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves the bill board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Restores the bill board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load save state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
void AttachInputs(CInputs* inputs, unsigned gameInputFlags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBillBoard(config):
|
||||||
|
* ~CBillBoard():
|
||||||
|
*
|
||||||
|
* Constructor and destructor. Memory is freed by destructor.
|
||||||
|
*
|
||||||
|
* Paramters:
|
||||||
|
* config Run-time configuration. The reference should be held because
|
||||||
|
* this changes at run-time.
|
||||||
|
*/
|
||||||
|
CBillBoard(const Util::Config::Node &config);
|
||||||
|
~CBillBoard(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read8(addr):
|
||||||
|
* IORead8(portNum):
|
||||||
|
*
|
||||||
|
* Methods for reading from Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A byte of data from the address or port.
|
||||||
|
*/
|
||||||
|
UINT8 IORead8(UINT32 portNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write8(addr, data):
|
||||||
|
* IORead8(portNum, data):
|
||||||
|
*
|
||||||
|
* Methods for writing to Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
* data Byte to write.
|
||||||
|
*/
|
||||||
|
void IOWrite8(UINT32 portNum, UINT8 data);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_BILLBOARD_H
|
426
Src/Model3/DriveBoard/DriveBoard.cpp
Normal file
426
Src/Model3/DriveBoard/DriveBoard.cpp
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DriveBoard.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CDriveBoard class: drive board (force feedback)
|
||||||
|
* emulation.
|
||||||
|
*
|
||||||
|
* NOTE: Simulation does not yet work. Drive board ROMs are required.
|
||||||
|
*
|
||||||
|
* State Management:
|
||||||
|
* -----------------
|
||||||
|
* - IsAttached: This indicates whether the drive board is present for the game
|
||||||
|
* and should be emulated, if possible. It is determined by whether a ROM was
|
||||||
|
* passed to the initializer. Entirely simulated implementations of the drive
|
||||||
|
* board should still take a ROM, even if it contains no data. The attached
|
||||||
|
* should be set only *once* at initialization and preferably by the base
|
||||||
|
* CDriveBoard class. Do not change this flag during run-time!
|
||||||
|
* - Disabled: This state indicates emulation should not proceed. Force
|
||||||
|
* feedback must be completely halted. This flag is used only to disable the
|
||||||
|
* board due to run-time errors and must *not* be re-enabled. It must not be
|
||||||
|
* used to "temporarily" disable the board. Only the Reset() method may
|
||||||
|
* enable emulation and then only if the board is attached. A valid reason
|
||||||
|
* for disabling the board during run-time is e.g., if a loaded save state is
|
||||||
|
* incompatible (wrong format or because it was saved while the board was
|
||||||
|
* disabled, rendering its state data invalid).
|
||||||
|
* - IsDisabled: This method is used internally only and should be used to test
|
||||||
|
* whether emulation should occur. It is the combination of attachment and
|
||||||
|
* enabled state.
|
||||||
|
* - Disable: Use this to disable the board. Drive board implementations should
|
||||||
|
* override this to send stop commands to force feedback motors and then call
|
||||||
|
* CDriveBoard::Disable() to update the disabled flag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define ROM_SIZE 0x9000
|
||||||
|
#define RAM_SIZE 0x2000 // Z80 RAM
|
||||||
|
|
||||||
|
static_assert(sizeof(bool) == 1); // Save state code relies on this -- we must fix this so that bools are copied to uint8_t explicitly
|
||||||
|
|
||||||
|
void CDriveBoard::SaveState(CBlockFile* SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("DriveBoard.2", __FILE__);
|
||||||
|
|
||||||
|
// Check board is attached and enabled
|
||||||
|
bool enabled = !IsDisabled();
|
||||||
|
SaveState->Write(&enabled, sizeof(enabled));
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
// Check if simulated
|
||||||
|
SaveState->Write(&m_simulated, sizeof(m_simulated));
|
||||||
|
if (m_simulated)
|
||||||
|
{
|
||||||
|
// TODO - save board simulation state
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// Save CPU state
|
||||||
|
m_z80.SaveState(SaveState, "DriveBoard Z80");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::LoadState(CBlockFile* SaveState)
|
||||||
|
{
|
||||||
|
if (SaveState->FindBlock("DriveBoard.2") != OKAY)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load base drive board state. Save state file is corrupt.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that board was enabled in saved state
|
||||||
|
bool isEnabled = !IsDisabled();
|
||||||
|
bool wasEnabled = false;
|
||||||
|
bool wasSimulated = false;
|
||||||
|
SaveState->Read(&wasEnabled, sizeof(wasEnabled));
|
||||||
|
if (wasEnabled)
|
||||||
|
{
|
||||||
|
// Simulated?
|
||||||
|
SaveState->Read(&wasSimulated, sizeof(wasSimulated));
|
||||||
|
if (wasSimulated)
|
||||||
|
{
|
||||||
|
// Derived classes may have simulation state (e.g., SkiBoard)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// Load CPU state
|
||||||
|
// TODO: we should have a way to check whether this succeeds... make CZ80::LoadState() return a bool
|
||||||
|
m_z80.LoadState(SaveState, "DriveBoard Z80");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the board was not in the same activity and simulation state when the
|
||||||
|
// save file was generated, we cannot safely resume and must disable it
|
||||||
|
if (wasEnabled != isEnabled || wasSimulated != m_simulated)
|
||||||
|
{
|
||||||
|
Disable();
|
||||||
|
ErrorLog("Halting drive board emulation due to mismatch in active and restored states.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::LoadLegacyState(const LegacyDriveBoardState &state, CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
static_assert(RAM_SIZE == sizeof(state.ram));
|
||||||
|
memcpy(m_ram, state.ram, RAM_SIZE);
|
||||||
|
m_initialized = state.initialized;
|
||||||
|
m_allowInterrupts = state.allowInterrupts;
|
||||||
|
m_dataSent = state.dataSent;
|
||||||
|
m_dataReceived = state.dataReceived;
|
||||||
|
m_z80.LoadState(SaveState, "DriveBoard Z80");
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::DriveBoardType CDriveBoard::GetType(void)
|
||||||
|
{
|
||||||
|
return Game::DRIVE_BOARD_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDriveBoard::IsAttached(void)
|
||||||
|
{
|
||||||
|
return m_attached;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDriveBoard::IsSimulated(void)
|
||||||
|
{
|
||||||
|
return m_simulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDriveBoard::IsDisabled(void)
|
||||||
|
{
|
||||||
|
bool enabled = !m_disabled;
|
||||||
|
return !(m_attached && enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::Disable(void)
|
||||||
|
{
|
||||||
|
m_disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::GetDIPSwitches(UINT8& dip1, UINT8& dip2)
|
||||||
|
{
|
||||||
|
dip1 = m_dip1;
|
||||||
|
dip2 = m_dip2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::GetDIPSwitches(UINT8& dip1)
|
||||||
|
{
|
||||||
|
dip1 = m_dip1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::SetDIPSwitches(UINT8 dip1, UINT8 dip2)
|
||||||
|
{
|
||||||
|
m_dip1 = dip1;
|
||||||
|
m_dip2 = dip2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::SetDIPSwitches(UINT8 dip1)
|
||||||
|
{
|
||||||
|
m_dip1 = dip1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CDriveBoard::GetForceFeedbackStrength()
|
||||||
|
{
|
||||||
|
return ((~(m_dip1 >> 2)) & 7) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::SetForceFeedbackStrength(unsigned strength)
|
||||||
|
{
|
||||||
|
m_dip1 = (m_dip1 & 0xE3) | (((~(strength - 1)) & 7) << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
CZ80* CDriveBoard::GetZ80(void)
|
||||||
|
{
|
||||||
|
return &m_z80;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UINT8 CDriveBoard::Read(void)
|
||||||
|
{
|
||||||
|
if (IsDisabled())
|
||||||
|
{
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
return m_dataReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::Write(UINT8 data)
|
||||||
|
{
|
||||||
|
m_dataSent = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CDriveBoard::Read8(UINT32 addr)
|
||||||
|
{
|
||||||
|
// TODO - shouldn't end of ROM be 0x7FFF not 0x8FFF?
|
||||||
|
if (addr < ROM_SIZE) // ROM is 0x0000-0x8FFF
|
||||||
|
return m_rom[addr];
|
||||||
|
else if (addr >= 0xE000) // RAM is 0xE000-0xFFFF
|
||||||
|
return m_ram[(addr - 0xE000) & 0x1FFF];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//DebugLog("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
|
||||||
|
DebugLog("Unhandled Z80 write to %08X (at PC = %04X)\n", addr, m_z80.GetPC());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CDriveBoard::IORead8(UINT32 portNum)
|
||||||
|
{
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::IOWrite8(UINT32 portNum, UINT8 data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::RunFrame(void)
|
||||||
|
{
|
||||||
|
if (IsDisabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming Z80 runs @ 4.0MHz and NMI triggers @ 60.0KHz for WheelBoard and JoystickBoard
|
||||||
|
// Assuming Z80 runs @ 8.0MHz and INT triggers @ 60.0KHz for BillBoard
|
||||||
|
// TODO - find out if Z80 frequency is correct and exact frequency of NMI interrupts (just guesswork at the moment!)
|
||||||
|
int cycles = (int)(m_z80Clock * 1000000 / 60);
|
||||||
|
int loopCycles = 10000;
|
||||||
|
while (cycles > 0)
|
||||||
|
{
|
||||||
|
if (m_allowInterrupts)
|
||||||
|
{
|
||||||
|
if(m_z80NMI)
|
||||||
|
m_z80.TriggerNMI();
|
||||||
|
else
|
||||||
|
m_z80.SetINT(true);
|
||||||
|
}
|
||||||
|
cycles -= m_z80.Run(std::min<int>(loopCycles, cycles));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDriveBoard::Init(const UINT8* romPtr)
|
||||||
|
{
|
||||||
|
// This constructor must present a valid ROM
|
||||||
|
if (!romPtr)
|
||||||
|
{
|
||||||
|
return ErrorLog("Internal error: no drive board ROM supplied.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign ROM (note that the ROM data has not yet been loaded)
|
||||||
|
m_rom = romPtr;
|
||||||
|
|
||||||
|
// 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 memory for drive board (needs %1.1f MB).", ramSizeMB);
|
||||||
|
}
|
||||||
|
memset(m_ram, 0, RAM_SIZE);
|
||||||
|
|
||||||
|
// Initialize Z80
|
||||||
|
m_z80.Init(this, NULL);
|
||||||
|
|
||||||
|
// We are attached
|
||||||
|
m_attached = true;
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy drive board (not attached)
|
||||||
|
bool CDriveBoard::Init(void)
|
||||||
|
{
|
||||||
|
// Use an empty dummy ROM otherwise so debugger doesn't crash if it
|
||||||
|
// tries to read our memory or if we accidentally run the Z80 (which
|
||||||
|
// should never happen in a detached board state).
|
||||||
|
if (!m_dummyROM)
|
||||||
|
{
|
||||||
|
uint8_t *rom = new (std::nothrow) uint8_t[ROM_SIZE];
|
||||||
|
if (NULL == rom)
|
||||||
|
{
|
||||||
|
return ErrorLog("Insufficient memory for drive board.");
|
||||||
|
}
|
||||||
|
memset(rom, 0xFF, ROM_SIZE);
|
||||||
|
m_dummyROM = rom;
|
||||||
|
}
|
||||||
|
bool result = Init(m_dummyROM);
|
||||||
|
m_attached = false; // force detached
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDriveBoard::Reset()
|
||||||
|
{
|
||||||
|
m_initialized = false;
|
||||||
|
m_initState = 0;
|
||||||
|
m_readMode = 0;
|
||||||
|
m_boardMode = 0;
|
||||||
|
m_wheelCenter = 0x80;
|
||||||
|
m_allowInterrupts = false;
|
||||||
|
m_dataSent = 0;
|
||||||
|
m_dataReceived = 0;
|
||||||
|
m_z80.Reset(); // always reset to provide a valid Z80 state
|
||||||
|
|
||||||
|
// Configure options (cannot be done in Init() because command line settings weren't yet parsed)
|
||||||
|
SetForceFeedbackStrength(m_config["ForceFeedbackStrength"].ValueAsDefault<unsigned>(5));
|
||||||
|
|
||||||
|
// Enable only if attached -- **this is the only place this flag should ever be cleared**
|
||||||
|
if (m_attached)
|
||||||
|
m_disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CDriveBoard::CDriveBoard(const Util::Config::Node& config)
|
||||||
|
: m_config(config),
|
||||||
|
m_attached(false),
|
||||||
|
m_disabled(true), // begin in disabled state -- can be enabled only 1) if attached and 2) by successful reset
|
||||||
|
m_simulated(false),
|
||||||
|
m_initialized(false),
|
||||||
|
m_allowInterrupts(false),
|
||||||
|
m_dataSent(0),
|
||||||
|
m_dataReceived(0),
|
||||||
|
m_dip1(0x00),
|
||||||
|
m_dip2(0x00),
|
||||||
|
m_initState(0),
|
||||||
|
m_statusFlags(0),
|
||||||
|
m_boardMode(0),
|
||||||
|
m_readMode(0),
|
||||||
|
m_wheelCenter(0),
|
||||||
|
m_cockpitCenter(0),
|
||||||
|
m_echoVal(0),
|
||||||
|
m_rom(NULL),
|
||||||
|
m_ram(NULL),
|
||||||
|
m_dummyROM(NULL),
|
||||||
|
m_z80Clock(4.0),
|
||||||
|
m_z80NMI(true),
|
||||||
|
m_inputs(NULL),
|
||||||
|
m_inputFlags(0),
|
||||||
|
m_outputs(NULL)
|
||||||
|
{
|
||||||
|
DebugLog("Built Drive Board\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CDriveBoard::~CDriveBoard(void)
|
||||||
|
{
|
||||||
|
if (m_ram != NULL)
|
||||||
|
{
|
||||||
|
delete[] m_ram;
|
||||||
|
m_ram = NULL;
|
||||||
|
}
|
||||||
|
if (m_dummyROM != NULL)
|
||||||
|
{
|
||||||
|
delete [] m_dummyROM;
|
||||||
|
m_dummyROM = NULL;
|
||||||
|
}
|
||||||
|
m_rom = NULL;
|
||||||
|
m_inputs = NULL;
|
||||||
|
m_outputs = NULL;
|
||||||
|
|
||||||
|
DebugLog("Destroyed Drive Board\n");
|
||||||
|
}
|
322
Src/Model3/DriveBoard/DriveBoard.h
Normal file
322
Src/Model3/DriveBoard/DriveBoard.h
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DriveBoard.h
|
||||||
|
*
|
||||||
|
* Header for the CDriveBoard (force feedback emulation) class.
|
||||||
|
* Abstract base class defining the common interface for wheel, joystick, ski, bill board types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_DRIVEBOARD_H
|
||||||
|
#define INCLUDED_DRIVEBOARD_H
|
||||||
|
|
||||||
|
#include "Util/NewConfig.h"
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CDriveBoard
|
||||||
|
*/
|
||||||
|
class CDriveBoard : public IBus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* GetType(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Drive board type.
|
||||||
|
*/
|
||||||
|
virtual Game::DriveBoardType GetType(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsAttached(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* True if the drive board is "attached" and should be emulated,
|
||||||
|
* otherwise false.
|
||||||
|
*/
|
||||||
|
virtual bool IsAttached(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsSimulated(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* True if the drive board is being simulated rather than actually
|
||||||
|
* emulated, otherwise false.
|
||||||
|
*/
|
||||||
|
virtual bool IsSimulated(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetDIPSwitches(dip1, dip2):
|
||||||
|
*
|
||||||
|
* Reads the two sets of DIP switches on the drive board.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dip1 Reference of variable to store DIP switch 1 to.
|
||||||
|
* dip2 DIP switch 2.
|
||||||
|
*/
|
||||||
|
virtual void GetDIPSwitches(UINT8 &dip1, UINT8 &dip2);
|
||||||
|
virtual void GetDIPSwitches(UINT8& dip1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetDIPSwitches(dip1, dip2):
|
||||||
|
*
|
||||||
|
* Sets the DIP switches.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dip1 DIP switch 1 value.
|
||||||
|
* dip2 DIP switch 2 value.
|
||||||
|
*/
|
||||||
|
virtual void SetDIPSwitches(UINT8 dip1, UINT8 dip2);
|
||||||
|
virtual void SetDIPSwitches(UINT8 dip1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetForceFeedbackStrength(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Strength of the force feedback based on drive board DIP switches (1-8).
|
||||||
|
*/
|
||||||
|
virtual unsigned GetForceFeedbackStrength(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetForceFeedbackStrength(strength):
|
||||||
|
*
|
||||||
|
* Sets the force feedback strength (modifies the DIP switch setting).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* strength A value ranging from 1 to 8.
|
||||||
|
*/
|
||||||
|
virtual void SetForceFeedbackStrength(unsigned strength);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetZ80(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The Z80 object.
|
||||||
|
*/
|
||||||
|
virtual CZ80 *GetZ80(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
virtual void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Restores the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load save state information from.
|
||||||
|
*/
|
||||||
|
virtual void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(romPtr):
|
||||||
|
*
|
||||||
|
* Initializes (and "attaches") the drive board. This should be called
|
||||||
|
* before other members.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* romPtr Pointer to the drive board ROM (Z80 program).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* FAIL if the drive board could not be initialized (prints own error
|
||||||
|
* message), otherwise OKAY.
|
||||||
|
*/
|
||||||
|
virtual bool Init(const UINT8 *romPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* Initializes a dummy drive board in a detached state. This should be called
|
||||||
|
* before other members. This initializer is provided in case a CDriveBoard
|
||||||
|
* object or pointer is needed but no drive board actually exists.
|
||||||
|
*/
|
||||||
|
bool Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachInputs(InputsPtr, gameInputFlags):
|
||||||
|
*
|
||||||
|
* Attaches inputs to the drive board (for access to the steering wheel
|
||||||
|
* position).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* inputs Pointer to the input object.
|
||||||
|
* gameInputFlags The current game's input flags.
|
||||||
|
*/
|
||||||
|
virtual void AttachInputs(CInputs *inputs, unsigned gameInputFlags);
|
||||||
|
|
||||||
|
virtual void AttachOutputs(COutputs *outputs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the drive board.
|
||||||
|
*/
|
||||||
|
virtual void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read():
|
||||||
|
*
|
||||||
|
* Reads data from the drive board.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data read.
|
||||||
|
*/
|
||||||
|
virtual UINT8 Read(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(data):
|
||||||
|
*
|
||||||
|
* Writes data to the drive board.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to send.
|
||||||
|
*/
|
||||||
|
virtual void Write(UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RunFrame(void):
|
||||||
|
*
|
||||||
|
* Emulates a single frame's worth of time on the drive board.
|
||||||
|
*/
|
||||||
|
virtual void RunFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CDriveBoard(config):
|
||||||
|
* ~CDriveBoard():
|
||||||
|
*
|
||||||
|
* Constructor and destructor. Memory is freed by destructor.
|
||||||
|
*
|
||||||
|
* Paramters:
|
||||||
|
* config Run-time configuration. The reference should be held because
|
||||||
|
* this changes at run-time.
|
||||||
|
*/
|
||||||
|
CDriveBoard(const Util::Config::Node& config);
|
||||||
|
virtual ~CDriveBoard(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read8(addr):
|
||||||
|
* IORead8(portNum):
|
||||||
|
*
|
||||||
|
* Methods for reading from Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A byte of data from the address or port.
|
||||||
|
*/
|
||||||
|
virtual UINT8 Read8(UINT32 addr);
|
||||||
|
virtual UINT8 IORead8(UINT32 portNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write8(addr, data):
|
||||||
|
* IORead8(portNum, data):
|
||||||
|
*
|
||||||
|
* Methods for writing to Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
* data Byte to write.
|
||||||
|
*/
|
||||||
|
virtual void Write8(UINT32 addr, UINT8 data);
|
||||||
|
virtual void IOWrite8(UINT32 portNum, UINT8 data);
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct LegacyDriveBoardState
|
||||||
|
{
|
||||||
|
uint8_t dip1;
|
||||||
|
uint8_t dip2;
|
||||||
|
uint8_t ram[0x2000];
|
||||||
|
uint8_t initialized;
|
||||||
|
uint8_t allowInterrupts;
|
||||||
|
uint8_t dataSent;
|
||||||
|
uint8_t dataReceived;
|
||||||
|
uint16_t adcPortRead;
|
||||||
|
uint8_t adcPortBit;
|
||||||
|
uint8_t uncenterVal1;
|
||||||
|
uint8_t uncenterVal2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable the drive board (without affecting attachment state). Used internally only to disable emulation.
|
||||||
|
virtual void Disable(void);
|
||||||
|
|
||||||
|
// Whether disabled and/or not attached -- used to determine whether to carry out emulation
|
||||||
|
bool IsDisabled(void);
|
||||||
|
|
||||||
|
// Attempt to load drive board data from old save states (prior to drive board refactor)
|
||||||
|
void LoadLegacyState(const LegacyDriveBoardState &state, CBlockFile *SaveState);
|
||||||
|
|
||||||
|
|
||||||
|
const Util::Config::Node& m_config;
|
||||||
|
|
||||||
|
bool m_attached; // True if drive board is attached
|
||||||
|
bool m_disabled; // True if emulation is internally disabled (e.g., by loading an incompatible save state). Can only be enabled once and if attached.
|
||||||
|
bool m_simulated; // True if drive board should be simulated rather than emulated
|
||||||
|
|
||||||
|
// Emulation state
|
||||||
|
bool m_initialized; // True if drive board has finished initialization
|
||||||
|
bool m_allowInterrupts; // True if drive board has enabled NMI interrupts
|
||||||
|
|
||||||
|
UINT8 m_dataSent; // Last command sent by main board
|
||||||
|
UINT8 m_dataReceived; // Data to send back to main board
|
||||||
|
|
||||||
|
UINT8 m_dip1; // Value of DIP switch 1
|
||||||
|
UINT8 m_dip2; // Value of DIP switch 2
|
||||||
|
|
||||||
|
// Simulation state
|
||||||
|
UINT8 m_initState;
|
||||||
|
UINT8 m_statusFlags;
|
||||||
|
UINT8 m_boardMode;
|
||||||
|
UINT8 m_readMode;
|
||||||
|
UINT8 m_wheelCenter;
|
||||||
|
UINT8 m_cockpitCenter;
|
||||||
|
UINT8 m_echoVal;
|
||||||
|
|
||||||
|
const UINT8* m_rom; // 32k ROM
|
||||||
|
UINT8* m_ram; // 8k RAM
|
||||||
|
|
||||||
|
const UINT8* m_dummyROM;
|
||||||
|
|
||||||
|
CZ80 m_z80; // Z80 CPU
|
||||||
|
float m_z80Clock; // Z80 clock frequency
|
||||||
|
bool m_z80NMI; // Non Masquable Interrupt or Interrupt
|
||||||
|
|
||||||
|
CInputs* m_inputs;
|
||||||
|
unsigned m_inputFlags;
|
||||||
|
|
||||||
|
COutputs* m_outputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_DRIVEBOARD_H
|
731
Src/Model3/DriveBoard/JoystickBoard.cpp
Normal file
731
Src/Model3/DriveBoard/JoystickBoard.cpp
Normal file
|
@ -0,0 +1,731 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JoyBoard.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CJoyBoard class: drive board (force feedback emulation
|
||||||
|
* for joystick) emulation.
|
||||||
|
*
|
||||||
|
* NOTE: This is largely a copy of CWheelBoard as it appears to be the same drive
|
||||||
|
* board. The joystick X axis is used rather than the wheel input. It is unknown
|
||||||
|
* how or whether the Y axis is involved at all.
|
||||||
|
*
|
||||||
|
* NOTE: Simulation does not yet work. Drive board ROMs are required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
Game::DriveBoardType CJoyBoard::GetType(void)
|
||||||
|
{
|
||||||
|
return Game::DRIVE_BOARD_JOYSTICK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::Get7SegDisplays(UINT8 &seg1Digit1, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2)
|
||||||
|
{
|
||||||
|
seg1Digit1 = m_seg1Digit1;
|
||||||
|
seg1Digit2 = m_seg1Digit2;
|
||||||
|
seg2Digit1 = m_seg2Digit1;
|
||||||
|
seg2Digit2 = m_seg2Digit2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
CDriveBoard::SaveState(SaveState);
|
||||||
|
|
||||||
|
SaveState->NewBlock("JoystickBoard", __FILE__);
|
||||||
|
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_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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
CDriveBoard::LoadState(SaveState);
|
||||||
|
|
||||||
|
if (SaveState->FindBlock("JoystickBoard") != OKAY)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load joystick drive board state. Save state file is corrupt.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasSimulated;
|
||||||
|
SaveState->Read(&wasSimulated, sizeof(wasSimulated));
|
||||||
|
if (wasSimulated)
|
||||||
|
{
|
||||||
|
// Simulation has never existed
|
||||||
|
ErrorLog("Save state contains unexpected data. Halting drive board emulation.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Load DIP switches and digit displays
|
||||||
|
SaveState->Read(&m_dip1, sizeof(m_dip1));
|
||||||
|
SaveState->Read(&m_dip2, sizeof(m_dip2));
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::Disable(void)
|
||||||
|
{
|
||||||
|
SendStopAll();
|
||||||
|
CDriveBoard::Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::Reset(void)
|
||||||
|
{
|
||||||
|
CDriveBoard::Reset();
|
||||||
|
|
||||||
|
m_seg1Digit1 = 0xFF;
|
||||||
|
m_seg1Digit2 = 0xFF;
|
||||||
|
m_seg2Digit1 = 0xFF;
|
||||||
|
m_seg2Digit2 = 0xFF;
|
||||||
|
|
||||||
|
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_uncenterVal1 = 0;
|
||||||
|
m_uncenterVal2 = 0;
|
||||||
|
|
||||||
|
m_lastConstForce = 0;
|
||||||
|
m_lastConstForceY = 0;
|
||||||
|
m_lastSelfCenter = 0;
|
||||||
|
m_lastFriction = 0;
|
||||||
|
m_lastVibrate = 0;
|
||||||
|
|
||||||
|
m_simulated = false; //TODO: make this run-time configurable when simulation mode is supported
|
||||||
|
|
||||||
|
if (!m_config["ForceFeedback"].ValueAsDefault<bool>(false))
|
||||||
|
Disable();
|
||||||
|
|
||||||
|
// Stop any effects that may still be playing
|
||||||
|
if (!IsDisabled())
|
||||||
|
SendStopAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CJoyBoard::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)
|
||||||
|
if (m_simulated)
|
||||||
|
return SimulateRead();
|
||||||
|
else
|
||||||
|
return CDriveBoard::Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::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);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CDriveBoard::Write(data);
|
||||||
|
if (data == 0xCB)
|
||||||
|
m_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CJoyBoard::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->analogJoyX->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 CJoyBoard::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->analogJoyX->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 CJoyBoard::RunFrame(void)
|
||||||
|
{
|
||||||
|
if (m_simulated)
|
||||||
|
SimulateFrame();
|
||||||
|
else
|
||||||
|
CDriveBoard::RunFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::SimulateFrame(void)
|
||||||
|
{
|
||||||
|
if (!m_initialized)
|
||||||
|
m_initState++;
|
||||||
|
// TODO - update m_statusFlags and play preset scripts according to board mode
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CJoyBoard::IORead8(UINT32 portNum)
|
||||||
|
{
|
||||||
|
UINT8 adcVal;
|
||||||
|
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x20: // DIP 1 value
|
||||||
|
return m_dip1;
|
||||||
|
case 0x21: // DIP 2 value
|
||||||
|
return m_dip2;
|
||||||
|
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 0x24: // Y analog axis for joystick
|
||||||
|
adcVal = ReadADCChannel1();
|
||||||
|
break;
|
||||||
|
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 0x26: // Cockpit bank position for deluxe racing cabinets
|
||||||
|
adcVal = ReadADCChannel3();
|
||||||
|
break;
|
||||||
|
case 0x27: // Not connected
|
||||||
|
adcVal = ReadADCChannel4();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
return (adcVal >> m_adcPortBit) & 0x01;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugLog("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
case 0x28: // PPC command
|
||||||
|
return m_dataSent;
|
||||||
|
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
|
||||||
|
// 1 0 = encoder error 2 - overheat error
|
||||||
|
// 1 1 = encoder error 3 - encoder error, reinitializes board
|
||||||
|
return 0x00;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 input on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::IOWrite8(UINT32 portNum, UINT8 data)
|
||||||
|
{
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x10: // Unsure? - single byte 0x03 sent at initialization, then occasionally writes 0x07 & 0xFA to port
|
||||||
|
return;
|
||||||
|
case 0x11: // Interrupt control
|
||||||
|
if (data == 0x57)
|
||||||
|
m_allowInterrupts = true;
|
||||||
|
else if (data == 0x53) // Strictly speaking 0x53 then 0x04
|
||||||
|
m_allowInterrupts = false;
|
||||||
|
return;
|
||||||
|
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 0x20: // Left digit of 7-segment display 1
|
||||||
|
m_seg1Digit1 = data;
|
||||||
|
return;
|
||||||
|
case 0x21: // Right digit of 7-segment display 1
|
||||||
|
m_seg1Digit2 = data;
|
||||||
|
return;
|
||||||
|
case 0x22: // Left digit of 7-segment display 2
|
||||||
|
m_seg2Digit1 = data;
|
||||||
|
return;
|
||||||
|
case 0x23: // Right digit of 7-segment display 2
|
||||||
|
m_seg2Digit2 = data;
|
||||||
|
return;
|
||||||
|
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 0x29: // Reply for PPC
|
||||||
|
m_dataReceived = data;
|
||||||
|
if (data == 0xCB)
|
||||||
|
m_initialized = true;
|
||||||
|
return;
|
||||||
|
case 0x2a: // Encoder motor data (x axis)
|
||||||
|
m_port42Out = data;
|
||||||
|
ProcessEncoderCmdJoystick();
|
||||||
|
return;
|
||||||
|
case 0x2d: // Clutch/lamp control (deluxe cabinets) ( or y axis)
|
||||||
|
m_port45Out = data;
|
||||||
|
ProcessEncoderCmdJoystick();
|
||||||
|
return;
|
||||||
|
case 0x2e: // Encoder motor control
|
||||||
|
m_port46Out = data;
|
||||||
|
return;
|
||||||
|
case 0xf0: // Unsure? - single byte 0xBB sent at initialization only
|
||||||
|
return;
|
||||||
|
case 0xf1: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog?
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 output on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::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:
|
||||||
|
//DebugLog("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 CJoyBoard::SendStopAll(void)
|
||||||
|
{
|
||||||
|
//DebugLog(">> Stop All Effects\n");
|
||||||
|
|
||||||
|
ForceFeedbackCmd ffCmd;
|
||||||
|
ffCmd.id = FFStop;
|
||||||
|
|
||||||
|
m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd);
|
||||||
|
m_inputs->analogJoyY->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastConstForce = 0;
|
||||||
|
m_lastSelfCenter = 0;
|
||||||
|
m_lastFriction = 0;
|
||||||
|
m_lastVibrate = 0;
|
||||||
|
m_lastConstForceY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::SendConstantForce(INT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastConstForce)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val > 0)
|
||||||
|
{
|
||||||
|
DebugLog(">> Force Right %02X [%8s", val, "");
|
||||||
|
for (unsigned i = 0; i < 8; i++)
|
||||||
|
DebugLog(i == 0 || i <= (val + 1) / 16 ? ">" : " ");
|
||||||
|
DebugLog("]\n");
|
||||||
|
}
|
||||||
|
else if (val < 0)
|
||||||
|
{
|
||||||
|
DebugLog(">> Force Left %02X [", -val);
|
||||||
|
for (unsigned i = 0; i < 8; i++)
|
||||||
|
DebugLog(i == 7 || i >= (val + 128) / 16 ? "<" : " ");
|
||||||
|
DebugLog("%8s]\n", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DebugLog(">> Stop Force [%16s]\n", "");
|
||||||
|
*/
|
||||||
|
|
||||||
|
ForceFeedbackCmd ffCmd;
|
||||||
|
ffCmd.id = FFConstantForce;
|
||||||
|
ffCmd.force = (float)val / (val >= 0 ? 127.0f : 128.0f);
|
||||||
|
|
||||||
|
m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastConstForce = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::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 CJoyBoard::SendSelfCenter(UINT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastSelfCenter)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val == 0)
|
||||||
|
DebugLog(">> Stop Self-Center\n");
|
||||||
|
else
|
||||||
|
DebugLog(">> Self-Center %02X\n", val);
|
||||||
|
*/
|
||||||
|
|
||||||
|
ForceFeedbackCmd ffCmd;
|
||||||
|
ffCmd.id = FFSelfCenter;
|
||||||
|
ffCmd.force = (float)val / 255.0f;
|
||||||
|
|
||||||
|
m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd);
|
||||||
|
m_inputs->analogJoyY->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastSelfCenter = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CJoyBoard::SendFriction(UINT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastFriction)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val == 0)
|
||||||
|
DebugLog(">> Stop Friction\n");
|
||||||
|
else
|
||||||
|
DebugLog(">> Friction %02X\n", val);
|
||||||
|
*/
|
||||||
|
|
||||||
|
ForceFeedbackCmd ffCmd;
|
||||||
|
ffCmd.id = FFFriction;
|
||||||
|
ffCmd.force = (float)val / 255.0f;
|
||||||
|
m_inputs->analogJoyX->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastFriction = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJoyBoard::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->analogJoyX->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastVibrate = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CJoyBoard::ReadADCChannel1()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CJoyBoard::ReadADCChannel2()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CJoyBoard::ReadADCChannel3()
|
||||||
|
{
|
||||||
|
return 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CJoyBoard::ReadADCChannel4()
|
||||||
|
{
|
||||||
|
return 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
CJoyBoard::CJoyBoard(const Util::Config::Node &config)
|
||||||
|
: CDriveBoard(config)
|
||||||
|
{
|
||||||
|
m_dip1 = 0xCF;
|
||||||
|
m_dip2 = 0xFF;
|
||||||
|
|
||||||
|
DebugLog("Built Drive Board (Joystick)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CJoyBoard::~CJoyBoard(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
/**
|
/**
|
||||||
** Supermodel
|
** Supermodel
|
||||||
** A Sega Model 3 Arcade Emulator.
|
** A Sega Model 3 Arcade Emulator.
|
||||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
||||||
|
** Harry Tuttle, and Spindizzi
|
||||||
**
|
**
|
||||||
** This file is part of Supermodel.
|
** This file is part of Supermodel.
|
||||||
**
|
**
|
||||||
** Supermodel is free software: you can redistribute it and/or modify it under
|
** 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
|
** 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)
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
** any later version.
|
** any later version.
|
||||||
**
|
**
|
||||||
|
@ -18,90 +19,32 @@
|
||||||
** You should have received a copy of the GNU General Public License along
|
** You should have received a copy of the GNU General Public License along
|
||||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DriveBoard.h
|
* JoyBoard.h
|
||||||
*
|
*
|
||||||
* Header for the CDriveBoard (force feedback emulation) class.
|
* Header for the CJoyBoard (force feedback emulation for joystick) class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef INCLUDED_DRIVEBOARD_H
|
#ifndef INCLUDED_JOYBOARD_H
|
||||||
#define INCLUDED_DRIVEBOARD_H
|
#define INCLUDED_JOYBOARD_H
|
||||||
|
|
||||||
#include "Util/NewConfig.h"
|
#include "Util/NewConfig.h"
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CDriveBoard
|
* CJoyBoard
|
||||||
*/
|
*/
|
||||||
class CDriveBoard : public IBus
|
class CJoyBoard : public CDriveBoard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum BoardType
|
|
||||||
{
|
|
||||||
Wheel=0,
|
|
||||||
Joystick,
|
|
||||||
SkiPad
|
|
||||||
};
|
|
||||||
|
|
||||||
BoardType m_boardType;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IsAttached(void):
|
* GetType(void):
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* True if the drive board is "attached" and should be emulated,
|
* Drive board type.
|
||||||
* otherwise false.
|
|
||||||
*/
|
*/
|
||||||
bool IsAttached(void);
|
Game::DriveBoardType GetType(void);
|
||||||
|
|
||||||
/*
|
|
||||||
* IsSimulated(void):
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* True if the drive board is being simulated rather than actually
|
|
||||||
* emulated, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsSimulated(void);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GetDIPSwitches(dip1, dip2):
|
|
||||||
*
|
|
||||||
* Reads the two sets of DIP switches on the drive board.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* dip1 Reference of variable to store DIP switch 1 to.
|
|
||||||
* dip2 DIP switch 2.
|
|
||||||
*/
|
|
||||||
void GetDIPSwitches(UINT8 &dip1, UINT8 &dip2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SetDIPSwitches(dip1, dip2):
|
|
||||||
*
|
|
||||||
* Sets the DIP switches.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* dip1 DIP switch 1 value.
|
|
||||||
* dip2 DIP switch 2 value.
|
|
||||||
*/
|
|
||||||
void SetDIPSwitches(UINT8 dip1, UINT8 dip2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GetSteeringStrength(void):
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* Strength of the steering based on drive board DIP switches (1-8).
|
|
||||||
*/
|
|
||||||
unsigned GetSteeringStrength(void);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SetSteeringStrength(steeringStrength):
|
|
||||||
*
|
|
||||||
* Sets the steering strength (modifies the DIP switch setting).
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* steeringStrength A value ranging from 1 to 8.
|
|
||||||
*/
|
|
||||||
void SetSteeringStrength(unsigned steeringStrength);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get7SegDisplays(seg1Digit1, seg1Digit2, seg2Digit1, seg2Digit2):
|
* Get7SegDisplays(seg1Digit1, seg1Digit2, seg2Digit1, seg2Digit2):
|
||||||
|
@ -117,13 +60,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void Get7SegDisplays(UINT8 &seg1Digit, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2);
|
void Get7SegDisplays(UINT8 &seg1Digit, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2);
|
||||||
|
|
||||||
/*
|
|
||||||
* GetZ80(void):
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* The Z80 object.
|
|
||||||
*/
|
|
||||||
CZ80 *GetZ80(void);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SaveState(SaveState):
|
* SaveState(SaveState):
|
||||||
|
@ -145,38 +81,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void LoadState(CBlockFile *SaveState);
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
/*
|
|
||||||
* Init(romPtr):
|
|
||||||
*
|
|
||||||
* Initializes (and "attaches") the drive board. This should be called
|
|
||||||
* before other members.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* romPtr Pointer to the drive board ROM (Z80 program). If this
|
|
||||||
* is NULL, then the drive board will not be emulated.
|
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* FAIL if the drive board could not be initialized (prints own error
|
|
||||||
* message), otherwise OKAY. If the drive board is not attached
|
|
||||||
* because no ROM was passed to it, no error is generated and the
|
|
||||||
* drive board is silently disabled (detached).
|
|
||||||
*/
|
|
||||||
bool Init(const UINT8 *romPtr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* AttachInputs(InputsPtr, gameInputFlags):
|
|
||||||
*
|
|
||||||
* Attaches inputs to the drive board (for access to the steering wheel
|
|
||||||
* position).
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* inputs Pointer to the input object.
|
|
||||||
* gameInputFlags The current game's input flags.
|
|
||||||
*/
|
|
||||||
void AttachInputs(CInputs *inputs, unsigned gameInputFlags);
|
|
||||||
|
|
||||||
void AttachOutputs(COutputs *outputs);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset(void):
|
* Reset(void):
|
||||||
*
|
*
|
||||||
|
@ -221,8 +125,8 @@ public:
|
||||||
* config Run-time configuration. The reference should be held because
|
* config Run-time configuration. The reference should be held because
|
||||||
* this changes at run-time.
|
* this changes at run-time.
|
||||||
*/
|
*/
|
||||||
CDriveBoard(const Util::Config::Node &config);
|
CJoyBoard(const Util::Config::Node &config);
|
||||||
~CDriveBoard(void);
|
~CJoyBoard(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read8(addr):
|
* Read8(addr):
|
||||||
|
@ -237,9 +141,8 @@ public:
|
||||||
* Returns:
|
* Returns:
|
||||||
* A byte of data from the address or port.
|
* A byte of data from the address or port.
|
||||||
*/
|
*/
|
||||||
UINT8 Read8(UINT32 addr);
|
|
||||||
UINT8 IORead8(UINT32 portNum);
|
UINT8 IORead8(UINT32 portNum);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write8(addr, data):
|
* Write8(addr, data):
|
||||||
* IORead8(portNum, data):
|
* IORead8(portNum, data):
|
||||||
|
@ -251,40 +154,18 @@ public:
|
||||||
* portNum Port address (0-255).
|
* portNum Port address (0-255).
|
||||||
* data Byte to write.
|
* data Byte to write.
|
||||||
*/
|
*/
|
||||||
void Write8(UINT32 addr, UINT8 data);
|
|
||||||
void IOWrite8(UINT32 portNum, UINT8 data);
|
void IOWrite8(UINT32 portNum, UINT8 data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Disable(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Util::Config::Node &m_config;
|
|
||||||
bool m_attached; // True if drive board is attached
|
|
||||||
bool m_tmpDisabled; // True if temporarily disabled by loading an incompatible save state
|
|
||||||
bool m_simulated; // True if drive board should be simulated rather than emulated
|
|
||||||
|
|
||||||
UINT8 m_dip1; // Value of DIP switch 1
|
|
||||||
UINT8 m_dip2; // Value of DIP switch 2
|
|
||||||
|
|
||||||
const UINT8* m_rom; // 32k ROM
|
|
||||||
UINT8* m_ram; // 8k RAM
|
|
||||||
|
|
||||||
CZ80 m_z80; // Z80 CPU @ 4MHz
|
|
||||||
|
|
||||||
CInputs *m_inputs;
|
|
||||||
unsigned m_inputFlags;
|
|
||||||
|
|
||||||
COutputs *m_outputs;
|
|
||||||
|
|
||||||
// Emulation state
|
|
||||||
bool m_initialized; // True if drive board has finished initialization
|
|
||||||
bool m_allowInterrupts; // True if drive board has enabled NMI interrupts
|
|
||||||
|
|
||||||
UINT8 m_seg1Digit1; // Current value of left digit on 7-segment display 1
|
UINT8 m_seg1Digit1; // Current value of left digit on 7-segment display 1
|
||||||
UINT8 m_seg1Digit2; // Current value of right digit on 7-segment display 1
|
UINT8 m_seg1Digit2; // Current value of right digit on 7-segment display 1
|
||||||
UINT8 m_seg2Digit1; // Current value of left digit on 7-segment display 2
|
UINT8 m_seg2Digit1; // Current value of left digit on 7-segment display 2
|
||||||
UINT8 m_seg2Digit2; // Current value of right digit on 7-segment display 2
|
UINT8 m_seg2Digit2; // Current value of right digit on 7-segment display 2
|
||||||
|
|
||||||
UINT8 m_dataSent; // Last command sent by main board
|
|
||||||
UINT8 m_dataReceived; // Data to send back to main board
|
|
||||||
|
|
||||||
UINT16 m_adcPortRead; // ADC port currently reading from
|
UINT16 m_adcPortRead; // ADC port currently reading from
|
||||||
UINT8 m_adcPortBit; // Bit number currently reading on ADC port
|
UINT8 m_adcPortBit; // Bit number currently reading on ADC port
|
||||||
|
|
||||||
|
@ -299,15 +180,6 @@ private:
|
||||||
UINT8 m_uncenterVal1; // First part of pending uncenter command
|
UINT8 m_uncenterVal1; // First part of pending uncenter command
|
||||||
UINT8 m_uncenterVal2; // Second part of pending uncenter command
|
UINT8 m_uncenterVal2; // Second part of pending uncenter command
|
||||||
|
|
||||||
// Simulation state
|
|
||||||
UINT8 m_initState;
|
|
||||||
UINT8 m_statusFlags;
|
|
||||||
UINT8 m_boardMode;
|
|
||||||
UINT8 m_readMode;
|
|
||||||
UINT8 m_wheelCenter;
|
|
||||||
UINT8 m_cockpitCenter;
|
|
||||||
UINT8 m_echoVal;
|
|
||||||
|
|
||||||
// Feedback state
|
// Feedback state
|
||||||
INT8 m_lastConstForce; // Last constant force command sent
|
INT8 m_lastConstForce; // Last constant force command sent
|
||||||
INT8 m_lastConstForceY; // Last constant force command sent y axis
|
INT8 m_lastConstForceY; // Last constant force command sent y axis
|
||||||
|
@ -321,10 +193,6 @@ private:
|
||||||
|
|
||||||
void SimulateFrame(void);
|
void SimulateFrame(void);
|
||||||
|
|
||||||
void EmulateFrame(void);
|
|
||||||
|
|
||||||
void ProcessEncoderCmd(void);
|
|
||||||
|
|
||||||
void ProcessEncoderCmdJoystick(void);
|
void ProcessEncoderCmdJoystick(void);
|
||||||
|
|
||||||
void SendStopAll(void);
|
void SendStopAll(void);
|
||||||
|
@ -348,4 +216,4 @@ private:
|
||||||
uint8_t ReadADCChannel4();
|
uint8_t ReadADCChannel4();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INCLUDED_DRIVEBOARD_H
|
#endif // INCLUDED_JOYBOARD_H
|
266
Src/Model3/DriveBoard/SkiBoard.cpp
Normal file
266
Src/Model3/DriveBoard/SkiBoard.cpp
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
/**
|
||||||
|
** 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)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
158
Src/Model3/DriveBoard/SkiBoard.h
Normal file
158
Src/Model3/DriveBoard/SkiBoard.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/**
|
||||||
|
** 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.h
|
||||||
|
*
|
||||||
|
* Header for the CSkiBoard (rumble skipad emulation) class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SKIBOARD_H
|
||||||
|
#define INCLUDED_SKIBOARD_H
|
||||||
|
|
||||||
|
#include "Util/NewConfig.h"
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSkiBoard
|
||||||
|
*/
|
||||||
|
class CSkiBoard : public CDriveBoard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* GetType(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Drive board type.
|
||||||
|
*/
|
||||||
|
Game::DriveBoardType GetType(void);
|
||||||
|
|
||||||
|
unsigned GetForceFeedbackStrength(void);
|
||||||
|
void SetForceFeedbackStrength(unsigned strength);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
//void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Restores the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load save state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(romPtr):
|
||||||
|
*
|
||||||
|
* Initializes (and "attaches") the drive board. This should be called
|
||||||
|
* before other members.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* romPtr Pointer to the drive board ROM (Z80 program). If this
|
||||||
|
* is NULL, then the drive board will not be emulated.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* FAIL if the drive board could not be initialized (prints own error
|
||||||
|
* message), otherwise OKAY. If the drive board is not attached
|
||||||
|
* because no ROM was passed to it, no error is generated and the
|
||||||
|
* drive board is silently disabled (detached).
|
||||||
|
*/
|
||||||
|
bool Init(const UINT8 *romPtr);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the drive board.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read():
|
||||||
|
*
|
||||||
|
* Reads data from the drive board.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data read.
|
||||||
|
*/
|
||||||
|
UINT8 Read(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(data):
|
||||||
|
*
|
||||||
|
* Writes data to the drive board.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to send.
|
||||||
|
*/
|
||||||
|
void Write(UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RunFrame(void):
|
||||||
|
*
|
||||||
|
* Emulates a single frame's worth of time on the drive board.
|
||||||
|
*/
|
||||||
|
void RunFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CWheelBoard(config):
|
||||||
|
* ~CWheelBoard():
|
||||||
|
*
|
||||||
|
* Constructor and destructor. Memory is freed by destructor.
|
||||||
|
*
|
||||||
|
* Paramters:
|
||||||
|
* config Run-time configuration. The reference should be held because
|
||||||
|
* this changes at run-time.
|
||||||
|
*/
|
||||||
|
CSkiBoard(const Util::Config::Node &config);
|
||||||
|
~CSkiBoard(void);
|
||||||
|
|
||||||
|
// needed by abstract class
|
||||||
|
UINT8 Read8(UINT32 addr);
|
||||||
|
void Write8(UINT32 addr, UINT8 data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Disable(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Feedback state
|
||||||
|
UINT8 m_lastVibrate; // Last vibrate command sent
|
||||||
|
|
||||||
|
UINT8 SimulateRead(void);
|
||||||
|
|
||||||
|
void SimulateWrite(UINT8 data);
|
||||||
|
|
||||||
|
void SendVibrate(UINT8 val);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_SKIBOARD_H
|
749
Src/Model3/DriveBoard/WheelBoard.cpp
Normal file
749
Src/Model3/DriveBoard/WheelBoard.cpp
Normal file
|
@ -0,0 +1,749 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WheelBoard.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CWheelBoard class: drive board (force feedback for wheel)
|
||||||
|
* emulation.
|
||||||
|
*
|
||||||
|
* NOTE: Simulation does not yet work. Drive board ROMs are required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
Game::DriveBoardType CWheelBoard::GetType(void)
|
||||||
|
{
|
||||||
|
return Game::DRIVE_BOARD_WHEEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::Get7SegDisplays(UINT8 &seg1Digit1, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2)
|
||||||
|
{
|
||||||
|
seg1Digit1 = m_seg1Digit1;
|
||||||
|
seg1Digit2 = m_seg1Digit2;
|
||||||
|
seg2Digit1 = m_seg2Digit1;
|
||||||
|
seg2Digit2 = m_seg2Digit2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
CDriveBoard::SaveState(SaveState);
|
||||||
|
|
||||||
|
SaveState->NewBlock("WheelBoard", __FILE__);
|
||||||
|
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_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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (SaveState->FindBlock("WheelBoard") != OKAY)
|
||||||
|
{
|
||||||
|
// Fall back to old "DriveBoad" state format
|
||||||
|
LoadLegacyState(SaveState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasSimulated;
|
||||||
|
SaveState->Read(&wasSimulated, sizeof(wasSimulated));
|
||||||
|
if (wasSimulated)
|
||||||
|
{
|
||||||
|
// Simulation has never existed
|
||||||
|
ErrorLog("Save state contains unexpected data. Halting drive board emulation.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Load DIP switches and digit displays
|
||||||
|
SaveState->Read(&m_dip1, sizeof(m_dip1));
|
||||||
|
SaveState->Read(&m_dip2, sizeof(m_dip2));
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
CDriveBoard::LoadState(SaveState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load save states created prior to DriveBoard refactor of SVN 847
|
||||||
|
void CWheelBoard::LoadLegacyState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (SaveState->FindBlock("DriveBoard") != OKAY)
|
||||||
|
{
|
||||||
|
// No wheel board or legacy drive board data found
|
||||||
|
ErrorLog("Unable to load wheel drive board state. Save state file is corrupt.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDriveBoard::LegacyDriveBoardState state;
|
||||||
|
|
||||||
|
bool isEnabled = !IsDisabled();
|
||||||
|
bool wasEnabled = false;
|
||||||
|
bool wasSimulated = false;
|
||||||
|
SaveState->Read(&wasEnabled, sizeof(wasEnabled));
|
||||||
|
if (wasEnabled)
|
||||||
|
{
|
||||||
|
SaveState->Read(&wasSimulated, sizeof(wasSimulated));
|
||||||
|
if (wasSimulated)
|
||||||
|
{
|
||||||
|
// Simulation has never actually existed
|
||||||
|
ErrorLog("Save state contains unexpected data. Halting drive board emulation.");
|
||||||
|
Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveState->Read(&state.dip1, sizeof(state.dip1));
|
||||||
|
SaveState->Read(&state.dip2, sizeof(state.dip2));
|
||||||
|
SaveState->Read(state.ram, 0x2000);
|
||||||
|
SaveState->Read(&state.initialized, sizeof(state.initialized));
|
||||||
|
SaveState->Read(&state.allowInterrupts, sizeof(state.allowInterrupts));
|
||||||
|
SaveState->Read(&state.dataSent, sizeof(state.dataSent));
|
||||||
|
SaveState->Read(&state.dataReceived, sizeof(state.dataReceived));
|
||||||
|
SaveState->Read(&state.adcPortRead, sizeof(state.adcPortRead));
|
||||||
|
SaveState->Read(&state.adcPortBit, sizeof(state.adcPortBit));
|
||||||
|
SaveState->Read(&state.uncenterVal1, sizeof(state.uncenterVal1));
|
||||||
|
SaveState->Read(&state.uncenterVal2, sizeof(state.uncenterVal2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasEnabled != isEnabled)
|
||||||
|
{
|
||||||
|
// If the board was not in the same activity state when the save file was
|
||||||
|
// generated, we cannot safely resume and must disable it
|
||||||
|
Disable();
|
||||||
|
ErrorLog("Halting drive board emulation due to mismatch in active and restored states.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Success: pass along to base class
|
||||||
|
CDriveBoard::LoadLegacyState(state, SaveState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::Disable(void)
|
||||||
|
{
|
||||||
|
SendStopAll();
|
||||||
|
CDriveBoard::Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::Reset(void)
|
||||||
|
{
|
||||||
|
CDriveBoard::Reset();
|
||||||
|
|
||||||
|
m_seg1Digit1 = 0xFF;
|
||||||
|
m_seg1Digit2 = 0xFF;
|
||||||
|
m_seg2Digit1 = 0xFF;
|
||||||
|
m_seg2Digit2 = 0xFF;
|
||||||
|
|
||||||
|
m_adcPortRead = 0;
|
||||||
|
m_adcPortBit = 0;
|
||||||
|
m_port42Out = 0;
|
||||||
|
m_port46Out = 0;
|
||||||
|
m_prev42Out = 0;
|
||||||
|
m_prev46Out = 0;
|
||||||
|
|
||||||
|
m_uncenterVal1 = 0;
|
||||||
|
m_uncenterVal2 = 0;
|
||||||
|
|
||||||
|
m_lastConstForce = 0;
|
||||||
|
m_lastSelfCenter = 0;
|
||||||
|
m_lastFriction = 0;
|
||||||
|
m_lastVibrate = 0;
|
||||||
|
|
||||||
|
m_simulated = false; //TODO: make this run-time configurable when simulation mode is supported
|
||||||
|
|
||||||
|
if (!m_config["ForceFeedback"].ValueAsDefault<bool>(false))
|
||||||
|
Disable();
|
||||||
|
|
||||||
|
// Stop any effects that may still be playing
|
||||||
|
if (!IsDisabled())
|
||||||
|
SendStopAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CWheelBoard::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)
|
||||||
|
if (m_simulated)
|
||||||
|
return SimulateRead();
|
||||||
|
else
|
||||||
|
return CDriveBoard::Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::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);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CDriveBoard::Write(data);
|
||||||
|
if (data == 0xCB)
|
||||||
|
m_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CWheelBoard::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 CWheelBoard::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 CWheelBoard::RunFrame(void)
|
||||||
|
{
|
||||||
|
if (IsDisabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_simulated)
|
||||||
|
SimulateFrame();
|
||||||
|
else
|
||||||
|
CDriveBoard::RunFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::SimulateFrame(void)
|
||||||
|
{
|
||||||
|
if (!m_initialized)
|
||||||
|
m_initState++;
|
||||||
|
// TODO - update m_statusFlags and play preset scripts according to board mode
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CWheelBoard::IORead8(UINT32 portNum)
|
||||||
|
{
|
||||||
|
UINT8 adcVal;
|
||||||
|
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x20: // DIP 1 value
|
||||||
|
return m_dip1;
|
||||||
|
case 0x21: // DIP 2 value
|
||||||
|
return m_dip2;
|
||||||
|
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 0x24: // Y analog axis for joystick
|
||||||
|
adcVal = ReadADCChannel1();
|
||||||
|
break;
|
||||||
|
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 0x26: // Cockpit bank position for deluxe racing cabinets
|
||||||
|
adcVal = ReadADCChannel3();
|
||||||
|
break;
|
||||||
|
case 0x27: // Not connected
|
||||||
|
adcVal = ReadADCChannel4();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
return (adcVal >> m_adcPortBit) & 0x01;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugLog("Unhandled Z80 input on ADC port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
case 0x28: // PPC command
|
||||||
|
return m_dataSent;
|
||||||
|
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
|
||||||
|
// 1 0 = encoder error 2 - overheat error
|
||||||
|
// 1 1 = encoder error 3 - encoder error, reinitializes board
|
||||||
|
return 0x00;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 input on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::IOWrite8(UINT32 portNum, UINT8 data)
|
||||||
|
{
|
||||||
|
switch (portNum)
|
||||||
|
{
|
||||||
|
case 0x10: // Unsure? - single byte 0x03 sent at initialization, then occasionally writes 0x07 & 0xFA to port
|
||||||
|
return;
|
||||||
|
case 0x11: // Interrupt control
|
||||||
|
if (data == 0x57)
|
||||||
|
m_allowInterrupts = true;
|
||||||
|
else if (data == 0x53) // Strictly speaking 0x53 then 0x04
|
||||||
|
m_allowInterrupts = false;
|
||||||
|
return;
|
||||||
|
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 0x20: // Left digit of 7-segment display 1
|
||||||
|
m_seg1Digit1 = data;
|
||||||
|
return;
|
||||||
|
case 0x21: // Right digit of 7-segment display 1
|
||||||
|
m_seg1Digit2 = data;
|
||||||
|
return;
|
||||||
|
case 0x22: // Left digit of 7-segment display 2
|
||||||
|
m_seg2Digit1 = data;
|
||||||
|
return;
|
||||||
|
case 0x23: // Right digit of 7-segment display 2
|
||||||
|
m_seg2Digit2 = data;
|
||||||
|
return;
|
||||||
|
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 0x29: // Reply for PPC
|
||||||
|
m_dataReceived = data;
|
||||||
|
if (data == 0xCC)
|
||||||
|
m_initialized = true;
|
||||||
|
return;
|
||||||
|
case 0x2a: // Encoder motor data (x axis)
|
||||||
|
m_port42Out = data;
|
||||||
|
ProcessEncoderCmd();
|
||||||
|
return;
|
||||||
|
case 0x2d: // Clutch/lamp control (deluxe cabinets) ( or y axis)
|
||||||
|
return;
|
||||||
|
case 0x2e: // Encoder motor control
|
||||||
|
m_port46Out = data;
|
||||||
|
return;
|
||||||
|
case 0xf0: // Unsure? - single byte 0xBB sent at initialization only
|
||||||
|
return;
|
||||||
|
case 0xf1: // Unsure? - single byte 0x4E sent regularly - some sort of watchdog?
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
DebugLog("Unhandled Z80 output on port %u (at PC = %04X)\n", portNum, m_z80.GetPC());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::ProcessEncoderCmd(void)
|
||||||
|
{
|
||||||
|
if (m_prev42Out != m_port42Out || m_prev46Out != m_port46Out)
|
||||||
|
{
|
||||||
|
//DebugLog("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:
|
||||||
|
//DebugLog("Unknown = 46 [%02X] / 42 [%02X]\n", m_port46Out, m_port42Out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_prev42Out = m_port42Out;
|
||||||
|
m_prev46Out = m_port46Out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CWheelBoard::SendStopAll(void)
|
||||||
|
{
|
||||||
|
//DebugLog(">> 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 CWheelBoard::SendConstantForce(INT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastConstForce)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val > 0)
|
||||||
|
{
|
||||||
|
DebugLog(">> Force Right %02X [%8s", val, "");
|
||||||
|
for (unsigned i = 0; i < 8; i++)
|
||||||
|
DebugLog(i == 0 || i <= (val + 1) / 16 ? ">" : " ");
|
||||||
|
DebugLog("]\n");
|
||||||
|
}
|
||||||
|
else if (val < 0)
|
||||||
|
{
|
||||||
|
DebugLog(">> Force Left %02X [", -val);
|
||||||
|
for (unsigned i = 0; i < 8; i++)
|
||||||
|
DebugLog(i == 7 || i >= (val + 128) / 16 ? "<" : " ");
|
||||||
|
DebugLog("%8s]\n", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DebugLog(">> 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 CWheelBoard::SendSelfCenter(UINT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastSelfCenter)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val == 0)
|
||||||
|
DebugLog(">> Stop Self-Center\n");
|
||||||
|
else
|
||||||
|
DebugLog(">> 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 CWheelBoard::SendFriction(UINT8 val)
|
||||||
|
{
|
||||||
|
if (val == m_lastFriction)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
if (val == 0)
|
||||||
|
DebugLog(">> Stop Friction\n");
|
||||||
|
else
|
||||||
|
DebugLog(">> Friction %02X\n", val);
|
||||||
|
*/
|
||||||
|
|
||||||
|
ForceFeedbackCmd ffCmd;
|
||||||
|
ffCmd.id = FFFriction;
|
||||||
|
ffCmd.force = (float)val / 255.0f;
|
||||||
|
m_inputs->steering->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastFriction = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWheelBoard::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->steering->SendForceFeedbackCmd(ffCmd);
|
||||||
|
|
||||||
|
m_lastVibrate = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CWheelBoard::ReadADCChannel1()
|
||||||
|
{
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CWheelBoard::ReadADCChannel2()
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CWheelBoard::ReadADCChannel3()
|
||||||
|
{
|
||||||
|
return 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CWheelBoard::ReadADCChannel4()
|
||||||
|
{
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
CWheelBoard::CWheelBoard(const Util::Config::Node &config)
|
||||||
|
: CDriveBoard(config)
|
||||||
|
{
|
||||||
|
m_dip1 = 0xCF;
|
||||||
|
m_dip2 = 0xFF;
|
||||||
|
|
||||||
|
DebugLog("Built Drive Board (wheel)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CWheelBoard::~CWheelBoard(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
215
Src/Model3/DriveBoard/WheelBoard.h
Normal file
215
Src/Model3/DriveBoard/WheelBoard.h
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
/**
|
||||||
|
** 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/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WheelBoard.h
|
||||||
|
*
|
||||||
|
* Header for the CWheelBoard (force feedback emulation for wheel) class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_WHEELBOARD_H
|
||||||
|
#define INCLUDED_WHEELBOARD_H
|
||||||
|
|
||||||
|
#include "Util/NewConfig.h"
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CWheelBoard
|
||||||
|
*/
|
||||||
|
class CWheelBoard : public CDriveBoard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* GetType(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Drive board type.
|
||||||
|
*/
|
||||||
|
Game::DriveBoardType GetType(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get7SegDisplays(seg1Digit1, seg1Digit2, seg2Digit1, seg2Digit2):
|
||||||
|
*
|
||||||
|
* Reads the 7-segment displays.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* seg1Digit1 Reference of variable to store digit 1 of the first 7-
|
||||||
|
* segment display to.
|
||||||
|
* seg1Digit2 First display, second digit.
|
||||||
|
* seg2Digit1 Second display, first digit.
|
||||||
|
* seg2Digit2 Second display, second digit.
|
||||||
|
*/
|
||||||
|
void Get7SegDisplays(UINT8 &seg1Digit, UINT8 &seg1Digit2, UINT8 &seg2Digit1, UINT8 &seg2Digit2);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Restores the drive board state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load save state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the drive board.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read():
|
||||||
|
*
|
||||||
|
* Reads data from the drive board.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data read.
|
||||||
|
*/
|
||||||
|
UINT8 Read(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(data):
|
||||||
|
*
|
||||||
|
* Writes data to the drive board.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to send.
|
||||||
|
*/
|
||||||
|
void Write(UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RunFrame(void):
|
||||||
|
*
|
||||||
|
* Emulates a single frame's worth of time on the drive board.
|
||||||
|
*/
|
||||||
|
void RunFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CWheelBoard(config):
|
||||||
|
* ~CWheelBoard():
|
||||||
|
*
|
||||||
|
* Constructor and destructor. Memory is freed by destructor.
|
||||||
|
*
|
||||||
|
* Paramters:
|
||||||
|
* config Run-time configuration. The reference should be held because
|
||||||
|
* this changes at run-time.
|
||||||
|
*/
|
||||||
|
CWheelBoard(const Util::Config::Node &config);
|
||||||
|
~CWheelBoard(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read8(addr):
|
||||||
|
* IORead8(portNum):
|
||||||
|
*
|
||||||
|
* Methods for reading from Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A byte of data from the address or port.
|
||||||
|
*/
|
||||||
|
UINT8 IORead8(UINT32 portNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write8(addr, data):
|
||||||
|
* IORead8(portNum, data):
|
||||||
|
*
|
||||||
|
* Methods for writing to Z80's memory and IO space. Required by CBus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in memory (0-0xFFFF).
|
||||||
|
* portNum Port address (0-255).
|
||||||
|
* data Byte to write.
|
||||||
|
*/
|
||||||
|
void IOWrite8(UINT32 portNum, UINT8 data);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Disable(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LoadLegacyState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
UINT8 m_seg1Digit1; // Current value of left digit on 7-segment display 1
|
||||||
|
UINT8 m_seg1Digit2; // Current value of right digit on 7-segment display 1
|
||||||
|
UINT8 m_seg2Digit1; // Current value of left digit on 7-segment display 2
|
||||||
|
UINT8 m_seg2Digit2; // Current value of right digit on 7-segment display 2
|
||||||
|
|
||||||
|
UINT16 m_adcPortRead; // ADC port currently reading from
|
||||||
|
UINT8 m_adcPortBit; // Bit number currently reading on ADC port
|
||||||
|
|
||||||
|
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_prev42Out; // Previous value sent to Z80 I/O port 42
|
||||||
|
UINT8 m_prev46Out; // Previous value sent to Z80 I/O port 46
|
||||||
|
|
||||||
|
UINT8 m_uncenterVal1; // First part of pending uncenter command
|
||||||
|
UINT8 m_uncenterVal2; // Second part of pending uncenter command
|
||||||
|
|
||||||
|
// Feedback state
|
||||||
|
INT8 m_lastConstForce; // Last constant force command sent
|
||||||
|
UINT8 m_lastSelfCenter; // Last self center command sent
|
||||||
|
UINT8 m_lastFriction; // Last friction command sent
|
||||||
|
UINT8 m_lastVibrate; // Last vibrate command sent
|
||||||
|
|
||||||
|
UINT8 SimulateRead(void);
|
||||||
|
|
||||||
|
void SimulateWrite(UINT8 data);
|
||||||
|
|
||||||
|
void SimulateFrame(void);
|
||||||
|
|
||||||
|
void ProcessEncoderCmd(void);
|
||||||
|
|
||||||
|
void SendStopAll(void);
|
||||||
|
|
||||||
|
void SendConstantForce(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_WHEELBOARD_H
|
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,13 @@
|
||||||
/**
|
/**
|
||||||
** Supermodel
|
** Supermodel
|
||||||
** A Sega Model 3 Arcade Emulator.
|
** A Sega Model 3 Arcade Emulator.
|
||||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
||||||
|
** Harry Tuttle, and Spindizzi
|
||||||
**
|
**
|
||||||
** This file is part of Supermodel.
|
** This file is part of Supermodel.
|
||||||
**
|
**
|
||||||
** Supermodel is free software: you can redistribute it and/or modify it under
|
** 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
|
** 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)
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
** any later version.
|
** any later version.
|
||||||
**
|
**
|
||||||
|
@ -18,10 +19,10 @@
|
||||||
** You should have received a copy of the GNU General Public License along
|
** You should have received a copy of the GNU General Public License along
|
||||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Model3.h
|
* Model3.h
|
||||||
*
|
*
|
||||||
* Header file defining the CModel3 and CModel3Config classes.
|
* Header file defining the CModel3 and CModel3Config classes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ public:
|
||||||
void Reset(void);
|
void Reset(void);
|
||||||
const Game &GetGame(void) const;
|
const Game &GetGame(void) const;
|
||||||
void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr);
|
void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr);
|
||||||
void AttachInputs(CInputs *InputsPtr);
|
void AttachInputs(CInputs *InputsPtr);
|
||||||
void AttachOutputs(COutputs *OutputsPtr);
|
void AttachOutputs(COutputs *OutputsPtr);
|
||||||
bool Init(void);
|
bool Init(void);
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ public:
|
||||||
void Write16(UINT32 addr, UINT16 data);
|
void Write16(UINT32 addr, UINT16 data);
|
||||||
void Write32(UINT32 addr, UINT32 data);
|
void Write32(UINT32 addr, UINT32 data);
|
||||||
void Write64(UINT32 addr, UINT64 data);
|
void Write64(UINT32 addr, UINT64 data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LoadGame(game, rom_set):
|
* LoadGame(game, rom_set):
|
||||||
*
|
*
|
||||||
|
@ -114,7 +115,7 @@ public:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetSoundBoard(void):
|
* GetSoundBoard(void):
|
||||||
*
|
*
|
||||||
* Returns a reference to the sound board.
|
* Returns a reference to the sound board.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
|
@ -162,8 +163,8 @@ public:
|
||||||
* CModel3(config):
|
* CModel3(config):
|
||||||
* ~CModel3(void):
|
* ~CModel3(void):
|
||||||
*
|
*
|
||||||
* Constructor and destructor for Model 3 class. Constructor performs a
|
* Constructor and destructor for Model 3 class. Constructor performs a
|
||||||
* bare-bones initialization of object; does not perform any memory
|
* bare-bones initialization of object; does not perform any memory
|
||||||
* allocation or any actions that can fail. The destructor will deallocate
|
* allocation or any actions that can fail. The destructor will deallocate
|
||||||
* memory and free resources used by the object (and its child objects).
|
* memory and free resources used by the object (and its child objects).
|
||||||
*
|
*
|
||||||
|
@ -207,7 +208,7 @@ private:
|
||||||
static int StartDriveBoardThread(void *data); // Callback to start drive board thread
|
static int StartDriveBoardThread(void *data); // Callback to start drive board thread
|
||||||
|
|
||||||
static void AudioCallback(void *data); // Audio buffer callback
|
static void AudioCallback(void *data); // Audio buffer callback
|
||||||
|
|
||||||
bool WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread (when not sync'd with render thread)
|
bool WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread (when not sync'd with render thread)
|
||||||
int RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread)
|
int RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread)
|
||||||
int RunSoundBoardThread(void); // Runs sound board thread (not sync'd in step with render thread, ie running at full speed)
|
int RunSoundBoardThread(void); // Runs sound board thread (not sync'd in step with render thread, ie running at full speed)
|
||||||
|
@ -221,20 +222,20 @@ private:
|
||||||
|
|
||||||
// Game and hardware information
|
// Game and hardware information
|
||||||
Game m_game;
|
Game m_game;
|
||||||
|
|
||||||
// Game inputs and outputs
|
// Game inputs and outputs
|
||||||
CInputs *Inputs;
|
CInputs *Inputs;
|
||||||
COutputs *Outputs;
|
COutputs *Outputs;
|
||||||
|
|
||||||
// Input registers (game controls)
|
// Input registers (game controls)
|
||||||
UINT8 inputBank;
|
UINT8 inputBank;
|
||||||
UINT8 serialFIFO1, serialFIFO2;
|
UINT8 serialFIFO1, serialFIFO2;
|
||||||
UINT8 gunReg;
|
UINT8 gunReg;
|
||||||
int adcChannel;
|
int adcChannel;
|
||||||
|
|
||||||
// MIDI port
|
// MIDI port
|
||||||
UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior
|
UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior
|
||||||
|
|
||||||
// Emulated core Model 3 memory regions
|
// Emulated core Model 3 memory regions
|
||||||
UINT8 *memoryPool; // single allocated region for all ROM and system RAM
|
UINT8 *memoryPool; // single allocated region for all ROM and system RAM
|
||||||
UINT8 *ram; // 8 MB PowerPC RAM
|
UINT8 *ram; // 8 MB PowerPC RAM
|
||||||
|
@ -252,12 +253,12 @@ private:
|
||||||
|
|
||||||
// Banked CROM
|
// Banked CROM
|
||||||
UINT8 *cromBank; // currently mapped in CROM bank
|
UINT8 *cromBank; // currently mapped in CROM bank
|
||||||
unsigned cromBankReg; // the CROM bank register
|
unsigned cromBankReg; // the CROM bank register
|
||||||
|
|
||||||
// Security device
|
// Security device
|
||||||
bool m_securityFirstRead = true;
|
bool m_securityFirstRead = true;
|
||||||
unsigned securityPtr; // pointer to current offset in security data
|
unsigned securityPtr; // pointer to current offset in security data
|
||||||
|
|
||||||
// PowerPC
|
// PowerPC
|
||||||
PPC_FETCH_REGION PPCFetchRegions[3];
|
PPC_FETCH_REGION PPCFetchRegions[3];
|
||||||
|
|
||||||
|
@ -285,11 +286,11 @@ private:
|
||||||
CCondVar *sndBrdNotifySync;
|
CCondVar *sndBrdNotifySync;
|
||||||
CSemaphore *drvBrdThreadSync;
|
CSemaphore *drvBrdThreadSync;
|
||||||
CMutex *notifyLock;
|
CMutex *notifyLock;
|
||||||
CCondVar *notifySync;
|
CCondVar *notifySync;
|
||||||
|
|
||||||
// Frame timings
|
// Frame timings
|
||||||
FrameTimings timings;
|
FrameTimings timings;
|
||||||
|
|
||||||
// Other devices
|
// Other devices
|
||||||
CIRQ IRQ; // Model 3 IRQ controller
|
CIRQ IRQ; // Model 3 IRQ controller
|
||||||
CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller
|
CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller
|
||||||
|
@ -301,7 +302,7 @@ private:
|
||||||
CReal3D GPU; // Real3D graphics hardware
|
CReal3D GPU; // Real3D graphics hardware
|
||||||
CSoundBoard SoundBoard; // Sound board
|
CSoundBoard SoundBoard; // Sound board
|
||||||
CDSB *DSB; // Digital Sound Board (type determined dynamically at load time)
|
CDSB *DSB; // Digital Sound Board (type determined dynamically at load time)
|
||||||
CDriveBoard DriveBoard; // Drive board
|
CDriveBoard *DriveBoard; // Drive board
|
||||||
CCrypto m_cryptoDevice; // Encryption device
|
CCrypto m_cryptoDevice; // Encryption device
|
||||||
CJTAG m_jtag; // JTAG interface
|
CJTAG m_jtag; // JTAG interface
|
||||||
#ifdef NET_BOARD
|
#ifdef NET_BOARD
|
||||||
|
|
|
@ -35,7 +35,12 @@ const char *COutputs::s_outputNames[] =
|
||||||
"LampView4",
|
"LampView4",
|
||||||
"LampLeader",
|
"LampLeader",
|
||||||
"RawDrive",
|
"RawDrive",
|
||||||
"RawLamps"
|
"RawLamps",
|
||||||
|
"BillDigit1",
|
||||||
|
"BillDigit2",
|
||||||
|
"BillDigit3",
|
||||||
|
"BillDigit4",
|
||||||
|
"BillDigit5"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *COutputs::GetOutputName(EOutputs output)
|
const char *COutputs::GetOutputName(EOutputs output)
|
||||||
|
|
|
@ -45,10 +45,15 @@ enum EOutputs
|
||||||
OutputLampView4,
|
OutputLampView4,
|
||||||
OutputLampLeader,
|
OutputLampLeader,
|
||||||
OutputRawDrive,
|
OutputRawDrive,
|
||||||
OutputRawLamps
|
OutputRawLamps,
|
||||||
|
OutputBill1,
|
||||||
|
OutputBill2,
|
||||||
|
OutputBill3,
|
||||||
|
OutputBill4,
|
||||||
|
OutputBill5
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NUM_OUTPUTS 9
|
#define NUM_OUTPUTS 14
|
||||||
|
|
||||||
class COutputs
|
class COutputs
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
** Supermodel
|
** Supermodel
|
||||||
** A Sega Model 3 Arcade Emulator.
|
** A Sega Model 3 Arcade Emulator.
|
||||||
** Copyright 2011-2020 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
|
||||||
** Harry Tuttle, and Spindizzi
|
** Harry Tuttle, and Spindizzi
|
||||||
**
|
**
|
||||||
** This file is part of Supermodel.
|
** This file is part of Supermodel.
|
||||||
|
@ -1443,7 +1443,7 @@ static Util::Config::Node DefaultConfig()
|
||||||
static void Title(void)
|
static void Title(void)
|
||||||
{
|
{
|
||||||
puts("Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION ")");
|
puts("Supermodel: A Sega Model 3 Arcade Emulator (Version " SUPERMODEL_VERSION ")");
|
||||||
puts("Copyright 2011-2020 by Bart Trzynadlowski, Nik Henson, Ian Curtis,");
|
puts("Copyright 2011-2021 by Bart Trzynadlowski, Nik Henson, Ian Curtis,");
|
||||||
puts(" Harry Tuttle, and Spindizzi\n");
|
puts(" Harry Tuttle, and Spindizzi\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,11 @@
|
||||||
#include "Sound/SCSP.h"
|
#include "Sound/SCSP.h"
|
||||||
#include "Model3/SoundBoard.h"
|
#include "Model3/SoundBoard.h"
|
||||||
#include "Model3/DSB.h"
|
#include "Model3/DSB.h"
|
||||||
#include "Model3/DriveBoard.h"
|
#include "Model3/DriveBoard/DriveBoard.h"
|
||||||
|
#include "Model3/DriveBoard/WheelBoard.h"
|
||||||
|
#include "Model3/DriveBoard/JoystickBoard.h"
|
||||||
|
#include "Model3/DriveBoard/SkiBoard.h"
|
||||||
|
#include "Model3/DriveBoard/BillBoard.h"
|
||||||
#ifdef NET_BOARD
|
#ifdef NET_BOARD
|
||||||
#include "Network/NetBoard.h"
|
#include "Network/NetBoard.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,11 +44,11 @@ namespace Util
|
||||||
public:
|
public:
|
||||||
class const_iterator;
|
class const_iterator;
|
||||||
|
|
||||||
class iterator: public std::iterator<std::forward_iterator_tag, Node>
|
class iterator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
ptr_t m_node;
|
ptr_t m_node;
|
||||||
friend class const_iterator;
|
friend class const_iterator;
|
||||||
public:
|
public:
|
||||||
inline iterator(ptr_t node = ptr_t())
|
inline iterator(ptr_t node = ptr_t())
|
||||||
: m_node(node)
|
: m_node(node)
|
||||||
|
@ -87,7 +87,7 @@ namespace Util
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class const_iterator: public std::iterator<std::forward_iterator_tag, const Node>
|
class const_iterator
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const_ptr_t m_node;
|
const_ptr_t m_node;
|
||||||
|
@ -147,7 +147,7 @@ namespace Util
|
||||||
{
|
{
|
||||||
return iterator();
|
return iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const_iterator begin() const
|
inline const_iterator begin() const
|
||||||
{
|
{
|
||||||
return iterator(m_first_child);
|
return iterator(m_first_child);
|
||||||
|
@ -217,7 +217,7 @@ namespace Util
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a child node. Multiple nodes of the same key may be added but only
|
// Add a child node. Multiple nodes of the same key may be added but only
|
||||||
// when specified as leaves. For example, adding "foo/bar" twice will
|
// when specified as leaves. For example, adding "foo/bar" twice will
|
||||||
// result in one "foo" with two "bar" children.
|
// result in one "foo" with two "bar" children.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Node &Add(const std::string &path, const T &value)
|
Node &Add(const std::string &path, const T &value)
|
||||||
|
@ -271,7 +271,7 @@ namespace Util
|
||||||
{
|
{
|
||||||
return !m_value;
|
return !m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if value exists (is not empty)
|
// True if value exists (is not empty)
|
||||||
inline bool Exists() const
|
inline bool Exists() const
|
||||||
{
|
{
|
||||||
|
@ -293,11 +293,11 @@ namespace Util
|
||||||
// Always succeeds -- failed lookups permanently create an empty node.
|
// Always succeeds -- failed lookups permanently create an empty node.
|
||||||
// Use with caution. Intended for hard-coded lookups.
|
// Use with caution. Intended for hard-coded lookups.
|
||||||
const Node &operator[](const std::string &path) const;
|
const Node &operator[](const std::string &path) const;
|
||||||
|
|
||||||
// These throw if the node is missing
|
// These throw if the node is missing
|
||||||
Node &Get(const std::string &path);
|
Node &Get(const std::string &path);
|
||||||
const Node &Get(const std::string &path) const;
|
const Node &Get(const std::string &path) const;
|
||||||
|
|
||||||
// This returns nullptr if node is missing
|
// This returns nullptr if node is missing
|
||||||
Node *TryGet(const std::string &path);
|
Node *TryGet(const std::string &path);
|
||||||
const Node *TryGet(const std::string &path) const;
|
const Node *TryGet(const std::string &path) const;
|
||||||
|
|
Loading…
Reference in a new issue