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.3 a 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 - 007FF FFF RAM
* 84000000 - 8400003F Real3D Status Registers
* 88000000 - 88000007 Real3D Command Port
* 8 C000000 - 8 C3FFFFF Real3D Culling RAM ( Low )
* 8E000000 - 8E0 FFFFF Real3D Culling RAM ( High )
* 90000000 - 9000000 B Real3D VROM Texture Port
* 94000000 - 940FF FFF Real3D Texture FIFO
* 98000000 - 980FF FFF Real3D Polygon RAM
* 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 53 C810 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
* + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - +
* | ? | SND | ? | NET | VD3 | VD2 | VBL | VD0 |
* + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - +
2016-04-02 21:50:40 +00:00
* SND SCSP ( sound )
* NET Network
* VD3 Unknown video - related
* VD2 Unknown video - related
* VBL VBlank start
* VD0 Unknown video - related ( ? )
* 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 ;
case 0x04 : // current input bank
data = 0xFF ;
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
}
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 ) ;
}
}
return data ;
case 0x08 : // game-specific inputs
data = 0xFF ;
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 ) ;
}
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
}
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
}
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 ;
}
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 : 1 D 2U
* Turn Right : 1U 2 D
* Forward : 1U 2U
* Reverse : 1 D 2 D
*/
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 : 1 R 2 R
* Jump : 1L 2 R
* Crouch : 1 R 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 ;
}
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 ) ;
}
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 ;
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
data = 0xFF ;
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
}
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 ) ;
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 ;
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
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 ;
}
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 ;
}
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 ;
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 ;
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 ;
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 15:43:59 +00: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 )
{
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 ;
}
DebugLog ( " Model 3: PCI %d-bit write request for device=%d, reg=%02X \n " , bits , device , reg ) ;
return 0 ;
}
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 ) ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
# ifdef NET_BOARD
printf ( " CMODEL3 : unknown R8 : %x \n " , addr > > 24 ) ;
# endif
break ;
}
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 ;
}
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 ] ;
// 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 ;
// MPC105
case 0xC0 : // F0C00CF8
return PCIBridge . ReadPCIConfigData ( 16 , addr & 3 ) ;
// 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 ;
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 16:10:41 +00: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 ;
}
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 ;
if ( ( addr & 3 ) )
{
data = Read16 ( addr + 0 ) < < 16 ;
data | = Read16 ( addr + 2 ) ;
return data ;
}
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
return * ( UINT32 * ) & ram [ addr ] ;
// 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 ) ] ;
// 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 ) ;
// 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 ;
// Backup RAM
case 0x0C :
case 0x0D :
return * ( UINT32 * ) & backupRAM [ ( addr & 0x1FFFF ) ] ;
// 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 ;
// MPC105
case 0xC0 : // F0C00CF8
return PCIBridge . ReadPCIConfigData ( 32 , 0 ) ;
// 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 ) ;
// RTC
case 0x14 :
data = ( RTC . ReadRegister ( ( addr > > 2 ) & 0xF ) < < 24 ) ;
data | = 0x00030000 ; // set these bits to pass battery voltage test
return data ;
// 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
// 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 ;
// 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 ) ;
}
2016-04-02 21:50:40 +00:00
break ;
// 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 ;
2021-04-14 01:20:45 +00:00
// Unknown
default :
# ifdef NET_BOARD
printf ( " CMODEL3 : unknown R32 : %x \n " , addr > > 24 ) ;
# endif
break ;
}
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 ;
}
// Other
switch ( ( addr > > 24 ) )
{
// Real3D DMA
case 0xC2 :
GPU . WriteDMARegister8 ( addr & 0xFF , data ) ;
break ;
// 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 ;
// System registers
case 0x10 :
WriteSystemRegister ( addr & 0x3F , data ) ;
break ;
// 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 ;
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 ;
// 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 ;
// 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 ;
}
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
{
* ( UINT16 * ) & ram [ addr ^ 2 ] = data ;
return ;
}
// 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 ;
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 ;
}
// RAM (most frequently accessed)
if ( addr < 0x00800000 )
{
* ( UINT32 * ) & ram [ addr ] = data ;
return ;
}
// 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 ;
// Real3D high culling RAM
case 0x8E : // 8E000000-8E100000
GPU . WriteHighCullingRAM ( addr & 0xFFFFF , FLIPENDIAN32 ( data ) ) ;
break ;
// 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 ;
// Real3D texture FIFO
case 0x94 : // 94000000-94100000
GPU . WriteTextureFIFO ( FLIPENDIAN32 ( data ) ) ;
break ;
// Real3D polygon RAM
case 0x98 : // 98000000-98400000
GPU . WritePolygonRAM ( addr & 0x3FFFFF , FLIPENDIAN32 ( data ) ) ;
break ;
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 ;
// 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 ;
// 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 ;
// 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 ;
// 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 ;
// Security board RAM
case 0x18 :
case 0x19 :
* ( UINT32 * ) & securityRAM [ ( addr & 0x1FFFF ) ] = data ;
break ;
// 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 ;
// 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 ;
}
goto Unknown32 ;
// 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 ;
// 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 ;
// 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 15:43:59 +00: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 ( ) ;
// 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 ;
// 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 ;
// 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 ( ) ;
}
// Render frame
RenderFrame ( ) ;
// Enter notify wait critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// 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 ;
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
}
2017-09-24 20:52:48 +00:00
void CModel3 : : RunMainBoardFrame ( void )
{
2019-11-07 20:29:17 +00:00
UINT32 start = CThread : : GetTicks ( ) ;
// Compute display and VBlank timings
unsigned ppcCycles = m_config [ " PowerPCFrequency " ] . ValueAs < unsigned > ( ) * 1000000 ;
2022-01-23 15:32:53 +00:00
unsigned frameCycles = ( unsigned ) ( ( float ) ppcCycles / 57.524160f ) ;
2019-11-07 20:29:17 +00:00
unsigned gapCycles = ( unsigned ) ( ( float ) frameCycles * 2.5f / 100.0f ) ; // we need a gap between asserting irq2 & irq 0x40
unsigned offsetCycles = ( unsigned ) ( ( float ) frameCycles * 33.f / 100.0f ) ;
unsigned dispCycles = frameCycles - gapCycles - offsetCycles ;
2019-11-08 20:36:11 +00:00
unsigned statusCycles = ( unsigned ) ( ( float ) frameCycles * ( 0.005f ) ) ;
2019-11-07 20:29:17 +00:00
// we think a frame looks like this on the model 2
// 66% of frame
// [irq2------------------ping_pong_flips------]
//
// Games will start writing a new frame at the ping_pong time. It could be the buffer swaps here.
// Need more h/w testing to confirm.
// What we are doing here is asserting IRQ2 at 33% of the frame, and treating the ping_pong flip as the front/back buffer swap
// This way the data for the correct frames, ends up in the right frames!
// 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
ppc_execute ( gapCycles ) ; // need a gap between asserting irqs
/*
* 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 ( ) ;
}
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 ;
// Reset thread flags
pauseThreads = false ;
stopThreads = false ;
// 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 ;
}
// 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 ;
// 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 ;
}
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 ;
// 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 ;
}
// 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 ;
// 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 ;
// 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 ;
}
// Now let threads know that they should exit
stopThreads = true ;
// 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 ( ) ;
}
// 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 ;
}
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 ? ' ! ' : ' ' ) ) ;
}
Some updates to Supermodel made at beginning of the year but only now got around to checking in (better late than never...):
- hooked up the remaining controls in Supermodel (except for Magical Truck Adventure which does not work at all yet). The new controls are:
* InputAnalogJoyTrigger2 and InputAnalogJoyEvent2 for the additional second trigger and event buttons that were missing from Star Wars Trilogy,
* InputRearBrake and InputMusicSelect for the rear brake and music selection buttons that were missing from Harley Davidson,
* InputAnalogGunXXX, InputAnalogTriggerXXX, InputAnalogGunXXX2 and InputAnalogTriggerXXX2 for the analogue guns of Ocean Hunter and LA Machineguns (NOTE: these controls must be calibrated in the games' service menus otherwise they will not work properly. Also, the alignment of the gun cursor does not line up very well with the mouse position at the moment, but at least the games are a bit more playable now, although still with numerous graphical glitches...)
* InputSkiXXX for the controls of Ski Champ, making the game playable now.
- hooked up existing InputViewChange control to Harley Davidson's view change button
- improved the handling of InputGearShiftUp/Down inputs so that they work better with the driving games. With Dirt Devils, ECA, Harley and LeMans this means they map directly to the game's own shift up/down controls, while with the 4-speed games such as Daytona 2, Scud Racer and Sega Rally 2, they simulate the user shifting up and down through the gears
- added defaults for the new controls to Supermodel.ini
- other small code tweaks:
* fix small bug with handling of pos/neg inputs mapping to a control with inverted range (0XFF to 0x00) - this was needed to get Ski Champ's X-axis to work properly
* removed Wait method from InputSystem and added to CThread as CThread::Sleep instead
* added FrameTimings struct to hold all frame timings in a single place
No networking code yet as just haven't had a chance to work on it since initial progress at the beginning of the year - am *hoping* might have some time to pick it up again over Christmas...
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 )
{
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-09-12 05:43:37 +00:00
}
2012-01-16 23:21:14 +00:00
int CModel3 : : StartDriveBoardThread ( void * data )
2011-07-20 21:14:00 +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 ;
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// 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 ;
// Process a single frame for PPC main board
RunMainBoardFrame ( ) ;
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// Let other threads know processing has finished
ppcBrdThreadRunning = false ;
ppcBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
// 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 ;
// Enter main notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// 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 ;
}
// 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 ;
// 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 ;
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
sndBrdThreadRunning = true ;
}
// Leave main notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
// 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 ;
// Keep processing frames until pausing or audio buffer is full
while ( true )
{
// Enter main notify critical section
bool paused ;
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
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 ;
if ( paused | | RunSoundBoardFrame ( ) )
break ;
//printf("Rerunning sound board\n");
}
// Enter main notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// Let other threads know processing has finished
sndBrdThreadRunning = false ;
sndBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
// 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 ;
// Check threads are not being stopped or paused
if ( stopThreads )
exit = true ;
else if ( ! pauseThreads )
{
wait = false ;
sndBrdThreadRunning = true ;
}
// Leave notify critical section
if ( ! notifyLock - > Unlock ( ) )
goto ThreadError ;
}
if ( exit )
return 0 ;
// Process a single frame for sound board
RunSoundBoardFrame ( ) ;
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// Let other threads know processing has finished
sndBrdThreadRunning = false ;
sndBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
// 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 ;
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// 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 ;
// Process a single frame for drive board
RunDriveBoardFrame ( ) ;
// Enter notify critical section
if ( ! notifyLock - > Lock ( ) )
goto ThreadError ;
// Let other threads know processing has finished
drvBrdThreadRunning = false ;
drvBrdThreadDone = true ;
if ( ! notifySync - > SignalAll ( ) )
goto ThreadError ;
// 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 ( ) ;
2016-04-02 21:50:40 +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 ( ) ;
gpusReady = false ;
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 : 64 MB . If < = 32 MB , mirror to high 32 MB .
* - Banked CROM : 128 MB . If < = 64 MB , mirror to high 64 MB .
* - Fixed CROM : 8 MB . If < 8 MB , loaded only in high part of space and low
* part is a mirror of ( banked ) CROM0 .
* - Sample ROM : 16 MB . If < = 8 MB , mirror to high 8 MB .
*/
if ( rom_set . get_rom ( " vrom " ) . size < = 32 * 0x100000 )
{
rom_set . get_rom ( " vrom " ) . CopyTo ( & vrom [ 0 ] , 32 * 100000 ) ;
rom_set . get_rom ( " vrom " ) . CopyTo ( & vrom [ 32 * 0x100000 ] , 32 * 0x100000 ) ;
}
else
rom_set . get_rom ( " vrom " ) . CopyTo ( vrom , 64 * 0x100000 ) ;
if ( rom_set . get_rom ( " banked_crom " ) . size < = 64 * 0x100000 )
2016-04-02 21:50:40 +00:00
{
2017-03-27 03:19:15 +00:00
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 ) ;
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 " )
2016-04-02 21:50:40 +00:00
{
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
2016-04-02 21:50:40 +00:00
}
2017-03-27 03:19:15 +00:00
else if ( game . stepping = = " 1.0 " )
2016-04-02 21:50:40 +00:00
{
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
2016-04-02 21:50:40 +00:00
}
else
2017-03-27 03:19:15 +00:00
{
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 ' ) ;
uint32_t real3DPCIID = game . real3d_pci_id ;
if ( 0 = = real3DPCIID )
{
real3DPCIID = stepping > = 0x20 ? CReal3D : : PCIID : : Step2x : CReal3D : : PCIID : : Step1x ;
2021-04-14 01:20:45 +00:00
}
GPU . SetStepping ( stepping , real3DPCIID ) ;
// 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
}
2016-03-21 23:25:29 +00:00
void CModel3 : : AttachRenderers ( CRender2D * Render2DPtr , IRender3D * Render3DPtr )
2011-04-24 01:14:00 +00:00
{
2016-04-02 21:50:40 +00:00
TileGen . AttachRenderer ( Render2DPtr ) ;
GPU . AttachRenderer ( Render3DPtr ) ;
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 > ( ) ) ,
TileGen ( config ) ,
GPU ( config ) ,
2021-04-14 01:20:45 +00:00
SoundBoard ( config ) ,
m_jtag ( GPU )
{
// 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 ;
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 ;
}
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 " ) ;
}