2011-04-24 01:14:00 +00:00
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
2021-02-18 10:29:15 +00:00
** Copyright 2011-2021 Bart Trzynadlowski, Nik Henson, Ian Curtis,
** Harry Tuttle, and Spindizzi
2011-04-24 01:14:00 +00:00
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
2021-02-18 10:29:15 +00:00
** the terms of the GNU General Public License as published by the Free
2011-04-24 01:14:00 +00:00
** 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/>.
**/
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
/*
* Model3.cpp
2021-02-18 10:29:15 +00:00
*
2011-04-24 01:14:00 +00:00
* Implementation of the CModel3 class: a complete Model 3 machine.
*
* To-Do List
* ----------
2012-07-12 06:13:24 +00:00
* - Save state format has changed slightly. No longer need dmaUnknownRegister
2021-02-18 10:29:15 +00:00
* in Real3D.cpp and security board-related variable was added to Model 3
2016-04-02 21:50:40 +00:00
* state. PowerPC timing variables have changed. Before 0.3a release,
* important to change format version #.
2017-08-29 01:28:21 +00:00
* - Remove FLIPENDIAN32() macros and have little endian devices flip data
* around themselves. Bus standard should be big endian.
2016-04-02 21:50:40 +00:00
* - ROM sets should probably be handled with a class that manages ROM
* loading, the game list, as well as ROM patching
* - Wrap up CPU emulation inside a class.
* - Update the to-do list! I forgot lots of other stuff here :)
2011-04-24 01:14:00 +00:00
*
* PowerPC Address Map (may be slightly out of date/incomplete)
* -------------------
2016-04-02 21:50:40 +00:00
* 00000000-007FFFFF RAM
* 84000000-8400003F Real3D Status Registers
* 88000000-88000007 Real3D Command Port
* 8C000000-8C3FFFFF Real3D Culling RAM (Low)
* 8E000000-8E0FFFFF Real3D Culling RAM (High)
* 90000000-9000000B Real3D VROM Texture Port
* 94000000-940FFFFF Real3D Texture FIFO
* 98000000-980FFFFF Real3D Polygon RAM
2023-09-03 16:51:18 +01:00
* C0000000-C000FFFF Netboard Shared RAM (Step 1.5+)
* C0010000-C00101FF Netboard Registers (Step 1.5+)
* C0020000-C002FFFF Netboard Program RAM (Step 1.5+)
2016-04-02 21:50:40 +00:00
* C0000000-C00000FF SCSI (Step 1.x)
* C1000000-C10000FF SCSI (Step 1.x) (Lost World expects it here)
* C2000000-C20000FF Real3D DMA (Step 2.x)
* F0040000-F004003F Input (Controls) Registers
* F0080000-F0080007 Sound Board Registers
* F00C0000-F00DFFFF Backup RAM
* F0100000-F010003F System Registers
* F0140000-F014003F Real-Time Clock
* F0180000-F019FFFF Security Board RAM
* F01A0000-F01A003F Security Board Registers
* F0800CF8-F0800CFF MPC105 CONFIG_ADDR (Step 1.x)
* F0C00CF8-F0800CFF MPC105 CONFIG_DATA (Step 1.x)
* F1000000-F10F7FFF Tile Generator Pattern Table
* F10F8000-F10FFFFF Tile Generator Name Table
* F1100000-F111FFFF Tile Generator Palette
* F1180000-F11800FF Tile Generator Registers
* F8FFF000-F8FFF0FF MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers
* F9000000-F90000FF NCR 53C810 Registers (Step 1.x?)
* FEC00000-FEDFFFFF MPC106 CONFIG_ADDR (Step 2.x)
* FEE00000-FEFFFFFF MPC106 CONFIG_DATA (Step 2.x)
* FF000000-FF7FFFFF Banked CROM (CROMxx)
* FF800000-FFFFFFFF Fixed CROM
2011-04-24 01:14:00 +00:00
*
* Endianness
* ----------
* We assume a little endian machine and so for speed, PowerPC RAM and ROM
* regions are byte reversed, which means that aligned words can be read and
2021-02-18 10:29:15 +00:00
* written without any conversion. Problems arise when the PowerPC accesses
2011-04-24 01:14:00 +00:00
* little endian devices, like the tile generator, MPC10x, or Real3D. Then, the
* access must be carried out carefully one byte at a time or by manually byte
* reversing first (because the PowerPC will already have byte reversed it).
*
* System Registers
* ----------------
*
* F0100014: IRQ Enable
* 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+
2023-09-03 16:51:18 +01:00
* | ? |SND| ? |NET|VGP|VDP|VBL|VD0|
2011-04-24 01:14:00 +00:00
* +---+---+---+---+---+---+---+---+
2016-04-02 21:50:40 +00:00
* SND SCSP (sound)
* NET Network
2023-09-03 16:51:18 +01:00
* VGP GP done (geometry processing)
* VDP DP done (display processing)
2016-04-02 21:50:40 +00:00
* VBL VBlank start
2023-09-03 16:51:18 +01:00
* VD0 Unknown video-related
2016-04-02 21:50:40 +00:00
* 0 = Disable, 1 = Enable
2011-04-24 01:14:00 +00:00
*
* Game Buttons
* ------------
*
* For further information, see ReadInputs().
*
* Offset 0x04, bank 0:
*
2016-04-02 21:50:40 +00:00
* 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+
2011-04-24 01:14:00 +00:00
* | ? | ? |ST2|ST1|SVA|TSA|CN2|CN1|
2016-04-02 21:50:40 +00:00
* +---+---+---+---+---+---+---+---+
* CNx Coin 1, Coin 2
* TSA Test Button A
* SVA Service Button A
* STx Start 1, Start 2
2011-04-24 01:14:00 +00:00
*
* Offset 0x04, bank 1:
*
2016-04-02 21:50:40 +00:00
* 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+
2011-04-24 01:14:00 +00:00
* |TSB|SVB|EEP| ? | ? | ? | ? | ? |
2016-04-02 21:50:40 +00:00
* +---+---+---+---+---+---+---+---+
* EEP Mapped to EEPROM (values written here are ignored)
* SVB Service Button B
* TSB Test Button B
2011-04-24 01:14:00 +00:00
*
* Offset 0x08:
*
2016-04-02 21:50:40 +00:00
* 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+
2011-04-24 01:14:00 +00:00
* |G27|G26|G25|G24|G23|G22|G21|G20|
2016-04-02 21:50:40 +00:00
* +---+---+---+---+---+---+---+---+
* G2x Game-specific
2011-04-24 01:14:00 +00:00
*
* Offset 0x0C:
*
2016-04-02 21:50:40 +00:00
* 7 6 5 4 3 2 1 0
* +---+---+---+---+---+---+---+---+
2011-04-24 01:14:00 +00:00
* |G37|G36|G35|G34|G33|G32|G31|G30|
2016-04-02 21:50:40 +00:00
* +---+---+---+---+---+---+---+---+
* G3x Game-specific
2011-04-24 01:14:00 +00:00
*
* Game-specific buttons:
*
2016-04-02 21:50:40 +00:00
* Scud Race:
* G27 ---
* G26 Shift 2 when combined w/ G25, Shift 1 when combined w/ G24
* G25 Shift 4
* G24 Shift 3
* G23 VR4 Green
2021-02-18 10:29:15 +00:00
* G22 VR3 Yellow
* G21 VR2 Blue
2016-04-02 21:50:40 +00:00
* G20 VR1 Red
2011-04-24 01:14:00 +00:00
*
2016-04-02 21:50:40 +00:00
* Virtua Fighter 3, Fighting Vipers 2:
* G27 Right
* G26 Left
* G25 Down
* G24 Up
* G23 Punch
* G22 Kick
* G21 Guard
* G20 Escape (VF3 only)
2011-04-24 01:14:00 +00:00
*
2016-04-02 21:50:40 +00:00
* Sega Rally 2:
* G21 Hand Brake
* G20 View Change
2011-04-24 01:14:00 +00:00
*
2016-04-02 21:50:40 +00:00
* Lost World, LA Machineguns:
* G20 Gun trigger
2011-09-12 05:43:37 +00:00
*
2016-04-02 21:50:40 +00:00
* Star Wars Trilogy:
* G20 Event button
* G25 Trigger
2011-04-24 01:14:00 +00:00
*
2016-04-02 21:50:40 +00:00
* Virtual On 2:
* G27 Left Lever Left
* G26 Left Lever Right
* G25 Left Lever Up
* G24 Left Lever Down
* G23 ---
* G22 ---
2021-02-18 10:29:15 +00:00
* G21 Left Turbo
2016-04-02 21:50:40 +00:00
* G20 Left Shot Trigger
* G37 Right Lever Left
* G36 Right Lever Right
* G35 Right Lever Up
* G34 Right Lever Down
* G33 ---
* G32 ---
2021-02-18 10:29:15 +00:00
* G31 Right Turbo
2016-04-02 21:50:40 +00:00
* G30 Right Shot Trigger
*
* Misc. Notes
* -----------
2021-02-18 10:29:15 +00:00
*
2016-04-02 21:50:40 +00:00
* daytona2:
* - Base address of program in CROM: 0x600000
* - 0x10019E is the location in RAM which contains link type.
* - Region menu can be accessed by entering test mode, holding start, and
2021-02-18 10:29:15 +00:00
* pressing: green, green, blue, yellow, red, yellow, blue
2016-04-02 21:50:40 +00:00
* (VR4,4,2,3,1,3,2).
2021-02-18 10:29:15 +00:00
*
2020-12-21 15:39:15 +00:00
* magtruck:
* Found a way to unlock region in Magical truck
* If Midi data port returns 0, magtruck is locked to japan region
* if Midi data port returns 1, magtruck region can be changed to export, usa, australian
* We decided to make a rom patch instead of introducing a specific config to allow the region to be set
* Reminder : to change region in magtruck, use the region menu code
* enter Service menu with Test, then Start P1, Start P1, Service, Start P1, Service, Test (default keys : 6 then 1 1 5 1 5 6)
* note : rom patch is active by default, comment the patch in games.xml if you want Japan region
2021-02-18 10:29:15 +00:00
*
2011-04-24 01:14:00 +00:00
*/
2021-11-22 17:15:06 +00:00
# include "Model3.h"
2011-04-24 01:14:00 +00:00
# include <new>
2011-08-19 20:43:07 +00:00
# include <cstdio>
# include <cstdlib>
# include <cstring>
2011-04-24 01:14:00 +00:00
# include "Supermodel.h"
2021-11-22 17:15:06 +00:00
# include "DriveBoard/BillBoard.h"
# include "DriveBoard/JoystickBoard.h"
# include "DriveBoard/SkiBoard.h"
# include "DriveBoard/WheelBoard.h"
2017-03-27 03:19:15 +00:00
# include "Game.h"
# include "ROMSet.h"
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
# include "Network/NetBoard.h"
# include "Network/SimNetBoard.h"
# endif // NET_BOARD
2021-11-22 17:15:06 +00:00
# include "OSD/Audio.h"
# include "OSD/Video.h"
2016-04-02 21:50:40 +00:00
# include "Util/Format.h"
2017-03-27 03:19:15 +00:00
# include "Util/ByteSwap.h"
2016-04-02 21:50:40 +00:00
# include <functional>
# include <set>
2017-03-27 03:19:15 +00:00
# include <iostream>
2020-07-01 15:56:21 +00:00
# include <algorithm>
2011-04-24 01:14:00 +00:00
/******************************************************************************
Model 3 Inputs
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
Game controls. The EEPROM is mapped here as well.
******************************************************************************/
UINT8 CModel3 : : ReadInputs ( unsigned reg )
{
2016-04-02 21:50:40 +00:00
UINT8 adc [ 8 ] ;
UINT8 data ;
reg & = 0x3F ;
switch ( reg )
{
case 0x00 : // input bank
return inputBank ;
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
case 0x04 : // current input bank
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
data = 0xFF ;
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
if ( ( inputBank & 1 ) = = 0 )
{
data & = ~ ( Inputs - > coin [ 0 ] - > value ) ; // Coin 1
data & = ~ ( Inputs - > coin [ 1 ] - > value < < 1 ) ; // Coin 2
data & = ~ ( Inputs - > test [ 0 ] - > value < < 2 ) ; // Test A
data & = ~ ( Inputs - > service [ 0 ] - > value < < 3 ) ; // Service A
data & = ~ ( Inputs - > start [ 0 ] - > value < < 4 ) ; // Start 1
data & = ~ ( Inputs - > start [ 1 ] - > value < < 5 ) ; // Start 2
}
else
{
data & = ~ ( Inputs - > service [ 1 ] - > value < < 6 ) ; // Service B
data & = ~ ( Inputs - > test [ 1 ] - > value < < 7 ) ; // Test B
data = ( data & 0xDF ) | ( EEPROM . Read ( ) < < 5 ) ; // bank 1 contains EEPROM data bit
}
2013-11-30 19:39:59 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SKI ) )
2016-04-02 21:50:40 +00:00
{
if ( ( inputBank & 1 ) = = 0 )
{
data & = ~ ( Inputs - > skiPollLeft - > value < < 5 ) ;
data & = ~ ( Inputs - > skiSelect1 - > value < < 6 ) ;
data & = ~ ( Inputs - > skiSelect2 - > value < < 7 ) ;
data & = ~ ( Inputs - > skiSelect3 - > value < < 4 ) ;
}
}
2013-11-30 19:39:59 +00:00
2016-04-02 21:50:40 +00:00
return data ;
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
case 0x08 : // game-specific inputs
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
data = 0xFF ;
2011-04-24 01:14:00 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SKI ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > skiPollRight - > value < < 0 ) ;
}
2013-11-30 19:39:59 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_JOYSTICK1 ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > up [ 0 ] - > value < < 5 ) ; // P1 Up
data & = ~ ( Inputs - > down [ 0 ] - > value < < 4 ) ; // P1 Down
data & = ~ ( Inputs - > left [ 0 ] - > value < < 7 ) ; // P1 Left
data & = ~ ( Inputs - > right [ 0 ] - > value < < 6 ) ; // P1 Right
}
2011-04-24 01:14:00 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_FIGHTING ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > escape [ 0 ] - > value < < 3 ) ; // P1 Escape
data & = ~ ( Inputs - > guard [ 0 ] - > value < < 2 ) ; // P1 Guard
data & = ~ ( Inputs - > kick [ 0 ] - > value < < 1 ) ; // P1 Kick
data & = ~ ( Inputs - > punch [ 0 ] - > value < < 0 ) ; // P1 Punch
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SPIKEOUT ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > shift - > value < < 2 ) ; // Shift
data & = ~ ( Inputs - > beat - > value < < 0 ) ; // Beat
data & = ~ ( Inputs - > charge - > value < < 1 ) ; // Charge
data & = ~ ( Inputs - > jump - > value < < 3 ) ; // Jump
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SOCCER ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > shortPass [ 0 ] - > value < < 2 ) ; // P1 Short Pass
data & = ~ ( Inputs - > longPass [ 0 ] - > value < < 0 ) ; // P1 Long Pass
data & = ~ ( Inputs - > shoot [ 0 ] - > value < < 1 ) ; // P1 Shoot
}
2011-04-24 01:14:00 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_VR4 ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > vr [ 0 ] - > value < < 0 ) ; // VR1 Red
data & = ~ ( Inputs - > vr [ 1 ] - > value < < 1 ) ; // VR2 Blue
data & = ~ ( Inputs - > vr [ 2 ] - > value < < 2 ) ; // VR3 Yellow
data & = ~ ( Inputs - > vr [ 3 ] - > value < < 3 ) ; // VR4 Green
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_VIEWCHANGE ) )
2016-04-02 21:50:40 +00:00
{
// Harley is wired slightly differently
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_HARLEY ) )
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > viewChange - > value < < 1 ) ; // View change
else
data & = ~ ( Inputs - > viewChange - > value < < 0 ) ; // View change
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SHIFT4 ) )
2016-04-02 21:50:40 +00:00
{
if ( Inputs - > gearShift4 - > value = = 2 ) // Shift 2
data & = ~ 0x60 ;
else if ( Inputs - > gearShift4 - > value = = 4 ) // Shift 4
data & = ~ 0x20 ;
if ( Inputs - > gearShift4 - > value = = 1 ) // Shift 1
data & = ~ 0x50 ;
else if ( Inputs - > gearShift4 - > value = = 3 ) // Shift 3
data & = ~ 0x10 ;
}
2013-11-30 19:39:59 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SHIFTUPDOWN ) )
2016-04-02 21:50:40 +00:00
{
// Harley is wired slightly differently
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_HARLEY ) )
2016-04-02 21:50:40 +00:00
{
2021-02-18 10:29:15 +00:00
if ( Inputs - > gearShiftUp - > value ) // Shift up
2017-04-07 04:30:27 +00:00
data & = ~ 0x20 ;
2016-04-02 21:50:40 +00:00
else if ( Inputs - > gearShiftDown - > value ) // Shift down
2017-04-07 04:30:27 +00:00
data & = ~ 0x10 ;
2016-04-02 21:50:40 +00:00
}
else
{
if ( Inputs - > gearShiftUp - > value ) // Shift up
data & = ~ 0x50 ;
else if ( Inputs - > gearShiftDown - > value ) // Shift down
data & = ~ 0x60 ;
}
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_HANDBRAKE ) )
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > handBrake - > value < < 1 ) ; // Hand brake
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_HARLEY ) )
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > musicSelect - > value < < 0 ) ; // Music select
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_GUN1 ) )
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > trigger [ 0 ] - > value < < 0 ) ; // P1 Trigger
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_ANALOG_JOYSTICK ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > analogJoyTrigger1 - > value < < 5 ) ; // Trigger 1
data & = ~ ( Inputs - > analogJoyTrigger2 - > value < < 4 ) ; // Trigger 2
data & = ~ ( Inputs - > analogJoyEvent1 - > value < < 0 ) ; // Event Button 1
data & = ~ ( Inputs - > analogJoyEvent2 - > value < < 1 ) ; // Event Button 2
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_TWIN_JOYSTICKS ) ) // First twin joystick
2016-04-02 21:50:40 +00:00
{
/*
* Process left joystick inputs first
*/
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Shot trigger and Turbo
data & = ~ ( Inputs - > twinJoyShot1 - > value < < 0 ) ;
data & = ~ ( Inputs - > twinJoyTurbo1 - > value < < 1 ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Stick
data & = ~ ( Inputs - > twinJoyLeft1 - > value < < 7 ) ;
data & = ~ ( Inputs - > twinJoyRight1 - > value < < 6 ) ;
data & = ~ ( Inputs - > twinJoyUp1 - > value < < 5 ) ;
data & = ~ ( Inputs - > twinJoyDown1 - > value < < 4 ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
/*
* Next, process twin joystick macro inputs (higher level inputs
* that map to actions on both joysticks simultaneously).
*/
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
/*
* Forward/reverse/turn are mutually exclusive.
*
* Turn Left: 1D 2U
* Turn Right: 1U 2D
* Forward: 1U 2U
* Reverse: 1D 2D
*/
if ( Inputs - > twinJoyTurnLeft - > value )
data & = ~ 0x10 ;
else if ( Inputs - > twinJoyTurnRight - > value )
data & = ~ 0x20 ;
else if ( Inputs - > twinJoyForward - > value )
data & = ~ 0x20 ;
else if ( Inputs - > twinJoyReverse - > value )
data & = ~ 0x10 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
/*
* Strafe/crouch/jump are mutually exclusive.
*
* Strafe Left: 1L 2L
* Strafe Right: 1R 2R
* Jump: 1L 2R
* Crouch: 1R 2L
*/
if ( Inputs - > twinJoyStrafeLeft - > value )
data & = ~ 0x80 ;
else if ( Inputs - > twinJoyStrafeRight - > value )
data & = ~ 0x40 ;
else if ( Inputs - > twinJoyJump - > value )
data & = ~ 0x80 ;
else if ( Inputs - > twinJoyCrouch - > value )
data & = ~ 0x40 ;
}
2011-09-07 07:21:56 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_ANALOG_GUN1 ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > analogTriggerLeft [ 0 ] - > value < < 0 ) ;
data & = ~ ( Inputs - > analogTriggerRight [ 0 ] - > value < < 1 ) ;
}
2013-11-30 19:39:59 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_MAGTRUCK ) )
2016-04-13 03:28:04 +00:00
data & = ~ ( Inputs - > magicalPedal1 - > value < < 0 ) ;
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_FISHING ) )
2016-05-26 04:13:22 +00:00
{
2021-01-05 14:24:57 +00:00
if ( m_game . name = = " getbassur " )
2020-08-25 08:48:25 +00:00
{
// bass fishing
data & = ~ ( Inputs - > fishingCast - > value < < 0 ) ;
data & = ~ ( Inputs - > fishingSelect - > value < < 1 ) ;
}
2021-01-05 14:24:57 +00:00
else
{
// get bass fishing
data & = ~ ( ! Inputs - > fishingCast - > value < < 4 ) ;
data & = ~ ( ! Inputs - > fishingSelect - > value < < 5 ) ;
}
2016-05-26 04:13:22 +00:00
}
2016-04-02 21:50:40 +00:00
return data ;
2011-04-24 01:14:00 +00:00
2022-06-09 21:10:39 +00:00
case 0x10 : // Drive board
return OutputRegister [ 0 ] ;
case 0x14 : // Lamps
return OutputRegister [ 1 ] ;
2016-04-02 21:50:40 +00:00
case 0x0C : // game-specific inputs
2011-09-07 07:21:56 +00:00
2016-04-02 21:50:40 +00:00
data = 0xFF ;
2011-09-07 07:21:56 +00:00
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) & & DriveBoard - > GetType ( ) ! = Game : : DRIVE_BOARD_BILLBOARD )
{
// If driveboard is set as billboard, don't read BillBoard reg (no inputs)
data = DriveBoard - > Read ( ) ;
}
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_JOYSTICK2 ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > up [ 1 ] - > value < < 5 ) ; // P2 Up
data & = ~ ( Inputs - > down [ 1 ] - > value < < 4 ) ; // P2 Down
data & = ~ ( Inputs - > left [ 1 ] - > value < < 7 ) ; // P2 Left
data & = ~ ( Inputs - > right [ 1 ] - > value < < 6 ) ; // P2 Right
}
2011-04-24 01:14:00 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_FIGHTING ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > escape [ 1 ] - > value < < 3 ) ; // P2 Escape
data & = ~ ( Inputs - > guard [ 1 ] - > value < < 2 ) ; // P2 Guard
data & = ~ ( Inputs - > kick [ 1 ] - > value < < 1 ) ; // P2 Kick
data & = ~ ( Inputs - > punch [ 1 ] - > value < < 0 ) ; // P2 Punch
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SOCCER ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > shortPass [ 1 ] - > value < < 2 ) ; // P2 Short Pass
data & = ~ ( Inputs - > longPass [ 1 ] - > value < < 0 ) ; // P2 Long Pass
data & = ~ ( Inputs - > shoot [ 1 ] - > value < < 1 ) ; // P2 Shoot
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_TWIN_JOYSTICKS ) ) // Second twin joystick (see register 0x08 for comments)
2016-04-02 21:50:40 +00:00
{
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > twinJoyShot2 - > value < < 0 ) ;
data & = ~ ( Inputs - > twinJoyTurbo2 - > value < < 1 ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > twinJoyLeft2 - > value < < 7 ) ;
data & = ~ ( Inputs - > twinJoyRight2 - > value < < 6 ) ;
data & = ~ ( Inputs - > twinJoyUp2 - > value < < 5 ) ;
data & = ~ ( Inputs - > twinJoyDown2 - > value < < 4 ) ;
2011-08-26 05:06:34 +00:00
2016-04-02 21:50:40 +00:00
if ( Inputs - > twinJoyTurnLeft - > value )
data & = ~ 0x20 ;
else if ( Inputs - > twinJoyTurnRight - > value )
data & = ~ 0x10 ;
else if ( Inputs - > twinJoyForward - > value )
data & = ~ 0x20 ;
else if ( Inputs - > twinJoyReverse - > value )
data & = ~ 0x10 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
if ( Inputs - > twinJoyStrafeLeft - > value )
data & = ~ 0x80 ;
else if ( Inputs - > twinJoyStrafeRight - > value )
data & = ~ 0x40 ;
else if ( Inputs - > twinJoyJump - > value )
data & = ~ 0x40 ;
else if ( Inputs - > twinJoyCrouch - > value )
data & = ~ 0x80 ;
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_GUN2 ) )
2016-04-02 21:50:40 +00:00
data & = ~ ( Inputs - > trigger [ 1 ] - > value < < 0 ) ; // P2 Trigger
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_ANALOG_GUN2 ) )
2016-04-02 21:50:40 +00:00
{
data & = ~ ( Inputs - > analogTriggerLeft [ 1 ] - > value < < 0 ) ;
data & = ~ ( Inputs - > analogTriggerRight [ 1 ] - > value < < 1 ) ;
}
2016-04-13 03:28:04 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_MAGTRUCK ) )
2016-04-13 03:28:04 +00:00
data & = ~ ( Inputs - > magicalPedal2 - > value < < 0 ) ;
2016-05-26 04:13:22 +00:00
2016-04-02 21:50:40 +00:00
return data ;
2011-04-24 01:14:00 +00:00
2020-08-26 16:05:23 +00:00
case 0x18 : // swtrilgy and getbass. Remove IO board error on getbass. Not sure, but may be related to device feedback ?
data = 0x7f ; // Note : when this returned value is wrong, there is a side effect on Ocean Hunter game, a sort of 3d interlaced effect
2023-03-31 10:36:50 +02:00
if ( m_game . name = = " bassdx " | | m_game . name = = " getbassdx " | | m_game . name = = " getbass " ) // Prevent I/O erreur after a while (related to tension)
{
data = 0x01 ;
}
2020-08-25 08:48:25 +00:00
return data ;
2016-04-02 21:50:40 +00:00
case 0x2C : // Serial FIFO 1
return serialFIFO1 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
case 0x30 : // Serial FIFO 2
return serialFIFO2 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
case 0x34 : // Serial FIFO full/empty flags
2018-01-29 19:31:23 +00:00
if ( m_game . inputs & ( Game : : INPUT_GUN1 | Game : : INPUT_GUN2 ) ) {
return 0x0C ;
}
2021-04-14 01:20:45 +00:00
else {
return 0 ;
}
case 0x3C : // ADC
// Load ADC channels with input data
2016-04-02 21:50:40 +00:00
memset ( adc , 0 , sizeof ( adc ) ) ;
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_VEHICLE ) )
2016-04-02 21:50:40 +00:00
{
adc [ 0 ] = ( UINT8 ) Inputs - > steering - > value ;
adc [ 1 ] = ( UINT8 ) Inputs - > accelerator - > value ;
adc [ 2 ] = ( UINT8 ) Inputs - > brake - > value ;
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_HARLEY ) )
2016-04-02 21:50:40 +00:00
adc [ 3 ] = ( UINT8 ) Inputs - > rearBrake - > value ;
}
2013-11-30 19:39:59 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_ANALOG_JOYSTICK ) )
2016-04-02 21:50:40 +00:00
{
adc [ 0 ] = ( UINT8 ) Inputs - > analogJoyY - > value ;
adc [ 1 ] = ( UINT8 ) Inputs - > analogJoyX - > value ;
}
2013-11-30 19:39:59 +00:00
2018-01-29 19:31:23 +00:00
if ( m_game . inputs & ( Game : : INPUT_ANALOG_GUN1 | Game : : INPUT_ANALOG_GUN2 ) )
2021-02-18 10:29:15 +00:00
{
2016-04-02 21:50:40 +00:00
adc [ 0 ] = ( UINT8 ) Inputs - > analogGunX [ 0 ] - > value ;
adc [ 2 ] = ( UINT8 ) Inputs - > analogGunY [ 0 ] - > value ;
adc [ 1 ] = ( UINT8 ) Inputs - > analogGunX [ 1 ] - > value ;
adc [ 3 ] = ( UINT8 ) Inputs - > analogGunY [ 1 ] - > value ;
2018-01-29 19:31:23 +00:00
2019-01-13 16:00:37 +00:00
// Unclear why this is necessary or how to cleanly fix it, so I'm
// disabling it but leaving it here for future reference. The proper fix is
// probably to allow users to define inverted controls for this game only,
// which means the input system must support loading per-game config (not
// all analog_gun games require axis inversion to be playable).
2019-11-08 20:36:11 +00:00
if ( m_game . name = = " lostwsga " | | m_game . name = = " lostwsgo " )
{ // to do, not a string compare
2018-01-29 19:33:29 +00:00
adc [ 0 ] = ( UINT8 ) Inputs - > analogGunX [ 0 ] - > value ; // order is different for some reason in lost world
adc [ 1 ] = 255 - ( UINT8 ) Inputs - > analogGunY [ 0 ] - > value ; // why are values inverted? is this the wrong place to fix this
2018-01-29 19:31:23 +00:00
adc [ 2 ] = ( UINT8 ) Inputs - > analogGunX [ 1 ] - > value ;
adc [ 3 ] = 255 - ( UINT8 ) Inputs - > analogGunY [ 1 ] - > value ;
2018-01-29 19:33:29 +00:00
}
2016-04-02 21:50:40 +00:00
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_SKI ) )
2016-04-02 21:50:40 +00:00
{
adc [ 0 ] = ( UINT8 ) Inputs - > skiY - > value ;
adc [ 1 ] = ( UINT8 ) Inputs - > skiX - > value ;
}
2016-04-13 03:28:04 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_MAGTRUCK ) )
2016-04-13 03:28:04 +00:00
{
adc [ 0 ] = uint8_t ( Inputs - > magicalLever1 - > value ) ;
adc [ 1 ] = uint8_t ( Inputs - > magicalLever2 - > value ) ;
}
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
if ( ( m_game . inputs & Game : : INPUT_FISHING ) )
2016-05-26 04:13:22 +00:00
{
adc [ 0 ] = uint8_t ( Inputs - > fishingRodY - > value ) ;
adc [ 1 ] = uint8_t ( Inputs - > fishingRodX - > value ) ;
2020-08-25 08:48:25 +00:00
adc [ 2 ] = uint8_t ( Inputs - > fishingTension - > value ) ; // get bass fishing only : Tension Sensor ?
2016-05-26 04:13:22 +00:00
adc [ 3 ] = uint8_t ( Inputs - > fishingReel - > value ) ;
adc [ 5 ] = uint8_t ( Inputs - > fishingStickX - > value ) ;
adc [ 4 ] = uint8_t ( Inputs - > fishingStickY - > value ) ;
}
2016-04-02 21:50:40 +00:00
// Read out appropriate channel
data = adc [ adcChannel & 7 ] ;
+ + adcChannel ;
return data ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
default :
break ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
return 0xFF ; // controls are active low
2011-04-24 01:14:00 +00:00
}
void CModel3 : : WriteInputs ( unsigned reg , UINT8 data )
{
2016-04-02 21:50:40 +00:00
switch ( reg & 0x3F )
{
case 0 :
EEPROM . Write ( ( data > > 6 ) & 1 , ( data > > 7 ) & 1 , ( data > > 5 ) & 1 ) ;
inputBank = data ;
break ;
2011-09-07 07:21:56 +00:00
2016-04-02 21:50:40 +00:00
case 0x10 : // Drive board
2022-06-09 21:10:39 +00:00
if ( DriveBoard - > IsAttached ( ) )
DriveBoard - > Write ( data ) ;
2016-06-07 01:51:16 +00:00
if ( NULL ! = Outputs ) // TODO - check gameInputs
Outputs - > SetValue ( OutputRawDrive , data ) ;
2022-06-09 21:10:39 +00:00
OutputRegister [ 0 ] = data ;
2016-04-02 21:50:40 +00:00
break ;
2011-09-07 07:21:56 +00:00
2016-04-02 21:50:40 +00:00
case 0x14 : // Lamp outputs (Daytona/Scud Race/Sega Rally/Le Mans 24)
if ( NULL ! = Outputs ) // TODO - check gameInputs
{
Outputs - > SetValue ( OutputLampStart , ! ! ( data & 0x04 ) ) ;
Outputs - > SetValue ( OutputLampView1 , ! ! ( data & 0x08 ) ) ;
Outputs - > SetValue ( OutputLampView2 , ! ! ( data & 0x10 ) ) ;
Outputs - > SetValue ( OutputLampView3 , ! ! ( data & 0x20 ) ) ;
Outputs - > SetValue ( OutputLampView4 , ! ! ( data & 0x40 ) ) ;
Outputs - > SetValue ( OutputLampLeader , ! ! ( data & 0x80 ) ) ;
2016-06-07 01:51:16 +00:00
Outputs - > SetValue ( OutputRawLamps , data ) ;
2016-04-02 21:50:40 +00:00
}
2022-06-09 21:10:39 +00:00
OutputRegister [ 1 ] = data ;
2016-04-02 21:50:40 +00:00
break ;
2012-07-15 21:04:46 +00:00
2016-04-02 21:50:40 +00:00
case 0x24 : // Serial FIFO 1
switch ( data ) // Command
{
case 0x00 : // Light gun register select
gunReg = serialFIFO2 ;
break ;
case 0x87 : // Read light gun register
serialFIFO1 = 0 ; // clear serial FIFO 1
serialFIFO2 = 0 ;
2022-07-11 17:43:59 +02:00
if ( ( m_game . inputs & Game : : INPUT_GUN1 ) | | ( m_game . inputs & Game : : INPUT_GUN2 ) )
2016-04-02 21:50:40 +00:00
{
switch ( gunReg )
{
case 0 : // Player 1 gun Y (low 8 bits)
serialFIFO2 = Inputs - > gunY [ 0 ] - > value & 0xFF ;
break ;
case 1 : // Player 1 gun Y (high 2 bits)
serialFIFO2 = ( Inputs - > gunY [ 0 ] - > value > > 8 ) & 3 ;
break ;
case 2 : // Player 1 gun X (low 8 bits)
serialFIFO2 = Inputs - > gunX [ 0 ] - > value & 0xFF ;
break ;
case 3 : // Player 1 gun X (high 2 bits)
serialFIFO2 = ( Inputs - > gunX [ 0 ] - > value > > 8 ) & 3 ;
break ;
case 4 : // Player 2 gun Y (low 8 bits)
serialFIFO2 = Inputs - > gunY [ 1 ] - > value & 0xFF ;
break ;
case 5 : // Player 2 gun Y (high 2 bits)
serialFIFO2 = ( Inputs - > gunY [ 1 ] - > value > > 8 ) & 3 ;
break ;
case 6 : // Player 2 gun X (low 8 bits)
serialFIFO2 = Inputs - > gunX [ 1 ] - > value & 0xFF ;
break ;
case 7 : // Player 2 gun X (high 2 bits)
serialFIFO2 = ( Inputs - > gunX [ 1 ] - > value > > 8 ) & 3 ;
break ;
case 8 : // Off-screen indicator (bit 0 = player 1, bit 1 = player 2, set indicates off screen)
serialFIFO2 = ( Inputs - > trigger [ 1 ] - > offscreenValue < < 1 ) | Inputs - > trigger [ 0 ] - > offscreenValue ;
break ;
default :
DebugLog ( " Unknown gun register: %X \n " , gunReg ) ;
break ;
}
}
break ;
default :
DebugLog ( " Uknown command to serial FIFO: %02X \n " , data ) ;
break ;
}
break ;
case 0x28 : // Serial FIFO 2
serialFIFO2 = data ;
break ;
case 0x3C :
adcChannel = data & 7 ;
break ;
default :
break ;
}
//printf("Controls: %X=%02X\n", reg, data);
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
Model 3 Security Device
2021-02-18 10:29:15 +00:00
2011-07-20 06:20:40 +00:00
The security device is present in some games. Virtual On and Dirt Devils read
tile pattern data from it. Spikeout calls a routine at PC=0x6FAC8 that writes/
reads the security device and, if the return value in R3 is 0, prints "ILLEGAL
2021-02-18 10:29:15 +00:00
ROM" and locks the game. Our habit of returning all 1's for unknown reads
2011-07-20 06:20:40 +00:00
seems to help avoid this.
2011-04-24 01:14:00 +00:00
******************************************************************************/
2016-04-02 21:50:40 +00:00
uint16_t CModel3 : : ReadSecurityRAM ( uint32_t addr )
2011-07-06 05:07:04 +00:00
{
2016-04-02 21:50:40 +00:00
if ( addr < 0x8000 )
return ( * ( uint32_t * ) & securityRAM [ addr * 4 ] ) > > 16 ;
return 0 ;
}
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
UINT32 CModel3 : : ReadSecurity ( unsigned reg )
{
2016-04-02 21:50:40 +00:00
switch ( reg )
{
case 0x00 : // Status
return 0 ;
case 0x1C : // Data
if ( m_securityFirstRead )
{
m_securityFirstRead = false ;
return 0xFFFF0000 ;
}
else
{
uint8_t * base_ptr ;
return m_cryptoDevice . Decrypt ( & base_ptr ) < < 16 ;
}
default :
DebugLog ( " Security read: reg=%X \n " , reg ) ;
break ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
return 0xFFFFFFFF ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : WriteSecurity ( unsigned reg , UINT32 data )
{
2016-04-02 21:50:40 +00:00
switch ( reg )
{
case 0x10 :
case 0x14 :
m_cryptoDevice . SetAddressLow ( 0 ) ;
m_cryptoDevice . SetAddressHigh ( 0 ) ;
m_securityFirstRead = true ;
break ;
case 0x18 :
{
uint16_t subKey = data > > 16 ;
subKey = ( ( subKey & 0xFF00 ) > > 8 ) | ( ( subKey & 0x00FF ) < < 8 ) ;
m_cryptoDevice . SetSubKey ( subKey ) ;
break ;
}
default :
2021-02-18 10:29:15 +00:00
DebugLog ( " Security write: reg=%X, data=%08X (PC=%08X, LR=%08X) \n " , reg , data , ppc_get_pc ( ) , ppc_get_lr ( ) ) ;
2016-04-02 21:50:40 +00:00
break ;
}
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
PCI Devices
2021-02-18 10:29:15 +00:00
Unknown PCI devices are handled here.
2011-04-24 01:14:00 +00:00
******************************************************************************/
UINT32 CModel3 : : ReadPCIConfigSpace ( unsigned device , unsigned reg , unsigned bits , unsigned offset )
2021-02-18 10:29:15 +00:00
{
2016-04-02 21:50:40 +00:00
if ( ( bits = = 8 ) | | ( bits = = 16 ) )
{
DebugLog ( " Model 3: %d-bit PCI read request for reg=%02X \n " , bits , reg ) ;
return 0 ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( device )
{
case 16 : // Used by Daytona 2
switch ( reg )
{
case 0 : // PCI vendor and device ID
return 0x182711DB ;
default :
break ;
}
default :
break ;
}
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " Model 3: PCI %d-bit write request for device=%d, reg=%02X \n " , bits , device , reg ) ;
return 0 ;
2011-04-24 01:14:00 +00:00
}
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
void CModel3 : : WritePCIConfigSpace ( unsigned device , unsigned reg , unsigned bits , unsigned offset , UINT32 data )
{
2016-04-02 21:50:40 +00:00
DebugLog ( " Model 3: PCI %d-bit write request for device=%d, reg=%02X, data=%08X \n " , bits , device , reg , data ) ;
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
Model 3 System Registers
2021-02-18 10:29:15 +00:00
2021-11-28 01:11:11 +00:00
NOTE: Different modules that generate IRQs, like the tilegen, Real3D, and
SCSP, should each call IRQ.Assert() on their own.
2011-04-24 01:14:00 +00:00
******************************************************************************/
// Set the CROM bank index (active low logic)
void CModel3 : : SetCROMBank ( unsigned idx )
{
2016-04-02 21:50:40 +00:00
cromBankReg = idx ;
idx = ( ~ idx ) & 0xF ;
cromBank = & crom [ 0x800000 + ( idx * 0x800000 ) ] ;
DebugLog ( " CROM bank setting: %d (%02X), PC=%08X, LR=%08X \n " , idx , cromBankReg , ppc_get_pc ( ) , ppc_get_lr ( ) ) ;
2011-04-24 01:14:00 +00:00
}
UINT8 CModel3 : : ReadSystemRegister ( unsigned reg )
{
2016-04-02 21:50:40 +00:00
switch ( reg & 0x3F )
{
case 0x08 : // CROM bank
return cromBankReg ;
case 0x14 : // IRQ enable
return IRQ . ReadIRQEnable ( ) ;
case 0x18 : // IRQ pending
return IRQ . ReadIRQState ( ) ;
case 0x1C : // unknown (apparently expects some or all bits set)
//DebugLog("System register %02X read\n", reg);
return 0xFF ;
case 0x10 : // JTAG Test Access Port
2017-09-24 20:52:48 +00:00
return m_jtag . Read ( ) < < 5 ;
2021-02-18 10:29:15 +00:00
default :
2016-04-02 21:50:40 +00:00
//DebugLog("System register %02X read\n", reg);
break ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
return 0xFF ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : WriteSystemRegister ( unsigned reg , UINT8 data )
{
2016-04-02 21:50:40 +00:00
switch ( reg & 0x3F )
{
case 0x08 : // CROM bank
SetCROMBank ( data ) ;
break ;
case 0x14 : // IRQ enable
IRQ . WriteIRQEnable ( data ) ;
DebugLog ( " IRQ ENABLE=%02X \n " , data ) ;
break ;
case 0x18 : // IRQ acknowledge
2017-08-29 01:28:21 +00:00
IRQ . Deassert ( data ) ;
2016-05-10 03:08:03 +00:00
DebugLog ( " IRQ ACK? %02X=%02X \n " , reg , data ) ;
2016-04-02 21:50:40 +00:00
break ;
case 0x0C : // JTAG Test Access Port
2017-09-24 20:52:48 +00:00
{
uint8_t tck = ( data > > 6 ) & 1 ;
uint8_t tms = ( data > > 2 ) & 1 ;
uint8_t tdi = ( data > > 5 ) & 1 ;
uint8_t trst = ( data > > 7 ) & 1 ; // not sure about this one (trst not required to exist by JTAG spec)
m_jtag . Write ( tck , tms , tdi , trst ) ;
2016-04-02 21:50:40 +00:00
break ;
2017-09-24 20:52:48 +00:00
}
2016-04-02 21:50:40 +00:00
case 0x0D :
case 0x0E :
case 0x0F :
2018-01-29 19:31:23 +00:00
case 0x1C : // LED control
2016-04-02 21:50:40 +00:00
break ;
default :
//DebugLog("System register %02X=%02X\n", reg, data);
break ;
}
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
2017-03-23 04:22:25 +00:00
Address Space Access Handlers
2021-02-18 10:29:15 +00:00
2011-09-12 05:43:37 +00:00
NOTE: Testing of some of the address ranges is not strict enough, especially
2017-03-23 04:22:25 +00:00
for the MPC10x. Write32() handles the MPC10x most correctly.
2011-04-24 01:14:00 +00:00
******************************************************************************/
2011-09-12 05:43:37 +00:00
2011-04-24 01:14:00 +00:00
/*
* CModel3::Read8(addr):
* CModel3::Read16(addr):
* CModel3::Read32(addr):
* CModel3::Read64(addr):
*
* Read handlers.
*/
2011-09-12 05:43:37 +00:00
UINT8 CModel3 : : Read8 ( UINT32 addr )
{
2016-04-02 21:50:40 +00:00
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
return ram [ addr ^ 3 ] ;
2021-02-18 10:29:15 +00:00
2021-04-14 01:20:45 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// CROM
case 0xFF :
if ( addr < 0xFF800000 )
return cromBank [ ( addr & 0x7FFFFF ) ^ 3 ] ;
else
return crom [ ( addr & 0x7FFFFF ) ^ 3 ] ;
// Real3D DMA
case 0xC2 :
return GPU . ReadDMARegister8 ( addr & 0xFF ) ;
// Various
case 0xF0 :
case 0xFE : // mirror
switch ( ( addr > > 16 ) & 0xFF )
{
// Inputs
case 0x04 :
return ReadInputs ( addr & 0x3F ) ;
// Sound Board
2020-12-21 15:39:15 +00:00
case 0x08 :
switch ( addr & 0xf )
{
case 0x0 : // MIDI data port
return 0x00 ; // Something to do with region locked in magtruck (0=locked, 1=unlocked). /!\ no effect if rom patch is activated!
case 0x4 : // MIDI control port
return 0x83 ; // magtruck country check
default :
return 0 ;
2021-04-14 01:20:45 +00:00
}
break ;
// System registers
case 0x10 :
return ReadSystemRegister ( addr & 0x3F ) ;
// RTC
case 0x14 :
if ( ( addr & 3 ) = = 1 ) // battery voltage test
return 0x03 ;
else if ( ( addr & 3 ) = = 0 )
return RTC . ReadRegister ( ( addr > > 2 ) & 0xF ) ;
return 0 ;
// Unknown
default :
//printf("CMODEL3 : unknown R8 mirror : %x\n", addr >> 16);
break ;
}
break ;
// Tile generator
case 0xF1 :
if ( addr < 0xF1120000 )
{
// Tile generator accesses its RAM as little endian, no adjustment needed here
return TileGen . ReadRAM8 ( addr & 0x1FFFFF ) ;
}
break ;
// 53C810 SCSI
case 0xC0 : // only on Step 1.0
# ifndef NET_BOARD
if ( m_game . stepping ! = " 1.0 " )
{
//printf("Model3 : Read8 %x\n", addr);
break ;
}
# endif
# ifdef NET_BOARD
if ( m_runNetBoard )
{
switch ( ( addr & 0x3ffff ) > > 16 )
{
case 0 :
2021-11-05 00:23:29 +00:00
return NetBoard - > ReadCommRAM8 ( ( addr & 0xFFFF ) ^ 2 ) ;
2021-04-14 01:20:45 +00:00
case 1 : // ioreg 32bits access in 16bits environment
if ( addr > 0xc00101ff )
{
printf ( " R8 ATTENTION OUT OF RANGE \n " ) ;
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , " Info " , " Out of Range " , NULL ) ;
}
2021-10-30 23:00:49 +00:00
return ( UINT8 ) NetBoard - > ReadIORegister ( ( addr & 0x1FF ) / 2 ) ;
2021-04-14 01:20:45 +00:00
case 2 :
case 3 :
return netRAM [ ( ( addr & 0x1FFFF ) / 2 ) ] ;
default :
printf ( " R8 ATTENTION OUT OF RANGE \n " ) ;
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , " Info " , " Out of Range " , NULL ) ;
break ;
}
}
else if ( m_game . stepping ! = " 1.0 " ) break ;
# endif
case 0xF9 :
case 0xC1 :
2016-04-02 21:50:40 +00:00
return SCSI . ReadRegister ( addr & 0xFF ) ;
2011-09-12 05:43:37 +00:00
2021-04-14 01:20:45 +00:00
// Unknown
default :
# ifdef NET_BOARD
printf ( " CMODEL3 : unknown R8 : %x \n " , addr > > 24 ) ;
# endif
break ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " PC=%08X \t read8 : %08X \n " , ppc_get_pc ( ) , addr ) ;
return 0xFF ;
2011-04-24 01:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
UINT16 CModel3 : : Read16 ( UINT32 addr )
{
2016-04-02 21:50:40 +00:00
UINT16 data ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
if ( ( addr & 1 ) )
{
data = Read8 ( addr + 0 ) < < 8 ;
data | = Read8 ( addr + 1 ) ;
return data ;
}
2011-09-12 05:43:37 +00:00
2021-02-18 10:29:15 +00:00
// RAM (most frequently accessed)
2016-04-02 21:50:40 +00:00
if ( addr < 0x00800000 )
return * ( UINT16 * ) & ram [ addr ^ 2 ] ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// CROM
case 0xFF :
if ( addr < 0xFF800000 )
return * ( UINT16 * ) & cromBank [ ( addr & 0x7FFFFF ) ^ 2 ] ;
else
return * ( UINT16 * ) & crom [ ( addr & 0x7FFFFF ) ^ 2 ] ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Various
case 0xF0 :
case 0xFE : // mirror
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( ( addr > > 16 ) & 0xFF )
{
// Backup RAM
case 0x0C :
case 0x0D :
return * ( UINT16 * ) & backupRAM [ ( addr & 0x1FFFF ) ^ 2 ] ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Sound Board
case 0x08 :
//printf("PPC: Read16 %08X\n", addr);
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC105
case 0xC0 : // F0C00CF8
return PCIBridge . ReadPCIConfigData ( 16 , addr & 3 ) ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC106
case 0xE0 :
case 0xE1 :
case 0xE2 :
case 0xE3 :
case 0xE4 :
case 0xE5 :
case 0xE6 :
case 0xE7 :
case 0xE8 :
case 0xE9 :
case 0xEA :
case 0xEB :
case 0xEC :
case 0xED :
case 0xEE :
case 0xEF :
return PCIBridge . ReadPCIConfigData ( 16 , addr & 3 ) ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
//printf("CMODEL3 : unknown R16 mirror : %x\n", addr >> 16);
break ;
}
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2017-03-23 04:22:25 +00:00
// Tile generator
case 0xF1 :
if ( addr < 0xF1120000 )
{
// Tile generator accesses its RAM as little endian, no adjustment needed here
uint16_t data = TileGen . ReadRAM16 ( addr & 0x1FFFFF ) ;
return FLIPENDIAN16 ( data ) ;
}
break ;
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
case 0xc0 : // spikeout call this
2022-07-11 18:10:41 +02:00
// interesting : poking @4 master to same value as slave (0x100) or simply !=0 -> connected and go in game, but freeze (prints comm error) as soon as players appear after the gate
2021-04-14 01:20:45 +00:00
// sort of sync ack ? who writes this 16b value ?
{
UINT16 result ;
switch ( ( addr & 0x3ffff ) > > 16 )
{
case 0 :
2021-11-05 00:23:29 +00:00
result = NetBoard - > ReadCommRAM16 ( ( addr & 0xFFFF ) ^ 2 ) ;
2021-04-14 01:20:45 +00:00
return FLIPENDIAN16 ( result ) ; // result
default :
printf ( " CMODEL3 : unknown R16 : %x (C0) \n " , addr ) ;
break ;
}
}
# endif
// Unknown
default :
# ifdef NET_BOARD
printf ( " CMODEL3 : unknown R16 : %x (%x) \n " , addr , addr > > 24 ) ;
SDL_ShowSimpleMessageBox ( SDL_MESSAGEBOX_ERROR , " Info " , " CMODEL3 : Unknown R16 " , NULL ) ;
# endif
break ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " PC=%08X \t read16: %08X \n " , ppc_get_pc ( ) , addr ) ;
return 0xFFFF ;
2011-04-24 01:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
UINT32 CModel3 : : Read32 ( UINT32 addr )
{
2016-04-02 21:50:40 +00:00
UINT32 data ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
if ( ( addr & 3 ) )
{
data = Read16 ( addr + 0 ) < < 16 ;
data | = Read16 ( addr + 2 ) ;
return data ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
return * ( UINT32 * ) & ram [ addr ] ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// CROM
2021-02-18 10:29:15 +00:00
case 0xFF :
2016-04-02 21:50:40 +00:00
if ( addr < 0xFF800000 )
return * ( UINT32 * ) & cromBank [ ( addr & 0x7FFFFF ) ] ;
else
return * ( UINT32 * ) & crom [ ( addr & 0x7FFFFF ) ] ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Real3D registers
case 0x84 :
2017-08-29 01:28:21 +00:00
data = GPU . ReadRegister ( addr & 0x3F ) ;
return FLIPENDIAN32 ( data ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Real3D DMA
case 0xC2 :
data = GPU . ReadDMARegister32 ( addr & 0xFF ) ;
return FLIPENDIAN32 ( data ) ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Various
case 0xF0 :
case 0xFE : // mirror
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( ( addr > > 16 ) & 0xFF )
{
2021-02-18 10:29:15 +00:00
// Inputs
2016-04-02 21:50:40 +00:00
case 0x04 :
data = ReadInputs ( ( addr & 0x3F ) + 0 ) < < 24 ;
data | = ReadInputs ( ( addr & 0x3F ) + 1 ) < < 16 ;
data | = ReadInputs ( ( addr & 0x3F ) + 2 ) < < 8 ;
data | = ReadInputs ( ( addr & 0x3F ) + 3 ) < < 0 ;
return data ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Sound Board
case 0x08 :
//printf("PPC: Read32 %08X\n", addr);
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Backup RAM
case 0x0C :
case 0x0D :
return * ( UINT32 * ) & backupRAM [ ( addr & 0x1FFFF ) ] ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// System registers
case 0x10 :
data = ReadSystemRegister ( ( addr & 0x3F ) + 0 ) < < 24 ;
data | = ReadSystemRegister ( ( addr & 0x3F ) + 1 ) < < 16 ;
data | = ReadSystemRegister ( ( addr & 0x3F ) + 2 ) < < 8 ;
data | = ReadSystemRegister ( ( addr & 0x3F ) + 3 ) < < 0 ;
return data ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC105
case 0xC0 : // F0C00CF8
return PCIBridge . ReadPCIConfigData ( 32 , 0 ) ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC106
case 0xE0 :
case 0xE1 :
case 0xE2 :
case 0xE3 :
case 0xE4 :
case 0xE5 :
case 0xE6 :
case 0xE7 :
case 0xE8 :
case 0xE9 :
case 0xEA :
case 0xEB :
case 0xEC :
case 0xED :
case 0xEE :
case 0xEF :
return PCIBridge . ReadPCIConfigData ( 32 , 0 ) ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// RTC
case 0x14 :
data = ( RTC . ReadRegister ( ( addr > > 2 ) & 0xF ) < < 24 ) ;
data | = 0x00030000 ; // set these bits to pass battery voltage test
return data ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Security board RAM
case 0x18 :
case 0x19 :
return * ( UINT32 * ) & securityRAM [ ( addr & 0x1FFFF ) ] ; // so far, only 32-bit access observed, so we use little endian access
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Security board registers
case 0x1A :
return ReadSecurity ( addr & 0x3F ) ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
//printf("CModel 3 unknown R32 mirror %x", (addr >> 16) & 0xFF);
break ;
}
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Tile generator
case 0xF1 :
// Tile generator accesses its RAM as little endian, must flip for big endian PowerPC
if ( addr < 0xF1120000 )
{
2017-03-23 04:22:25 +00:00
data = TileGen . ReadRAM32 ( addr & 0x1FFFFF ) ;
2016-04-02 21:50:40 +00:00
return FLIPENDIAN32 ( data ) ;
}
2016-05-08 20:30:45 +00:00
else if ( ( addr > = 0xF1180000 ) & & ( addr < 0xF1180100 ) )
{
data = TileGen . ReadRegister ( addr & 0xFF ) ;
return FLIPENDIAN32 ( data ) ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// 53C810 SCSI
case 0xC0 : // only on Step 1.0
2018-01-07 14:07:59 +00:00
# ifndef NET_BOARD
2017-03-27 03:19:15 +00:00
if ( m_game . stepping ! = " 1.0 " ) // check for Step 1.0
2016-04-02 21:50:40 +00:00
break ;
2018-01-07 14:07:59 +00:00
# endif
# ifdef NET_BOARD
2020-06-12 17:33:21 +00:00
if ( m_runNetBoard )
2018-01-07 14:07:59 +00:00
{
UINT32 result ;
2021-02-18 10:29:15 +00:00
2018-01-07 14:07:59 +00:00
switch ( ( addr & 0x3ffff ) > > 16 )
{
2021-04-14 01:20:45 +00:00
case 0 :
2021-11-05 00:23:29 +00:00
result = NetBoard - > ReadCommRAM32 ( addr & 0xFFFF ) ;
2021-04-14 01:20:45 +00:00
result = FLIPENDIAN32 ( result ) ;
return ( ( result < < 16 ) | ( result > > 16 ) ) ;
case 1 : // ioreg 32bits access to 16bits range
2018-01-07 14:07:59 +00:00
if ( addr > 0xc00101ff )
{
printf ( " R32 ATTENTION OUT OF RANGE \n " ) ;
}
2021-10-30 23:00:49 +00:00
result = NetBoard - > ReadIORegister ( ( addr & 0x1FF ) / 2 ) ;
2018-01-07 14:07:59 +00:00
return FLIPENDIAN32 ( result ) ;
case 2 :
case 3 :
result = ( * ( UINT32 * ) & netRAM [ ( ( addr & 0x1FFFF ) / 2 ) ] ) & 0x0000ffff ;
return FLIPENDIAN32 ( result ) ; // result
default :
printf ( " R32 ATTENTION OUT OF RANGE \n " ) ;
break ;
2021-04-14 01:20:45 +00:00
}
}
else if ( m_game . stepping ! = " 1.0 " ) break ;
# endif
case 0xF9 :
case 0xC1 :
2016-04-02 21:50:40 +00:00
data = ( SCSI . ReadRegister ( ( addr + 0 ) & 0xFF ) < < 24 ) ;
data | = ( SCSI . ReadRegister ( ( addr + 1 ) & 0xFF ) < < 16 ) ;
data | = ( SCSI . ReadRegister ( ( addr + 2 ) & 0xFF ) < < 8 ) ;
data | = ( SCSI . ReadRegister ( ( addr + 3 ) & 0xFF ) < < 0 ) ;
return data ;
2011-09-12 05:43:37 +00:00
2021-04-14 01:20:45 +00:00
// Unknown
default :
# ifdef NET_BOARD
printf ( " CMODEL3 : unknown R32 : %x \n " , addr > > 24 ) ;
# endif
break ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " PC=%08X \t read32: %08X \n " , ppc_get_pc ( ) , addr ) ;
return 0xFFFFFFFF ;
2011-09-12 05:43:37 +00:00
}
2011-04-24 01:14:00 +00:00
UINT64 CModel3 : : Read64 ( UINT32 addr )
{
2016-04-02 21:50:40 +00:00
UINT64 data ;
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
data = Read32 ( addr + 0 ) ;
data < < = 32 ;
data | = Read32 ( addr + 4 ) ;
2018-01-07 14:07:59 +00:00
//printf("read64 %x = %x\n",addr,data);
2016-04-02 21:50:40 +00:00
return data ;
2011-04-24 01:14:00 +00:00
}
/*
* CModel3::Write8(addr, data):
* CModel3::Write16(addr, data):
* CModel3::Write32(addr, data):
* CModel3::Write64(addr, data):
*
* Write handlers.
*/
2011-09-12 05:43:37 +00:00
void CModel3 : : Write8 ( UINT32 addr , UINT8 data )
{
2016-04-02 21:50:40 +00:00
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
{
ram [ addr ^ 3 ] = data ;
return ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// Real3D DMA
case 0xC2 :
GPU . WriteDMARegister8 ( addr & 0xFF , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Various
case 0xF0 :
case 0xFE : // mirror
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( ( addr > > 16 ) & 0xFF )
{
2021-02-18 10:29:15 +00:00
// Inputs
case 0x04 :
2016-04-02 21:50:40 +00:00
WriteInputs ( addr & 0x3F , data ) ;
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Sound Board
case 0x08 :
//printf("PPC: %08X=%02X * (PC=%08X, LR=%08X)\n", addr, data, ppc_get_pc(), ppc_get_lr());
if ( ( addr & 0xF ) = = 0 ) // MIDI data port
SoundBoard . WriteMIDIPort ( data ) ;
else if ( ( addr & 0xF ) = = 4 ) // MIDI control port
midiCtrlPort = data ;
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Backup RAM
case 0x0C :
case 0x0D :
backupRAM [ ( addr & 0x1FFFF ) ^ 3 ] = data ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// System registers
case 0x10 :
WriteSystemRegister ( addr & 0x3F , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// RTC
case 0x14 :
if ( ( addr & 3 ) = = 0 )
RTC . WriteRegister ( ( addr > > 2 ) & 0xF , data ) ;
break ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
//printf("CMODEL3 : unknown W8 mirror : %x\n", addr >> 16);
break ;
}
2021-02-18 10:29:15 +00:00
DebugLog ( " PC=%08X \t write8 : %08X=%02X \n " , ppc_get_pc ( ) , addr , data ) ;
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2017-03-23 04:22:25 +00:00
// Tile generator
case 0xF1 :
if ( addr < 0xF1120000 )
{
// Tile generator accesses its RAM as little endian, no adjustment needed here
2021-04-14 01:20:45 +00:00
TileGen . WriteRAM8 ( addr & 0x1FFFFF , data ) ;
break ;
}
goto Unknown8 ;
// MPC105/106
case 0xF8 :
2016-04-02 21:50:40 +00:00
PCIBridge . WriteRegister ( addr & 0xFF , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// 53C810 SCSI
case 0xC0 : // only on Step 1.0
2018-01-07 14:07:59 +00:00
# ifndef NET_BOARD
2017-03-27 03:19:15 +00:00
if ( m_game . stepping ! = " 1.0 " )
2016-04-02 21:50:40 +00:00
goto Unknown8 ;
2018-01-07 14:07:59 +00:00
# endif
# ifdef NET_BOARD
2020-06-12 17:33:21 +00:00
if ( m_runNetBoard )
2018-01-07 14:07:59 +00:00
{
2018-02-24 15:53:18 +00:00
//printf("CModel 3 : write8 %x<-%x\n", addr, data);
2018-01-07 14:07:59 +00:00
switch ( ( addr & 0x3ffff ) > > 16 )
{
case 0 :
2021-11-05 00:23:29 +00:00
NetBoard - > WriteCommRAM8 ( ( addr & 0xFFFF ) ^ 2 , data ) ;
2018-01-07 14:07:59 +00:00
break ;
case 1 : // ioreg 32bits access to 16bits range
if ( addr > 0xc00101ff )
{
printf ( " W8 ATTENTION OUT OF RANGE \n " ) ;
}
2021-10-30 23:00:49 +00:00
NetBoard - > WriteIORegister ( ( addr & 0x1FF ) / 2 , data ) ;
2018-01-07 14:07:59 +00:00
break ;
case 2 :
case 3 :
* ( UINT8 * ) & netRAM [ ( addr & 0x1FFFF ) / 2 ] = data ;
break ;
default :
printf ( " W8 ATTENTION OUT OF RANGE \n " ) ;
break ;
}
2021-02-18 10:29:15 +00:00
2021-04-14 01:20:45 +00:00
break ;
}
else if ( m_game . stepping ! = " 1.0 " ) break ;
# endif
case 0xF9 :
case 0xC1 :
2016-04-02 21:50:40 +00:00
SCSI . WriteRegister ( addr & 0xFF , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Unknown:
default :
Unknown8 :
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
//printf("CMODEL3 : unknown W8 : %x\n", addr >> 24); // harleyb unknown 0xF1
# endif
2016-04-02 21:50:40 +00:00
DebugLog ( " PC=%08X \t write8 : %08X=%02X \n " , ppc_get_pc ( ) , addr , data ) ;
break ;
}
2011-04-24 01:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
void CModel3 : : Write16 ( UINT32 addr , UINT16 data )
{
2016-04-02 21:50:40 +00:00
if ( ( addr & 1 ) )
{
Write8 ( addr + 0 , data > > 8 ) ;
Write8 ( addr + 1 , data & 0xFF ) ;
return ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
{
* ( UINT16 * ) & ram [ addr ^ 2 ] = data ;
return ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// Various
case 0xF0 :
case 0xFE : // mirror
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( ( addr > > 16 ) & 0xFF )
{
// Sound Board
case 0x08 :
//printf("%08X=%04X\n", addr, data);
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Backup RAM
case 0x0C :
case 0x0D :
* ( UINT16 * ) & backupRAM [ ( addr & 0x1FFFF ) ^ 2 ] = data ;
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// MPC105
case 0xC0 : // F0C00CF8
PCIBridge . WritePCIConfigData ( 16 , addr & 2 , data ) ;
break ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
//printf("CMODEL3 : unknown W16 mirror : %x\n", addr >> 16);
break ;
}
2021-02-18 10:29:15 +00:00
DebugLog ( " PC=%08X \t write16 : %08X=%04X \n " , ppc_get_pc ( ) , addr , data ) ;
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2017-03-23 04:22:25 +00:00
// Tile generator
case 0xF1 :
if ( addr < 0xF1120000 )
{
// Tile generator accesses its RAM as little endian, no adjustment needed here
TileGen . WriteRAM16 ( addr & 0x1FFFFF , FLIPENDIAN16 ( data ) ) ;
}
goto Unknown16 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// MPC105/106
case 0xF8 :
// Write in big endian order, like a real PowerPC
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 0 , data > > 8 ) ;
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 1 , data & 0xFF ) ;
break ;
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
case 0xC0 : // skichamp only
//printf("CModel 3 : write16 %x<-%x\n", addr, data);
switch ( ( addr & 0x3ffff ) > > 16 )
{
case 0 :
2021-11-05 00:23:29 +00:00
NetBoard - > WriteCommRAM16 ( ( addr & 0xFFFF ) ^ 2 , FLIPENDIAN16 ( data ) ) ;
2021-04-14 01:20:45 +00:00
break ;
default :
//printf("CMODEL3 : unknown W16 : %x\n", addr >> 24);
break ;
}
break ;
# endif
// Unknown
default :
Unknown16 :
DebugLog ( " PC=%08X \t write16: %08X=%04X \n " , ppc_get_pc ( ) , addr , data ) ;
break ;
}
}
2011-09-12 05:43:37 +00:00
void CModel3 : : Write32 ( UINT32 addr , UINT32 data )
2011-04-24 01:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
if ( ( addr & 3 ) )
{
Write16 ( addr + 0 , data > > 16 ) ;
Write16 ( addr + 2 , data ) ;
return ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
{
* ( UINT32 * ) & ram [ addr ] = data ;
return ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Other
switch ( ( addr > > 24 ) )
{
// Real3D trigger
case 0x88 : // 88000000
GPU . Flush ( ) ;
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Real3D low culling RAM
case 0x8C : // 8C000000-8C400000
GPU . WriteLowCullingRAM ( addr & 0x3FFFFF , FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Real3D high culling RAM
case 0x8E : // 8E000000-8E100000
GPU . WriteHighCullingRAM ( addr & 0xFFFFF , FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Real3D texture port
2016-04-10 23:57:55 +00:00
case 0x90 : // 90000000-90??????
2016-04-02 21:50:40 +00:00
GPU . WriteTexturePort ( addr & 0xFF , FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Real3D texture FIFO
case 0x94 : // 94000000-94100000
GPU . WriteTextureFIFO ( FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Real3D polygon RAM
case 0x98 : // 98000000-98400000
GPU . WritePolygonRAM ( addr & 0x3FFFFF , FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-05-26 04:13:22 +00:00
// Real3D configuration registers
case 0x9C : // 9Cxxxxxx
//printf("%08X=%08X\n", addr, data); //TODO: flip endian?
break ;
2016-04-02 21:50:40 +00:00
// Real3D DMA
case 0xC2 : // C2000000-C2000100
GPU . WriteDMARegister32 ( addr & 0xFF , FLIPENDIAN32 ( data ) ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Various
case 0xF0 :
case 0xFE : // mirror
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
switch ( ( addr > > 16 ) & 0xFF )
{
2021-02-18 10:29:15 +00:00
// Inputs
2016-04-02 21:50:40 +00:00
case 0x04 :
WriteInputs ( ( addr & 0x3F ) + 0 , ( data > > 24 ) & 0xFF ) ;
WriteInputs ( ( addr & 0x3F ) + 1 , ( data > > 16 ) & 0xFF ) ;
WriteInputs ( ( addr & 0x3F ) + 2 , ( data > > 8 ) & 0xFF ) ;
WriteInputs ( ( addr & 0x3F ) + 3 , ( data > > 0 ) & 0xFF ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Sound Board
case 0x08 :
//printf("PPC: %08X=%08X\n", addr, data);
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Backup RAM
case 0x0C :
case 0x0D :
* ( UINT32 * ) & backupRAM [ ( addr & 0x1FFFF ) ] = data ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC105
case 0x80 : // F0800CF8 (never observed at 0xFExxxxxx)
PCIBridge . WritePCIConfigAddress ( data ) ;
2021-04-14 01:20:45 +00:00
break ;
// MPC105/106
case 0xC0 : case 0xD0 : case 0xE0 :
case 0xC1 : case 0xD1 : case 0xE1 :
case 0xC2 : case 0xD2 : case 0xE2 :
case 0xC3 : case 0xD3 : case 0xE3 :
case 0xC4 : case 0xD4 : case 0xE4 :
case 0xC5 : case 0xD5 : case 0xE5 :
case 0xC6 : case 0xD6 : case 0xE6 :
case 0xC7 : case 0xD7 : case 0xE7 :
case 0xC8 : case 0xD8 : case 0xE8 :
case 0xC9 : case 0xD9 : case 0xE9 :
case 0xCA : case 0xDA : case 0xEA :
case 0xCB : case 0xDB : case 0xEB :
case 0xCC : case 0xDC : case 0xEC :
case 0xCD : case 0xDD : case 0xED :
case 0xCE : case 0xDE : case 0xEE :
case 0xCF : case 0xDF : case 0xEF :
if ( ( addr > = 0xF0C00CF8 ) & & ( addr < 0xF0C00D00 ) ) // MPC105
PCIBridge . WritePCIConfigData ( 32 , 0 , data ) ;
else if ( ( addr > = 0xFEC00000 ) & & ( addr < 0xFEE00000 ) ) // MPC106
2016-04-02 21:50:40 +00:00
PCIBridge . WritePCIConfigAddress ( data ) ;
else if ( ( addr > = 0xFEE00000 ) & & ( addr < 0xFEF00000 ) ) // MPC106
PCIBridge . WritePCIConfigData ( 32 , 0 , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// System registers
case 0x10 :
WriteSystemRegister ( ( addr & 0x3F ) + 0 , ( data > > 24 ) & 0xFF ) ;
WriteSystemRegister ( ( addr & 0x3F ) + 1 , ( data > > 16 ) & 0xFF ) ;
WriteSystemRegister ( ( addr & 0x3F ) + 2 , ( data > > 8 ) & 0xFF ) ;
WriteSystemRegister ( ( addr & 0x3F ) + 3 , ( data > > 0 ) & 0xFF ) ;
break ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// RTC
case 0x14 :
RTC . WriteRegister ( ( addr > > 2 ) & 0xF , data ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Security board RAM
case 0x18 :
case 0x19 :
* ( UINT32 * ) & securityRAM [ ( addr & 0x1FFFF ) ] = data ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Security board registers
2021-04-14 01:20:45 +00:00
case 0x1A :
WriteSecurity ( addr & 0x3F , data ) ;
break ;
// Unknown
default :
//printf("CMODEL3 : unknown W32 mirror : %x\n", addr >> 16);
break ;
}
2021-02-18 10:29:15 +00:00
DebugLog ( " PC=%08X \t write32: %08X=%08X \n " , ppc_get_pc ( ) , addr , data ) ;
2016-04-02 21:50:40 +00:00
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Tile generator
case 0xF1 :
if ( addr < 0xF1120000 )
{
// Tile generator accesses its RAM as little endian, must flip for big endian PowerPC
data = FLIPENDIAN32 ( data ) ;
2017-03-23 04:22:25 +00:00
TileGen . WriteRAM32 ( addr & 0x1FFFFF , data ) ;
2016-04-02 21:50:40 +00:00
break ;
}
else if ( ( addr > = 0xF1180000 ) & & ( addr < 0xF1180100 ) )
{
TileGen . WriteRegister ( addr & 0xFF , FLIPENDIAN32 ( data ) ) ;
break ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
goto Unknown32 ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// MPC105/106
case 0xF8 : // F8FFF000-F8FFF100
// Write in big endian order, like a real PowerPC
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 0 , ( data > > 24 ) & 0xFF ) ;
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 1 , ( data > > 16 ) & 0xFF ) ;
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 2 , ( data > > 8 ) & 0xFF ) ;
PCIBridge . WriteRegister ( ( addr & 0xFF ) + 3 , data & 0xFF ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// 53C810 SCSI
2021-04-14 01:20:45 +00:00
case 0xC0 : // step 1.0 only
# ifndef NET_BOARD
if ( m_game . stepping ! = " 1.0 " )
goto Unknown32 ;
# endif
# ifdef NET_BOARD
if ( m_runNetBoard )
2018-01-07 14:07:59 +00:00
{
2020-06-17 16:31:22 +00:00
UINT32 temp ;
2018-01-07 14:07:59 +00:00
switch ( ( addr & 0x3ffff ) > > 16 )
{
2021-04-14 01:20:45 +00:00
case 0 :
temp = FLIPENDIAN32 ( data ) ;
2021-11-05 00:23:29 +00:00
NetBoard - > WriteCommRAM32 ( addr & 0xFFFF , ( temp < < 16 ) | ( temp > > 16 ) ) ;
2021-04-14 01:20:45 +00:00
break ;
case 1 : // ioreg 32bits access to 16bits range
2018-01-07 14:07:59 +00:00
if ( addr > 0xc00101ff )
{
printf ( " W32 ATTENTION OUT OF RANGE \n " ) ;
}
2021-10-30 23:00:49 +00:00
NetBoard - > WriteIORegister ( ( addr & 0x1FF ) / 2 , FLIPENDIAN16 ( data > > 16 ) ) ;
2018-01-07 14:07:59 +00:00
break ;
case 2 :
case 3 :
* ( UINT16 * ) & netRAM [ ( ( addr & 0x1FFFF ) / 2 ) ] = FLIPENDIAN16 ( data > > 16 ) ;
break ;
2021-10-30 23:00:49 +00:00
2018-01-07 14:07:59 +00:00
default :
printf ( " W32 ATTENTION OUT OF RANGE \n " ) ;
2021-04-14 01:20:45 +00:00
break ;
2018-01-07 14:07:59 +00:00
}
2021-04-14 01:20:45 +00:00
break ;
}
else if ( m_game . stepping ! = " 1.0 " ) break ;
# endif
case 0xF9 :
case 0xC1 :
2016-04-02 21:50:40 +00:00
SCSI . WriteRegister ( ( addr & 0xFF ) + 0 , ( data > > 24 ) & 0xFF ) ;
SCSI . WriteRegister ( ( addr & 0xFF ) + 1 , ( data > > 16 ) & 0xFF ) ;
SCSI . WriteRegister ( ( addr & 0xFF ) + 2 , ( data > > 8 ) & 0xFF ) ;
SCSI . WriteRegister ( ( addr & 0xFF ) + 3 , data & 0xFF ) ;
break ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Unknown
default :
Unknown32 :
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
2020-07-01 15:56:21 +00:00
if ( m_runNetBoard ) printf ( " CMODEL3 : unknown W32 : %x (%x) data=%d \n " , addr , addr > > 24 , data ) ;
2018-01-07 14:07:59 +00:00
# endif
2016-05-26 04:13:22 +00:00
//printf("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data);
2016-04-02 21:50:40 +00:00
DebugLog ( " PC=%08X \t write32: %08X=%08X \n " , ppc_get_pc ( ) , addr , data ) ;
break ;
}
2011-09-12 05:43:37 +00:00
}
2021-04-14 01:20:45 +00:00
void CModel3 : : Write64 ( UINT32 addr , UINT64 data )
{
//printf("write64 %x <- %x\n", addr, data);
Write32 ( addr + 0 , ( UINT32 ) ( data > > 32 ) ) ;
Write32 ( addr + 4 , ( UINT32 ) data ) ;
}
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
/******************************************************************************
Emulation and Interface Functions
******************************************************************************/
void CModel3 : : SaveState ( CBlockFile * SaveState )
{
2016-04-02 21:50:40 +00:00
// Write Model 3 state
SaveState - > NewBlock ( " Model 3 " , __FILE__ ) ;
SaveState - > Write ( & inputBank , sizeof ( inputBank ) ) ;
SaveState - > Write ( & serialFIFO1 , sizeof ( serialFIFO1 ) ) ;
SaveState - > Write ( & serialFIFO2 , sizeof ( serialFIFO2 ) ) ;
SaveState - > Write ( & gunReg , sizeof ( gunReg ) ) ;
SaveState - > Write ( & adcChannel , sizeof ( adcChannel ) ) ;
SaveState - > Write ( & cromBankReg , sizeof ( cromBankReg ) ) ;
SaveState - > Write ( & securityPtr , sizeof ( securityPtr ) ) ;
SaveState - > Write ( ram , 0x800000 ) ;
SaveState - > Write ( backupRAM , 0x20000 ) ;
SaveState - > Write ( securityRAM , 0x20000 ) ;
SaveState - > Write ( & midiCtrlPort , sizeof ( midiCtrlPort ) ) ;
int32_t securityFirstRead = m_securityFirstRead ;
SaveState - > Write ( & securityFirstRead , sizeof ( securityFirstRead ) ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// All devices...
ppc_save_state ( SaveState ) ;
IRQ . SaveState ( SaveState ) ;
PCIBridge . SaveState ( SaveState ) ;
SCSI . SaveState ( SaveState ) ;
EEPROM . SaveState ( SaveState ) ;
TileGen . SaveState ( SaveState ) ;
GPU . SaveState ( SaveState ) ;
SoundBoard . SaveState ( SaveState ) ; // also saves DSB state
2021-02-18 10:29:15 +00:00
DriveBoard - > SaveState ( SaveState ) ;
2016-04-02 21:50:40 +00:00
m_cryptoDevice . SaveState ( SaveState ) ;
2017-09-24 20:52:48 +00:00
m_jtag . SaveState ( SaveState ) ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : LoadState ( CBlockFile * SaveState )
{
2016-04-02 21:50:40 +00:00
// Load Model 3 state
if ( OKAY ! = SaveState - > FindBlock ( " Model 3 " ) )
{
ErrorLog ( " Unable to load Model 3 core state. Save state file is corrupt. " ) ;
return ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
SaveState - > Read ( & inputBank , sizeof ( inputBank ) ) ;
SaveState - > Read ( & serialFIFO1 , sizeof ( serialFIFO1 ) ) ;
SaveState - > Read ( & serialFIFO2 , sizeof ( serialFIFO2 ) ) ;
SaveState - > Read ( & gunReg , sizeof ( gunReg ) ) ;
SaveState - > Read ( & adcChannel , sizeof ( adcChannel ) ) ;
SaveState - > Read ( & cromBankReg , sizeof ( cromBankReg ) ) ;
SetCROMBank ( cromBankReg ) ; // update CROM bank
SaveState - > Read ( & securityPtr , sizeof ( securityPtr ) ) ;
SaveState - > Read ( ram , 0x800000 ) ;
SaveState - > Read ( backupRAM , 0x20000 ) ;
SaveState - > Read ( securityRAM , 0x20000 ) ;
SaveState - > Read ( & midiCtrlPort , sizeof ( midiCtrlPort ) ) ;
int32_t securityFirstRead ;
2022-07-11 17:43:59 +02:00
SaveState - > Read ( & securityFirstRead , sizeof ( securityFirstRead ) ) ;
2016-05-03 18:38:06 +00:00
m_securityFirstRead = securityFirstRead ! = 0 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// All devices...
GPU . LoadState ( SaveState ) ;
TileGen . LoadState ( SaveState ) ;
EEPROM . LoadState ( SaveState ) ;
SCSI . LoadState ( SaveState ) ;
PCIBridge . LoadState ( SaveState ) ;
IRQ . LoadState ( SaveState ) ;
ppc_load_state ( SaveState ) ;
SoundBoard . LoadState ( SaveState ) ;
2021-02-18 10:29:15 +00:00
DriveBoard - > LoadState ( SaveState ) ;
2016-04-02 21:50:40 +00:00
m_cryptoDevice . LoadState ( SaveState ) ;
2017-09-24 20:52:48 +00:00
m_jtag . LoadState ( SaveState ) ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : SaveNVRAM ( CBlockFile * NVRAM )
{
2016-04-02 21:50:40 +00:00
// Load EEPROM
EEPROM . SaveState ( NVRAM ) ;
2011-04-24 01:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Save backup RAM
NVRAM - > NewBlock ( " Backup RAM " , __FILE__ ) ;
NVRAM - > Write ( backupRAM , 0x20000 ) ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : LoadNVRAM ( CBlockFile * NVRAM )
{
2016-04-02 21:50:40 +00:00
// Load EEPROM
EEPROM . LoadState ( NVRAM ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Load backup RAM
if ( OKAY ! = NVRAM - > FindBlock ( " Backup RAM " ) )
{
ErrorLog ( " Unable to load Model 3 backup RAM. NVRAM file is corrupt. " ) ;
return ;
}
NVRAM - > Read ( backupRAM , 0x20000 ) ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : ClearNVRAM ( void )
{
2016-04-02 21:50:40 +00:00
memset ( backupRAM , 0 , 0x20000 ) ;
EEPROM . Clear ( ) ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : RunFrame ( void )
2011-09-12 05:43:37 +00:00
{
2016-04-02 21:50:40 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// See if currently running multi-threaded
2017-03-27 03:19:15 +00:00
if ( m_multiThreaded )
2016-04-02 21:50:40 +00:00
{
// If so, check all threads are up and running
if ( ! StartThreads ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Wake threads for PPC main board (if multi-threading GPU), sound board (if sync'd) and drive board (if attached) so they can process a frame
2021-02-18 10:29:15 +00:00
if ( ( m_gpuMultiThreaded & & ! ppcBrdThreadSync - > Post ( ) ) | |
( syncSndBrdThread & & ! sndBrdThreadSync - > Post ( ) ) | |
( DriveBoard - > IsAttached ( ) & & ! drvBrdThreadSync - > Post ( ) ) )
2016-04-02 21:50:40 +00:00
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// If not multi-threading GPU, then run PPC main board for a frame and sync GPUs now in this thread
2017-03-27 03:19:15 +00:00
if ( ! m_gpuMultiThreaded )
2016-04-02 21:50:40 +00:00
{
RunMainBoardFrame ( ) ;
SyncGPUs ( ) ;
}
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Render frame
RenderFrame ( ) ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify wait critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Wait for PPC main board, sound board and drive board threads to finish their work (if they are running and haven't finished already)
2021-02-18 10:29:15 +00:00
while ( ( m_gpuMultiThreaded & & ! ppcBrdThreadDone ) | |
( syncSndBrdThread & & ! sndBrdThreadDone ) | |
( DriveBoard - > IsAttached ( ) & & ! drvBrdThreadDone ) )
2016-04-02 21:50:40 +00:00
{
if ( ! notifySync - > Wait ( notifyLock ) )
goto ThreadError ;
}
ppcBrdThreadDone = false ;
sndBrdThreadDone = false ;
drvBrdThreadDone = false ;
2018-01-07 14:07:59 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify wait critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2021-04-14 01:20:45 +00:00
// If multi-threading GPU, then sync GPUs last while PPC main board thread is waiting
if ( m_gpuMultiThreaded )
SyncGPUs ( ) ;
# ifdef NET_BOARD
if ( NetBoard - > IsRunning ( ) & & m_config [ " SimulateNet " ] . ValueAs < bool > ( ) )
RunNetBoardFrame ( ) ;
# endif
}
else
{
2016-04-02 21:50:40 +00:00
// If not multi-threaded, then just process and render a single frame for PPC main board, sound board and drive board in turn in this thread
RunMainBoardFrame ( ) ;
SyncGPUs ( ) ;
RenderFrame ( ) ;
RunSoundBoardFrame ( ) ;
2021-04-14 01:20:45 +00:00
if ( DriveBoard - > IsAttached ( ) )
RunDriveBoardFrame ( ) ;
# ifdef NET_BOARD
if ( NetBoard - > IsRunning ( ) )
RunNetBoardFrame ( ) ;
# endif
}
timings . frameTicks = CThread : : GetTicks ( ) - start ;
2022-06-09 21:10:39 +00:00
// Frame counter
timings . frameId + + ;
2016-04-02 21:50:40 +00:00
return ;
2011-07-20 21:14:00 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in CModel3::RunFrame: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2011-07-20 21:14:00 +00:00
}
2024-01-28 21:08:12 -08:00
static unsigned GetCPUClockFrequencyInHz ( const Game & game , Util : : Config : : Node & config )
{
unsigned mhz = config [ " PowerPCFrequency " ] . ValueAsDefault < unsigned > ( 0 ) ;
if ( ! mhz )
{
if ( game . stepping = = " 1.0 " )
{
mhz = 66 ;
}
else if ( game . stepping = = " 1.5 " )
{
mhz = 100 ;
}
else // 2.x
{
mhz = 166 ;
}
}
return mhz * 1000000 ;
}
2017-09-24 20:52:48 +00:00
void CModel3 : : RunMainBoardFrame ( void )
{
2019-11-07 20:29:17 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
2017-09-24 20:52:48 +00:00
2023-09-25 14:45:57 +02:00
/*
2024-01-28 21:08:12 -08:00
* Compute display timings. Refresh rate is 57.524160 Hz and we assume frame timing is the same as System 24:
*
* - 25 scanlines from /VSYNC high to /BLANK high (top border)
* - 384 scanlines from /BLANK high to /BLANK low (active display)
* - 11 scanlines from /BLANK low to /VSYNC low (bottom border)
* - 4 scanlines from /VSYNC low to /VSYNC high (vertical sync. pulse)
2023-09-25 14:45:57 +02:00
*
2024-01-28 21:08:12 -08:00
* 424 lines total: 384 display and 40 blanking/vsync.
2023-09-25 14:45:57 +02:00
*/
2024-01-28 21:08:12 -08:00
unsigned ppcCycles = GetCPUClockFrequencyInHz ( m_game , m_config ) ;
2022-01-23 15:32:53 +00:00
unsigned frameCycles = ( unsigned ) ( ( float ) ppcCycles / 57.524160f ) ;
2023-09-03 19:29:58 +01:00
unsigned lineCycles = frameCycles / 424 ;
unsigned dispCycles = lineCycles * ( TileGen . ReadRegister ( 0x08 ) + 40 ) ;
unsigned offsetCycles = frameCycles - dispCycles ;
2022-11-22 01:43:38 +00:00
unsigned statusCycles = ( unsigned ) ( ( float ) frameCycles * ( 0.005f ) ) ;
2017-09-24 20:52:48 +00:00
2023-09-03 16:51:18 +01:00
// Games will start writing a new frame after the ping-pong buffers have been flipped, which is indicated by the
2023-09-03 19:29:58 +01:00
// ping-pong status bit. The timing of ping-pong flip is determined by the value of tilegen register 0x08, which
// is the number of active video lines to display before ping-pong flip occurs. Most games set it to 238 or 239
// so that ping-pong flip occurs 66% of the frame time after IRQ2, though a few games set it to a higher value.
2017-09-24 20:52:48 +00:00
2019-11-07 20:29:17 +00:00
// Scale PPC timer ratio according to speed at which the PowerPC is being emulated so that the observed running frequency of the PPC timer
2021-02-18 10:29:15 +00:00
// registers is more or less correct. This is needed to get the Virtua Striker 2 series of games running at the right speed (they are
2019-11-07 20:29:17 +00:00
// too slow otherwise). Other games appear to not be affected by this ratio so much as their running speed depends more on the timing of
// the Real3D status bit below.
ppc_set_timer_ratio ( ppc_get_bus_freq_multipler ( ) * 2 * ppcCycles / ppc_get_cycles_per_sec ( ) ) ;
// VBlank
if ( gpusReady )
{
TileGen . BeginVBlank ( ) ;
GPU . BeginVBlank ( statusCycles ) ; // Games poll the ping_pong at startup. Values aren't 100% accurate so we stretch the frame a bit to ensure writes happen in the correct frame
ppc_execute ( offsetCycles ) ;
IRQ . Assert ( 0x02 ) ; // start at 33% of the frame
2022-11-22 01:43:38 +00:00
2022-11-22 02:02:10 +00:00
// keep running cycles until IRQ2 is acknowledged
2022-11-25 23:02:37 +00:00
// Ski Champ can hang if we check the MIDI control port too early
// and miss MIDI interrupts pending before the next IRQ2
2022-11-22 02:02:10 +00:00
while ( IRQ . ReadIRQEnable ( ) & 0x2 & & IRQ . ReadIRQState ( ) & 0x2 & & dispCycles > 1000 )
{
ppc_execute ( 1000 ) ;
dispCycles - = 1000 ;
}
2019-11-07 20:29:17 +00:00
/*
* Sound:
*
* Bit 0x20 of the MIDI control port appears to enable periodic interrupts,
* which are used to send MIDI commands. Often games will write 0x27, send
* a series of commands, and write 0x06 to stop. Other games, like Star
* Wars Trilogy and Sega Rally 2, will enable interrupts at the beginning
* by writing 0x37 and will disable/enable interrupts to control command
* output.
*/
//printf("\t-- BEGIN (Ctrl=%02X, IRQEn=%02X, IRQPend=%02X) --\n", midiCtrlPort, IRQ.ReadIRQEnable()&0x40, IRQ.ReadIRQState());
int irqCount = 0 ;
while ( ( midiCtrlPort & 0x20 ) )
//while (midiCtrlPort == 0x27) // 27 triggers IRQ sequence, 06 stops it
{
// Don't waste time firing MIDI interrupts if game has disabled them
if ( ( IRQ . ReadIRQEnable ( ) & 0x40 ) = = 0 )
break ;
// Process MIDI interrupt
IRQ . Assert ( 0x40 ) ;
ppc_execute ( 200 ) ; // give PowerPC time to acknowledge IR
IRQ . Deassert ( 0x40 ) ;
ppc_execute ( 200 ) ; // acknowledge that IRQ was deasserted (TODO: is this really needed?)
dispCycles - = 400 ;
+ + irqCount ;
if ( irqCount > 128 )
{
break ;
}
}
IRQ . Assert ( 0x0D ) ;
// End VBlank
GPU . EndVBlank ( ) ;
TileGen . EndVBlank ( ) ;
}
// Run the PowerPC for the active display part of the frame
ppc_execute ( dispCycles ) ;
timings . ppcTicks = CThread : : GetTicks ( ) - start ;
2017-09-24 20:52:48 +00:00
}
2012-01-16 23:21:14 +00:00
void CModel3 : : SyncGPUs ( void )
{
2016-04-02 21:50:40 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
timings . syncSize = GPU . SyncSnapshots ( ) + TileGen . SyncSnapshots ( ) ;
gpusReady = true ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
timings . syncTicks = CThread : : GetTicks ( ) - start ;
2012-01-16 23:21:14 +00:00
}
void CModel3 : : RenderFrame ( void )
{
2016-04-02 21:50:40 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Call OSD video callbacks
if ( BeginFrameVideo ( ) & & gpusReady )
{
// Render frame
TileGen . BeginFrame ( ) ;
GPU . BeginFrame ( ) ;
2016-05-08 19:27:08 +00:00
TileGen . PreRenderFrame ( ) ;
TileGen . RenderFrameBottom ( ) ;
2016-04-02 21:50:40 +00:00
GPU . RenderFrame ( ) ;
2016-05-08 19:27:08 +00:00
TileGen . RenderFrameTop ( ) ;
2016-04-02 21:50:40 +00:00
GPU . EndFrame ( ) ;
TileGen . EndFrame ( ) ;
2023-12-26 18:25:03 +00:00
m_superAA - > Draw ( ) ;
2016-04-02 21:50:40 +00:00
}
2012-02-13 23:37:48 +00:00
2016-04-02 21:50:40 +00:00
EndFrameVideo ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
timings . renderTicks = CThread : : GetTicks ( ) - start ;
2012-01-16 23:21:14 +00:00
}
bool CModel3 : : RunSoundBoardFrame ( void )
{
2016-04-02 21:50:40 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
bool bufferFull = SoundBoard . RunFrame ( ) ;
timings . sndTicks = CThread : : GetTicks ( ) - start ;
return bufferFull ;
2012-01-16 23:21:14 +00:00
}
void CModel3 : : RunDriveBoardFrame ( void )
{
2016-04-02 21:50:40 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
2021-02-18 10:29:15 +00:00
DriveBoard - > RunFrame ( ) ;
2016-04-02 21:50:40 +00:00
timings . drvTicks = CThread : : GetTicks ( ) - start ;
2012-01-16 23:21:14 +00:00
}
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
void CModel3 : : RunNetBoardFrame ( void )
{
NetBoard - > RunFrame ( ) ;
}
# endif
2011-09-12 05:43:37 +00:00
bool CModel3 : : StartThreads ( void )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
if ( startedThreads )
return true ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Create synchronization objects
2017-03-27 03:19:15 +00:00
if ( m_gpuMultiThreaded )
2016-04-02 21:50:40 +00:00
{
ppcBrdThreadSync = CThread : : CreateSemaphore ( 0 ) ;
if ( ppcBrdThreadSync = = NULL )
goto ThreadError ;
}
sndBrdThreadSync = CThread : : CreateSemaphore ( 0 ) ;
if ( sndBrdThreadSync = = NULL )
goto ThreadError ;
sndBrdNotifyLock = CThread : : CreateMutex ( ) ;
if ( sndBrdNotifyLock = = NULL )
goto ThreadError ;
sndBrdNotifySync = CThread : : CreateCondVar ( ) ;
if ( sndBrdNotifySync = = NULL )
goto ThreadError ;
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
2016-04-02 21:50:40 +00:00
{
drvBrdThreadSync = CThread : : CreateSemaphore ( 0 ) ;
if ( drvBrdThreadSync = = NULL )
goto ThreadError ;
}
notifyLock = CThread : : CreateMutex ( ) ;
if ( notifyLock = = NULL )
goto ThreadError ;
notifySync = CThread : : CreateCondVar ( ) ;
if ( notifySync = = NULL )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Reset thread flags
pauseThreads = false ;
stopThreads = false ;
2012-07-23 20:35:10 +00:00
2016-04-02 21:50:40 +00:00
// Create PPC main board thread, if multi-threading GPU
2017-03-27 03:19:15 +00:00
if ( m_gpuMultiThreaded )
2016-04-02 21:50:40 +00:00
{
2020-04-19 08:34:58 +00:00
ppcBrdThread = CThread : : CreateThread ( " MainBoard " , StartMainBoardThread , this ) ;
2016-04-02 21:50:40 +00:00
if ( ppcBrdThread = = NULL )
goto ThreadError ;
}
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Create sound board thread (sync'd or unsync'd)
if ( syncSndBrdThread )
2020-04-19 08:34:58 +00:00
sndBrdThread = CThread : : CreateThread ( " SoundBoardSync " , StartSoundBoardThreadSyncd , this ) ;
2016-04-02 21:50:40 +00:00
else
2020-04-19 08:34:58 +00:00
sndBrdThread = CThread : : CreateThread ( " SoundBoardNoSync " , StartSoundBoardThread , this ) ;
2016-04-02 21:50:40 +00:00
if ( sndBrdThread = = NULL )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Create drive board thread, if drive board is attached
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
2016-04-02 21:50:40 +00:00
{
2020-04-19 08:34:58 +00:00
drvBrdThread = CThread : : CreateThread ( " DriveBoard " , StartDriveBoardThread , this ) ;
2016-04-02 21:50:40 +00:00
if ( drvBrdThread = = NULL )
goto ThreadError ;
}
2011-09-12 05:43:37 +00:00
2021-04-14 01:20:45 +00:00
// Set audio callback if sound board thread is unsync'd
if ( ! syncSndBrdThread )
{
SetAudioCallback ( AudioCallback , this ) ;
}
startedThreads = true ;
2016-04-02 21:50:40 +00:00
return true ;
2011-07-20 21:14:00 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Unable to create threads and/or synchronization objects: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
DeleteThreadObjects ( ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return false ;
2011-07-20 21:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
bool CModel3 : : PauseThreads ( void )
{
2016-04-02 21:50:40 +00:00
if ( ! startedThreads )
return true ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Let threads know that they should pause and wait for all of them to do so
pauseThreads = true ;
while ( ppcBrdThreadRunning | | sndBrdThreadRunning | | drvBrdThreadRunning )
{
if ( ! notifySync - > Wait ( notifyLock ) )
goto ThreadError ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
return true ;
2011-09-12 05:43:37 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in CModel3::PauseThreads: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return false ;
2011-09-12 05:43:37 +00:00
}
2012-01-16 23:21:14 +00:00
bool CModel3 : : ResumeThreads ( void )
2021-02-18 10:29:15 +00:00
{
2016-04-02 21:50:40 +00:00
if ( ! startedThreads )
return true ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Let all threads know that they can continue running
pauseThreads = false ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
return true ;
2012-01-16 23:21:14 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in CModel3::ResumeThreads: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return false ;
2011-09-12 05:43:37 +00:00
}
2012-07-23 20:35:10 +00:00
bool CModel3 : : StopThreads ( void )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
if ( ! startedThreads )
return true ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// If sound board thread is unsync'd then remove audio callback
if ( ! syncSndBrdThread )
SetAudioCallback ( NULL , NULL ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2012-07-23 20:35:10 +00:00
2016-04-02 21:50:40 +00:00
// Let threads know that they should pause and wait for all of them to do so
pauseThreads = true ;
while ( ppcBrdThreadRunning | | sndBrdThreadRunning | | drvBrdThreadRunning )
{
if ( ! notifySync - > Wait ( notifyLock ) )
goto ThreadError ;
}
2012-07-23 20:35:10 +00:00
2016-04-02 21:50:40 +00:00
// Now let threads know that they should exit
stopThreads = true ;
2012-07-23 20:35:10 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Resume each thread in turn and wait for them to exit
if ( ppcBrdThread ! = NULL )
{
if ( ppcBrdThreadSync - > Post ( ) )
ppcBrdThread - > Wait ( ) ;
}
if ( sndBrdThread ! = NULL )
{
if ( syncSndBrdThread )
{
if ( sndBrdThreadSync - > Post ( ) )
sndBrdThread - > Wait ( ) ;
}
else
{
if ( WakeSoundBoardThread ( ) )
sndBrdThread - > Wait ( ) ;
}
}
if ( drvBrdThread ! = NULL )
{
if ( drvBrdThreadSync - > Post ( ) )
drvBrdThread - > Wait ( ) ;
}
2012-07-23 20:35:10 +00:00
2016-04-02 21:50:40 +00:00
// Delete all thread and synchronization objects
DeleteThreadObjects ( ) ;
startedThreads = false ;
return true ;
2012-07-23 20:35:10 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in CModel3::StopThreads: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return false ;
2011-07-20 21:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
void CModel3 : : DeleteThreadObjects ( void )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
// Delete PPC main board, sound board and drive board threads
if ( ppcBrdThread ! = NULL )
{
delete ppcBrdThread ;
ppcBrdThread = NULL ;
}
if ( sndBrdThread ! = NULL )
{
delete sndBrdThread ;
sndBrdThread = NULL ;
}
if ( drvBrdThread ! = NULL )
{
delete drvBrdThread ;
drvBrdThread = NULL ;
}
2011-07-20 21:14:00 +00:00
2018-01-07 14:07:59 +00:00
2016-04-02 21:50:40 +00:00
// Delete synchronization objects
if ( ppcBrdThreadSync ! = NULL )
{
delete ppcBrdThreadSync ;
ppcBrdThreadSync = NULL ;
}
if ( sndBrdThreadSync ! = NULL )
{
delete sndBrdThreadSync ;
sndBrdThreadSync = NULL ;
}
if ( drvBrdThreadSync ! = NULL )
{
delete drvBrdThreadSync ;
drvBrdThreadSync = NULL ;
}
2018-01-07 14:07:59 +00:00
2016-04-02 21:50:40 +00:00
if ( sndBrdNotifyLock ! = NULL )
{
delete sndBrdNotifyLock ;
sndBrdNotifyLock = NULL ;
}
if ( sndBrdNotifySync ! = NULL )
{
delete sndBrdNotifySync ;
sndBrdNotifySync = NULL ;
}
if ( notifyLock ! = NULL )
{
delete notifyLock ;
notifyLock = NULL ;
}
if ( notifySync ! = NULL )
{
delete notifySync ;
notifySync = NULL ;
}
2011-07-20 21:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
2012-01-16 23:21:14 +00:00
void CModel3 : : DumpTimings ( void )
{
2016-04-02 21:50:40 +00:00
printf ( " PPC:%3ums%c render:%3ums%c sync:%4uK%c%3ums%c snd:%3ums%c drv:%3ums%c frame:%3ums%c \n " ,
timings . ppcTicks , ( timings . ppcTicks > timings . renderTicks ? ' ! ' : ' , ' ) ,
2021-02-18 10:29:15 +00:00
timings . renderTicks , ( timings . renderTicks > timings . ppcTicks ? ' ! ' : ' , ' ) ,
timings . syncSize / 1024 , ( timings . syncSize / 1024 > 128 ? ' ! ' : ' , ' ) ,
2016-04-02 21:50:40 +00:00
timings . syncTicks , ( timings . syncTicks > 1 ? ' ! ' : ' , ' ) ,
2021-04-14 01:20:45 +00:00
timings . sndTicks , ( timings . sndTicks > 10 ? ' ! ' : ' , ' ) ,
timings . drvTicks , ( timings . drvTicks > 10 ? ' ! ' : ' , ' ) ,
timings . frameTicks , ( timings . frameTicks > 16 ? ' ! ' : ' ' ) ) ;
}
2013-11-30 19:39:59 +00:00
FrameTimings CModel3 : : GetTimings ( void )
{
2016-04-02 21:50:40 +00:00
return timings ;
2012-01-16 23:21:14 +00:00
}
int CModel3 : : StartMainBoardThread ( void * data )
{
2016-04-02 21:50:40 +00:00
// Call method on CModel3 to run PPC main board thread
CModel3 * model3 = ( CModel3 * ) data ;
return model3 - > RunMainBoardThread ( ) ;
2012-01-16 23:21:14 +00:00
}
2011-07-20 21:14:00 +00:00
int CModel3 : : StartSoundBoardThread ( void * data )
{
2016-04-02 21:50:40 +00:00
// Call method on CModel3 to run sound board thread (unsync'd)
CModel3 * model3 = ( CModel3 * ) data ;
return model3 - > RunSoundBoardThread ( ) ;
2011-07-20 21:14:00 +00:00
}
2011-09-12 05:43:37 +00:00
int CModel3 : : StartSoundBoardThreadSyncd ( void * data )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
// Call method on CModel3 to run sound board thread (sync'd)
CModel3 * model3 = ( CModel3 * ) data ;
return model3 - > RunSoundBoardThreadSyncd ( ) ;
2011-07-20 21:14:00 +00:00
}
2012-01-16 23:21:14 +00:00
int CModel3 : : StartDriveBoardThread ( void * data )
2011-09-12 05:43:37 +00:00
{
2016-04-02 21:50:40 +00:00
// Call method on CModel3 to run drive board thread
CModel3 * model3 = ( CModel3 * ) data ;
2021-04-14 01:20:45 +00:00
return model3 - > RunDriveBoardThread ( ) ;
}
int CModel3 : : RunMainBoardThread ( void )
{
for ( ; ; )
2016-04-02 21:50:40 +00:00
{
bool wait = true ;
bool exit = false ;
while ( wait & & ! exit )
{
// Wait on PPC main board thread semaphore
if ( ! ppcBrdThreadSync - > Wait ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
ppcBrdThreadRunning = true ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
if ( exit )
return 0 ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Process a single frame for PPC main board
RunMainBoardFrame ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Let other threads know processing has finished
ppcBrdThreadRunning = false ;
ppcBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
2012-01-16 23:21:14 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in RunMainBoardThread: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return 1 ;
2012-01-16 23:21:14 +00:00
}
2011-09-12 05:43:37 +00:00
void CModel3 : : AudioCallback ( void * data )
{
2016-04-02 21:50:40 +00:00
// Call method on CModel3 to wake sound board thread
CModel3 * model3 = ( CModel3 * ) data ;
model3 - > WakeSoundBoardThread ( ) ;
2011-09-12 05:43:37 +00:00
}
2012-07-23 20:35:10 +00:00
bool CModel3 : : WakeSoundBoardThread ( void )
2011-09-12 05:43:37 +00:00
{
2016-04-02 21:50:40 +00:00
// Enter sound board notify critical section
bool wake ;
if ( ! sndBrdNotifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Enter main notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// See if sound board thread is currently running
wake = ! sndBrdThreadRunning ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Leave main notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Only send wake notification to sound board thread if it was not running
if ( wake )
{
// Signal to sound board thread that it should start processing again
sndBrdWakeNotify = true ;
if ( ! sndBrdNotifySync - > Signal ( ) )
goto ThreadError ;
}
2012-07-24 20:18:30 +00:00
2016-04-02 21:50:40 +00:00
// Leave sound board notify critical section
if ( ! sndBrdNotifyLock - > Unlock ( ) )
goto ThreadError ;
return wake ;
2011-09-12 05:43:37 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in WakeSoundBoardThread: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return false ;
2011-09-12 05:43:37 +00:00
}
2012-07-23 20:35:10 +00:00
int CModel3 : : RunSoundBoardThread ( void )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
for ( ; ; )
{
bool wait = true ;
bool exit = false ;
while ( wait & & ! exit )
{
// Enter sound board notify critical section
if ( ! sndBrdNotifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Wait for notification from audio callback
while ( ! sndBrdWakeNotify )
{
if ( ! sndBrdNotifySync - > Wait ( sndBrdNotifyLock ) )
goto ThreadError ;
}
sndBrdWakeNotify = false ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Enter main notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
sndBrdThreadRunning = true ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Leave main notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
2012-07-24 20:18:30 +00:00
2016-04-02 21:50:40 +00:00
// Leave sound board notify critical section
if ( ! sndBrdNotifyLock - > Unlock ( ) )
2021-02-18 10:29:15 +00:00
goto ThreadError ;
2016-04-02 21:50:40 +00:00
}
if ( exit )
return 0 ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Keep processing frames until pausing or audio buffer is full
while ( true )
{
// Enter main notify critical section
bool paused ;
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
paused = pauseThreads ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Leave main notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
if ( paused | | RunSoundBoardFrame ( ) )
break ;
//printf("Rerunning sound board\n");
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Enter main notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Let other threads know processing has finished
sndBrdThreadRunning = false ;
sndBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Leave main notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
2011-09-12 05:43:37 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in RunSoundBoardThread: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return 1 ;
2011-09-12 05:43:37 +00:00
}
2012-07-23 20:35:10 +00:00
int CModel3 : : RunSoundBoardThreadSyncd ( void )
2011-09-12 05:43:37 +00:00
{
2016-04-02 21:50:40 +00:00
for ( ; ; )
{
bool wait = true ;
bool exit = false ;
while ( wait & & ! exit )
{
// Wait on sound board thread semaphore
if ( ! sndBrdThreadSync - > Wait ( ) )
goto ThreadError ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
sndBrdThreadRunning = true ;
}
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
if ( exit )
return 0 ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Process a single frame for sound board
RunSoundBoardFrame ( ) ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Let other threads know processing has finished
sndBrdThreadRunning = false ;
sndBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
2011-07-20 21:14:00 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in RunSoundBoardThreadSyncd: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2016-04-02 21:50:40 +00:00
return 1 ;
2011-07-20 21:14:00 +00:00
}
2012-07-23 20:35:10 +00:00
int CModel3 : : RunDriveBoardThread ( void )
2011-07-20 21:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
for ( ; ; )
{
2021-02-18 10:29:15 +00:00
bool wait = true ;
2016-04-02 21:50:40 +00:00
bool exit = false ;
while ( wait & & ! exit )
{
// Wait on drive board thread semaphore
if ( ! drvBrdThreadSync - > Wait ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-09-12 05:43:37 +00:00
2016-04-02 21:50:40 +00:00
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
drvBrdThreadRunning = true ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
if ( exit )
return 0 ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Process a single frame for drive board
RunDriveBoardFrame ( ) ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Let other threads know processing has finished
drvBrdThreadRunning = false ;
drvBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
2011-07-20 21:14:00 +00:00
2016-04-02 21:50:40 +00:00
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
2011-07-20 21:14:00 +00:00
ThreadError :
2016-04-02 21:50:40 +00:00
ErrorLog ( " Threading error in RunDriveBoardThread: %s \n Switching back to single-threaded mode. \n " , CThread : : GetLastError ( ) ) ;
2017-03-27 03:19:15 +00:00
m_multiThreaded = false ;
2021-04-14 01:20:45 +00:00
return 1 ;
}
void CModel3 : : Reset ( void )
{
// Clear memory (but do not modify backup RAM!)
2016-04-02 21:50:40 +00:00
memset ( ram , 0 , 0x800000 ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Initial bank is bank 0
SetCROMBank ( 0xFF ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Reset security device
securityPtr = 0 ;
m_securityFirstRead = true ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Reset inputs
inputBank = 0 ;
serialFIFO1 = 0 ;
serialFIFO2 = 0 ;
adcChannel = 0 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// MIDI
midiCtrlPort = 0 ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Reset all devices
ppc_reset ( ) ;
IRQ . Reset ( ) ;
PCIBridge . Reset ( ) ;
PCIBus . Reset ( ) ;
SCSI . Reset ( ) ;
RTC . Reset ( ) ;
EEPROM . Reset ( ) ;
TileGen . Reset ( ) ;
GPU . Reset ( ) ;
SoundBoard . Reset ( ) ;
2017-09-24 20:52:48 +00:00
m_jtag . Reset ( ) ;
2011-09-07 07:21:56 +00:00
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
DriveBoard - > Reset ( ) ;
2018-01-07 14:07:59 +00:00
2016-04-02 21:50:40 +00:00
m_cryptoDevice . Reset ( ) ;
2012-01-16 23:21:14 +00:00
2016-04-02 21:50:40 +00:00
gpusReady = false ;
2013-11-30 19:39:59 +00:00
2016-04-02 21:50:40 +00:00
timings . ppcTicks = 0 ;
timings . syncSize = 0 ;
timings . syncTicks = 0 ;
timings . renderTicks = 0 ;
timings . sndTicks = 0 ;
timings . drvTicks = 0 ;
2018-01-07 14:07:59 +00:00
# ifdef NET_BOARD
timings . netTicks = 0 ;
2021-04-14 01:20:45 +00:00
NetBoard - > Reset ( ) ;
2018-01-07 14:07:59 +00:00
# endif
2016-04-02 21:50:40 +00:00
timings . frameTicks = 0 ;
2022-06-09 21:10:39 +00:00
timings . frameId = 0 ;
2016-04-02 21:50:40 +00:00
DebugLog ( " Model 3 reset \n " ) ;
2011-04-24 01:14:00 +00:00
}
/******************************************************************************
Initialization, Shutdown, and ROM Management
******************************************************************************/
2017-03-27 03:19:15 +00:00
const Game & CModel3 : : GetGame ( ) const
2011-04-24 01:14:00 +00:00
{
2017-03-27 03:19:15 +00:00
return m_game ;
2011-04-24 01:14:00 +00:00
}
2021-02-18 10:29:15 +00:00
2011-04-24 01:14:00 +00:00
// Stepping-dependent parameters (MPC10x type, etc.) are initialized here
2017-03-27 03:19:15 +00:00
bool CModel3 : : LoadGame ( const Game & game , const ROMSet & rom_set )
2011-04-24 01:14:00 +00:00
{
2017-03-27 03:19:15 +00:00
m_game = Game ( ) ;
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
/*
* Copy in ROM data with mirroring as necessary for the following cases:
*
* - VROM: 64MB. If <= 32MB, mirror to high 32MB.
* - Banked CROM: 128MB. If <= 64MB, mirror to high 64MB.
* - Fixed CROM: 8MB. If < 8MB, loaded only in high part of space and low
* part is a mirror of (banked) CROM0.
* - Sample ROM: 16MB. If <= 8MB, mirror to high 8MB.
*/
if ( rom_set . get_rom ( " vrom " ) . size < = 32 * 0x100000 )
2016-04-02 21:50:40 +00:00
{
2017-03-27 03:19:15 +00:00
rom_set . get_rom ( " vrom " ) . CopyTo ( & vrom [ 0 ] , 32 * 100000 ) ;
rom_set . get_rom ( " vrom " ) . CopyTo ( & vrom [ 32 * 0x100000 ] , 32 * 0x100000 ) ;
2016-04-02 21:50:40 +00:00
}
else
2017-03-27 03:19:15 +00:00
rom_set . get_rom ( " vrom " ) . CopyTo ( vrom , 64 * 0x100000 ) ;
if ( rom_set . get_rom ( " banked_crom " ) . size < = 64 * 0x100000 )
{
rom_set . get_rom ( " banked_crom " ) . CopyTo ( & crom [ 8 * 0x100000 + 0 ] , 64 * 0x100000 ) ;
rom_set . get_rom ( " banked_crom " ) . CopyTo ( & crom [ 8 * 0x100000 + 64 * 0x100000 ] , 64 * 0x100000 ) ;
}
else
rom_set . get_rom ( " banked_crom " ) . CopyTo ( & crom [ 8 * 0x100000 + 0 ] , 128 * 0x100000 ) ;
size_t crom_size = rom_set . get_rom ( " crom " ) . size ;
rom_set . get_rom ( " crom " ) . CopyTo ( & crom [ 8 * 0x100000 - crom_size ] , crom_size ) ;
if ( crom_size < 8 * 0x100000 )
rom_set . get_rom ( " banked_crom " ) . CopyTo ( & crom [ 0 ] , 8 * 0x100000 - crom_size ) ;
if ( rom_set . get_rom ( " sound_samples " ) . size < = 8 * 0x100000 )
{
rom_set . get_rom ( " sound_samples " ) . CopyTo ( & sampleROM [ 0 ] , 8 * 0x100000 ) ;
rom_set . get_rom ( " sound_samples " ) . CopyTo ( & sampleROM [ 8 * 0x100000 ] , 8 * 0x100000 ) ;
}
else
rom_set . get_rom ( " sound_samples " ) . CopyTo ( sampleROM , 16 * 0x100000 ) ;
rom_set . get_rom ( " sound_program " ) . CopyTo ( soundROM , 512 * 1024 ) ;
2021-04-14 01:20:45 +00:00
rom_set . get_rom ( " mpeg_program " ) . CopyTo ( dsbROM , 128 * 1024 ) ;
rom_set . get_rom ( " mpeg_music " ) . CopyTo ( mpegROM , 16 * 0x100000 ) ;
rom_set . get_rom ( " driveboard_program " ) . CopyTo ( driveROM , 64 * 1024 ) ;
// Convert PowerPC and 68K ROMs to little endian words
Util : : FlipEndian32 ( crom , 8 * 0x100000 + 128 * 0x100000 ) ;
Util : : FlipEndian16 ( soundROM , 512 * 1024 ) ;
2017-03-27 03:19:15 +00:00
Util : : FlipEndian16 ( sampleROM , 16 * 0x100000 ) ;
2011-04-24 01:14:00 +00:00
2019-01-13 16:00:37 +00:00
// Configure CPU and PCI bridge
2017-03-27 03:19:15 +00:00
PPC_CONFIG ppc_config ;
if ( game . stepping = = " 2.0 " | | game . stepping = = " 2.1 " )
{
2019-01-13 16:00:37 +00:00
ppc_config . pvr = PPC_MODEL_603R ; // 166 MHz
2017-03-27 03:19:15 +00:00
ppc_config . bus_frequency = BUS_FREQUENCY_66MHZ ;
ppc_config . bus_frequency_multiplier = 0x25 ; // 2.5X multiplier
PCIBridge . SetModel ( 0x106 ) ; // MPC106
2021-02-18 10:29:15 +00:00
}
2017-03-27 03:19:15 +00:00
else if ( game . stepping = = " 1.5 " )
{
2021-04-14 01:20:45 +00:00
ppc_config . pvr = PPC_MODEL_603E ; // 100 MHz
ppc_config . bus_frequency = BUS_FREQUENCY_66MHZ ;
ppc_config . bus_frequency_multiplier = 0x15 ; // 1.5X multiplier
PCIBridge . SetModel ( 0x105 ) ; // MPC105
2017-03-27 03:19:15 +00:00
}
else if ( game . stepping = = " 1.0 " )
{
2021-04-14 01:20:45 +00:00
ppc_config . pvr = PPC_MODEL_603R ; // 66 MHz
ppc_config . bus_frequency = BUS_FREQUENCY_66MHZ ;
ppc_config . bus_frequency_multiplier = 0x10 ; // 1X multiplier
PCIBridge . SetModel ( 0x105 ) ; // MPC105
2017-03-27 03:19:15 +00:00
}
else
{
ErrorLog ( " Cannot configure Model 3 because game uses unrecognized stepping (%s). " , game . stepping . c_str ( ) ) ;
return FAIL ;
}
2021-02-18 10:29:15 +00:00
2019-01-13 16:00:37 +00:00
if ( ! game . pci_bridge . empty ( ) )
{
if ( game . pci_bridge = = " MPC105 " )
PCIBridge . SetModel ( 0x105 ) ;
else if ( game . pci_bridge = = " MPC106 " )
PCIBridge . SetModel ( 0x106 ) ;
else
ErrorLog ( " Unknown PCI bridge specified in ROM set definition file (%s) . Defaulting to MPC % X . " , game.pci_bridge.c_str(), PCIBridge.GetModel()) ;
}
2021-02-18 10:29:15 +00:00
2019-01-13 16:00:37 +00:00
// Initialize CPU
2017-03-27 03:19:15 +00:00
ppc_init ( & ppc_config ) ;
2016-04-02 21:50:40 +00:00
ppc_attach_bus ( this ) ;
2021-02-18 10:29:15 +00:00
PPCFetchRegions [ 0 ] . start = 0 ;
2016-04-02 21:50:40 +00:00
PPCFetchRegions [ 0 ] . end = 0x007FFFFF ;
PPCFetchRegions [ 0 ] . ptr = ( UINT32 * ) ram ;
2021-02-18 10:29:15 +00:00
PPCFetchRegions [ 1 ] . start = 0xFF800000 ;
2016-04-02 21:50:40 +00:00
PPCFetchRegions [ 1 ] . end = 0xFFFFFFFF ;
PPCFetchRegions [ 1 ] . ptr = ( UINT32 * ) crom ;
PPCFetchRegions [ 2 ] . start = 0 ;
PPCFetchRegions [ 2 ] . end = 0 ;
PPCFetchRegions [ 2 ] . ptr = NULL ;
ppc_set_fetch ( PPCFetchRegions ) ;
2021-04-14 01:20:45 +00:00
// Initialize Real3D
2019-01-13 16:00:37 +00:00
int stepping = ( ( game . stepping [ 0 ] - ' 0 ' ) < < 4 ) | ( game . stepping [ 2 ] - ' 0 ' ) ;
2023-08-19 23:47:00 +01:00
GPU . SetStepping ( stepping ) ;
2021-04-14 01:20:45 +00:00
// MPEG board (if present)
if ( rom_set . get_rom ( " mpeg_program " ) . size )
2016-04-02 21:50:40 +00:00
{
2017-03-27 03:19:15 +00:00
if ( game . mpeg_board = = " DSB1 " )
{
DSB = new ( std : : nothrow ) CDSB1 ( m_config ) ;
if ( NULL = = DSB )
2021-02-18 10:29:15 +00:00
return ErrorLog ( " Insufficient memory for Digital Sound Board object. " ) ;
2017-03-27 03:19:15 +00:00
}
else if ( game . mpeg_board = = " DSB2 " )
{
Util : : FlipEndian16 ( dsbROM , 128 * 1024 ) ; // 68K program needs to be byte swapped
DSB = new ( std : : nothrow ) CDSB2 ( m_config ) ;
if ( NULL = = DSB )
2021-02-18 10:29:15 +00:00
return ErrorLog ( " Insufficient memory for Digital Sound Board object. " ) ;
2017-03-27 03:19:15 +00:00
}
else if ( game . mpeg_board . empty ( ) )
ErrorLog ( " No MPEG board type defined in game XML for MPEG ROMs. " ) ;
else
ErrorLog ( " Unknown MPEG board type '%s'. Only 'DSB1' and 'DSB2' are supported. " , game . mpeg_board . c_str ( ) ) ;
if ( DSB & & OKAY ! = DSB - > Init ( dsbROM , mpegROM ) )
2016-04-02 21:50:40 +00:00
return FAIL ;
}
SoundBoard . AttachDSB ( DSB ) ;
2018-01-07 14:07:59 +00:00
2016-04-02 21:50:40 +00:00
// Drive board (if present)
2021-02-18 10:29:15 +00:00
if ( game . driveboard_type = = Game : : DRIVE_BOARD_WHEEL & & rom_set . get_rom ( " driveboard_program " ) . size )
2016-04-02 21:50:40 +00:00
{
2021-02-18 10:29:15 +00:00
DriveBoard = new CWheelBoard ( m_config ) ;
if ( DriveBoard - > Init ( driveROM ) )
2017-03-27 03:19:15 +00:00
return FAIL ;
2020-10-24 09:12:54 +00:00
}
2021-02-18 10:29:15 +00:00
else if ( game . driveboard_type = = Game : : DRIVE_BOARD_JOYSTICK & & rom_set . get_rom ( " driveboard_program " ) . size )
2020-10-24 09:12:54 +00:00
{
2021-02-18 10:29:15 +00:00
DriveBoard = new CJoyBoard ( m_config ) ;
if ( DriveBoard - > Init ( driveROM ) )
2020-10-24 09:12:54 +00:00
return FAIL ;
2016-04-02 21:50:40 +00:00
}
2021-02-18 10:29:15 +00:00
else if ( game . driveboard_type = = Game : : DRIVE_BOARD_BILLBOARD & & rom_set . get_rom ( " driveboard_program " ) . size )
2020-12-30 17:14:14 +00:00
{
2021-02-18 10:29:15 +00:00
DriveBoard = new CBillBoard ( m_config ) ;
if ( DriveBoard - > Init ( driveROM ) )
return FAIL ;
}
else if ( game . driveboard_type = = Game : : DRIVE_BOARD_SKI )
{
DriveBoard = new CSkiBoard ( m_config ) ;
if ( DriveBoard - > Init ( driveROM ) ) // no actual ROM data loaded (ski feedback is simulated)
return FAIL ;
2020-12-30 17:14:14 +00:00
}
2016-04-02 21:50:40 +00:00
else
2021-02-18 10:29:15 +00:00
{
// Dummy drive board (presents itself as not attached)
DriveBoard = new CDriveBoard ( m_config ) ;
if ( DriveBoard - > Init ( ) )
return FAIL ;
}
2016-04-02 21:50:40 +00:00
// Security board encryption device
2017-03-27 03:19:15 +00:00
m_cryptoDevice . Init ( game . encryption_key , std : : bind ( & CModel3 : : ReadSecurityRAM , this , std : : placeholders : : _1 ) ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Print game information
2017-03-27 03:19:15 +00:00
std : : set < std : : string > extra_hw ;
2020-07-01 15:56:21 +00:00
2017-03-27 03:19:15 +00:00
if ( DSB )
extra_hw . insert ( Util : : Format ( ) < < " Digital Sound Board (Type " < < game . mpeg_board < < " ) " ) ;
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
{
if ( DriveBoard - > GetType ( ) = = Game : : DRIVE_BOARD_BILLBOARD )
extra_hw . insert ( " Billboard " ) ;
else
extra_hw . insert ( " Drive Board " ) ;
}
2018-01-07 14:07:59 +00:00
if ( game . encryption_key )
extra_hw . insert ( " Security Board " ) ;
2021-04-14 01:20:45 +00:00
if ( game . netboard_present )
2020-10-24 09:12:54 +00:00
extra_hw . insert ( " Net Board " ) ;
2018-01-07 14:07:59 +00:00
if ( ! game . version . empty ( ) )
std : : cout < < " Title: " < < game . title < < " ( " < < game . version < < " ) " < < std : : endl ;
else
std : : cout < < " Title: " < < game . title < < std : : endl ;
std : : cout < < " ROM Set: " < < game . name < < std : : endl ;
std : : cout < < " Developer: " < < game . manufacturer < < std : : endl ;
std : : cout < < " Year: " < < game . year < < std : : endl ;
2017-03-27 03:19:15 +00:00
std : : cout < < " Stepping: " < < game . stepping < < std : : endl ;
if ( ! extra_hw . empty ( ) )
std : : cout < < " Extra Hardware: " < < Util : : Format ( " , " ) . Join ( extra_hw ) < < std : : endl ;
std : : cout < < std : : endl ;
2021-02-18 10:29:15 +00:00
2017-03-27 03:19:15 +00:00
m_game = game ;
2018-02-24 15:53:18 +00:00
# ifdef NET_BOARD
2021-04-14 01:20:45 +00:00
NetBoard - > GetGame ( m_game ) ;
if ( OKAY ! = NetBoard - > Init ( netRAM , netBuffer ) )
2020-07-01 15:56:21 +00:00
{
2021-02-18 10:29:15 +00:00
return FAIL ;
2020-07-01 15:56:21 +00:00
}
2021-04-14 01:20:45 +00:00
m_runNetBoard = m_game . stepping ! = " 1.0 " & & NetBoard - > IsAttached ( ) ;
2018-02-24 15:53:18 +00:00
# endif
2016-04-02 21:50:40 +00:00
return OKAY ;
2011-04-24 01:14:00 +00:00
}
2023-12-26 18:25:03 +00:00
void CModel3 : : AttachRenderers ( CRender2D * Render2DPtr , IRender3D * Render3DPtr , SuperAA * superAA )
2011-04-24 01:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
TileGen . AttachRenderer ( Render2DPtr ) ;
GPU . AttachRenderer ( Render3DPtr ) ;
2023-12-26 18:25:03 +00:00
m_superAA = superAA ;
2011-04-24 01:14:00 +00:00
}
void CModel3 : : AttachInputs ( CInputs * InputsPtr )
{
2016-04-02 21:50:40 +00:00
Inputs = InputsPtr ;
2011-04-24 01:14:00 +00:00
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
DriveBoard - > AttachInputs ( Inputs , m_game . inputs ) ;
2011-09-07 07:21:56 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " Model 3 attached inputs \n " ) ;
2011-04-24 01:14:00 +00:00
}
2012-07-15 21:04:46 +00:00
void CModel3 : : AttachOutputs ( COutputs * OutputsPtr )
{
2016-04-02 21:50:40 +00:00
Outputs = OutputsPtr ;
2017-03-27 03:19:15 +00:00
Outputs - > SetGame ( m_game ) ;
2016-04-02 21:50:40 +00:00
Outputs - > Attached ( ) ;
2012-07-15 21:04:46 +00:00
2021-02-18 10:29:15 +00:00
if ( DriveBoard - > IsAttached ( ) )
DriveBoard - > AttachOutputs ( Outputs ) ;
2012-07-15 21:04:46 +00:00
2016-04-02 21:50:40 +00:00
DebugLog ( " Model 3 attached outputs \n " ) ;
2012-07-15 21:04:46 +00:00
}
2021-04-14 01:20:45 +00:00
const static int RAM_SIZE = 0x800000 ; //8MB
const static int CROM_SIZE = 0x800000 ; //8MB
const static int CROMxx_SIZE = 0x8000000 ; //128MB
const static int VROM_SIZE = 0x4000000 ; //64MB
const static int BACKUPRAM_SIZE = 0x20000 ; //128KB
const static int SECURITYRAM_SIZE = 0x20000 ; //128KB
const static int SOUNDROM_SIZE = 0x80000 ; //512KB
const static int SAMPLEROM_SIZE = 0x1000000 ; //16MB
const static int DSBPROGROM_SIZE = 0x20000 ; //128KB
const static int DSBMPEGROM_SIZE = 0x1000000 ; //16MB
const static int DRIVEROM_SIZE = 0x10000 ; //64KB
const static int NETBUFFER_SIZE = 0x20000 ; //128KB
2021-10-30 23:00:49 +00:00
const static int NETRAM_SIZE = 0x10000 ; //64KB
2021-04-14 01:20:45 +00:00
const static int MEM_POOL_SIZE = RAM_SIZE + CROM_SIZE +
CROMxx_SIZE + VROM_SIZE +
BACKUPRAM_SIZE + SECURITYRAM_SIZE +
SOUNDROM_SIZE + SAMPLEROM_SIZE +
DSBPROGROM_SIZE + DSBMPEGROM_SIZE +
DRIVEROM_SIZE + NETBUFFER_SIZE +
NETRAM_SIZE ;
const static int RAM_OFFSET = 0 ;
const static int CROM_OFFSET = RAM_OFFSET + RAM_SIZE ;
const static int CROMxx_OFFSET = CROM_OFFSET + CROM_SIZE ;
const static int VROM_OFFSET = CROMxx_OFFSET + CROMxx_SIZE ;
const static int BACKUPRAM_OFFSET = VROM_OFFSET + VROM_SIZE ;
const static int SECURITYRAM_OFFSET = BACKUPRAM_OFFSET + BACKUPRAM_SIZE ;
const static int SOUNDROM_OFFSET = SECURITYRAM_OFFSET + SECURITYRAM_SIZE ;
const static int SAMPLEROM_OFFSET = SOUNDROM_OFFSET + SOUNDROM_SIZE ;
const static int DSBPROGROM_OFFSET = SAMPLEROM_OFFSET + SAMPLEROM_SIZE ;
const static int DSBMPEGROM_OFFSET = DSBPROGROM_OFFSET + DSBPROGROM_SIZE ;
const static int DRIVEROM_OFFSET = DSBMPEGROM_OFFSET + DSBMPEGROM_SIZE ;
const static int NETBUFFER_OFFSET = DRIVEROM_OFFSET + DRIVEROM_SIZE ;
2020-06-17 11:41:41 +00:00
const static int NETRAM_OFFSET = NETBUFFER_OFFSET + NETBUFFER_SIZE ;
2011-07-31 02:37:31 +00:00
// Model 3 initialization. Some initialization is deferred until ROMs are loaded in LoadROMSet()
2011-09-08 06:34:18 +00:00
bool CModel3 : : Init ( void )
2021-02-18 10:29:15 +00:00
{
2020-06-17 11:41:41 +00:00
float memSizeMB = ( float ) MEM_POOL_SIZE / ( float ) 0x100000 ;
2016-04-02 21:50:40 +00:00
// Allocate all memory for ROMs and PPC RAM
2020-06-17 11:41:41 +00:00
memoryPool = new ( std : : nothrow ) UINT8 [ MEM_POOL_SIZE ] ;
2016-04-02 21:50:40 +00:00
if ( NULL = = memoryPool )
return ErrorLog ( " Insufficient memory for Model 3 object (needs %1.1f MB). " , memSizeMB ) ;
2020-06-17 11:41:41 +00:00
memset ( memoryPool , 0 , MEM_POOL_SIZE ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Set up pointers
2020-06-17 11:41:41 +00:00
ram = & memoryPool [ RAM_OFFSET ] ;
crom = & memoryPool [ CROM_OFFSET ] ;
vrom = & memoryPool [ VROM_OFFSET ] ;
soundROM = & memoryPool [ SOUNDROM_OFFSET ] ;
sampleROM = & memoryPool [ SAMPLEROM_OFFSET ] ;
dsbROM = & memoryPool [ DSBPROGROM_OFFSET ] ;
mpegROM = & memoryPool [ DSBMPEGROM_OFFSET ] ;
backupRAM = & memoryPool [ BACKUPRAM_OFFSET ] ;
securityRAM = & memoryPool [ SECURITYRAM_OFFSET ] ;
driveROM = & memoryPool [ DRIVEROM_OFFSET ] ;
netRAM = & memoryPool [ NETRAM_OFFSET ] ;
netBuffer = & memoryPool [ NETBUFFER_OFFSET ] ;
2016-04-02 21:50:40 +00:00
SetCROMBank ( 0xFF ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Initialize other devices (PowerPC, DSB, and security board initialized after ROMs loaded)
IRQ . Init ( ) ;
PCIBridge . Init ( ) ;
PCIBus . Init ( ) ;
SCSI . Init ( this , & IRQ , 0x100 ) ; // SCSI is actually a non-maskable interrupt, so we give it a bit number outside of 8-bit range
RTC . Init ( ) ;
EEPROM . Init ( ) ;
if ( OKAY ! = TileGen . Init ( & IRQ ) )
return FAIL ;
if ( OKAY ! = GPU . Init ( vrom , this , & IRQ , 0x100 ) ) // same for Real3D DMA interrupt
return FAIL ;
if ( OKAY ! = SoundBoard . Init ( soundROM , sampleROM ) )
2021-04-14 01:20:45 +00:00
return FAIL ;
PCIBridge . AttachPCIBus ( & PCIBus ) ;
PCIBus . AttachDevice ( 13 , & GPU ) ;
PCIBus . AttachDevice ( 14 , & SCSI ) ;
2016-04-02 21:50:40 +00:00
PCIBus . AttachDevice ( 16 , this ) ;
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
if ( m_config [ " SimulateNet " ] . ValueAs < bool > ( ) )
NetBoard = new CSimNetBoard ( m_config ) ;
else
NetBoard = new CNetBoard ( m_config ) ;
# endif // NET_BOARD
2016-04-02 21:50:40 +00:00
DebugLog ( " Initialized Model 3 (allocated %1.1f MB) \n " , memSizeMB ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
return OKAY ;
2011-04-24 01:14:00 +00:00
}
2021-04-14 01:20:45 +00:00
CSoundBoard * CModel3 : : GetSoundBoard ( void )
{
return & SoundBoard ;
}
CDriveBoard * CModel3 : : GetDriveBoard ( void )
2011-09-15 21:10:38 +00:00
{
2021-02-18 10:29:15 +00:00
return DriveBoard ;
2011-09-15 21:10:38 +00:00
}
2021-04-14 01:20:45 +00:00
# ifdef NET_BOARD
INetBoard * CModel3 : : GetNetBoard ( void )
{
return NetBoard ;
}
# endif
2022-06-09 21:10:39 +00:00
CModel3 : : CModel3 ( Util : : Config : : Node & config )
2017-03-27 03:19:15 +00:00
: m_config ( config ) ,
m_multiThreaded ( config [ " MultiThreaded " ] . ValueAs < bool > ( ) ) ,
m_gpuMultiThreaded ( config [ " GPUMultiThreaded " ] . ValueAs < bool > ( ) ) ,
2023-12-02 20:05:46 +01:00
sndBrdWakeNotify ( false ) ,
2017-03-27 03:19:15 +00:00
TileGen ( config ) ,
GPU ( config ) ,
2021-04-14 01:20:45 +00:00
SoundBoard ( config ) ,
2023-12-26 18:25:03 +00:00
m_jtag ( GPU ) ,
m_superAA ( nullptr )
2021-04-14 01:20:45 +00:00
{
// Initialize pointers so dtor can know whether to free them
memoryPool = NULL ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Various uninitialized pointers
Inputs = NULL ;
Outputs = NULL ;
ram = NULL ;
crom = NULL ;
vrom = NULL ;
soundROM = NULL ;
sampleROM = NULL ;
dsbROM = NULL ;
mpegROM = NULL ;
cromBank = NULL ;
backupRAM = NULL ;
securityRAM = NULL ;
2018-01-07 14:07:59 +00:00
netRAM = NULL ;
2021-04-14 01:20:45 +00:00
netBuffer = NULL ;
DSB = NULL ;
DriveBoard = NULL ;
2022-12-23 11:29:30 +01:00
# ifdef NET_BOARD
NetBoard = NULL ;
# endif
2021-04-14 01:20:45 +00:00
securityPtr = 0 ;
startedThreads = false ;
pauseThreads = false ;
stopThreads = false ;
ppcBrdThread = NULL ;
sndBrdThread = NULL ;
drvBrdThread = NULL ;
ppcBrdThreadRunning = false ;
ppcBrdThreadDone = false ;
sndBrdThreadRunning = false ;
sndBrdThreadDone = false ;
drvBrdThreadRunning = false ;
drvBrdThreadDone = false ;
syncSndBrdThread = false ;
ppcBrdThreadSync = NULL ;
sndBrdThreadSync = NULL ;
drvBrdThreadSync = NULL ;
notifyLock = NULL ;
notifySync = NULL ;
DebugLog ( " Built Model 3 \n " ) ;
}
2016-04-02 21:50:40 +00:00
// Dumps a memory region to a file for debugging purposes
2020-04-19 08:34:58 +00:00
#if 0
2016-04-02 21:50:40 +00:00
static void Dump(const char *file, uint8_t *buf, size_t size, bool reverse32, bool reverse16)
{
FILE *fp = fopen(file, "wb");
if (NULL != fp)
{
if (reverse32)
2017-08-19 19:27:58 +00:00
Util::FlipEndian32(buf, size);
2016-04-02 21:50:40 +00:00
else if (reverse16)
2017-08-19 19:27:58 +00:00
Util::FlipEndian16(buf, size);
2016-04-02 21:50:40 +00:00
fwrite(buf, sizeof(UINT8), size, fp);
fclose(fp);
printf("dumped %s\n", file);
}
else
printf("unable to dump %s\n", file);
}
2020-04-19 08:34:58 +00:00
#endif
2016-04-02 21:50:40 +00:00
2011-04-24 01:14:00 +00:00
CModel3 : : ~ CModel3 ( void )
{
2016-04-02 21:50:40 +00:00
// Debug: dump some files
//Dump("ram", ram, 0x800000, true, false);
//Dump("vrom", vrom, 0x4000000, true, false);
2017-08-19 19:27:58 +00:00
//Dump("crom", crom, 0x800000, true, false);
2016-04-02 21:50:40 +00:00
//Dump("bankedCrom", &crom[0x800000], 0x7000000, true, false);
//Dump("soundROM", soundROM, 0x80000, false, true);
2021-02-18 10:29:15 +00:00
//Dump("sampleROM", sampleROM, 0x800000, false, true);
2016-04-02 21:50:40 +00:00
// Stop all threads
StopThreads ( ) ;
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
// Free memory
if ( memoryPool ! = NULL )
{
delete [ ] memoryPool ;
memoryPool = NULL ;
}
2021-02-18 10:29:15 +00:00
2016-04-02 21:50:40 +00:00
if ( DSB ! = NULL )
{
delete DSB ;
2021-04-14 01:20:45 +00:00
DSB = NULL ;
}
2021-02-18 10:29:15 +00:00
if ( DriveBoard ! = NULL )
{
delete DriveBoard ;
2021-04-14 01:20:45 +00:00
DriveBoard = NULL ;
}
2022-12-23 11:29:30 +01:00
# ifdef NET_BOARD
if ( NetBoard ! = NULL )
{
delete NetBoard ;
NetBoard = NULL ;
}
# endif
2021-04-14 01:20:45 +00:00
Inputs = NULL ;
Outputs = NULL ;
ram = NULL ;
2016-04-02 21:50:40 +00:00
crom = NULL ;
vrom = NULL ;
soundROM = NULL ;
sampleROM = NULL ;
dsbROM = NULL ;
mpegROM = NULL ;
cromBank = NULL ;
backupRAM = NULL ;
securityRAM = NULL ;
2018-01-07 14:07:59 +00:00
netRAM = NULL ;
2021-04-14 01:20:45 +00:00
netBuffer = NULL ;
DebugLog ( " Destroyed Model 3 \n " ) ;
}