From 6e5c301de8ac5dfcc8039a2d97bd70c77fc074c9 Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Sat, 2 Apr 2016 21:50:40 +0000 Subject: [PATCH] Encryption device emulation (thanks to MAME), fixed warnings in Model3.cpp, added a string formatter helper, and updated Win32 GCC Makefile. --- Makefiles/Makefile.SDL.Win32.GCC | 13 +- Src/Model3/Crypto.cpp | 1031 ++++++ Src/Model3/Crypto.h | 123 + Src/Model3/Model3.cpp | 5300 +++++++++++++++--------------- Src/Model3/Model3.h | 802 ++--- Src/Util/Format.cpp | 33 + Src/Util/Format.h | 68 + 7 files changed, 4254 insertions(+), 3116 deletions(-) create mode 100644 Src/Model3/Crypto.cpp create mode 100644 Src/Model3/Crypto.h create mode 100644 Src/Util/Format.cpp create mode 100644 Src/Util/Format.h diff --git a/Makefiles/Makefile.SDL.Win32.GCC b/Makefiles/Makefile.SDL.Win32.GCC index 0fab556..22f382d 100644 --- a/Makefiles/Makefile.SDL.Win32.GCC +++ b/Makefiles/Makefile.SDL.Win32.GCC @@ -57,7 +57,8 @@ BOOST_INCLUDEPATH = /mingw64/boost_1_55_0 # ifeq ($(strip $(BITS)),64) SDL_LIBPATH = /mingw64/lib64 - SDL_INCLUDEPATH = /mingw64/x86_64-w64-mingw32/include/SDL + #SDL_INCLUDEPATH = /mingw64/x86_64-w64-mingw32/include/SDL + SDL_INCLUDEPATH = /mingw64/SDL-1.2.15/include else SDL_LIBPATH = /mingw/lib SDL_INCLUDEPATH = /mingw/include/SDL @@ -68,6 +69,7 @@ endif # ifeq ($(strip $(BITS)),64) WINSDK_LIBPATH = /Program\ Files/Microsoft\ SDKs/Windows/v7.0/Lib/x64 + #WINSDK_LIBPATH = /Program\ Files\ (x86)/Windows\ Kits/10/Lib/10.0.10586.0/um/x64 else WINSDK_LIBPATH = /Program\ Files/Microsoft\ SDKs/Windows/v7.0/Lib endif @@ -98,7 +100,7 @@ LD = g++ COMPILER_FLAGS = -I$(SDL_INCLUDEPATH) -ISrc/ -ISrc/OSD/ -ISrc/OSD/SDL/ -ISrc/OSD/Windows/ -c -Wall -DSUPERMODEL_WIN32 -DGLEW_STATIC -O3 CFLAGS = $(COMPILER_FLAGS) CPPFLAGS = $(COMPILER_FLAGS) -I$(BOOST_INCLUDEPATH) -std=c++11 -LFLAGS = -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lz -l:$(WINSDK_LIBPATH)/WbemUuid.lib -s +LFLAGS = -o $(OUTFILE) $(OBJ) -L$(SDL_LIBPATH) -lmingw32 -lSDLmain -lSDL -lopengl32 -lglu32 -ldinput8 -ldxguid -lole32 -loleaut32 -lwbemuuid -lz -s #-l:$(WINSDK_LIBPATH)/WbemUuid.lib -s # # Build options... @@ -131,7 +133,9 @@ OBJ = $(OBJ_DIR)/PPCDisasm.o $(OBJ_DIR)/Games.o $(OBJ_DIR)/Config.o $(OBJ_DIR)/I $(OBJ_DIR)/Outputs.o $(OBJ_DIR)/WinOutputs.o \ $(OBJ_DIR)/amp_audio.o $(OBJ_DIR)/amp_dump.o $(OBJ_DIR)/amp_getbits.o $(OBJ_DIR)/amp_getdata.o $(OBJ_DIR)/amp_huffman.o \ $(OBJ_DIR)/amp_layer2.o $(OBJ_DIR)/amp_layer3.o $(OBJ_DIR)/amp_misc2.o $(OBJ_DIR)/amp_position.o $(OBJ_DIR)/amp_transform.o \ - $(OBJ_DIR)/amp_util.o + $(OBJ_DIR)/amp_util.o \ + $(OBJ_DIR)/Crypto.o \ + $(OBJ_DIR)/Format.o # If built-in debugger enabled, include all debugging classes @@ -249,6 +253,9 @@ $(OBJ_DIR)/%.o: Src/OSD/Windows/%.cpp $(OBJ_DIR)/%.o: Src/Pkgs/%.c $(CC) $< $(CFLAGS) -o $(OBJ_DIR)/$(*F).o + +$(OBJ_DIR)/%.o: Src/Util/%.cpp Src/Util/%.h + $(CC) $< $(CPPFLAGS) -o $(OBJ_DIR)/$(*F).o # # AMP MPEG decoder library diff --git a/Src/Model3/Crypto.cpp b/Src/Model3/Crypto.cpp new file mode 100644 index 0000000..506da85 --- /dev/null +++ b/Src/Model3/Crypto.cpp @@ -0,0 +1,1031 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011-2016 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel is free software: you can redistribute it and/or modify it under + ** the terms of the GNU General Public License as published by the Free + ** Software Foundation, either version 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel is distributed in the hope that it will be useful, but WITHOUT + ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + ** more details. + ** + ** You should have received a copy of the GNU General Public License along + ** with Supermodel. If not, see . + **/ + +/* + * Crypto.cpp + * + * Security board encryption device emulation. This code was taken from MAME + * (http://mamedev.org). + */ + +// license:BSD-3-Clause +// copyright-holders:Andreas Naive, Olivier Galibert, David Haywood +/* + re: Tecmo World Cup '98 (ST-V) (from ANY) + + I got one of the card in subject open it up to check the rom version + and made a discovery... + The protection chip has the part number on it "315-5881", it's the same + used on naomi M2 carts as you can see here + http://imagizer.imageshack.us/a/img540/7634/BsqvD8.jpg + + The same chip 315-5881 but with a Lattice IspLSI2032 (Sega part + 315-6050) was used on some Model3 games... + +*/ + +#include "Model3/Crypto.h" +#include "BlockFile.h" +#include "OSD/Logger.h" +#include +#include +#include +//#include + +typedef uint8_t UINT8; +typedef uint16_t UINT16; +typedef uint32_t UINT32; + +// useful macros to deal with bit shuffling encryptions +#define BIT(x,n) (((x)>>(n))&1) + +#define BITSWAP8(val,B7,B6,B5,B4,B3,B2,B1,B0) \ + ((BIT(val,B7) << 7) | (BIT(val,B6) << 6) | (BIT(val,B5) << 5) | (BIT(val,B4) << 4) | \ + (BIT(val,B3) << 3) | (BIT(val,B2) << 2) | (BIT(val,B1) << 1) | (BIT(val,B0) << 0)) + +#define BITSWAP16(val,B15,B14,B13,B12,B11,B10,B9,B8,B7,B6,B5,B4,B3,B2,B1,B0) \ + ((BIT(val,B15) << 15) | (BIT(val,B14) << 14) | (BIT(val,B13) << 13) | (BIT(val,B12) << 12) | \ + (BIT(val,B11) << 11) | (BIT(val,B10) << 10) | (BIT(val, B9) << 9) | (BIT(val, B8) << 8) | \ + (BIT(val, B7) << 7) | (BIT(val, B6) << 6) | (BIT(val, B5) << 5) | (BIT(val, B4) << 4) | \ + (BIT(val, B3) << 3) | (BIT(val, B2) << 2) | (BIT(val, B1) << 1) | (BIT(val, B0) << 0)) + +#define BITSWAP24(val,B23,B22,B21,B20,B19,B18,B17,B16,B15,B14,B13,B12,B11,B10,B9,B8,B7,B6,B5,B4,B3,B2,B1,B0) \ + ((BIT(val,B23) << 23) | (BIT(val,B22) << 22) | (BIT(val,B21) << 21) | (BIT(val,B20) << 20) | \ + (BIT(val,B19) << 19) | (BIT(val,B18) << 18) | (BIT(val,B17) << 17) | (BIT(val,B16) << 16) | \ + (BIT(val,B15) << 15) | (BIT(val,B14) << 14) | (BIT(val,B13) << 13) | (BIT(val,B12) << 12) | \ + (BIT(val,B11) << 11) | (BIT(val,B10) << 10) | (BIT(val, B9) << 9) | (BIT(val, B8) << 8) | \ + (BIT(val, B7) << 7) | (BIT(val, B6) << 6) | (BIT(val, B5) << 5) | (BIT(val, B4) << 4) | \ + (BIT(val, B3) << 3) | (BIT(val, B2) << 2) | (BIT(val, B1) << 1) | (BIT(val, B0) << 0)) + +#define BITSWAP32(val,B31,B30,B29,B28,B27,B26,B25,B24,B23,B22,B21,B20,B19,B18,B17,B16,B15,B14,B13,B12,B11,B10,B9,B8,B7,B6,B5,B4,B3,B2,B1,B0) \ + ((BIT(val,B31) << 31) | (BIT(val,B30) << 30) | (BIT(val,B29) << 29) | (BIT(val,B28) << 28) | \ + (BIT(val,B27) << 27) | (BIT(val,B26) << 26) | (BIT(val,B25) << 25) | (BIT(val,B24) << 24) | \ + (BIT(val,B23) << 23) | (BIT(val,B22) << 22) | (BIT(val,B21) << 21) | (BIT(val,B20) << 20) | \ + (BIT(val,B19) << 19) | (BIT(val,B18) << 18) | (BIT(val,B17) << 17) | (BIT(val,B16) << 16) | \ + (BIT(val,B15) << 15) | (BIT(val,B14) << 14) | (BIT(val,B13) << 13) | (BIT(val,B12) << 12) | \ + (BIT(val,B11) << 11) | (BIT(val,B10) << 10) | (BIT(val, B9) << 9) | (BIT(val, B8) << 8) | \ + (BIT(val, B7) << 7) | (BIT(val, B6) << 6) | (BIT(val, B5) << 5) | (BIT(val, B4) << 4) | \ + (BIT(val, B3) << 3) | (BIT(val, B2) << 2) | (BIT(val, B1) << 1) | (BIT(val, B0) << 0)) + +enum { +// BUFFER_SIZE = 32768, LINE_SIZE = 512, + BUFFER_SIZE = 2, LINE_SIZE = 512, // this should be a stream, without any 'BUFFER_SIZE' ? I guess the SH4 DMA implementation isn't on a timer tho? + FLAG_COMPRESSED = 0x20000 +}; + +CCrypto::CCrypto() + : key(0) +{ +} + +void CCrypto::SaveState(CBlockFile *SaveState) +{ + if (!key) + return; // no security board + SaveState->NewBlock("Sega 315-5881", __FILE__); + SaveState->Write(buffer.get(), BUFFER_SIZE); + SaveState->Write(line_buffer.get(), LINE_SIZE); + SaveState->Write(line_buffer_prev.get(), LINE_SIZE); + SaveState->Write(&prot_cur_address, sizeof(prot_cur_address)); + SaveState->Write(&subkey, sizeof(subkey)); + SaveState->Write(&enc_ready, sizeof(enc_ready)); + SaveState->Write(&dec_hist, sizeof(dec_hist)); + SaveState->Write(&dec_header, sizeof(dec_header)); + SaveState->Write(&buffer_pos, sizeof(buffer_pos)); + SaveState->Write(&line_buffer_pos, sizeof(line_buffer_pos)); + SaveState->Write(&line_buffer_size, sizeof(line_buffer_size)); +} + +void CCrypto::LoadState(CBlockFile *SaveState) +{ + if (!key) + return; + if (SaveState->FindBlock("Sega 315-5881")) + { + ErrorLog("Unable to load security board encryption device state. Save state file is corrupt."); + return; + } + SaveState->Read(buffer.get(), BUFFER_SIZE); + SaveState->Read(line_buffer.get(), LINE_SIZE); + SaveState->Read(line_buffer_prev.get(), LINE_SIZE); + SaveState->Read(&prot_cur_address, sizeof(prot_cur_address)); + SaveState->Read(&subkey, sizeof(subkey)); + SaveState->Read(&enc_ready, sizeof(enc_ready)); + SaveState->Read(&dec_hist, sizeof(dec_hist)); + SaveState->Read(&dec_header, sizeof(dec_header)); + SaveState->Read(&buffer_pos, sizeof(buffer_pos)); + SaveState->Read(&line_buffer_pos, sizeof(line_buffer_pos)); + SaveState->Read(&line_buffer_size, sizeof(line_buffer_size)); +} + +void CCrypto::Init(uint32_t encryptionKey, std::function ReadRAMCallback) +{ + /* + buffer = std::make_unique(BUFFER_SIZE); + line_buffer = std::make_unique(LINE_SIZE); + line_buffer_prev = std::make_unique(LINE_SIZE); + */ + buffer = std::unique_ptr{ new UINT8[BUFFER_SIZE] }; + line_buffer = std::unique_ptr{ new UINT8[LINE_SIZE] }; + line_buffer_prev = std::unique_ptr{ new UINT8[LINE_SIZE] }; + + m_read = ReadRAMCallback; + //m_read.bind_relative_to(*owner()); + +/* + save_pointer(NAME(buffer.get()), BUFFER_SIZE); + save_pointer(NAME(line_buffer.get()), LINE_SIZE); + save_pointer(NAME(line_buffer_prev.get()), LINE_SIZE); + save_item(NAME(prot_cur_address)); + save_item(NAME(subkey)); + save_item(NAME(enc_ready)); + save_item(NAME(dec_hist)); + save_item(NAME(dec_header)); + save_item(NAME(buffer_pos)); + save_item(NAME(line_buffer_pos)); + save_item(NAME(line_buffer_size)); +*/ + + key = encryptionKey; +} + +void CCrypto::Reset() +{ + memset(buffer.get(), 0, BUFFER_SIZE); + memset(line_buffer.get(), 0, LINE_SIZE); + memset(line_buffer_prev.get(), 0, LINE_SIZE); + + prot_cur_address = 0; + subkey = 0; + dec_hist = 0; + dec_header = 0; + enc_ready = false; + + buffer_pos = 0; + line_buffer_pos = 0; + line_buffer_size = 0; + buffer_bit = 0; +} + +UINT16 CCrypto::Decrypt(UINT8 **base) +{ + if (key == 0) + return 0xFFFF; + if(!enc_ready) + enc_start(); + if(dec_header & FLAG_COMPRESSED) { + if (line_buffer_pos == line_buffer_size) // if there's no data left to read.. + { + if (done_compression == 1) + enc_start(); + + + + line_fill(); + } + *base = line_buffer.get() + line_buffer_pos; + line_buffer_pos += 2; + } else { + if(buffer_pos == BUFFER_SIZE) + enc_fill(); + *base = buffer.get() + buffer_pos; + buffer_pos += 2; + } + + return ((*base)[0] << 8) | (*base)[1]; +} + +void CCrypto::SetAddressLow(UINT16 data) +{ + prot_cur_address = (prot_cur_address & 0xffff0000) | data; + enc_ready = false; +} + +void CCrypto::SetAddressHigh(UINT16 data) +{ + prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); + enc_ready = false; + + buffer_bit = 7; + buffer_bit2 = 15; +} + +void CCrypto::SetSubKey(UINT16 data) +{ + subkey = data; + enc_ready = false; +} + +/*************************************************************************** + DECRYPTION EMULATION + +By convention, we label the three known cart protection methods this way (using Deunan Knute's wording): +M1: DMA read of protected ROM area +M2: special read of ROM area which supplies decryption key first +M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area + +Notes below refer to M2 & M3. + +The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher. + +Every stream can be composed by several substreams; there are 18 header bits at the start of every substream, with +a 1+9+8 format; the highest bit control the mode of operation: set to 1 means that the substream needs to be decompressed +after being decrypted. The other two blocks (A||B) encode the length of the substream, as (A+1)*(B+1). When a +substream end, the header of the next one, if existing, follows inmediatly. + +The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been +conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests +on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some +more testing would be needed for clarifying that. + +After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits, +the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the +internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a +given plaintext word, and the remaining 2 to the next plaintext word. + +The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits), +the game-key (>=30 bits; probably 64) and the sequence-key (16 bits) and output a middle result (16 bits) which will act +as another key for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key +and the result from the first FN and will output the decrypted word (16 bits). + +Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input is the +XOR of at most one bit from the previous round and at most one bit from the different keys. + +The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and, +indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident, +as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). + +Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is +mapped to multiples of 128K. + +Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example, +you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change +accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been +chosen so as to make the key for CAPSNK equal to 0. + +It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys +as of january/2015 show small randomness and big correlations, making possible that some unseen bits could make the +decryption need those incomplete parts. + +SEGA apparently used his security part label (317-xxxx-yyy) as part of the key; the mapping of the current keys to the chip label +is given by the following function: + +void key2label(uint32_t key) +{ + int bcd0 = ((BIT(key,17)<<3)|(BIT(key,7)<<2)|(BIT(key,14)<<1)|BIT(key,19))^9; + int bcd1 = ((BIT(key,20)<<3)|(BIT(key,1)<<2)|(BIT(key,4)<<1)|BIT(key,13))^5; + int bcd2 = (BIT(key,9)<<1)|BIT(key,22); + int bcd3 = ((BIT(key,9)<<2)|BIT(key,9))^5; + + char chiplabel[13]; + sprintf(chiplabel, "317-%d%d%d%d-%s", bcd3, bcd2, bcd1, bcd0, (BIT(key,5)?"JPN":"COM")); + + printf("%s", chiplabel); +} + +Given the use of the BCD-encoded security module labels, it's expected that at least other 6 additional bits be present in the +real keys but undetected in the current implementation (due to them being set to fixed values on all the known 315-5881 chip labels). +That would rise the bit count at least to 35. + +Other key bits not directly related to the 315-5881 label still show low entropies, making possible that +they be derived from other non-random sources. + +In the second Feistel Network, every key bit seem to be used at most once (the various uses of current bit #9 are fictitious, as +that bit really represent various bits in the real key; see comments on the use of the labels above). Given that, it seems probable +that the real key is 64 bits long, exactly as in the related CPS-2 scheme, and the designers tried to cover all 96 input bits with +the bits provening from the game key, the sequence key and the result from the first feistel network (64+16+16=96). In the first +Feistel Network, as only 80 bits are available, some bits would be used twice (as can be partially seen in the current implementation). +The fact that only 30 bits out of the expected 64 have been observed till now would be due to the generation of the key by composing +low-entropy sources. + +****************************************************************************************/ + +const CCrypto::sbox CCrypto::fn1_sboxes[4][4] = { + { // 1st round + { + { + 0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0, + 2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3, + }, + {3,4,5,7,-1,-1}, + {0,4} + }, + + { + { + 2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1, + 0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2, + }, + {0,1,2,5,6,7}, + {1,6} + }, + + { + { + 0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1, + 1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3, + }, + {0,2,5,6,7,-1}, + {2,3} + }, + + { + { + 3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1, + 1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3, + }, + {1,2,3,4,6,7}, + {5,7} + }, + }, + { // 2nd round + { + { + 3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3, + 2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3, + }, + {0,1,3,4,5,7}, + {0,4} + }, + + { + { + 2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0, + 3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1, + }, + {0,1,3,4,6,-1}, + {1,5} + }, + + { + { + 2,2,2,3,1,1,0,1,3,3,1,1,2,2,2,0,0,3,2,3,3,0,2,1,2,2,3,0,1,3,0,0, + 3,2,0,3,2,0,1,0,0,1,2,2,3,3,0,2,2,1,3,1,1,1,1,2,0,3,1,0,0,2,3,2, + }, + {1,2,5,6,7,6}, + {2,7} + }, + + { + { + 0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3, + 0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0, + }, + {2,3,4,5,6,7}, + {3,6} + }, + }, + { // 3rd round + { + { + 0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2, + 0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1, + }, + {1,2,3,4,5,7}, + {0,5} + }, + + { + { + 1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1, + 1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1, + }, + {0,2,4,5,6,7}, + {1,6} + }, + + { + { + 0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0, + // unused? + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + }, + {0,2,4,6,7,-1}, + {2,3} + }, + + { + { + 0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0, + 2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3, + }, + {0,1,2,3,6,-1}, + {4,7} + }, + }, + { // 4th round + { + { + 0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1, + 3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1, + }, + {0,1,3,4,5,6}, + {0,5} + }, + + { + { + 0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1, + 3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3, + }, + {0,1,2,3,5,7}, + {1,7} + }, + + { + { + 0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1, + 3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0, + }, + {2,3,4,5,6,7}, + {2,3} + }, + + { + { + 0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0, + 1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1, + }, + {0,1,2,4,6,7}, + {4,6} + }, + }, +}; + + +const CCrypto::sbox CCrypto::fn2_sboxes[4][4] = { + { // 1st round + { + { + 3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2, + 2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2, + }, + {1,3,4,5,6,7}, + {0,7} + }, + + { + { + 0,1,3,0,1,1,2,3,2,0,0,3,2,1,3,1,3,3,0,0,1,0,0,3,0,3,3,2,3,2,0,1, + 3,2,3,2,2,1,3,1,1,1,0,3,3,2,2,1,1,2,0,2,0,1,1,0,1,0,1,1,2,0,3,0, + }, + {0,3,5,6,5,0}, + {1,2} + }, + + { + { + 0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1, + 0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2, + }, + {0,2,3,4,7,-1}, + {3,4} + }, + + { + { + 2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0, + 2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1, + }, + {1,2,5,6,-1,-1}, + {5,6} + }, + }, + { // 2nd round + { + { + 2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0, + 2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0, + }, + {0,1,4,5,6,7}, + {0,7} + }, + + { + { + 0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1, + 1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3, + }, + {0,2,3,6,7,-1}, + {1,5} + }, + + { + { + 1,0,3,0,0,1,2,1,0,0,1,0,0,0,2,3,2,2,0,2,0,1,3,0,2,0,1,3,2,3,0,1, + 1,2,2,2,1,3,0,3,0,1,1,0,3,2,3,3,2,0,0,3,1,2,1,3,3,2,1,0,2,1,2,3, + }, + {2,3,4,6,7,2}, + {2,3} + }, + + { + { + 2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0, + 3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0, + }, + {1,2,3,4,5,6}, + {4,6} + }, + }, + { // 3rd round + { + { + 0,3,0,1,3,0,0,2,1,0,1,3,2,2,2,0,3,3,3,0,2,2,0,3,0,0,2,3,0,3,2,1, + 3,3,0,3,0,2,3,3,1,1,1,0,2,2,1,1,3,0,3,1,2,0,2,0,0,0,3,2,1,1,0,0, + }, + {1,4,5,6,7,5}, + {0,5} + }, + + { + { + 0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3, + 2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2, + }, + {0,2,3,4,6,7}, + {1,7} + }, + + { + { + 2,2,0,3,0,3,1,0,1,1,2,3,2,3,1,0,0,0,3,2,2,0,2,3,1,3,2,0,3,3,1,3, + // unused? + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + }, + {1,2,4,7,2,-1}, + {2,4} + }, + + { + { + 0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0, + 3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3, + }, + {0,1,2,3,5,7}, + {3,6} + }, + }, + { // 4th round + { + { + 0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3, + 0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1, + }, + {0,1,2,4,7,-1}, + {0,5} + }, + + { + { + 2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2, + 0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1, + }, + {0,1,3,5,6,-1}, + {1,3} + }, + + { + { + 0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3, + 3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1, + }, + {0,2,3,4,5,7}, + {2,7} + }, + + { + { + 0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3, + 2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2, + }, + {2,3,4,5,6,7}, + {4,6} + }, + }, +}; + +const int CCrypto::fn1_game_key_scheduling[FN1GK][2] = { + {1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11}, + {6,51}, {7,92}, {8,89}, {9,9}, {9,39}, {9,58}, {10,90}, {11,6}, + {12,64}, {13,49}, {14,44}, {15,40}, {16,69}, {17,15}, {18,23}, {18,43}, + {19,82}, {20,81}, {21,32}, {22,5}, {23,66}, {24,13}, {24,45}, {25,12}, + {25,35}, {26,61}, {27,10}, {27,59}, {28,25}, {29,86} +}; + +const int CCrypto::fn2_game_key_scheduling[FN2GK][2] = { + {0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38}, + {8,39}, {9,55}, {9,86}, {9,87}, {10,50}, {11,57}, {12,59}, {13,61}, + {14,63}, {15,67}, {16,72}, {17,83}, {18,88}, {19,94}, {20,35}, {21,17}, + {22,6}, {23,85}, {24,16}, {25,25}, {26,92}, {27,47}, {28,28}, {29,90} +}; + +const int CCrypto::fn1_sequence_key_scheduling[20][2] = { + {0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48}, + {6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55}, + {13,74}, {14,19}, {14,80}, {15,26} +}; + +const int CCrypto::fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64}; + +const int CCrypto::fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58}; + +int CCrypto::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys) +{ + int k,m; + int aux; + int result=0; + + for (m=0; m<4; ++m) { // 4 sboxes + for (k=0, aux=0; k<6; ++k) + if (sboxes[m].inputs[k]!=-1) + aux |= BIT(input, sboxes[m].inputs[k]) << k; + + aux = sboxes[m].table[(aux^subkeys)&0x3f]; + + for (k=0; k<2; ++k) + result |= BIT(aux,k) << sboxes[m].outputs[k]; + + subkeys >>=6; + } + + return result; +} + +/************************** +This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways. +The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key & +sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by +noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work +of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here. +**************************/ + +UINT16 CCrypto::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data) +{ + int j; + int aux, aux2; + int A, B; + int middle_result; + UINT32 fn1_subkeys[4]; + UINT32 fn2_subkeys[4]; + + /* Game-key scheduling; this could be done just once per game at initialization time */ + memset(fn1_subkeys, 0, sizeof(UINT32) * 4); + memset(fn2_subkeys, 0, sizeof(UINT32) * 4); + + for (j = 0; j < FN1GK; ++j) { + if (BIT(game_key, fn1_game_key_scheduling[j][0]) != 0) { + aux = fn1_game_key_scheduling[j][1] % 24; + aux2 = fn1_game_key_scheduling[j][1] / 24; + fn1_subkeys[aux2] ^= (1 << aux); + } + } + + for (j = 0; j < FN2GK; ++j) { + if (BIT(game_key, fn2_game_key_scheduling[j][0]) != 0) { + aux = fn2_game_key_scheduling[j][1] % 24; + aux2 = fn2_game_key_scheduling[j][1] / 24; + fn2_subkeys[aux2] ^= (1 << aux); + } + } + /********************************************************/ + + /* Sequence-key scheduling; this could be done just once per decryption run */ + for (j = 0; j < 20; ++j) { + if (BIT(sequence_key, fn1_sequence_key_scheduling[j][0]) != 0) { + aux = fn1_sequence_key_scheduling[j][1] % 24; + aux2 = fn1_sequence_key_scheduling[j][1] / 24; + fn1_subkeys[aux2] ^= (1 << aux); + } + } + + for (j = 0; j < 16; ++j) { + if (BIT(sequence_key, j) != 0) { + aux = fn2_sequence_key_scheduling[j] % 24; + aux2 = fn2_sequence_key_scheduling[j] / 24; + fn2_subkeys[aux2] ^= (1 << aux); + } + } + + /**************************************************************/ + + // First Feistel Network + + aux = BITSWAP16(counter, 5, 12, 14, 13, 9, 3, 6, 4, 8, 1, 15, 11, 0, 7, 10, 2); + + // 1st round + B = aux >> 8; + A = (aux & 0xff) ^ feistel_function(B, fn1_sboxes[0], fn1_subkeys[0]); + + // 2nd round + B ^= feistel_function(A, fn1_sboxes[1], fn1_subkeys[1]); + + // 3rd round + A ^= feistel_function(B, fn1_sboxes[2], fn1_subkeys[2]); + + // 4th round + B ^= feistel_function(A, fn1_sboxes[3], fn1_subkeys[3]); + + middle_result = (B << 8) | A; + + + /* Middle-result-key sheduling */ + for (j = 0; j < 16; ++j) { + if (BIT(middle_result, j) != 0) { + aux = fn2_middle_result_scheduling[j] % 24; + aux2 = fn2_middle_result_scheduling[j] / 24; + fn2_subkeys[aux2] ^= (1 << aux); + } + } + /*********************/ + + // Second Feistel Network + + aux = BITSWAP16(data, 14, 3, 8, 12, 13, 7, 15, 4, 6, 2, 9, 5, 11, 0, 1, 10); + + // 1st round + B = aux >> 8; + A = (aux & 0xff) ^ feistel_function(B, fn2_sboxes[0], fn2_subkeys[0]); + + // 2nd round + B ^= feistel_function(A, fn2_sboxes[1], fn2_subkeys[1]); + + // 3rd round + A ^= feistel_function(B, fn2_sboxes[2], fn2_subkeys[2]); + + // 4th round + B ^= feistel_function(A, fn2_sboxes[3], fn2_subkeys[3]); + + aux = (B << 8) | A; + + aux = BITSWAP16(aux, 15, 7, 6, 14, 13, 12, 5, 4, 3, 2, 11, 10, 9, 1, 0, 8); + + return aux; +} + + +UINT16 CCrypto::get_decrypted_16() +{ + UINT16 enc; + + enc = m_read(prot_cur_address); + + UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc); + UINT16 res = (dec & 3) | (dec_hist & 0xfffc); + dec_hist = dec; + + prot_cur_address ++; + +// printf("get_decrypted_16 %04x\n", res); + + return res; +} + + +void CCrypto::enc_start() +{ + block_pos = 0; + done_compression = 0; + buffer_pos = BUFFER_SIZE; + + if (buffer_bit2 != 15) // if we have remaining bits in the decompression buffer we shouldn't read the next word yet but should instead use the bits we have?? (twcup98) (might just be because we should be pulling bytes not words?) + { +// printf("buffer_bit2 is %d\n", buffer_bit2); + dec_header = (buffer2a & 0x0003) << 16; + } + else + { + dec_hist = 0; // seems to be needed by astrass at least otherwise any call after the first one will be influenced by the one before it. + dec_header = get_decrypted_16() << 16; + } + + dec_header |= get_decrypted_16(); + + // the lower header bits are 2 values that multiply together to get the current stream length + // in astrass the first block is 0xffff (for a 0x10000 block) followed by 0x3f3f (for a 0x1000 block) + // etc. after each block a new header must be read, it looks like compressed and uncompressed blocks + // can be mixed like this, I don't know if the length is src length of decompressed length. + // deathcox and others confirm format as 0x20000 bit as compressed bit, 0x1ff00 bits as block size 1, 0x000ff bits as block size 2 + // for compressed streams the 'line size' is block size 1. + + block_numlines = ((dec_header & 0x000000ff) >> 0) + 1; + int blocky = ((dec_header & 0x0001ff00) >> 8) + 1; + block_size = block_numlines * blocky; + + if(dec_header & FLAG_COMPRESSED) { + line_buffer_size = blocky; + line_buffer_pos = line_buffer_size; + buffer_bit = 7; + buffer_bit2 = 15; + } + + //printf("header %08x\n", dec_header); + enc_ready = true; +} + +void CCrypto::enc_fill() +{ + //assert(buffer_pos == BUFFER_SIZE); + for(int i = 0; i != BUFFER_SIZE; i+=2) { + UINT16 val = get_decrypted_16(); + buffer[i] = val; + buffer[i+1] = val >> 8; + block_pos+=2; + + if (!(dec_header & FLAG_COMPRESSED)) + { + if (block_pos == block_size) + { + // if we reach the size specified we need to read a new header + // todo: for compressed blocks this depends on OUTPUT size, not input size, so things get messy + + enc_start(); + } + } + } + buffer_pos = 0; +} + +/* node format +0xxxxxxx - next node index +1a0bbccc - end node + a - 0 = repeat + 1 = fetch + b - if a = 1 + 00 - fetch 0 + 01 - fetch 1 + 11 - fetch -1 + if a = 0 + 000 + c - repeat/fetch counter + count = ccc + 1 +11111111 - empty node +*/ +const UINT8 CCrypto::trees[9][2][32] = { + { + {0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2, + 0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0, + 0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde, + 0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,}, + {0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85, + 0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,}, + }, + { + {0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10, + 0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19, + 0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14, + 0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8, + 0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb, + 0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2, + 0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, +}; + +int CCrypto::get_compressed_bit() +{ +// if(buffer_pos == BUFFER_SIZE) +// enc_fill(); + + if (buffer_bit2 == 15) + { + buffer_bit2 = 0; + buffer2a = get_decrypted_16(); + buffer2[0] = buffer2a; + buffer2[1] = buffer2a >> 8; + // block_pos+=2; + buffer_pos = 0; + + } + else + { + buffer_bit2++; + } + +// if (buffer_bit ==7) printf("using byte %02x\n", buffer2[(buffer_pos&1) ^ 1]); + + int res = (buffer2[(buffer_pos&1)^1] >> buffer_bit) & 1; + buffer_bit--; + if(buffer_bit == -1) { + buffer_bit = 7; + buffer_pos++; + } + return res; +} +void CCrypto::line_fill() +{ + //assert(line_buffer_pos == line_buffer_size); + UINT8 *lp = line_buffer.get(); + UINT8 *lc = line_buffer_prev.get(); + + line_buffer.swap(line_buffer_prev); + + line_buffer_pos = 0; + + for(int i=0; i != line_buffer_size;) { + // vlc 0: start of line + // vlc 1: interior of line + // vlc 2-9: 7-1 bytes from end of line + + int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0; + + UINT32 tmp = 0; + while (!(tmp&0x80)) + if(get_compressed_bit()) + tmp = trees[slot][1][tmp]; + else + tmp = trees[slot][0][tmp]; + if(tmp != 0xff) { + int count = (tmp & 7) + 1; + + if(tmp&0x40) { + // Copy from previous line + + static int offsets[4] = {0, 1, 0, -1}; + int offset = offsets[(tmp & 0x18) >> 3]; + for(int j=0; j != count; j++) { + lc[i^1] = lp[((i+offset) % line_buffer_size)^1]; + i++; + } + + } else { + // Get a byte in the stream and write n times + UINT8 byte; + byte = get_compressed_bit() << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = byte | get_compressed_bit(); + for(int j=0; j != count; j++) + lc[(i++)^1] = byte; + + } + } + } + + block_pos++; + if (block_numlines == block_pos) + { + done_compression = 1; + } + else + { + } +} diff --git a/Src/Model3/Crypto.h b/Src/Model3/Crypto.h new file mode 100644 index 0000000..113a8fd --- /dev/null +++ b/Src/Model3/Crypto.h @@ -0,0 +1,123 @@ +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011-2016 Bart Trzynadlowski, Nik Henson + ** + ** This file is part of Supermodel. + ** + ** Supermodel is free software: you can redistribute it and/or modify it under + ** the terms of the GNU General Public License as published by the Free + ** Software Foundation, either version 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel is distributed in the hope that it will be useful, but WITHOUT + ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + ** more details. + ** + ** You should have received a copy of the GNU General Public License along + ** with Supermodel. If not, see . + **/ + +/* + * Crypto.h + * + * Header file for security board encryption device. This code was taken from + * MAME (http://mamedev.org). + */ + +// license:BSD-3-Clause +// copyright-holders:David Haywood + +#pragma once + +#ifndef __SEGA315_5881_CRYPT__ +#define __SEGA315_5881_CRYPT__ + +#include +#include +#include + +class CBlockFile; + +class CCrypto +{ +public: + // construction/destruction + CCrypto(); + + void SaveState(CBlockFile *SaveState); + void LoadState(CBlockFile *SaveState); + void Init(uint32_t encryptionKey, std::function ReadRAMCallback); + void Reset(); + + + uint16_t Decrypt(uint8_t **base); + void SetAddressLow(uint16_t data); + void SetAddressHigh(uint16_t data); + void SetSubKey(uint16_t data); + + std::function m_read; + + /* + static void set_read_cb(device_t &device,sega_m2_read_delegate readcb) + { + sega_315_5881_crypt_device &dev = downcast(device); + dev.m_read = readcb; + } + */ + +private: + + uint32_t key; + + std::unique_ptr buffer; + std::unique_ptr line_buffer; + std::unique_ptr line_buffer_prev; + uint32_t prot_cur_address; + uint16_t subkey, dec_hist; + uint32_t dec_header; + + bool enc_ready; + + int buffer_pos, line_buffer_pos, line_buffer_size, buffer_bit, buffer_bit2; + uint8_t buffer2[2]; + uint16_t buffer2a; + + int block_size; + int block_pos; + int block_numlines; + int done_compression; + + struct sbox { + uint8_t table[64]; + int inputs[6]; // positions of the inputs bits, -1 means no input except from key + int outputs[2]; // positions of the output bits + }; + + static const sbox fn1_sboxes[4][4]; + static const sbox fn2_sboxes[4][4]; + + static const int FN1GK = 38; + static const int FN2GK = 32; + static const int fn1_game_key_scheduling[FN1GK][2]; + static const int fn2_game_key_scheduling[FN2GK][2]; + static const int fn1_sequence_key_scheduling[20][2]; + static const int fn2_sequence_key_scheduling[16]; + static const int fn2_middle_result_scheduling[16]; + + static const uint8_t trees[9][2][32]; + + int feistel_function(int input, const struct sbox *sboxes, uint32_t subkeys); + uint16_t block_decrypt(uint32_t game_key, uint16_t sequence_key, uint16_t counter, uint16_t data); + + uint16_t get_decrypted_16(); + int get_compressed_bit(); + + void enc_start(); + void enc_fill(); + void line_fill(); + +}; + +#endif diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp index 3aed4b4..04018cf 100644 --- a/Src/Model3/Model3.cpp +++ b/Src/Model3/Model3.cpp @@ -1,7 +1,7 @@ /** ** Supermodel ** A Sega Model 3 Arcade Emulator. - ** Copyright 2011 Bart Trzynadlowski, Nik Henson + ** Copyright 2011-2016 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** @@ -27,45 +27,46 @@ * To-Do List * ---------- * - Save state format has changed slightly. No longer need dmaUnknownRegister - * in Real3D.cpp. PowerPC timing variables have changed. Before 0.3a - * release, important to change format version #. - * - 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 :) + * in Real3D.cpp and security board-related variable was added to Model 3 + * state. PowerPC timing variables have changed. Before 0.3a release, + * important to change format version #. + * - 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 :) * * PowerPC Address Map (may be slightly out of date/incomplete) * ------------------- - * 00000000-007FFFFF RAM - * 84000000-8400003F Real3D Status Registers - * 88000000-88000007 Real3D Command Port - * 8C000000-8C3FFFFF Real3D Culling RAM (Low) - * 8E000000-8E0FFFFF Real3D Culling RAM (High) - * 90000000-9000000B Real3D VROM Texture Port - * 94000000-940FFFFF Real3D Texture FIFO - * 98000000-980FFFFF Real3D Polygon RAM - * C0000000-C00000FF SCSI (Step 1.x) - * C1000000-C10000FF SCSI (Step 1.x) (Lost World expects it here) - * C2000000-C20000FF Real3D DMA (Step 2.x) - * F0040000-F004003F Input (Controls) Registers - * F0080000-F0080007 Sound Board Registers - * F00C0000-F00DFFFF Backup RAM - * F0100000-F010003F System Registers - * F0140000-F014003F Real-Time Clock - * F0180000-F019FFFF Security Board RAM - * F01A0000-F01A003F Security Board Registers - * F0800CF8-F0800CFF MPC105 CONFIG_ADDR (Step 1.x) - * F0C00CF8-F0800CFF MPC105 CONFIG_DATA (Step 1.x) - * F1000000-F10F7FFF Tile Generator Pattern Table - * F10F8000-F10FFFFF Tile Generator Name Table - * F1100000-F111FFFF Tile Generator Palette - * F1180000-F11800FF Tile Generator Registers - * F8FFF000-F8FFF0FF MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers - * F9000000-F90000FF NCR 53C810 Registers (Step 1.x?) - * FEC00000-FEDFFFFF MPC106 CONFIG_ADDR (Step 2.x) - * FEE00000-FEFFFFFF MPC106 CONFIG_DATA (Step 2.x) - * FF000000-FF7FFFFF Banked CROM (CROMxx) - * FF800000-FFFFFFFF Fixed CROM + * 00000000-007FFFFF RAM + * 84000000-8400003F Real3D Status Registers + * 88000000-88000007 Real3D Command Port + * 8C000000-8C3FFFFF Real3D Culling RAM (Low) + * 8E000000-8E0FFFFF Real3D Culling RAM (High) + * 90000000-9000000B Real3D VROM Texture Port + * 94000000-940FFFFF Real3D Texture FIFO + * 98000000-980FFFFF Real3D Polygon RAM + * C0000000-C00000FF SCSI (Step 1.x) + * C1000000-C10000FF SCSI (Step 1.x) (Lost World expects it here) + * C2000000-C20000FF Real3D DMA (Step 2.x) + * F0040000-F004003F Input (Controls) Registers + * F0080000-F0080007 Sound Board Registers + * F00C0000-F00DFFFF Backup RAM + * F0100000-F010003F System Registers + * F0140000-F014003F Real-Time Clock + * F0180000-F019FFFF Security Board RAM + * F01A0000-F01A003F Security Board Registers + * F0800CF8-F0800CFF MPC105 CONFIG_ADDR (Step 1.x) + * F0C00CF8-F0800CFF MPC105 CONFIG_DATA (Step 1.x) + * F1000000-F10F7FFF Tile Generator Pattern Table + * F10F8000-F10FFFFF Tile Generator Name Table + * F1100000-F111FFFF Tile Generator Palette + * F1180000-F11800FF Tile Generator Registers + * F8FFF000-F8FFF0FF MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers + * F9000000-F90000FF NCR 53C810 Registers (Step 1.x?) + * FEC00000-FEDFFFFF MPC106 CONFIG_ADDR (Step 2.x) + * FEE00000-FEFFFFFF MPC106 CONFIG_DATA (Step 2.x) + * FF000000-FF7FFFFF Banked CROM (CROMxx) + * FF800000-FFFFFFFF Fixed CROM * * Endianness * ---------- @@ -84,13 +85,13 @@ * +---+---+---+---+---+---+---+---+ * | ? |SND| ? |NET|VD3|VD2|VBL|VD0| * +---+---+---+---+---+---+---+---+ - * SND SCSP (sound) - * NET Network - * VD3 Unknown video-related - * VD2 Unknown video-related - * VBL VBlank start - * VD0 Unknown video-related (?) - * 0 = Disable, 1 = Enable + * SND SCSP (sound) + * NET Network + * VD3 Unknown video-related + * VD2 Unknown video-related + * VBL VBlank start + * VD0 Unknown video-related (?) + * 0 = Disable, 1 = Enable * * Game Buttons * ------------ @@ -99,91 +100,101 @@ * * Offset 0x04, bank 0: * - * 7 6 5 4 3 2 1 0 - * +---+---+---+---+---+---+---+---+ + * 7 6 5 4 3 2 1 0 + * +---+---+---+---+---+---+---+---+ * | ? | ? |ST2|ST1|SVA|TSA|CN2|CN1| - * +---+---+---+---+---+---+---+---+ - * CNx Coin 1, Coin 2 - * TSA Test Button A - * SVA Service Button A - * STx Start 1, Start 2 + * +---+---+---+---+---+---+---+---+ + * CNx Coin 1, Coin 2 + * TSA Test Button A + * SVA Service Button A + * STx Start 1, Start 2 * * Offset 0x04, bank 1: * - * 7 6 5 4 3 2 1 0 - * +---+---+---+---+---+---+---+---+ + * 7 6 5 4 3 2 1 0 + * +---+---+---+---+---+---+---+---+ * |TSB|SVB|EEP| ? | ? | ? | ? | ? | - * +---+---+---+---+---+---+---+---+ - * EEP Mapped to EEPROM (values written here are ignored) - * SVB Service Button B - * TSB Test Button B + * +---+---+---+---+---+---+---+---+ + * EEP Mapped to EEPROM (values written here are ignored) + * SVB Service Button B + * TSB Test Button B * * Offset 0x08: * - * 7 6 5 4 3 2 1 0 - * +---+---+---+---+---+---+---+---+ + * 7 6 5 4 3 2 1 0 + * +---+---+---+---+---+---+---+---+ * |G27|G26|G25|G24|G23|G22|G21|G20| - * +---+---+---+---+---+---+---+---+ - * G2x Game-specific + * +---+---+---+---+---+---+---+---+ + * G2x Game-specific * * Offset 0x0C: * - * 7 6 5 4 3 2 1 0 - * +---+---+---+---+---+---+---+---+ + * 7 6 5 4 3 2 1 0 + * +---+---+---+---+---+---+---+---+ * |G37|G36|G35|G34|G33|G32|G31|G30| - * +---+---+---+---+---+---+---+---+ - * G3x Game-specific + * +---+---+---+---+---+---+---+---+ + * G3x Game-specific * * Game-specific buttons: * - * 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 - * G22 VR3 Yellow - * G21 VR2 Blue - * G20 VR1 Red + * 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 + * G22 VR3 Yellow + * G21 VR2 Blue + * G20 VR1 Red * - * Virtua Fighter 3, Fighting Vipers 2: - * G27 Right - * G26 Left - * G25 Down - * G24 Up - * G23 Punch - * G22 Kick - * G21 Guard - * G20 Escape (VF3 only) + * Virtua Fighter 3, Fighting Vipers 2: + * G27 Right + * G26 Left + * G25 Down + * G24 Up + * G23 Punch + * G22 Kick + * G21 Guard + * G20 Escape (VF3 only) * - * Sega Rally 2: - * G21 Hand Brake - * G20 View Change + * Sega Rally 2: + * G21 Hand Brake + * G20 View Change * - * Lost World, LA Machineguns: - * G20 Gun trigger + * Lost World, LA Machineguns: + * G20 Gun trigger * - * Star Wars Trilogy: - * G20 Event button - * G25 Trigger + * Star Wars Trilogy: + * G20 Event button + * G25 Trigger * - * Virtual On 2: - * G27 Left Lever Left - * G26 Left Lever Right - * G25 Left Lever Up - * G24 Left Lever Down - * G23 --- - * G22 --- - * G21 Left Turbo - * G20 Left Shot Trigger - * G37 Right Lever Left - * G36 Right Lever Right - * G35 Right Lever Up - * G34 Right Lever Down - * G33 --- - * G32 --- - * G31 Right Turbo - * G30 Right Shot Trigger + * Virtual On 2: + * G27 Left Lever Left + * G26 Left Lever Right + * G25 Left Lever Up + * G24 Left Lever Down + * G23 --- + * G22 --- + * G21 Left Turbo + * G20 Left Shot Trigger + * G37 Right Lever Left + * G36 Right Lever Right + * G35 Right Lever Up + * G34 Right Lever Down + * G33 --- + * G32 --- + * G31 Right Turbo + * G30 Right Shot Trigger + * + * Misc. Notes + * ----------- + * + * 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 + * pressing: green, green, blue, yellow, red, yellow, blue + * (VR4,4,2,3,1,3,2). */ #include @@ -191,6 +202,10 @@ #include #include #include "Supermodel.h" +#include "Util/Format.h" +#include +#include +#include /****************************************************************************** Model 3 Inputs @@ -200,429 +215,429 @@ UINT8 CModel3::ReadInputs(unsigned reg) { - UINT8 adc[8]; - UINT8 data; - - reg &= 0x3F; - switch (reg) - { - case 0x00: // input bank - return inputBank; + UINT8 adc[8]; + UINT8 data; + + reg &= 0x3F; + switch (reg) + { + case 0x00: // input bank + return inputBank; - case 0x04: // current input bank + case 0x04: // current input bank - data = 0xFF; + 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 - } + 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 + } - if ((Game->inputFlags&GAME_INPUT_SKI)) - { - if ((inputBank&1) == 0) - { - data &= ~(Inputs->skiPollLeft->value<<5); - data &= ~(Inputs->skiSelect1->value<<6); - data &= ~(Inputs->skiSelect2->value<<7); - data &= ~(Inputs->skiSelect3->value<<4); - } - } + if ((Game->inputFlags&GAME_INPUT_SKI)) + { + 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; + return data; - case 0x08: // game-specific inputs + case 0x08: // game-specific inputs - data = 0xFF; + data = 0xFF; - if ((Game->inputFlags&GAME_INPUT_SKI)) - { - data &= ~(Inputs->skiPollRight->value<<0); - } + if ((Game->inputFlags&GAME_INPUT_SKI)) + { + data &= ~(Inputs->skiPollRight->value<<0); + } - if ((Game->inputFlags&GAME_INPUT_JOYSTICK1)) - { - 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 - } + if ((Game->inputFlags&GAME_INPUT_JOYSTICK1)) + { + 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 + } - if ((Game->inputFlags&GAME_INPUT_FIGHTING)) - { - 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 - } - - if ((Game->inputFlags&GAME_INPUT_SPIKEOUT)) - { - data &= ~(Inputs->shift->value<<2); // Shift - data &= ~(Inputs->beat->value<<0); // Beat - data &= ~(Inputs->charge->value<<1); // Charge - data &= ~(Inputs->jump->value<<3); // Jump - } - - if ((Game->inputFlags&GAME_INPUT_SOCCER)) - { - 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 - } + if ((Game->inputFlags&GAME_INPUT_FIGHTING)) + { + 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 + } + + if ((Game->inputFlags&GAME_INPUT_SPIKEOUT)) + { + data &= ~(Inputs->shift->value<<2); // Shift + data &= ~(Inputs->beat->value<<0); // Beat + data &= ~(Inputs->charge->value<<1); // Charge + data &= ~(Inputs->jump->value<<3); // Jump + } + + if ((Game->inputFlags&GAME_INPUT_SOCCER)) + { + 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 + } - if ((Game->inputFlags&GAME_INPUT_VR4)) - { - 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 - } - - if ((Game->inputFlags&GAME_INPUT_VIEWCHANGE)) - { - // Harley is wired slightly differently - if ((Game->inputFlags&GAME_INPUT_HARLEY)) - data &= ~(Inputs->viewChange->value<<1); // View change - else - data &= ~(Inputs->viewChange->value<<0); // View change - } - - if ((Game->inputFlags&GAME_INPUT_SHIFT4)) - { - 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; - } + if ((Game->inputFlags&GAME_INPUT_VR4)) + { + 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 + } + + if ((Game->inputFlags&GAME_INPUT_VIEWCHANGE)) + { + // Harley is wired slightly differently + if ((Game->inputFlags&GAME_INPUT_HARLEY)) + data &= ~(Inputs->viewChange->value<<1); // View change + else + data &= ~(Inputs->viewChange->value<<0); // View change + } + + if ((Game->inputFlags&GAME_INPUT_SHIFT4)) + { + 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; + } - if ((Game->inputFlags&GAME_INPUT_SHIFTUPDOWN)) - { - // Harley is wired slightly differently - if ((Game->inputFlags&GAME_INPUT_HARLEY)) - { - if (Inputs->gearShiftUp->value) // Shift up - data &= ~0x60; - else if (Inputs->gearShiftDown->value) // Shift down - data &= ~0x50; - } - else - { - if (Inputs->gearShiftUp->value) // Shift up - data &= ~0x50; - else if (Inputs->gearShiftDown->value) // Shift down - data &= ~0x60; - } - } - - if ((Game->inputFlags&GAME_INPUT_HANDBRAKE)) - data &= ~(Inputs->handBrake->value<<1); // Hand brake - - if ((Game->inputFlags&GAME_INPUT_HARLEY)) - data &= ~(Inputs->musicSelect->value<<0); // Music select - - if ((Game->inputFlags&GAME_INPUT_GUN1)) - data &= ~(Inputs->trigger[0]->value<<0); // P1 Trigger - - if ((Game->inputFlags&GAME_INPUT_ANALOG_JOYSTICK)) - { - 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 - } - - if ((Game->inputFlags&GAME_INPUT_TWIN_JOYSTICKS)) // First twin joystick - { - /* - * Process left joystick inputs first - */ - - // Shot trigger and Turbo - data &= ~(Inputs->twinJoyShot1->value<<0); - data &= ~(Inputs->twinJoyTurbo1->value<<1); - - // Stick - data &= ~(Inputs->twinJoyLeft1->value<<7); - data &= ~(Inputs->twinJoyRight1->value<<6); - data &= ~(Inputs->twinJoyUp1->value<<5); - data &= ~(Inputs->twinJoyDown1->value<<4); - - /* - * Next, process twin joystick macro inputs (higher level inputs - * that map to actions on both joysticks simultaneously). - */ - - /* - * Forward/reverse/turn are mutually exclusive. - * - * Turn Left: 1D 2U - * Turn Right: 1U 2D - * Forward: 1U 2U - * Reverse: 1D 2D - */ - if (Inputs->twinJoyTurnLeft->value) - data &= ~0x10; - else if (Inputs->twinJoyTurnRight->value) - data &= ~0x20; - else if (Inputs->twinJoyForward->value) - data &= ~0x20; - else if (Inputs->twinJoyReverse->value) - data &= ~0x10; - - /* - * Strafe/crouch/jump are mutually exclusive. - * - * Strafe Left: 1L 2L - * Strafe Right: 1R 2R - * Jump: 1L 2R - * Crouch: 1R 2L - */ - if (Inputs->twinJoyStrafeLeft->value) - data &= ~0x80; - else if (Inputs->twinJoyStrafeRight->value) - data &= ~0x40; - else if (Inputs->twinJoyJump->value) - data &= ~0x80; - else if (Inputs->twinJoyCrouch->value) - data &= ~0x40; - } + if ((Game->inputFlags&GAME_INPUT_SHIFTUPDOWN)) + { + // Harley is wired slightly differently + if ((Game->inputFlags&GAME_INPUT_HARLEY)) + { + if (Inputs->gearShiftUp->value) // Shift up + data &= ~0x60; + else if (Inputs->gearShiftDown->value) // Shift down + data &= ~0x50; + } + else + { + if (Inputs->gearShiftUp->value) // Shift up + data &= ~0x50; + else if (Inputs->gearShiftDown->value) // Shift down + data &= ~0x60; + } + } + + if ((Game->inputFlags&GAME_INPUT_HANDBRAKE)) + data &= ~(Inputs->handBrake->value<<1); // Hand brake + + if ((Game->inputFlags&GAME_INPUT_HARLEY)) + data &= ~(Inputs->musicSelect->value<<0); // Music select + + if ((Game->inputFlags&GAME_INPUT_GUN1)) + data &= ~(Inputs->trigger[0]->value<<0); // P1 Trigger + + if ((Game->inputFlags&GAME_INPUT_ANALOG_JOYSTICK)) + { + 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 + } + + if ((Game->inputFlags&GAME_INPUT_TWIN_JOYSTICKS)) // First twin joystick + { + /* + * Process left joystick inputs first + */ + + // Shot trigger and Turbo + data &= ~(Inputs->twinJoyShot1->value<<0); + data &= ~(Inputs->twinJoyTurbo1->value<<1); + + // Stick + data &= ~(Inputs->twinJoyLeft1->value<<7); + data &= ~(Inputs->twinJoyRight1->value<<6); + data &= ~(Inputs->twinJoyUp1->value<<5); + data &= ~(Inputs->twinJoyDown1->value<<4); + + /* + * Next, process twin joystick macro inputs (higher level inputs + * that map to actions on both joysticks simultaneously). + */ + + /* + * Forward/reverse/turn are mutually exclusive. + * + * Turn Left: 1D 2U + * Turn Right: 1U 2D + * Forward: 1U 2U + * Reverse: 1D 2D + */ + if (Inputs->twinJoyTurnLeft->value) + data &= ~0x10; + else if (Inputs->twinJoyTurnRight->value) + data &= ~0x20; + else if (Inputs->twinJoyForward->value) + data &= ~0x20; + else if (Inputs->twinJoyReverse->value) + data &= ~0x10; + + /* + * Strafe/crouch/jump are mutually exclusive. + * + * Strafe Left: 1L 2L + * Strafe Right: 1R 2R + * Jump: 1L 2R + * Crouch: 1R 2L + */ + if (Inputs->twinJoyStrafeLeft->value) + data &= ~0x80; + else if (Inputs->twinJoyStrafeRight->value) + data &= ~0x40; + else if (Inputs->twinJoyJump->value) + data &= ~0x80; + else if (Inputs->twinJoyCrouch->value) + data &= ~0x40; + } - if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN1)) - { - data &= ~(Inputs->analogTriggerLeft[0]->value<<0); - data &= ~(Inputs->analogTriggerRight[0]->value<<1); - } + if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN1)) + { + data &= ~(Inputs->analogTriggerLeft[0]->value<<0); + data &= ~(Inputs->analogTriggerRight[0]->value<<1); + } - return data; + return data; - case 0x0C: // game-specific inputs + case 0x0C: // game-specific inputs - data = 0xFF; + data = 0xFF; - if (DriveBoard.IsAttached()) - data = DriveBoard.Read(); - - if ((Game->inputFlags&GAME_INPUT_JOYSTICK2)) - { - 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 - } + if (DriveBoard.IsAttached()) + data = DriveBoard.Read(); + + if ((Game->inputFlags&GAME_INPUT_JOYSTICK2)) + { + 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 + } - if ((Game->inputFlags&GAME_INPUT_FIGHTING)) - { - 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 - } - - if ((Game->inputFlags&GAME_INPUT_SOCCER)) - { - 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 - } - - if ((Game->inputFlags&GAME_INPUT_TWIN_JOYSTICKS)) // Second twin joystick (see register 0x08 for comments) - { - - data &= ~(Inputs->twinJoyShot2->value<<0); - data &= ~(Inputs->twinJoyTurbo2->value<<1); - - data &= ~(Inputs->twinJoyLeft2->value<<7); - data &= ~(Inputs->twinJoyRight2->value<<6); - data &= ~(Inputs->twinJoyUp2->value<<5); - data &= ~(Inputs->twinJoyDown2->value<<4); + if ((Game->inputFlags&GAME_INPUT_FIGHTING)) + { + 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 + } + + if ((Game->inputFlags&GAME_INPUT_SOCCER)) + { + 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 + } + + if ((Game->inputFlags&GAME_INPUT_TWIN_JOYSTICKS)) // Second twin joystick (see register 0x08 for comments) + { + + data &= ~(Inputs->twinJoyShot2->value<<0); + data &= ~(Inputs->twinJoyTurbo2->value<<1); + + 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; - - 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; - } - - if ((Game->inputFlags&GAME_INPUT_GUN2)) - data &= ~(Inputs->trigger[1]->value<<0); // P2 Trigger - - if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN2)) - { - data &= ~(Inputs->analogTriggerLeft[1]->value<<0); - data &= ~(Inputs->analogTriggerRight[1]->value<<1); - } - - return data; + 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; + + 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; + } + + if ((Game->inputFlags&GAME_INPUT_GUN2)) + data &= ~(Inputs->trigger[1]->value<<0); // P2 Trigger + + if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN2)) + { + data &= ~(Inputs->analogTriggerLeft[1]->value<<0); + data &= ~(Inputs->analogTriggerRight[1]->value<<1); + } + + return data; - case 0x2C: // Serial FIFO 1 - return serialFIFO1; - - case 0x30: // Serial FIFO 2 - return serialFIFO2; - - case 0x34: // Serial FIFO full/empty flags - return 0x0C; - - case 0x3C: // ADC + case 0x2C: // Serial FIFO 1 + return serialFIFO1; + + case 0x30: // Serial FIFO 2 + return serialFIFO2; + + case 0x34: // Serial FIFO full/empty flags + return 0x0C; + + case 0x3C: // ADC - // Load ADC channels with input data - memset(adc, 0, sizeof(adc)); - if ((Game->inputFlags&GAME_INPUT_VEHICLE)) - { - adc[0] = (UINT8)Inputs->steering->value; - adc[1] = (UINT8)Inputs->accelerator->value; - adc[2] = (UINT8)Inputs->brake->value; - if ((Game->inputFlags&GAME_INPUT_HARLEY)) - adc[3] = (UINT8)Inputs->rearBrake->value; - } + // Load ADC channels with input data + memset(adc, 0, sizeof(adc)); + if ((Game->inputFlags&GAME_INPUT_VEHICLE)) + { + adc[0] = (UINT8)Inputs->steering->value; + adc[1] = (UINT8)Inputs->accelerator->value; + adc[2] = (UINT8)Inputs->brake->value; + if ((Game->inputFlags&GAME_INPUT_HARLEY)) + adc[3] = (UINT8)Inputs->rearBrake->value; + } - if ((Game->inputFlags&GAME_INPUT_ANALOG_JOYSTICK)) - { - adc[0] = (UINT8)Inputs->analogJoyY->value; - adc[1] = (UINT8)Inputs->analogJoyX->value; - } + if ((Game->inputFlags&GAME_INPUT_ANALOG_JOYSTICK)) + { + adc[0] = (UINT8)Inputs->analogJoyY->value; + adc[1] = (UINT8)Inputs->analogJoyX->value; + } - if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN1)||(Game->inputFlags&GAME_INPUT_ANALOG_GUN2)) - { - 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; - } - - if ((Game->inputFlags&GAME_INPUT_SKI)) - { - adc[0] = (UINT8)Inputs->skiY->value; - adc[1] = (UINT8)Inputs->skiX->value; - } - - // Read out appropriate channel - data = adc[adcChannel&7]; - ++adcChannel; - return data; - - default: - break; - } - - return 0xFF; // controls are active low + if ((Game->inputFlags&GAME_INPUT_ANALOG_GUN1)||(Game->inputFlags&GAME_INPUT_ANALOG_GUN2)) + { + 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; + } + + if ((Game->inputFlags&GAME_INPUT_SKI)) + { + adc[0] = (UINT8)Inputs->skiY->value; + adc[1] = (UINT8)Inputs->skiX->value; + } + + // Read out appropriate channel + data = adc[adcChannel&7]; + ++adcChannel; + return data; + + default: + break; + } + + return 0xFF; // controls are active low } void CModel3::WriteInputs(unsigned reg, UINT8 data) { - switch (reg&0x3F) - { - case 0: - EEPROM.Write((data>>6)&1,(data>>7)&1,(data>>5)&1); - inputBank = data; - break; + switch (reg&0x3F) + { + case 0: + EEPROM.Write((data>>6)&1,(data>>7)&1,(data>>5)&1); + inputBank = data; + break; - case 0x10: // Drive board - if (DriveBoard.IsAttached()) - DriveBoard.Write(data); - break; + case 0x10: // Drive board + if (DriveBoard.IsAttached()) + DriveBoard.Write(data); + 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)); - } - 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)); + } + 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; - if ((Game->inputFlags&GAME_INPUT_GUN1||Game->inputFlags&GAME_INPUT_GUN2)) - { - 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); + 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; + if ((Game->inputFlags&GAME_INPUT_GUN1||Game->inputFlags&GAME_INPUT_GUN2)) + { + 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); } @@ -636,141 +651,59 @@ void CModel3::WriteInputs(unsigned reg, UINT8 data) seems to help avoid this. ******************************************************************************/ -static const UINT16 spikeoutSecurity[] = +uint16_t CModel3::ReadSecurityRAM(uint32_t addr) { - 0x0000, - 0x4f4d, 0x4544, 0x2d4c, 0x2033, 0x7953, 0x7473, 0x6d65, 0x5020, - 0x6f72, 0x7267, 0x6d61, 0x4320, 0x706f, 0x7279, 0x6769, 0x7468, - 0x2820, 0x2943, 0x3120, 0x3939, 0x2035, 0x4553, 0x4147, 0x4520, - 0x746e, 0x7265, 0x7270, 0x7369, 0x7365, 0x4c2c, 0x4454, 0x202e, - 0x6c41, 0x206c, 0x6972, 0x6867, 0x2074, 0x6572, 0x6573, 0x7672, - 0x6465, 0x202e, 0x2020, 0x0020 -}; - -static const UINT16 vs298Security[] = -{ - 0x0000, // dummy read - 0x4A20, 0x5041, 0x4E41, 0x4920, 0x4154, 0x594C, 0x4220, 0x4152, 0x4953, 0x204C, - 0x5241, 0x4547, 0x544E, 0x4E49, 0x2041, 0x4547, 0x4D52, 0x4E41, 0x2059, 0x4E45, - 0x4C47, 0x4E41, 0x2044, 0x454E, 0x4854, 0x5245, 0x414C, 0x444E, 0x2053, 0x5246, - 0x4E41, 0x4543, 0x4320, 0x4C4F, 0x4D4F, 0x4942, 0x2041, 0x4150, 0x4152, 0x5547, - 0x5941, 0x4220, 0x4C55, 0x4147, 0x4952, 0x2041, 0x5053, 0x4941, 0x204E, 0x5243, - 0x414F, 0x4954, 0x2041, 0x4542, 0x474C, 0x5549, 0x204D, 0x494E, 0x4547, 0x4952, - 0x2041, 0x4153, 0x4455, 0x2049, 0x4F4B, 0x4552, 0x2041, 0x4544, 0x4D4E, 0x5241, - 0x204B, 0x4F52, 0x414D, 0x494E, 0x2041, 0x4353, 0x544F, 0x414C, 0x444E, 0x5520, - 0x4153, 0x5320, 0x554F, 0x4854, 0x4641, 0x4952, 0x4143, 0x4D20, 0x5845, 0x4349, - 0x204F, 0x5559, 0x4F47, 0x4C53, 0x5641, 0x4149, 0x4620, 0x5F43, 0x4553, 0x4147 -}; - -static const UINT16 ecaSecurity[] = -{ - 0x0000, - 0x2d2f, 0x202d, 0x4d45, 0x5245, 0x4547, 0x434e, 0x2059, 0x4143, - 0x4c4c, 0x4120, 0x424d, 0x4c55, 0x4e41, 0x4543, 0x2d20, 0x0a2d, - 0x6f43, 0x7970, 0x6952, 0x6867, 0x2074, 0x4553, 0x4147, 0x4520, - 0x746e, 0x7265, 0x7270, 0x7369, 0x7365, 0x202c, 0x744c, 0x2e64, - 0x530a, 0x666f, 0x7774, 0x7261, 0x2065, 0x2652, 0x2044, 0x6544, - 0x7470, 0x202e, 0x3123, 0x660a, 0x726f, 0x7420, 0x7365, 0x0a74, -}; - -static const UINT16 oceanhunSecurity[57] = -{ - 0x0000, // dummy read - - 0x3d3d, 0x203d, 0x434f, 0x4145, 0x204e, 0x5548, 0x544e, 0x5245, - 0x3d20, 0x3d3d, 0x430a, 0x706f, 0x5279, 0x6769, 0x7468, 0x5320, - 0x4745, 0x2041, 0x6e45, 0x6574, 0x7072, 0x6972, 0x6573, 0x2c73, - 0x4c20, 0x6474, 0x0a2e, 0x6d41, 0x7375, 0x6d65, 0x6e65, 0x2074, - 0x2652, 0x2044, 0x6544, 0x7470, 0x202e, 0x3123, 0x4b0a, 0x7a61, - 0x6e75, 0x7261, 0x2069, 0x7354, 0x6b75, 0x6d61, 0x746f, 0x206f, - 0x6553, 0x7463, 0x6f69, 0x206e, 0x614d, 0x616e, 0x6567, 0x0a72 -}; - -static const UINT16 swtrilgySecurity[57] = -{ - 0xffff, - 0x3d3d, 0x3d3d, 0x203d, 0x5453, 0x5241, 0x5720, 0x5241, 0x2053, - 0x3d3d, 0x3d3d, 0x0a3d, 0x6f43, 0x7970, 0x6952, 0x6867, 0x2074, - 0x4553, 0x4147, 0x4520, 0x746e, 0x7265, 0x7270, 0x7369, 0x7365, - 0x202c, 0x744c, 0x2e64, 0x410a, 0x756d, 0x6573, 0x656d, 0x746e, - 0x5220, 0x4426, 0x4420, 0x7065, 0x2e74, 0x2320, 0x3231, 0x4b0a, - 0x7461, 0x7573, 0x6179, 0x7573, 0x4120, 0x646e, 0x206f, 0x2026, - 0x614b, 0x6f79, 0x6f6b, 0x5920, 0x6d61, 0x6d61, 0x746f, 0x0a6f -}; - -static const UINT16 fvipers2Security[65] = -{ - 0x2a2a, - 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x202a, 0x5b5b, - 0x4620, 0x6769, 0x7468, 0x6e69, 0x2067, 0x6956, 0x6570, 0x7372, - 0x3220, 0x5d20, 0x205d, 0x6e69, 0x3c20, 0x4d3c, 0x444f, 0x4c45, - 0x332d, 0x3e3e, 0x4320, 0x706f, 0x7279, 0x6769, 0x7468, 0x2820, - 0x2943, 0x3931, 0x3839, 0x5320, 0x4745, 0x2041, 0x6e45, 0x6574, - 0x7072, 0x6972, 0x6573, 0x2c73, 0x544c, 0x2e44, 0x2020, 0x4120, - 0x6c6c, 0x7220, 0x6769, 0x7468, 0x7220, 0x7365, 0x7265, 0x6576, - 0x2e64, 0x2a20, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a, 0x2a2a -}; - + if (addr < 0x8000) + return (*(uint32_t *) &securityRAM[addr * 4]) >> 16; + return 0; +} + UINT32 CModel3::ReadSecurity(unsigned reg) { - UINT32 data; - - switch (reg) - { - case 0x00: // Status - return 0; - case 0x1C: // Data - if (!strcmp(Game->id, "spikeout") || !strcmp(Game->id, "spikeofe")) - { - data = (spikeoutSecurity[securityPtr++] << 16); - securityPtr %= (sizeof(spikeoutSecurity)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "vs298") || - !strcmp(Game->id, "vs2v991") || !strcmp(Game->id, "vs299") || - !strcmp(Game->id, "vs299a") || !strcmp(Game->id, "vs299b")) - { - data = (vs298Security[securityPtr++] << 16); - securityPtr %= (sizeof(vs298Security)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax")) - { - data = (ecaSecurity[securityPtr++] << 16); - securityPtr %= (sizeof(ecaSecurity)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "oceanhun")) - { - data = (oceanhunSecurity[securityPtr++] << 16); - securityPtr %= (sizeof(oceanhunSecurity)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "swtrilgy") || !strcmp(Game->id, "swtrilgya")) - { - data = (swtrilgySecurity[securityPtr++] << 16); - securityPtr %= (sizeof(swtrilgySecurity)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "fvipers2")) - { - data = (fvipers2Security[securityPtr++] << 16); - securityPtr %= (sizeof(fvipers2Security)/sizeof(UINT16)); - } - else if (!strcmp(Game->id, "von2")) - data = 0xFFFFFFFF; - else - { - data = 0xFFFFFFFF; - DebugLog("Security read: reg=%X, PC=%08X, LR=%08X\n", reg, ppc_get_pc(), ppc_get_lr()); - } - return data; - default: - DebugLog("Security read: reg=%X\n", reg); - break; - } - - return 0xFFFFFFFF; + 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; + } + + return 0xFFFFFFFF; } void CModel3::WriteSecurity(unsigned reg, UINT32 data) { - DebugLog("Security write: reg=%X, data=%08X (PC=%08X, LR=%08X)\n", reg, data, ppc_get_pc(), ppc_get_lr()); + 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: + DebugLog("Security write: reg=%X, data=%08X (PC=%08X, LR=%08X)\n", reg, data, ppc_get_pc(), ppc_get_lr()); + break; + } } @@ -781,34 +714,34 @@ void CModel3::WriteSecurity(unsigned reg, UINT32 data) ******************************************************************************/ UINT32 CModel3::ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset) -{ - if ((bits==8) || (bits==16)) - { - DebugLog("Model 3: %d-bit PCI read request for reg=%02X\n", bits, reg); - return 0; - } - - switch (device) - { - case 16: // Used by Daytona 2 - switch (reg) - { - case 0: // PCI vendor and device ID - return 0x182711DB; - default: - break; - } - default: - break; - } +{ + if ((bits==8) || (bits==16)) + { + DebugLog("Model 3: %d-bit PCI read request for reg=%02X\n", bits, reg); + return 0; + } + + 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; + DebugLog("Model 3: PCI %d-bit write request for device=%d, reg=%02X\n", bits, device, reg); + return 0; } - + void CModel3::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data) { - DebugLog("Model 3: PCI %d-bit write request for device=%d, reg=%02X, data=%08X\n", bits, device, reg, data); + DebugLog("Model 3: PCI %d-bit write request for device=%d, reg=%02X, data=%08X\n", bits, device, reg, data); } @@ -827,62 +760,61 @@ void CModel3::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, // Set the CROM bank index (active low logic) void CModel3::SetCROMBank(unsigned idx) { - 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()); - //printf("CROM bank setting: %d (%02X), PC=%08X, LR=%08X\n", idx, cromBankReg, ppc_get_pc(), ppc_get_lr()); + 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()); } UINT8 CModel3::ReadSystemRegister(unsigned reg) { - 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 - return (GPU.ReadTAP()<< 5); - default: - //DebugLog("System register %02X read\n", reg); - break; - } - - return 0xFF; + 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 + return (GPU.ReadTAP()<< 5); + default: + //DebugLog("System register %02X read\n", reg); + break; + } + + return 0xFF; } void CModel3::WriteSystemRegister(unsigned reg, UINT8 data) { - 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 - DebugLog("IRQ SETTING! %02X=%02X\n", reg, data); - break; - case 0x0C: // JTAG Test Access Port - GPU.WriteTAP((data>>6)&1,(data>>2)&1,(data>>5)&1,(data>>7)&1); // TCK, TMS, TDI, TRST - break; - case 0x0D: - case 0x0E: - case 0x0F: - case 0x1C: // LED control? - break; - default: - //DebugLog("System register %02X=%02X\n", reg, data); - break; - } + 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 + DebugLog("IRQ SETTING! %02X=%02X\n", reg, data); + break; + case 0x0C: // JTAG Test Access Port + GPU.WriteTAP((data>>6)&1,(data>>2)&1,(data>>5)&1,(data>>7)&1); // TCK, TMS, TDI, TRST + break; + case 0x0D: + case 0x0E: + case 0x0F: + case 0x1C: // LED control? + break; + default: + //DebugLog("System register %02X=%02X\n", reg, data); + break; + } } @@ -910,311 +842,311 @@ void CModel3::WriteSystemRegister(unsigned reg, UINT8 data) */ UINT8 CModel3::Read8(UINT32 addr) { - // RAM (most frequently accessed) - if (addr<0x00800000) - return ram[addr^3]; - - // Other - switch ((addr>>24)) - { - // CROM - case 0xFF: - if (addr < 0xFF800000) - return cromBank[(addr&0x7FFFFF)^3]; - else - return crom[(addr&0x7FFFFF)^3]; + // RAM (most frequently accessed) + if (addr<0x00800000) + return ram[addr^3]; + + // 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); + // 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 - case 0x08: - if ((addr&0xF) == 4) // MIDI control port - return 0xFF; // one of these bits (0x80?) indicates "ready" - break; + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Inputs + case 0x04: + return ReadInputs(addr&0x3F); + + // Sound Board + case 0x08: + if ((addr&0xF) == 4) // MIDI control port + return 0xFF; // one of these bits (0x80?) indicates "ready" + break; - // System registers - case 0x10: - return ReadSystemRegister(addr&0x3F); + // 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; + // 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: - break; - } + // Unknown + default: + break; + } - break; + break; - // 53C810 SCSI - case 0xC0: // only on Step 1.0 - if (Game->step != 0x10) // check for Step 1.0 - break; - case 0xF9: - case 0xC1: - return SCSI.ReadRegister(addr&0xFF); + // 53C810 SCSI + case 0xC0: // only on Step 1.0 + if (Game->step != 0x10) + break; + case 0xF9: + case 0xC1: + return SCSI.ReadRegister(addr&0xFF); - // Unknown - default: - break; - } + // Unknown + default: + break; + } - DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr); - return 0xFF; + DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr); + return 0xFF; } UINT16 CModel3::Read16(UINT32 addr) { - UINT16 data; - - if ((addr&1)) - { - data = Read8(addr+0)<<8; - data |= Read8(addr+1); - return data; - } + UINT16 data; + + if ((addr&1)) + { + data = Read8(addr+0)<<8; + data |= Read8(addr+1); + return data; + } - // RAM (most frequently accessed) - if (addr<0x00800000) - return *(UINT16 *) &ram[addr^2]; + // RAM (most frequently accessed) + 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]; - - // Various - case 0xF0: - case 0xFE: // mirror - - switch ((addr>>16)&0xFF) - { - // Backup RAM - case 0x0C: - case 0x0D: - return *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2]; - - // Sound Board - case 0x08: - //printf("PPC: Read16 %08X\n", addr); - break; + // Other + switch ((addr>>24)) + { + // CROM + case 0xFF: + if (addr < 0xFF800000) + return *(UINT16 *) &cromBank[(addr&0x7FFFFF)^2]; + else + return *(UINT16 *) &crom[(addr&0x7FFFFF)^2]; + + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Backup RAM + case 0x0C: + case 0x0D: + return *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2]; + + // Sound Board + case 0x08: + //printf("PPC: Read16 %08X\n", addr); + break; - // MPC105 - case 0xC0: // F0C00CF8 - return PCIBridge.ReadPCIConfigData(16,addr&3); + // 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); + // 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); - // Unknown - default: - break; - } + // Unknown + default: + break; + } - break; + break; - // Unknown - default: - break; - } + // Unknown + default: + break; + } - DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr); - return 0xFFFF; + DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr); + return 0xFFFF; } UINT32 CModel3::Read32(UINT32 addr) { - UINT32 data; + UINT32 data; - if ((addr&3)) - { - data = Read16(addr+0)<<16; - data |= Read16(addr+2); - return 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]; + // RAM (most frequently accessed) + if (addr < 0x00800000) + return *(UINT32 *) &ram[addr]; - // Other - switch ((addr>>24)) - { - // CROM - case 0xFF: - if (addr < 0xFF800000) - return *(UINT32 *) &cromBank[(addr&0x7FFFFF)]; - else - return *(UINT32 *) &crom[(addr&0x7FFFFF)]; + // Other + switch ((addr>>24)) + { + // CROM + case 0xFF: + if (addr < 0xFF800000) + return *(UINT32 *) &cromBank[(addr&0x7FFFFF)]; + else + return *(UINT32 *) &crom[(addr&0x7FFFFF)]; - // Real3D registers - case 0x84: - return GPU.ReadRegister(addr&0x3F); - - // Real3D DMA - case 0xC2: - data = GPU.ReadDMARegister32(addr&0xFF); - return FLIPENDIAN32(data); + // Real3D registers + case 0x84: + return GPU.ReadRegister(addr&0x3F); + + // Real3D DMA + case 0xC2: + data = GPU.ReadDMARegister32(addr&0xFF); + return FLIPENDIAN32(data); - // Various - case 0xF0: - case 0xFE: // mirror - - switch ((addr>>16)&0xFF) - { - // Inputs - 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; - - // Sound Board - case 0x08: - //printf("PPC: Read32 %08X\n", addr); - break; + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Inputs + 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; + + // Sound Board + case 0x08: + //printf("PPC: Read32 %08X\n", addr); + break; - // Backup RAM - case 0x0C: - case 0x0D: - return *(UINT32 *) &backupRAM[(addr&0x1FFFF)]; + // 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; + // 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); + // 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); + // 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; + // 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 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); - - // Unknown - default: - break; - } + // Security board registers + case 0x1A: + return ReadSecurity(addr&0x3F); + + // Unknown + default: + break; + } - break; + break; - // Tile generator - case 0xF1: - if (addr==0xF1180000) // fixes 2D graphics (TO-DO: integrate register reads into TileGen.cpp) - return 0; + // Tile generator + case 0xF1: + if (addr==0xF1180000) // fixes 2D graphics (TO-DO: integrate register reads into TileGen.cpp) + return 0; - // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC - if (addr < 0xF1120000) - { - data = TileGen.ReadRAM(addr&0x1FFFFF); - return FLIPENDIAN32(data); - } + // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC + if (addr < 0xF1120000) + { + data = TileGen.ReadRAM(addr&0x1FFFFF); + return FLIPENDIAN32(data); + } - break; + break; - // 53C810 SCSI - case 0xC0: // only on Step 1.0 - if (Game->step != 0x10) // check for Step 1.0 - break; - case 0xF9: - case 0xC1: - 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; + // 53C810 SCSI + case 0xC0: // only on Step 1.0 + if (Game->step != 0x10) // check for Step 1.0 + break; + case 0xF9: + case 0xC1: + 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; - // Unknown - default: - break; - } + // Unknown + default: + break; + } - DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr); - return 0xFFFFFFFF; + DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr); + return 0xFFFFFFFF; } UINT64 CModel3::Read64(UINT32 addr) { - UINT64 data; + UINT64 data; - data = Read32(addr+0); - data <<= 32; - data |= Read32(addr+4); + data = Read32(addr+0); + data <<= 32; + data |= Read32(addr+4); - return data; + return data; } /* @@ -1227,338 +1159,339 @@ UINT64 CModel3::Read64(UINT32 addr) */ void CModel3::Write8(UINT32 addr, UINT8 data) { - // RAM (most frequently accessed) - if (addr < 0x00800000) - { - ram[addr^3] = data; - return; - } + // 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; + // Other + switch ((addr>>24)) + { + // Real3D DMA + case 0xC2: + GPU.WriteDMARegister8(addr&0xFF,data); + break; - // Various - case 0xF0: - case 0xFE: // mirror - - switch ((addr>>16)&0xFF) - { - // Inputs - case 0x04: - WriteInputs(addr&0x3F,data); - break; - - // 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; - - // Backup RAM - case 0x0C: - case 0x0D: - backupRAM[(addr&0x1FFFF)^3] = data; - break; + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Inputs + case 0x04: + WriteInputs(addr&0x3F,data); + break; + + // 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; + + // Backup RAM + case 0x0C: + case 0x0D: + backupRAM[(addr&0x1FFFF)^3] = data; + break; - // System registers - case 0x10: - WriteSystemRegister(addr&0x3F,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; - - // Unknown - default: - break; - } + // RTC + case 0x14: + if ((addr&3)==0) + RTC.WriteRegister((addr>>2)&0xF,data); + break; + + // Unknown + default: + break; + } - DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); - break; + DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); + break; - // MPC105/106 - case 0xF8: - PCIBridge.WriteRegister(addr&0xFF,data); - break; + // MPC105/106 + case 0xF8: + PCIBridge.WriteRegister(addr&0xFF,data); + break; - // 53C810 SCSI - case 0xC0: // only on Step 1.0 - if (Game->step != 0x10) - goto Unknown8; - case 0xF9: - case 0xC1: - SCSI.WriteRegister(addr&0xFF,data); - break; + // 53C810 SCSI + case 0xC0: // only on Step 1.0 + if (Game->step != 0x10) + goto Unknown8; + case 0xF9: + case 0xC1: + SCSI.WriteRegister(addr&0xFF,data); + break; - // Unknown: - default: - Unknown8: - DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); - break; - } + // Unknown: + default: + Unknown8: + DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); + break; + } } void CModel3::Write16(UINT32 addr, UINT16 data) { - if ((addr&1)) - { - Write8(addr+0,data>>8); - Write8(addr+1,data&0xFF); - return; - } + 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; - } + // RAM (most frequently accessed) + if (addr < 0x00800000) + { + *(UINT16 *) &ram[addr^2] = data; + return; + } - // Other - switch ((addr>>24)) - { - // Various - case 0xF0: - case 0xFE: // mirror - - switch ((addr>>16)&0xFF) - { - // Sound Board - case 0x08: - //printf("%08X=%04X\n", addr, data); - break; - - // Backup RAM - case 0x0C: - case 0x0D: - *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data; - break; - - // MPC105 - case 0xC0: // F0C00CF8 - PCIBridge.WritePCIConfigData(16,addr&2,data); - break; + // Other + switch ((addr>>24)) + { + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Sound Board + case 0x08: + //printf("%08X=%04X\n", addr, data); + break; + + // Backup RAM + case 0x0C: + case 0x0D: + *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data; + break; + + // MPC105 + case 0xC0: // F0C00CF8 + PCIBridge.WritePCIConfigData(16,addr&2,data); + break; - // Unknown - default: - break; - } + // Unknown + default: + break; + } - DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data); - break; + DebugLog("PC=%08X\twrite16 : %08X=%04X\n", ppc_get_pc(), addr, data); + break; - // 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; + // 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; - // Unknown - default: - DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); - break; - } -} + // Unknown + default: + DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); + break; + } +} void CModel3::Write32(UINT32 addr, UINT32 data) { // Debugging VF3 missing stage textures //if ((addr == 0xb4000)) printf("B4000 written @ pc=%08x lr=%08x\n", ppc_get_pc(), ppc_get_lr()); - if ((addr&3)) - { - Write16(addr+0,data>>16); - Write16(addr+2,data); - return; - } + 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; - } + // RAM (most frequently accessed) + if (addr<0x00800000) + { + *(UINT32 *) &ram[addr] = data; + return; + } - // Other - switch ((addr>>24)) - { - // Real3D trigger - case 0x88: // 88000000 - GPU.Flush(); - break; - - // Real3D low culling RAM - case 0x8C: // 8C000000-8C400000 - GPU.WriteLowCullingRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); - break; + // Other + switch ((addr>>24)) + { + // Real3D trigger + case 0x88: // 88000000 + GPU.Flush(); + break; + + // 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 high culling RAM + case 0x8E: // 8E000000-8E100000 + GPU.WriteHighCullingRAM(addr&0xFFFFF,FLIPENDIAN32(data)); + break; - // Real3D texture port - case 0x90: // 90000000-90000018 - GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data)); - break; + // Real3D texture port + case 0x90: // 90000000-90000018 + GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data)); + break; - // Real3D texture FIFO - case 0x94: // 94000000-94100000 - GPU.WriteTextureFIFO(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; + // Real3D polygon RAM + case 0x98: // 98000000-98400000 + GPU.WritePolygonRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); + break; - // Real3D DMA - case 0xC2: // C2000000-C2000100 - GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data)); - break; + // Real3D DMA + case 0xC2: // C2000000-C2000100 + GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data)); + break; - // Various - case 0xF0: - case 0xFE: // mirror - - switch ((addr>>16)&0xFF) - { - // Inputs - 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; + // Various + case 0xF0: + case 0xFE: // mirror + + switch ((addr>>16)&0xFF) + { + // Inputs + 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; - - // Backup RAM - case 0x0C: - case 0x0D: - *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data; - break; + // Sound Board + case 0x08: + //printf("PPC: %08X=%08X\n", addr, data); + break; + + // Backup RAM + case 0x0C: + case 0x0D: + *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data; + break; - // MPC105 - case 0x80: // F0800CF8 (never observed at 0xFExxxxxx) - PCIBridge.WritePCIConfigAddress(data); - break; + // MPC105 + case 0x80: // F0800CF8 (never observed at 0xFExxxxxx) + PCIBridge.WritePCIConfigAddress(data); + 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 - PCIBridge.WritePCIConfigAddress(data); - else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 - PCIBridge.WritePCIConfigData(32,0,data); - 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 + 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; - - // RTC - case 0x14: - RTC.WriteRegister((addr>>2)&0xF,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; + + // RTC + case 0x14: + RTC.WriteRegister((addr>>2)&0xF,data); + break; - // Security board RAM - case 0x18: - *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data; - break; + // Security board RAM + case 0x18: + case 0x19: + *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data; + break; - // Security board registers - case 0x1A: - WriteSecurity(addr&0x3F,data); - break; - - // Unknown - default: - break; - } + // Security board registers + case 0x1A: + WriteSecurity(addr&0x3F,data); + break; + + // Unknown + default: + break; + } - DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); - break; + DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); + 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); - TileGen.WriteRAM(addr&0x1FFFFF,data); - break; - } - else if ((addr>=0xF1180000) && (addr<0xF1180100)) - { - TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data)); - 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); + TileGen.WriteRAM(addr&0x1FFFFF,data); + break; + } + else if ((addr>=0xF1180000) && (addr<0xF1180100)) + { + TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data)); + break; + } - goto Unknown32; + 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; + // 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 - case 0xC0: // step 1.0 only - if (Game->step != 0x10) - goto Unknown32; - case 0xF9: - case 0xC1: - 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; + // 53C810 SCSI + case 0xC0: // step 1.0 only + if (Game->step != 0x10) + goto Unknown32; + case 0xF9: + case 0xC1: + 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: - DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); - break; - } + // Unknown + default: + Unknown32: + DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); + break; + } } void CModel3::Write64(UINT32 addr, UINT64 data) @@ -1589,156 +1522,156 @@ void CModel3::Write64(UINT32 addr, UINT64 data) */ UINT8 CModel3::Read8(UINT32 addr) { - if (addr<0x00800000) - return ram[addr^3]; - else if ((addr>=0xFF000000) && (addr<0xFF800000)) - return cromBank[(addr&0x7FFFFF)^3]; - else if (addr>=0xFF800000) - return crom[(addr&0x7FFFFF)^3]; - else if ((addr>=0xC2000000) && (addr<0xC2000100)) - return GPU.ReadDMARegister8(addr&0xFF); - else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) - return ReadInputs(addr&0x3F); - else if (((addr>=0xF0080000) && (addr<=0xF0080007)) || ((addr>=0xFE080000) && (addr<=0xFE080007))) - { - if ((addr&0xF) == 4) // MIDI control port - return 0xFF; - } - else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) - return backupRAM[(addr&0x1FFFF)^3]; - else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) - return ReadSystemRegister(addr&0x3F); - else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) - { - if ((addr&3)==1) // battery voltage test - return 0x03; - else if ((addr&3)==0) - return RTC.ReadRegister((addr>>2)&0xF); - return 0; - } - else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) - return SCSI.ReadRegister(addr&0xFF); - - DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr); - return 0xFF; + if (addr<0x00800000) + return ram[addr^3]; + else if ((addr>=0xFF000000) && (addr<0xFF800000)) + return cromBank[(addr&0x7FFFFF)^3]; + else if (addr>=0xFF800000) + return crom[(addr&0x7FFFFF)^3]; + else if ((addr>=0xC2000000) && (addr<0xC2000100)) + return GPU.ReadDMARegister8(addr&0xFF); + else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) + return ReadInputs(addr&0x3F); + else if (((addr>=0xF0080000) && (addr<=0xF0080007)) || ((addr>=0xFE080000) && (addr<=0xFE080007))) + { + if ((addr&0xF) == 4) // MIDI control port + return 0xFF; + } + else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) + return backupRAM[(addr&0x1FFFF)^3]; + else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) + return ReadSystemRegister(addr&0x3F); + else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) + { + if ((addr&3)==1) // battery voltage test + return 0x03; + else if ((addr&3)==0) + return RTC.ReadRegister((addr>>2)&0xF); + return 0; + } + else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) + return SCSI.ReadRegister(addr&0xFF); + + DebugLog("PC=%08X\tread8 : %08X\n", ppc_get_pc(), addr); + return 0xFF; } UINT16 CModel3::Read16(UINT32 addr) { - UINT16 data; - - if ((addr&1)) - { - data = Read8(addr+0)<<8; - data |= Read8(addr+1); - return data; - } - - if (addr<0x00800000) - return *(UINT16 *) &ram[addr^2]; - else if ((addr>=0xFF000000) && (addr<0xFF800000)) - return *(UINT16 *) &cromBank[(addr&0x7FFFFF)^2]; - else if (addr>=0xFF800000) - return *(UINT16 *) &crom[(addr&0x7FFFFF)^2]; - else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) - return *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2]; - else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 - return PCIBridge.ReadPCIConfigData(16,addr&3); - else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 - return PCIBridge.ReadPCIConfigData(16,addr&3); + UINT16 data; + + if ((addr&1)) + { + data = Read8(addr+0)<<8; + data |= Read8(addr+1); + return data; + } + + if (addr<0x00800000) + return *(UINT16 *) &ram[addr^2]; + else if ((addr>=0xFF000000) && (addr<0xFF800000)) + return *(UINT16 *) &cromBank[(addr&0x7FFFFF)^2]; + else if (addr>=0xFF800000) + return *(UINT16 *) &crom[(addr&0x7FFFFF)^2]; + else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) + return *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2]; + else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 + return PCIBridge.ReadPCIConfigData(16,addr&3); + else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 + return PCIBridge.ReadPCIConfigData(16,addr&3); - DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr); - return 0xFFFF; + DebugLog("PC=%08X\tread16: %08X\n", ppc_get_pc(), addr); + return 0xFFFF; } UINT32 CModel3::Read32(UINT32 addr) { - UINT32 data; - - if ((addr&3)) - { - data = Read16(addr+0)<<16; - data |= Read16(addr+2); - return data; - } + UINT32 data; + + if ((addr&3)) + { + data = Read16(addr+0)<<16; + data |= Read16(addr+2); + return data; + } - if (addr<0x00800000) - return *(UINT32 *) &ram[addr]; - else if ((addr>=0xFF000000) && (addr<0xFF800000)) - return *(UINT32 *) &cromBank[(addr&0x7FFFFF)]; - else if (addr>=0xFF800000) - return *(UINT32 *) &crom[(addr&0x7FFFFF)]; - else if ((addr>=0x84000000) && (addr<0x8400003F)) - return GPU.ReadRegister(addr&0x3F); - else if ((addr>=0xC2000000) && (addr<0xC2000100)) - { - data = GPU.ReadDMARegister32(addr&0xFF); - return FLIPENDIAN32(data); - } - else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) - { - 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; - } - else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) - return *(UINT32 *) &backupRAM[(addr&0x1FFFF)]; - else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) - { - 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; - } - else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 - return PCIBridge.ReadPCIConfigData(32,0); - else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 - return PCIBridge.ReadPCIConfigData(32,0); - else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) - { - data = (RTC.ReadRegister((addr>>2)&0xF) << 24); - data |= 0x00030000; // set these bits to pass battery voltage test - return data; - } - else if (((addr>=0xF0180000) && (addr<0xF019FFFF)) || ((addr>=0xFE180000) && (addr<0xFE19FFFF))) - return *(UINT32 *) &securityRAM[(addr&0x1FFFF)]; // so far, only 32-bit access observed, so we use little endian access - else if (((addr>=0xF01A0000) && (addr<0xF01A003F)) || ((addr>=0xFE1A0000) && (addr<0xFE1A003F))) - return ReadSecurity(addr&0x3F); - else if ((addr>=0xF1000000) && (addr<0xF1120000)) - { - // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC - data = TileGen.ReadRAM(addr&0x1FFFFF); - return FLIPENDIAN32(data); - } - else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) - { - 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; - } - - // FIXES 2D GRAPHICS (to-do: integrate this into tilegen.cpp) - if (addr==0xF1180000) - return 0; - - DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr); - return 0xFFFFFFFF; + if (addr<0x00800000) + return *(UINT32 *) &ram[addr]; + else if ((addr>=0xFF000000) && (addr<0xFF800000)) + return *(UINT32 *) &cromBank[(addr&0x7FFFFF)]; + else if (addr>=0xFF800000) + return *(UINT32 *) &crom[(addr&0x7FFFFF)]; + else if ((addr>=0x84000000) && (addr<0x8400003F)) + return GPU.ReadRegister(addr&0x3F); + else if ((addr>=0xC2000000) && (addr<0xC2000100)) + { + data = GPU.ReadDMARegister32(addr&0xFF); + return FLIPENDIAN32(data); + } + else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) + { + 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; + } + else if (((addr>=0xF00C0000) && (addr<0xF00DFFFF)) || ((addr>=0xFE0C0000) && (addr<0xFE0DFFFF))) + return *(UINT32 *) &backupRAM[(addr&0x1FFFF)]; + else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) + { + 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; + } + else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 + return PCIBridge.ReadPCIConfigData(32,0); + else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 + return PCIBridge.ReadPCIConfigData(32,0); + else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) + { + data = (RTC.ReadRegister((addr>>2)&0xF) << 24); + data |= 0x00030000; // set these bits to pass battery voltage test + return data; + } + else if (((addr>=0xF0180000) && (addr<0xF019FFFF)) || ((addr>=0xFE180000) && (addr<0xFE19FFFF))) + return *(UINT32 *) &securityRAM[(addr&0x1FFFF)]; // so far, only 32-bit access observed, so we use little endian access + else if (((addr>=0xF01A0000) && (addr<0xF01A003F)) || ((addr>=0xFE1A0000) && (addr<0xFE1A003F))) + return ReadSecurity(addr&0x3F); + else if ((addr>=0xF1000000) && (addr<0xF1120000)) + { + // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC + data = TileGen.ReadRAM(addr&0x1FFFFF); + return FLIPENDIAN32(data); + } + else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) + { + 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; + } + + // FIXES 2D GRAPHICS (to-do: integrate this into tilegen.cpp) + if (addr==0xF1180000) + return 0; + + DebugLog("PC=%08X\tread32: %08X\n", ppc_get_pc(), addr); + return 0xFFFFFFFF; } UINT64 CModel3::Read64(UINT32 addr) { UINT64 data; - data = Read32(addr+0); - data <<= 32; - data |= Read32(addr+4); + data = Read32(addr+0); + data <<= 32; + data |= Read32(addr+4); - return data; + return data; } /* @@ -1750,151 +1683,151 @@ UINT64 CModel3::Read64(UINT32 addr) * Write handlers. */ void CModel3::Write8(UINT32 addr, UINT8 data) -{ - if (addr<0x00800000) - ram[addr^3] = data; - else if ((addr>=0xC2000000) && (addr<0xC2000100)) - GPU.WriteDMARegister8(addr&0xFF,data); - else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) - WriteInputs(addr&0x3F,data); - else if (((addr>=0xF0080000) && (addr<=0xF0080007)) || ((addr>=0xFE080000) && (addr<=0xFE080007))) - { - if ((addr&0xF) == 0) // MIDI data port - SoundBoard.WriteMIDIPort(data); - else if ((addr&0xF) == 4) // MIDI control port - midiCtrlPort = data; - } - else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) - backupRAM[(addr&0x1FFFF)^3] = data; - else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) - WriteSystemRegister(addr&0x3F,data); - else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) - { - if ((addr&3)==0) - RTC.WriteRegister((addr>>2)&0xF,data); - } - else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) - PCIBridge.WriteRegister(addr&0xFF,data); - else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) - SCSI.WriteRegister(addr&0xFF,data); - else - { - DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); - //printf("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); - } +{ + if (addr<0x00800000) + ram[addr^3] = data; + else if ((addr>=0xC2000000) && (addr<0xC2000100)) + GPU.WriteDMARegister8(addr&0xFF,data); + else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) + WriteInputs(addr&0x3F,data); + else if (((addr>=0xF0080000) && (addr<=0xF0080007)) || ((addr>=0xFE080000) && (addr<=0xFE080007))) + { + if ((addr&0xF) == 0) // MIDI data port + SoundBoard.WriteMIDIPort(data); + else if ((addr&0xF) == 4) // MIDI control port + midiCtrlPort = data; + } + else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) + backupRAM[(addr&0x1FFFF)^3] = data; + else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) + WriteSystemRegister(addr&0x3F,data); + else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) + { + if ((addr&3)==0) + RTC.WriteRegister((addr>>2)&0xF,data); + } + else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) + PCIBridge.WriteRegister(addr&0xFF,data); + else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) + SCSI.WriteRegister(addr&0xFF,data); + else + { + DebugLog("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); + //printf("PC=%08X\twrite8 : %08X=%02X\n", ppc_get_pc(), addr, data); + } } void CModel3::Write16(UINT32 addr, UINT16 data) { - if ((addr&1)) - { - Write8(addr+0,data>>8); - Write8(addr+1,data&0xFF); - return; - } - - if (addr<0x00800000) - *(UINT16 *) &ram[addr^2] = data; - else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) - *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data; - else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) - PCIBridge.WritePCIConfigData(16,addr&2,data); - else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) - { - // Write in big endian order, like a real PowerPC - PCIBridge.WriteRegister((addr&0xFF)+0,data>>8); - PCIBridge.WriteRegister((addr&0xFF)+1,data&0xFF); - } - else - { - DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); - //printf("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); - } + if ((addr&1)) + { + Write8(addr+0,data>>8); + Write8(addr+1,data&0xFF); + return; + } + + if (addr<0x00800000) + *(UINT16 *) &ram[addr^2] = data; + else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) + *(UINT16 *) &backupRAM[(addr&0x1FFFF)^2] = data; + else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) + PCIBridge.WritePCIConfigData(16,addr&2,data); + else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) + { + // Write in big endian order, like a real PowerPC + PCIBridge.WriteRegister((addr&0xFF)+0,data>>8); + PCIBridge.WriteRegister((addr&0xFF)+1,data&0xFF); + } + else + { + DebugLog("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); + //printf("PC=%08X\twrite16: %08X=%04X\n", ppc_get_pc(), addr, data); + } } void CModel3::Write32(UINT32 addr, UINT32 data) -{ - if ((addr&3)) - { - Write16(addr+0,data>>16); - Write16(addr+2,data); - return; - } +{ + if ((addr&3)) + { + Write16(addr+0,data>>16); + Write16(addr+2,data); + return; + } - if (addr<0x00800000) - *(UINT32 *) &ram[addr] = data; - else if ((addr>=0x88000000) && (addr<0x88000008)) - GPU.Flush(); - else if ((addr>=0x8C000000) && (addr<0x8C400000)) - GPU.WriteLowCullingRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); - else if ((addr>=0x8E000000) && (addr<0x8E100000)) - GPU.WriteHighCullingRAM(addr&0xFFFFF,FLIPENDIAN32(data)); - else if ((addr>=0x90000000) && (addr<0x90000018)) - GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data)); - else if ((addr>=0x94000000) && (addr<0x94100000)) - GPU.WriteTextureFIFO(FLIPENDIAN32(data)); - else if ((addr>=0x98000000) && (addr<0x98400000)) - GPU.WritePolygonRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); - else if ((addr>=0xC2000000) && (addr<0xC2000100)) - GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data)); - else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) - { - 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); - } - else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) - *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data; - else if ((addr>=0xF0800CF8) && (addr<0xF0800D00)) // MPC105 - PCIBridge.WritePCIConfigAddress(data); - else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 - PCIBridge.WritePCIConfigData(32,0,data); - else if ((addr>=0xFEC00000) && (addr<0xFEE00000)) // MPC106 - PCIBridge.WritePCIConfigAddress(data); - else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 - PCIBridge.WritePCIConfigData(32,0,data); - else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) - { - 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); - } - else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) - RTC.WriteRegister((addr>>2)&0xF,data); - else if (((addr>=0xF0180000) && (addr<0xF019FFFF)) || ((addr>=0xFE180000) && (addr<0xFE19FFFF))) - *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data; // so far, only 32-bit access observed, so just store little endian - else if (((addr>=0xF01A0000) && (addr<0xF01A003F)) || ((addr>=0xFE1A0000) && (addr<0xFE1A003F))) - WriteSecurity(addr&0x3F,data); - else if ((addr>=0xF1000000) && (addr<0xF1120000)) - { - // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC - data = FLIPENDIAN32(data); - TileGen.WriteRAM(addr&0x1FFFFF,data); - } - else if ((addr>=0xF1180000) && (addr<0xF1180100)) - TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data)); - else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) - { - // 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); - } - else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) - { - 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); - } - else - { - //printf("%08X=%08X\n", addr, data); - DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); - } + if (addr<0x00800000) + *(UINT32 *) &ram[addr] = data; + else if ((addr>=0x88000000) && (addr<0x88000008)) + GPU.Flush(); + else if ((addr>=0x8C000000) && (addr<0x8C400000)) + GPU.WriteLowCullingRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); + else if ((addr>=0x8E000000) && (addr<0x8E100000)) + GPU.WriteHighCullingRAM(addr&0xFFFFF,FLIPENDIAN32(data)); + else if ((addr>=0x90000000) && (addr<0x90000018)) + GPU.WriteTexturePort(addr&0xFF,FLIPENDIAN32(data)); + else if ((addr>=0x94000000) && (addr<0x94100000)) + GPU.WriteTextureFIFO(FLIPENDIAN32(data)); + else if ((addr>=0x98000000) && (addr<0x98400000)) + GPU.WritePolygonRAM(addr&0x3FFFFF,FLIPENDIAN32(data)); + else if ((addr>=0xC2000000) && (addr<0xC2000100)) + GPU.WriteDMARegister32(addr&0xFF,FLIPENDIAN32(data)); + else if (((addr>=0xF0040000) && (addr<0xF0040040)) || ((addr>=0xFE040000) && (addr<0xFE040040))) + { + 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); + } + else if (((addr>=0xF00C0000) && (addr<0xF00E0000)) || ((addr>=0xFE0C0000) && (addr<0xFE0E0000))) + *(UINT32 *) &backupRAM[(addr&0x1FFFF)] = data; + else if ((addr>=0xF0800CF8) && (addr<0xF0800D00)) // MPC105 + PCIBridge.WritePCIConfigAddress(data); + else if ((addr>=0xF0C00CF8) && (addr<0xF0C00D00)) // MPC105 + PCIBridge.WritePCIConfigData(32,0,data); + else if ((addr>=0xFEC00000) && (addr<0xFEE00000)) // MPC106 + PCIBridge.WritePCIConfigAddress(data); + else if ((addr>=0xFEE00000) && (addr<0xFEF00000)) // MPC106 + PCIBridge.WritePCIConfigData(32,0,data); + else if (((addr>=0xF0100000) && (addr<0xF0100040)) || ((addr>=0xFE100000) && (addr<0xFE100040))) + { + 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); + } + else if (((addr>=0xF0140000) && (addr<0xF0140040)) || ((addr>=0xFE140000) && (addr<0xFE140040))) + RTC.WriteRegister((addr>>2)&0xF,data); + else if (((addr>=0xF0180000) && (addr<0xF019FFFF)) || ((addr>=0xFE180000) && (addr<0xFE19FFFF))) + *(UINT32 *) &securityRAM[(addr&0x1FFFF)] = data; // so far, only 32-bit access observed, so just store little endian + else if (((addr>=0xF01A0000) && (addr<0xF01A003F)) || ((addr>=0xFE1A0000) && (addr<0xFE1A003F))) + WriteSecurity(addr&0x3F,data); + else if ((addr>=0xF1000000) && (addr<0xF1120000)) + { + // Tile generator accesses its RAM as little endian, must flip for big endian PowerPC + data = FLIPENDIAN32(data); + TileGen.WriteRAM(addr&0x1FFFFF,data); + } + else if ((addr>=0xF1180000) && (addr<0xF1180100)) + TileGen.WriteRegister(addr&0xFF,FLIPENDIAN32(data)); + else if ((addr>=0xF8FFF000) && (addr<0xF8FFF100)) + { + // 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); + } + else if (((addr>=0xF9000000) && (addr<0xF9000100)) || ((addr>=0xC1000000) && (addr<0xC1000100)) || ((Game->step==0x10) && ((addr>=0xC0000000) && (addr<0xC0000100)))) + { + 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); + } + else + { + //printf("%08X=%08X\n", addr, data); + DebugLog("PC=%08X\twrite32: %08X=%08X\n", ppc_get_pc(), addr, data); + } } void CModel3::Write64(UINT32 addr, UINT64 data) @@ -1903,7 +1836,6 @@ void CModel3::Write64(UINT32 addr, UINT64 data) Write32(addr+4, (UINT32) data); } - #endif @@ -1913,945 +1845,950 @@ void CModel3::Write64(UINT32 addr, UINT64 data) void CModel3::SaveState(CBlockFile *SaveState) { - // 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)); - - // 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 - DriveBoard.SaveState(SaveState); + // 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)); + + // 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 + DriveBoard.SaveState(SaveState); + m_cryptoDevice.SaveState(SaveState); } void CModel3::LoadState(CBlockFile *SaveState) { - // Load Model 3 state - if (OKAY != SaveState->FindBlock("Model 3")) - { - ErrorLog("Unable to load Model 3 core state. Save state file is corrupt."); - return; - } - - 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)); - - // 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); - DriveBoard.LoadState(SaveState); + // Load Model 3 state + if (OKAY != SaveState->FindBlock("Model 3")) + { + ErrorLog("Unable to load Model 3 core state. Save state file is corrupt."); + return; + } + + 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; + SaveState->Write(&securityFirstRead, sizeof(securityFirstRead)); + m_securityFirstRead = securityFirstRead; + + // 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); + DriveBoard.LoadState(SaveState); + m_cryptoDevice.LoadState(SaveState); } void CModel3::SaveNVRAM(CBlockFile *NVRAM) { - // Load EEPROM - EEPROM.SaveState(NVRAM); + // Load EEPROM + EEPROM.SaveState(NVRAM); - // Save backup RAM - NVRAM->NewBlock("Backup RAM", __FILE__); - NVRAM->Write(backupRAM, 0x20000); + // Save backup RAM + NVRAM->NewBlock("Backup RAM", __FILE__); + NVRAM->Write(backupRAM, 0x20000); } void CModel3::LoadNVRAM(CBlockFile *NVRAM) { - // Load EEPROM - EEPROM.LoadState(NVRAM); - - // 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); + // Load EEPROM + EEPROM.LoadState(NVRAM); + + // 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); } void CModel3::ClearNVRAM(void) { - memset(backupRAM, 0, 0x20000); - EEPROM.Clear(); + memset(backupRAM, 0, 0x20000); + EEPROM.Clear(); } void CModel3::RunFrame(void) { - UINT32 start = CThread::GetTicks(); + UINT32 start = CThread::GetTicks(); - // See if currently running multi-threaded - if (g_Config.multiThreaded) - { - // If so, check all threads are up and running - if (!StartThreads()) - goto ThreadError; + // See if currently running multi-threaded + if (g_Config.multiThreaded) + { + // 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 - if (g_Config.gpuMultiThreaded && !ppcBrdThreadSync->Post() || - syncSndBrdThread && !sndBrdThreadSync->Post() || - DriveBoard.IsAttached() && !drvBrdThreadSync->Post()) - 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 + if ((g_Config.gpuMultiThreaded && !ppcBrdThreadSync->Post()) || + (syncSndBrdThread && !sndBrdThreadSync->Post()) || + (DriveBoard.IsAttached() && !drvBrdThreadSync->Post())) + goto ThreadError; - // If not multi-threading GPU, then run PPC main board for a frame and sync GPUs now in this thread - if (!g_Config.gpuMultiThreaded) - { - RunMainBoardFrame(); - SyncGPUs(); - } + // If not multi-threading GPU, then run PPC main board for a frame and sync GPUs now in this thread + if (!g_Config.gpuMultiThreaded) + { + RunMainBoardFrame(); + SyncGPUs(); + } - // Render frame - RenderFrame(); + // Render frame + RenderFrame(); - // Enter notify wait critical section - if (!notifyLock->Lock()) - goto ThreadError; + // 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) - while (g_Config.gpuMultiThreaded && !ppcBrdThreadDone || - syncSndBrdThread && !sndBrdThreadDone || - DriveBoard.IsAttached() && !drvBrdThreadDone) - { - if (!notifySync->Wait(notifyLock)) - goto ThreadError; - } - ppcBrdThreadDone = false; - sndBrdThreadDone = false; - drvBrdThreadDone = false; - - // Leave notify wait critical section - if (!notifyLock->Unlock()) - 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) + while ((g_Config.gpuMultiThreaded && !ppcBrdThreadDone) || + (syncSndBrdThread && !sndBrdThreadDone) || + (DriveBoard.IsAttached() && !drvBrdThreadDone)) + { + if (!notifySync->Wait(notifyLock)) + goto ThreadError; + } + ppcBrdThreadDone = false; + sndBrdThreadDone = false; + drvBrdThreadDone = false; + + // Leave notify wait critical section + if (!notifyLock->Unlock()) + goto ThreadError; - // If multi-threading GPU, then sync GPUs last while PPC main board thread is waiting - if (g_Config.gpuMultiThreaded) - SyncGPUs(); - } - else - { - // 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(); - if (DriveBoard.IsAttached()) - RunDriveBoardFrame(); - } + // If multi-threading GPU, then sync GPUs last while PPC main board thread is waiting + if (g_Config.gpuMultiThreaded) + SyncGPUs(); + } + else + { + // 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(); + if (DriveBoard.IsAttached()) + RunDriveBoardFrame(); + } - timings.frameTicks = CThread::GetTicks() - start; + timings.frameTicks = CThread::GetTicks() - start; - return; + return; ThreadError: - ErrorLog("Threading error in CModel3::RunFrame: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; + ErrorLog("Threading error in CModel3::RunFrame: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; } void CModel3::RunMainBoardFrame(void) { - UINT32 start = CThread::GetTicks(); + UINT32 start = CThread::GetTicks(); - // Compute display and VBlank timings - unsigned ppcCycles = g_Config.GetPowerPCFrequency() * 1000000; - unsigned frameCycles = ppcCycles / 60; - unsigned vblCycles = (unsigned)((float) frameCycles * 2.5f/100.0f); // 2.5% vblank (ridiculously short and wrong but bigger values cause flicker in Daytona) - unsigned dispCycles = frameCycles - vblCycles; - - // Scale PPC timer ratio according to speed at which the PowerPC is being emulated so that the observed running frequency of the PPC timer - // 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 - // 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()); - - // Compute timing of the Real3D status bit. This value directly affects the speed at which all the games except Virtua Stiker 2 run. - // Currently it is not known exactly what this bit represents nor why such wildly varying values are needed for the different step models. - // The values below were arrived at by trial and error and clearly more investigation is required. If it turns out that the status bit is - // connected to the end of VBlank then the code below should be removed and the timing handled via GPU.VBlankEnd() instead. - unsigned statusCycles; - if (Game->step >= 0x20) - { - // For some reason, Fighting Vipers 2 and Daytona USA 2 require completely different timing to the rest of the step 2.x games - if (!strcmp(Game->id, "daytona2") || (Game->step == 0x20 && !strcmp(Game->id, "fvipers2"))) - statusCycles = (unsigned)((float)frameCycles * 24.0f/100.0f); - else - statusCycles = (unsigned)((float)frameCycles * 9.12f/100.0f); - } - else if (Game->step == 0x15) - statusCycles = (unsigned)((float)frameCycles * 5.5f/100.0f); - else - statusCycles = (unsigned)((float)frameCycles * 48.0f/100.0f); + // Compute display and VBlank timings + unsigned ppcCycles = g_Config.GetPowerPCFrequency() * 1000000; + unsigned frameCycles = ppcCycles / 60; + unsigned vblCycles = (unsigned)((float) frameCycles * 2.5f/100.0f); // 2.5% vblank (ridiculously short and wrong but bigger values cause flicker in Daytona) + unsigned dispCycles = frameCycles - vblCycles; + + // Scale PPC timer ratio according to speed at which the PowerPC is being emulated so that the observed running frequency of the PPC timer + // 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 + // 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()); + + // Compute timing of the Real3D status bit. This value directly affects the speed at which all the games except Virtua Stiker 2 run. + // Currently it is not known exactly what this bit represents nor why such wildly varying values are needed for the different step models. + // The values below were arrived at by trial and error and clearly more investigation is required. If it turns out that the status bit is + // connected to the end of VBlank then the code below should be removed and the timing handled via GPU.VBlankEnd() instead. + unsigned statusCycles; + if (Game->step >= 0x20) + { + // For some reason, Fighting Vipers 2 and Daytona USA 2 require completely different timing to the rest of the step 2.x games + if (!strcmp(Game->id, "daytona2") || (Game->step == 0x20 && !strcmp(Game->id, "fvipers2"))) + statusCycles = (unsigned)((float)frameCycles * 24.0f/100.0f); + else + statusCycles = (unsigned)((float)frameCycles * 9.12f/100.0f); + } + else if (Game->step == 0x15) + statusCycles = (unsigned)((float)frameCycles * 5.5f/100.0f); + else + statusCycles = (unsigned)((float)frameCycles * 48.0f/100.0f); - // VBlank - if (gpusReady) - { - TileGen.BeginVBlank(); - GPU.BeginVBlank(statusCycles); - IRQ.Assert(0x02); - ppc_execute(vblCycles); - //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); - - /* - * 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 IRQ - IRQ.Deassert(0x40); - ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?) - dispCycles -= 400; + // VBlank + if (gpusReady) + { + TileGen.BeginVBlank(); + GPU.BeginVBlank(statusCycles); + IRQ.Assert(0x02); + ppc_execute(vblCycles); + //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); + + /* + * 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 IRQ + IRQ.Deassert(0x40); + ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?) + dispCycles -= 400; - ++irqCount; - if (irqCount > 128) - { - //printf("\tMIDI FIFO OVERFLOW! (IRQEn=%02X, IRQPend=%02X)\n", IRQ.ReadIRQEnable()&0x40, IRQ.ReadIRQState()); - break; - } - } - //printf("\t-- END --\n"); - //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); - - // End VBlank - GPU.EndVBlank(); - TileGen.EndVBlank(); - IRQ.Assert(0x0D); - } + ++irqCount; + if (irqCount > 128) + { + //printf("\tMIDI FIFO OVERFLOW! (IRQEn=%02X, IRQPend=%02X)\n", IRQ.ReadIRQEnable()&0x40, IRQ.ReadIRQState()); + break; + } + } + //printf("\t-- END --\n"); + //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); + + // End VBlank + GPU.EndVBlank(); + TileGen.EndVBlank(); + IRQ.Assert(0x0D); + } - // Run the PowerPC for the active display part of the frame - ppc_execute(dispCycles); - //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); + // Run the PowerPC for the active display part of the frame + ppc_execute(dispCycles); + //printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr()); - timings.ppcTicks = CThread::GetTicks() - start; + timings.ppcTicks = CThread::GetTicks() - start; } void CModel3::SyncGPUs(void) { - UINT32 start = CThread::GetTicks(); + UINT32 start = CThread::GetTicks(); - timings.syncSize = GPU.SyncSnapshots() + TileGen.SyncSnapshots(); - gpusReady = true; + timings.syncSize = GPU.SyncSnapshots() + TileGen.SyncSnapshots(); + gpusReady = true; - timings.syncTicks = CThread::GetTicks() - start; + timings.syncTicks = CThread::GetTicks() - start; } void CModel3::RenderFrame(void) { - UINT32 start = CThread::GetTicks(); + UINT32 start = CThread::GetTicks(); - // Call OSD video callbacks - if (BeginFrameVideo() && gpusReady) - { - // Render frame - TileGen.BeginFrame(); - GPU.BeginFrame(); - GPU.RenderFrame(); - GPU.EndFrame(); - TileGen.EndFrame(); - } + // Call OSD video callbacks + if (BeginFrameVideo() && gpusReady) + { + // Render frame + TileGen.BeginFrame(); + GPU.BeginFrame(); + GPU.RenderFrame(); + GPU.EndFrame(); + TileGen.EndFrame(); + } - EndFrameVideo(); + EndFrameVideo(); - timings.renderTicks = CThread::GetTicks() - start; + timings.renderTicks = CThread::GetTicks() - start; } bool CModel3::RunSoundBoardFrame(void) { - UINT32 start = CThread::GetTicks(); - - bool bufferFull = SoundBoard.RunFrame(); - - timings.sndTicks = CThread::GetTicks() - start; - - return bufferFull; + UINT32 start = CThread::GetTicks(); + bool bufferFull = SoundBoard.RunFrame(); + timings.sndTicks = CThread::GetTicks() - start; + return bufferFull; } void CModel3::RunDriveBoardFrame(void) { - UINT32 start = CThread::GetTicks(); - - DriveBoard.RunFrame(); - - timings.drvTicks = CThread::GetTicks() - start; + UINT32 start = CThread::GetTicks(); + DriveBoard.RunFrame(); + timings.drvTicks = CThread::GetTicks() - start; } bool CModel3::StartThreads(void) { - if (startedThreads) - return true; - - // Create synchronization objects - if (g_Config.gpuMultiThreaded) - { - 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; - if (DriveBoard.IsAttached()) - { - 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; + if (startedThreads) + return true; + + // Create synchronization objects + if (g_Config.gpuMultiThreaded) + { + 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; + if (DriveBoard.IsAttached()) + { + 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; + // Reset thread flags + pauseThreads = false; + stopThreads = false; - // Create PPC main board thread, if multi-threading GPU - if (g_Config.gpuMultiThreaded) - { - ppcBrdThread = CThread::CreateThread(StartMainBoardThread, this); - if (ppcBrdThread == NULL) - goto ThreadError; - } + // Create PPC main board thread, if multi-threading GPU + if (g_Config.gpuMultiThreaded) + { + ppcBrdThread = CThread::CreateThread(StartMainBoardThread, this); + if (ppcBrdThread == NULL) + goto ThreadError; + } - // Create sound board thread (sync'd or unsync'd) - if (syncSndBrdThread) - sndBrdThread = CThread::CreateThread(StartSoundBoardThreadSyncd, this); - else - sndBrdThread = CThread::CreateThread(StartSoundBoardThread, this); - if (sndBrdThread == NULL) - goto ThreadError; + // Create sound board thread (sync'd or unsync'd) + if (syncSndBrdThread) + sndBrdThread = CThread::CreateThread(StartSoundBoardThreadSyncd, this); + else + sndBrdThread = CThread::CreateThread(StartSoundBoardThread, this); + if (sndBrdThread == NULL) + goto ThreadError; - // Create drive board thread, if drive board is attached - if (DriveBoard.IsAttached()) - { - drvBrdThread = CThread::CreateThread(StartDriveBoardThread, this); - if (drvBrdThread == NULL) - goto ThreadError; - } + // Create drive board thread, if drive board is attached + if (DriveBoard.IsAttached()) + { + drvBrdThread = CThread::CreateThread(StartDriveBoardThread, this); + if (drvBrdThread == NULL) + goto ThreadError; + } - // Set audio callback if sound board thread is unsync'd - if (!syncSndBrdThread) - SetAudioCallback(AudioCallback, this); - - startedThreads = true; - return true; + // Set audio callback if sound board thread is unsync'd + if (!syncSndBrdThread) + SetAudioCallback(AudioCallback, this); + + startedThreads = true; + return true; ThreadError: - ErrorLog("Unable to create threads and/or synchronization objects: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - DeleteThreadObjects(); - g_Config.multiThreaded = false; - return false; + ErrorLog("Unable to create threads and/or synchronization objects: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + DeleteThreadObjects(); + g_Config.multiThreaded = false; + return false; } bool CModel3::PauseThreads(void) { - if (!startedThreads) - return true; - - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + if (!startedThreads) + return true; + + // 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; - } + // 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; + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + return true; ThreadError: - ErrorLog("Threading error in CModel3::PauseThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return false; + ErrorLog("Threading error in CModel3::PauseThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return false; } bool CModel3::ResumeThreads(void) -{ - if (!startedThreads) - return true; +{ + if (!startedThreads) + return true; - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // Enter notify critical section + if (!notifyLock->Lock()) + goto ThreadError; - // Let all threads know that they can continue running - pauseThreads = false; + // Let all threads know that they can continue running + pauseThreads = false; - // Leave notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; - return true; + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + return true; ThreadError: - ErrorLog("Threading error in CModel3::ResumeThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return false; + ErrorLog("Threading error in CModel3::ResumeThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return false; } bool CModel3::StopThreads(void) { - if (!startedThreads) - return true; + if (!startedThreads) + return true; - // If sound board thread is unsync'd then remove audio callback - if (!syncSndBrdThread) - SetAudioCallback(NULL, NULL); - - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // If sound board thread is unsync'd then remove audio callback + if (!syncSndBrdThread) + SetAudioCallback(NULL, NULL); + + // 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; - } + // 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; + // Now let threads know that they should exit + stopThreads = true; - // Leave notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; - - // 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(); - } + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + + // 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; + // Delete all thread and synchronization objects + DeleteThreadObjects(); + startedThreads = false; + return true; ThreadError: - ErrorLog("Threading error in CModel3::StopThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return false; + ErrorLog("Threading error in CModel3::StopThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return false; } void CModel3::DeleteThreadObjects(void) { - // 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; - } + // 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; + } - // Delete synchronization objects - if (ppcBrdThreadSync != NULL) - { - delete ppcBrdThreadSync; - ppcBrdThreadSync = NULL; - } - if (sndBrdThreadSync != NULL) - { - delete sndBrdThreadSync; - sndBrdThreadSync = NULL; - } - if (drvBrdThreadSync != NULL) - { - delete drvBrdThreadSync; - drvBrdThreadSync = NULL; - } - 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; - } + // Delete synchronization objects + if (ppcBrdThreadSync != NULL) + { + delete ppcBrdThreadSync; + ppcBrdThreadSync = NULL; + } + if (sndBrdThreadSync != NULL) + { + delete sndBrdThreadSync; + sndBrdThreadSync = NULL; + } + if (drvBrdThreadSync != NULL) + { + delete drvBrdThreadSync; + drvBrdThreadSync = NULL; + } + 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; + } } void CModel3::DumpTimings(void) { - 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 ? '!' : ','), - timings.renderTicks, (timings.renderTicks > timings.ppcTicks ? '!' : ','), - timings.syncSize / 1024, (timings.syncSize / 1024 > 128 ? '!' : ','), - timings.syncTicks, (timings.syncTicks > 1 ? '!' : ','), - timings.sndTicks, (timings.sndTicks > 10 ? '!' : ','), - timings.drvTicks, (timings.drvTicks > 10 ? '!' : ','), - timings.frameTicks, (timings.frameTicks > 16 ? '!' : ' ')); + 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 ? '!' : ','), + timings.renderTicks, (timings.renderTicks > timings.ppcTicks ? '!' : ','), + timings.syncSize / 1024, (timings.syncSize / 1024 > 128 ? '!' : ','), + timings.syncTicks, (timings.syncTicks > 1 ? '!' : ','), + timings.sndTicks, (timings.sndTicks > 10 ? '!' : ','), + timings.drvTicks, (timings.drvTicks > 10 ? '!' : ','), + timings.frameTicks, (timings.frameTicks > 16 ? '!' : ' ')); } FrameTimings CModel3::GetTimings(void) { - return timings; + return timings; } int CModel3::StartMainBoardThread(void *data) { - // Call method on CModel3 to run PPC main board thread - CModel3 *model3 = (CModel3*)data; - return model3->RunMainBoardThread(); + // Call method on CModel3 to run PPC main board thread + CModel3 *model3 = (CModel3*)data; + return model3->RunMainBoardThread(); } int CModel3::StartSoundBoardThread(void *data) { - // Call method on CModel3 to run sound board thread (unsync'd) - CModel3 *model3 = (CModel3*)data; - return model3->RunSoundBoardThread(); + // Call method on CModel3 to run sound board thread (unsync'd) + CModel3 *model3 = (CModel3*)data; + return model3->RunSoundBoardThread(); } int CModel3::StartSoundBoardThreadSyncd(void *data) { - // Call method on CModel3 to run sound board thread (sync'd) - CModel3 *model3 = (CModel3*)data; - return model3->RunSoundBoardThreadSyncd(); + // Call method on CModel3 to run sound board thread (sync'd) + CModel3 *model3 = (CModel3*)data; + return model3->RunSoundBoardThreadSyncd(); } int CModel3::StartDriveBoardThread(void *data) { - // Call method on CModel3 to run drive board thread - CModel3 *model3 = (CModel3*)data; - return model3->RunDriveBoardThread(); + // Call method on CModel3 to run drive board thread + CModel3 *model3 = (CModel3*)data; + return model3->RunDriveBoardThread(); } int CModel3::RunMainBoardThread(void) { - for (;;) - { - bool wait = true; - bool exit = false; - while (wait && !exit) - { - // Wait on PPC main board thread semaphore - if (!ppcBrdThreadSync->Wait()) - goto ThreadError; + for (;;) + { + 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; + // 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; - } - - // Leave notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; - } - if (exit) - return 0; + // Check threads are not being stopped or paused + if (stopThreads) + exit = true; + else if (!pauseThreads) + { + wait = false; + ppcBrdThreadRunning = true; + } + + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } + if (exit) + return 0; - // Process a single frame for PPC main board - RunMainBoardFrame(); + // Process a single frame for PPC main board + RunMainBoardFrame(); - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // 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; + // 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; - } + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } ThreadError: - ErrorLog("Threading error in RunMainBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return 1; + ErrorLog("Threading error in RunMainBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return 1; } void CModel3::AudioCallback(void *data) { - // Call method on CModel3 to wake sound board thread - CModel3 *model3 = (CModel3*)data; - model3->WakeSoundBoardThread(); + // Call method on CModel3 to wake sound board thread + CModel3 *model3 = (CModel3*)data; + model3->WakeSoundBoardThread(); } bool CModel3::WakeSoundBoardThread(void) { - // Enter sound board notify critical section - bool wake; - if (!sndBrdNotifyLock->Lock()) - goto ThreadError; + // Enter sound board notify critical section + bool wake; + if (!sndBrdNotifyLock->Lock()) + goto ThreadError; - // Enter main notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // Enter main notify critical section + if (!notifyLock->Lock()) + goto ThreadError; - // See if sound board thread is currently running - wake = !sndBrdThreadRunning; - - // Leave main notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; - - // 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; - } + // See if sound board thread is currently running + wake = !sndBrdThreadRunning; + + // Leave main notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + + // 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; + // Leave sound board notify critical section + if (!sndBrdNotifyLock->Unlock()) + goto ThreadError; + return wake; ThreadError: - ErrorLog("Threading error in WakeSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return false; + ErrorLog("Threading error in WakeSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return false; } int CModel3::RunSoundBoardThread(void) { - for (;;) - { - bool wait = true; - bool exit = false; - while (wait && !exit) - { - // Enter sound board notify critical section - if (!sndBrdNotifyLock->Lock()) - goto ThreadError; + 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; - - // Enter main notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // Wait for notification from audio callback + while (!sndBrdWakeNotify) + { + if (!sndBrdNotifySync->Wait(sndBrdNotifyLock)) + goto ThreadError; + } + sndBrdWakeNotify = false; + + // 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; - } + // 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 main notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; - // Leave sound board notify critical section - if (!sndBrdNotifyLock->Unlock()) - goto ThreadError; - } - if (exit) - return 0; + // Leave sound board notify critical section + if (!sndBrdNotifyLock->Unlock()) + goto ThreadError; + } + 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; + // 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; - - // Leave main notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; + paused = pauseThreads; + + // Leave main notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; - if (paused || RunSoundBoardFrame()) - break; - //printf("Rerunning sound board\n"); - } + if (paused || RunSoundBoardFrame()) + break; + //printf("Rerunning sound board\n"); + } - // Enter main notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // 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; + // 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; - } + // Leave main notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } ThreadError: - ErrorLog("Threading error in RunSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return 1; + ErrorLog("Threading error in RunSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return 1; } int CModel3::RunSoundBoardThreadSyncd(void) { - for (;;) - { - bool wait = true; - bool exit = false; - while (wait && !exit) - { - // Wait on sound board thread semaphore - if (!sndBrdThreadSync->Wait()) - goto ThreadError; - - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + for (;;) + { + bool wait = true; + bool exit = false; + while (wait && !exit) + { + // Wait on sound board thread semaphore + if (!sndBrdThreadSync->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; - sndBrdThreadRunning = true; - } + // 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; + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } + if (exit) + return 0; - // Process a single frame for sound board - RunSoundBoardFrame(); + // Process a single frame for sound board + RunSoundBoardFrame(); - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // 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; + // 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; - } + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } ThreadError: - ErrorLog("Threading error in RunSoundBoardThreadSyncd: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return 1; + ErrorLog("Threading error in RunSoundBoardThreadSyncd: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return 1; } int CModel3::RunDriveBoardThread(void) { - for (;;) - { - bool wait = true; - bool exit = false; - while (wait && !exit) - { - // Wait on drive board thread semaphore - if (!drvBrdThreadSync->Wait()) - goto ThreadError; + for (;;) + { + bool wait = true; + 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; + // 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; - } - - // Leave notify critical section - if (!notifyLock->Unlock()) - goto ThreadError; - } - if (exit) - return 0; + // Check threads are not being stopped or paused + if (stopThreads) + exit = true; + else if (!pauseThreads) + { + wait = false; + drvBrdThreadRunning = true; + } + + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } + if (exit) + return 0; - // Process a single frame for drive board - RunDriveBoardFrame(); + // Process a single frame for drive board + RunDriveBoardFrame(); - // Enter notify critical section - if (!notifyLock->Lock()) - goto ThreadError; + // 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; + // 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; - } + // Leave notify critical section + if (!notifyLock->Unlock()) + goto ThreadError; + } ThreadError: - ErrorLog("Threading error in RunDriveBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); - g_Config.multiThreaded = false; - return 1; + ErrorLog("Threading error in RunDriveBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError()); + g_Config.multiThreaded = false; + return 1; } void CModel3::Reset(void) { - // Clear memory (but do not modify backup RAM!) - memset(ram, 0, 0x800000); - - // Initial bank is bank 0 - SetCROMBank(0xFF); - - // Reset security device - securityPtr = 0; - - // Reset inputs - inputBank = 0; - serialFIFO1 = 0; - serialFIFO2 = 0; - adcChannel = 0; - - // MIDI - midiCtrlPort = 0; - - // Reset all devices - ppc_reset(); - IRQ.Reset(); - PCIBridge.Reset(); - PCIBus.Reset(); - SCSI.Reset(); - RTC.Reset(); - EEPROM.Reset(); - TileGen.Reset(); - GPU.Reset(); - SoundBoard.Reset(); + // Clear memory (but do not modify backup RAM!) + memset(ram, 0, 0x800000); + + // Initial bank is bank 0 + SetCROMBank(0xFF); + + // Reset security device + securityPtr = 0; + m_securityFirstRead = true; + + // Reset inputs + inputBank = 0; + serialFIFO1 = 0; + serialFIFO2 = 0; + adcChannel = 0; + + // MIDI + midiCtrlPort = 0; + + // Reset all devices + ppc_reset(); + IRQ.Reset(); + PCIBridge.Reset(); + PCIBus.Reset(); + SCSI.Reset(); + RTC.Reset(); + EEPROM.Reset(); + TileGen.Reset(); + GPU.Reset(); + SoundBoard.Reset(); - if (DriveBoard.IsAttached()) - DriveBoard.Reset(); + if (DriveBoard.IsAttached()) + DriveBoard.Reset(); + + m_cryptoDevice.Reset(); - gpusReady = false; + gpusReady = false; - timings.ppcTicks = 0; - timings.syncSize = 0; - timings.syncTicks = 0; - timings.renderTicks = 0; - timings.sndTicks = 0; - timings.drvTicks = 0; - timings.frameTicks = 0; - - DebugLog("Model 3 reset\n"); + timings.ppcTicks = 0; + timings.syncSize = 0; + timings.syncTicks = 0; + timings.renderTicks = 0; + timings.sndTicks = 0; + timings.drvTicks = 0; + timings.frameTicks = 0; + + DebugLog("Model 3 reset\n"); } @@ -2862,524 +2799,461 @@ void CModel3::Reset(void) // Apply patches to games void CModel3::Patch(void) { - if (!strcmp(Game->id, "lemans24")) - { - // Base offset of program in CROM: 6473C0 - *(UINT32 *) &crom[0x6D8C4C] = 0x00000002; // comm. mode: 00=master, 01=slave, 02=satellite - *(UINT32 *) &crom[0x73FE38] = 0x38840004; // an actual bug in the game code - *(UINT32 *) &crom[0x73EB5C] = 0x60000000; - *(UINT32 *) &crom[0x73EDD0] = 0x60000000; - *(UINT32 *) &crom[0x73EDC4] = 0x60000000; - } - else if (!strcmp(Game->id, "lostwsga")) - { - *(UINT32 *) &crom[0x7374f4] = 0x38840004; // an actual bug in the game code - } - else if (!strcmp(Game->id, "vs215") || - !strcmp(Game->id, "vs215o") || !strcmp(Game->id, "vs29815")) - { - // VS215 is a modification of VS2 that runs on Step 1.5 hardware. I - // suspect the code here is trying to detect the system type but am too - // lazy to figure it out right now. - *(UINT32 *) &crom[0x7001A8] = 0x48000000+(0xFFF01630-0xFFF001A8); // force a jump to FFF01630 - } - else if (!strcmp(Game->id, "vs298")) - { - // Base offset of program in CROM: 600000 - // Inexplicably, at PC=AFC1C, a call is made to FC78, which is right in the middle of some - // totally unrelated initialization code (ASIC checks). This causes an invalid pointer to be fetched. - // Perhaps FC78 should be overwritten with other program data by then? Why is it not? - // Or, 300138 needs to be written with a non-zero value, it is loaded from EEPROM but is 0. - *(UINT32 *) &crom[0x6AFC1C] = 0x60000000; - } - else if (!strcmp(Game->id, "srally2")) - { - *(UINT32 *) &crom[0x7C0C4] = 0x60000000; - *(UINT32 *) &crom[0x7C0C8] = 0x60000000; - *(UINT32 *) &crom[0x7C0CC] = 0x60000000; - } - else if (!strcmp(Game->id, "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 pressing: green, green, blue, yellow, red, yellow, blue (VR4,4,2,3,1,3,2) - *(UINT32 *) &crom[0x68468c] = 0x60000000; // protection device - *(UINT32 *) &crom[0x6063c4] = 0x60000000; // needed to allow levels to load - *(UINT32 *) &crom[0x616434] = 0x60000000; // prevents PPC from executing invalid code (MMU?) - *(UINT32 *) &crom[0x69f4e4] = 0x60000000; // "" - } - else if (!strcmp(Game->id, "dayto2pe")) - { - //*(UINT32 *) &crom[0x606784] = 0x60000000; - *(UINT32 *) &crom[0x69A3FC] = 0x60000000; // MAME says: jump to encrypted code - *(UINT32 *) &crom[0x618B28] = 0x60000000; // MAME says: jump to encrypted code - } - else if (!strcmp(Game->id, "harley")) - { - *(UINT32 *) &crom[0x50E8D4] = 0x60000000; - *(UINT32 *) &crom[0x50E8F4] = 0x60000000; - *(UINT32 *) &crom[0x50FB84] = 0x60000000; - } - else if (!strcmp(Game->id, "harleyb")) - { - *(UINT32 *) &crom[0x50ECB4] = 0x60000000; - *(UINT32 *) &crom[0x50ECD4] = 0x60000000; - *(UINT32 *) &crom[0x50FF64] = 0x60000000; - } - else if (!strcmp(Game->id, "swtrilgy")) - { - *(UINT32 *) &crom[0xF0E48] = 0x60000000; - *(UINT32 *) &crom[0x043DC] = 0x48000090; // related to joystick feedback - *(UINT32 *) &crom[0x029A0] = 0x60000000; - *(UINT32 *) &crom[0x02A0C] = 0x60000000; - } - else if (!strcmp(Game->id, "swtrilgya")) - { - *(UINT32 *) &crom[0xF6DD0] = 0x60000000; // from MAME - } - else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax")) - { - *(UINT32 *) &crom[0x535580] = 0x60000000; - *(UINT32 *) &crom[0x5023B4] = 0x60000000; - *(UINT32 *) &crom[0x5023D4] = 0x60000000; - } + if (!strcmp(Game->id, "lemans24")) + { + // Base offset of program in CROM: 6473C0 + *(UINT32 *) &crom[0x6D8C4C] = 0x00000002; // comm. mode: 00=master, 01=slave, 02=satellite + *(UINT32 *) &crom[0x73FE38] = 0x38840004; // an actual bug in the game code + *(UINT32 *) &crom[0x73EB5C] = 0x60000000; + *(UINT32 *) &crom[0x73EDD0] = 0x60000000; + *(UINT32 *) &crom[0x73EDC4] = 0x60000000; + } + else if (!strcmp(Game->id, "lostwsga")) + { + *(UINT32 *) &crom[0x7374f4] = 0x38840004; // an actual bug in the game code + } + else if (!strcmp(Game->id, "vs215") || !strcmp(Game->id, "vs215o") || !strcmp(Game->id, "vs29815")) + { + // VS215 is a modification of VS2 that runs on Step 1.5 hardware. I + // suspect the code here is trying to detect the system type but am too + // lazy to figure it out right now. + *(UINT32 *) &crom[0x7001A8] = 0x48000000+(0xFFF01630-0xFFF001A8); // force a jump to FFF01630 + } + else if (!strcmp(Game->id, "vs298")) + { + // Base offset of program in CROM: 600000 + // Inexplicably, at PC=AFC1C, a call is made to FC78, which is right in the middle of some + // totally unrelated initialization code (ASIC checks). This causes an invalid pointer to be fetched. + // Perhaps FC78 should be overwritten with other program data by then? Why is it not? + // Or, 300138 needs to be written with a non-zero value, it is loaded from EEPROM but is 0. + *(UINT32 *) &crom[0x6AFC1C] = 0x60000000; + } + else if (!strcmp(Game->id, "srally2")) + { + *(UINT32 *) &crom[0x7C0C4] = 0x60000000; + *(UINT32 *) &crom[0x7C0C8] = 0x60000000; + *(UINT32 *) &crom[0x7C0CC] = 0x60000000; + } + else if (!strcmp(Game->id, "harley")) + { + *(UINT32 *) &crom[0x50E8D4] = 0x60000000; + *(UINT32 *) &crom[0x50E8F4] = 0x60000000; + *(UINT32 *) &crom[0x50FB84] = 0x60000000; + } + else if (!strcmp(Game->id, "harleyb")) + { + *(UINT32 *) &crom[0x50ECB4] = 0x60000000; + *(UINT32 *) &crom[0x50ECD4] = 0x60000000; + *(UINT32 *) &crom[0x50FF64] = 0x60000000; + } + else if (!strcmp(Game->id, "swtrilgy")) + { + *(UINT32 *) &crom[0xF0E48] = 0x60000000; + *(UINT32 *) &crom[0x043DC] = 0x48000090; // related to joystick feedback + *(UINT32 *) &crom[0x029A0] = 0x60000000; + *(UINT32 *) &crom[0x02A0C] = 0x60000000; + } + else if (!strcmp(Game->id, "swtrilgya")) + { + *(UINT32 *) &crom[0xF6DD0] = 0x60000000; // from MAME + } + else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax")) + { + *(UINT32 *) &crom[0x535580] = 0x60000000; + //*(UINT32 *) &crom[0x5023B4] = 0x60000000; + //*(UINT32 *) &crom[0x5023D4] = 0x60000000; + } } // Reverses all aligned 16-bit words, thereby switching their endianness (assumes buffer size is divisible by 2) -static void Reverse16(UINT8 *buf, unsigned size) +static void Reverse16(uint8_t *buf, size_t size) { - unsigned i; - UINT8 tmp; - - for (i = 0; i < size; i += 2) - { - tmp = buf[i+0]; - buf[i+0] = buf[i+1]; - buf[i+1] = tmp; - } + for (size_t i = 0; i < size; i += 2) + { + uint8_t tmp = buf[i+0]; + buf[i+0] = buf[i+1]; + buf[i+1] = tmp; + } } // Reverses all aligned 32-bit words, thereby switching their endianness (assumes buffer size is divisible by 4) -static void Reverse32(UINT8 *buf, unsigned size) +static void Reverse32(uint8_t *buf, size_t size) { - unsigned i; - UINT8 tmp1, tmp2; - - for (i = 0; i < size; i += 4) - { - tmp1 = buf[i+0]; - tmp2 = buf[i+1]; - buf[i+0] = buf[i+3]; - buf[i+1] = buf[i+2]; - buf[i+2] = tmp2; - buf[i+3] = tmp1; - } -} - -// Reads in CROMs directly (for debugging purposes only) -static void ReadCROMDirectly(UINT8 *crom, char *fileName[4], unsigned combinedSize) -{ - FILE *fp[4] = { NULL, NULL, NULL, NULL }; - - // Open all files - for (int i = 0; i < 4; i++) - { - fp[i] = fopen(fileName[i],"rb"); - if (NULL == fp[i]) - { - ErrorLog("Unable to open file for reading: %s.", fileName[i]); - goto ReadCleanup; - } - } - - // Read and interleave 2 bytes at a time - for (unsigned i = 0; i < combinedSize; i += 8) - { - fread(&crom[i+0], 1, 2, fp[0]); - fread(&crom[i+2], 1, 2, fp[1]); - fread(&crom[i+4], 1, 2, fp[2]); - fread(&crom[i+6], 1, 2, fp[3]); - } - Reverse16(crom, combinedSize); // byte reverse ROMs - - // Reverse 32 bit words for Supermodel - Reverse32(crom, combinedSize); - -ReadCleanup: - for (int i = 0; i < 4; i++) - { - if (fp[i] != NULL) - fclose(fp[i]); - } -} - -// Dumps a memory region to a file for debugging purposes -static void Dump(const char *file, UINT8 *buf, unsigned size, bool reverse32, bool reverse16) -{ - FILE *fp; - - fp = fopen(file, "wb"); - if (NULL != fp) - { - if (reverse32) - Reverse32(buf, size); - else if (reverse16) - Reverse16(buf, size); - fwrite(buf, sizeof(UINT8), size, fp); - fclose(fp); - printf("dumped %s\n", file); - } - else - printf("unable to dump %s\n", file); + for (size_t i = 0; i < size; i += 4) + { + uint8_t tmp1 = buf[i+0]; + uint8_t tmp2 = buf[i+1]; + buf[i+0] = buf[i+3]; + buf[i+1] = buf[i+2]; + buf[i+2] = tmp2; + buf[i+3] = tmp1; + } } // Offsets of memory regions within Model 3's pool -#define OFFSET_RAM 0 // 8 MB -#define OFFSET_CROM 0x800000 // 8 MB (fixed CROM) -#define OFFSET_CROMxx 0x1000000 // 128 MB (banked CROM0-3 must follow fixed CROM) -#define OFFSET_VROM 0x9000000 // 64 MB -#define OFFSET_BACKUPRAM 0xD000000 // 128 KB -#define OFFSET_SECURITYRAM 0xD020000 // 128 KB -#define OFFSET_SOUNDROM 0xD040000 // 512 KB (68K sound board program) -#define OFFSET_SAMPLEROM 0xD0C0000 // 16 MB (sound board samples) -#define OFFSET_DSBPROGROM 0xE0C0000 // 128 KB (DSB program) -#define OFFSET_DSBMPEGROM 0xE0E0000 // 16 MB (DSB MPEG data -- Z80 version only uses 8MB) -#define OFFSET_DRIVEROM 0xF0E0000 // 64 KB -#define MEMORY_POOL_SIZE (0x800000 + 0x800000 + 0x8000000 + 0x4000000 + 0x20000 + 0x20000 + 0x80000 + 0x1000000 + 0x20000 + 0x1000000 + 0x10000) +#define OFFSET_RAM 0 // 8 MB +#define OFFSET_CROM 0x800000 // 8 MB (fixed CROM) +#define OFFSET_CROMxx 0x1000000 // 128 MB (banked CROM0-3 must follow fixed CROM) +#define OFFSET_VROM 0x9000000 // 64 MB +#define OFFSET_BACKUPRAM 0xD000000 // 128 KB +#define OFFSET_SECURITYRAM 0xD020000 // 128 KB +#define OFFSET_SOUNDROM 0xD040000 // 512 KB (68K sound board program) +#define OFFSET_SAMPLEROM 0xD0C0000 // 16 MB (sound board samples) +#define OFFSET_DSBPROGROM 0xE0C0000 // 128 KB (DSB program) +#define OFFSET_DSBMPEGROM 0xE0E0000 // 16 MB (DSB MPEG data -- Z80 version only uses 8MB) +#define OFFSET_DRIVEROM 0xF0E0000 // 64 KB +#define MEMORY_POOL_SIZE (0x800000 + 0x800000 + 0x8000000 + 0x4000000 + 0x20000 + 0x20000 + 0x80000 + 0x1000000 + 0x20000 + 0x1000000 + 0x10000) // 64-bit magic number used to detect loading of optional ROMs -#define MAGIC_NUMBER 0x4C444D5245505553ULL +#define MAGIC_NUMBER 0x4C444D5245505553ULL const struct GameInfo * CModel3::GetGameInfo(void) { - return Game; + return Game; } - + // Stepping-dependent parameters (MPC10x type, etc.) are initialized here bool CModel3::LoadROMSet(const struct GameInfo *GameList, const char *zipFile) { - struct ROMMap Map[] = - { - { "CROM", crom }, - { "CROMxx", &crom[0x800000] }, - { "VROM", vrom }, - { "SndProg", soundROM }, - { "Samples", sampleROM }, - { "DSBProg", dsbROM }, - { "DSBMPEG", mpegROM }, - { "DriveBd", driveROM }, - { NULL, NULL } - }; - PPC_CONFIG PPCConfig; - - // Magic numbers to detect if optional ROMs are loaded - *(UINT64 *) driveROM = MAGIC_NUMBER; - - // Load game - Game = LoadROMSetFromZIPFile(Map, GameList, zipFile, true); - if (NULL == Game) - return ErrorLog("Failed to load ROM set."); - - // Perform mirroring as necessary - if (Game->vromSize < 0x4000000) // VROM is actually 64 MB - CopyRegion(vrom, Game->vromSize, 0x4000000, vrom, Game->vromSize); - if (Game->cromSize < 0x800000) // low part of fixed CROM region contains CROM0 - CopyRegion(crom, 0, 0x800000-Game->cromSize, &crom[0x800000], 0x800000); - if (Game->mirrorLow64MB) // for games w/ 64 MB or less banked CROM, mirror to upper 128 MB - CopyRegion(&crom[0x800000], 0x4000000, 0x8000000, &crom[0x800000], 0x4000000); - if (Game->sampleSize < 0x1000000) // if less than 16 MB of sample ROMs, mirror - CopyRegion(sampleROM, 0x800000, 0x1000000, sampleROM, 0x800000); - - // Byte reverse the PowerPC ROMs (convert to little endian words) - Reverse32(crom, 0x800000+0x8000000); - - // Byte swap sound board 68K ROMs (convert to little endian words) - Reverse16(soundROM, 0x80000); - Reverse16(sampleROM, 0x1000000); - - // Initialize CPU and configure hardware (CPU speed is set in Init()) - if (Game->step >= 0x20) // Step 2.0+ - { - PPCConfig.pvr = PPC_MODEL_603R; // 166 MHz - PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; - PPCConfig.bus_frequency_multiplier = 0x25; // 2.5X multiplier - PCIBridge.SetModel(0x106); // MPC106 - } - else if (Game->step == 0x15) // Step 1.5 - { - PPCConfig.pvr = PPC_MODEL_603E; // 100 MHz - PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; - PPCConfig.bus_frequency_multiplier = 0x15; // 1.5X multiplier - if (!strcmp(Game->id, "scudp1")) // some Step 1.x games use MPC106 - PCIBridge.SetModel(0x106); - else - PCIBridge.SetModel(0x105); // MPC105 - } - else if (Game->step == 0x10) // Step 1.0 - { - PPCConfig.pvr = PPC_MODEL_603R; // 66 MHz - PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; - PPCConfig.bus_frequency_multiplier = 0x10; // 1X multiplier - if (!strcmp(Game->id, "bass") || !strcmp(Game->id, "getbass")) // some Step 1.x games use MPC106 - PCIBridge.SetModel(0x106); - else - PCIBridge.SetModel(0x105); // MPC105 - } - else - return ErrorLog("Game uses an unrecognized stepping (%d.%d), cannot configure Model 3.", (Game->step>>4)&0xF, Game->step&0xF); + struct ROMMap Map[] = + { + { "CROM", crom }, + { "CROMxx", &crom[0x800000] }, + { "VROM", vrom }, + { "SndProg", soundROM }, + { "Samples", sampleROM }, + { "DSBProg", dsbROM }, + { "DSBMPEG", mpegROM }, + { "DriveBd", driveROM }, + { NULL, NULL } + }; + PPC_CONFIG PPCConfig; + + // Magic numbers to detect if optional ROMs are loaded + *(UINT64 *) driveROM = MAGIC_NUMBER; + + // Load game + Game = LoadROMSetFromZIPFile(Map, GameList, zipFile, true); + if (NULL == Game) + return ErrorLog("Failed to load ROM set."); + + // Perform mirroring as necessary + if (Game->vromSize < 0x4000000) // VROM is actually 64 MB + CopyRegion(vrom, Game->vromSize, 0x4000000, vrom, Game->vromSize); + if (Game->cromSize < 0x800000) // low part of fixed CROM region contains CROM0 + CopyRegion(crom, 0, 0x800000-Game->cromSize, &crom[0x800000], 0x800000); + if (Game->mirrorLow64MB) // for games w/ 64 MB or less banked CROM, mirror to upper 128 MB + CopyRegion(&crom[0x800000], 0x4000000, 0x8000000, &crom[0x800000], 0x4000000); + if (Game->sampleSize < 0x1000000) // if less than 16 MB of sample ROMs, mirror + CopyRegion(sampleROM, 0x800000, 0x1000000, sampleROM, 0x800000); + + // Byte reverse the PowerPC ROMs (convert to little endian words) + Reverse32(crom, 0x800000+0x8000000); + + // Byte swap sound board 68K ROMs (convert to little endian words) + Reverse16(soundROM, 0x80000); + Reverse16(sampleROM, 0x1000000); + + // Initialize CPU and configure hardware (CPU speed is set in Init()) + if (Game->step >= 0x20) // Step 2.0+ + { + PPCConfig.pvr = PPC_MODEL_603R; // 166 MHz + PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; + PPCConfig.bus_frequency_multiplier = 0x25; // 2.5X multiplier + PCIBridge.SetModel(0x106); // MPC106 + } + else if (Game->step == 0x15) // Step 1.5 + { + PPCConfig.pvr = PPC_MODEL_603E; // 100 MHz + PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; + PPCConfig.bus_frequency_multiplier = 0x15; // 1.5X multiplier + if (!strcmp(Game->id, "scudp1")) // some Step 1.x games use MPC106 + PCIBridge.SetModel(0x106); + else + PCIBridge.SetModel(0x105); // MPC105 + } + else if (Game->step == 0x10) // Step 1.0 + { + PPCConfig.pvr = PPC_MODEL_603R; // 66 MHz + PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ; + PPCConfig.bus_frequency_multiplier = 0x10; // 1X multiplier + if (!strcmp(Game->id, "bass") || !strcmp(Game->id, "getbass")) // some Step 1.x games use MPC106 + PCIBridge.SetModel(0x106); + else + PCIBridge.SetModel(0x105); // MPC105 + } + else + return ErrorLog("Game uses an unrecognized stepping (%d.%d), cannot configure Model 3.", (Game->step>>4)&0xF, Game->step&0xF); - GPU.SetStep(Game->step); + GPU.SetStep(Game->step); - ppc_init(&PPCConfig); - ppc_attach_bus(this); + ppc_init(&PPCConfig); + ppc_attach_bus(this); - PPCFetchRegions[0].start = 0; - PPCFetchRegions[0].end = 0x007FFFFF; - PPCFetchRegions[0].ptr = (UINT32 *) ram; - PPCFetchRegions[1].start = 0xFF800000; - 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); - - // DSB board (if present) - if (Game->mpegBoard == 1) // Z80 board, do not byte swap program ROM - { - DSB = new(std::nothrow) CDSB1(); - if (NULL == DSB) - return ErrorLog("Insufficient memory for Digital Sound Board object."); - if (OKAY != DSB->Init(dsbROM,mpegROM)) - return FAIL; - } - else if (Game->mpegBoard == 2) // 68K board - { - Reverse16(dsbROM, 0x20000); // byte swap program ROM - DSB = new(std::nothrow) CDSB2(); - if (NULL == DSB) - return ErrorLog("Insufficient memory for Digital Sound Board object."); - if (OKAY != DSB->Init(dsbROM,mpegROM)) - return FAIL; - } - SoundBoard.AttachDSB(DSB); - - // Drive board (if present) - if (Game->driveBoard) - { - // Was the optional drive board ROM loaded? - if (MAGIC_NUMBER != *(UINT64 *) driveROM) // magic number overwritten by ROM - { - if (DriveBoard.Init(driveROM)) - return FAIL; - } - else - DriveBoard.Init(NULL); - } - else - DriveBoard.Init(NULL); // disable - - // Apply ROM patches - Patch(); - - // Print game information - printf(" Title: %s\n", Game->title); - printf(" ROM Set: %s\n", Game->id); - printf(" Developer: %s\n", Game->mfgName); - printf(" Year: %d\n", Game->year); - printf(" Step: %d.%d\n", (Game->step>>4)&0xF, Game->step&0xF); - if (Game->mpegBoard) - { - printf(" Extra Hardware: Digital Sound Board (Type %d)", Game->mpegBoard); - if (Game->driveBoard) - printf(", Drive Board"); - printf("\n"); - } - else if (Game->driveBoard) - printf(" Extra Hardware: Drive Board\n"); - printf("\n"); - - // Load patched CROM directly (for testing out ROM patches) - //char *fileName[4] = { "epr-20599a.patched.20","epr-20598a.patched.19","epr-20597a.patched.18","epr-20596a.patched.17" }; - //ReadCROMDirectly(crom, fileName, 0x800000); - - return OKAY; + PPCFetchRegions[0].start = 0; + PPCFetchRegions[0].end = 0x007FFFFF; + PPCFetchRegions[0].ptr = (UINT32 *) ram; + PPCFetchRegions[1].start = 0xFF800000; + 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); + + // DSB board (if present) + if (Game->mpegBoard == 1) // Z80 board, do not byte swap program ROM + { + DSB = new(std::nothrow) CDSB1(); + if (NULL == DSB) + return ErrorLog("Insufficient memory for Digital Sound Board object."); + if (OKAY != DSB->Init(dsbROM,mpegROM)) + return FAIL; + } + else if (Game->mpegBoard == 2) // 68K board + { + Reverse16(dsbROM, 0x20000); // byte swap program ROM + DSB = new(std::nothrow) CDSB2(); + if (NULL == DSB) + return ErrorLog("Insufficient memory for Digital Sound Board object."); + if (OKAY != DSB->Init(dsbROM,mpegROM)) + return FAIL; + } + SoundBoard.AttachDSB(DSB); + + // Drive board (if present) + if (Game->driveBoard) + { + // Was the optional drive board ROM loaded? + if (MAGIC_NUMBER != *(UINT64 *) driveROM) // magic number overwritten by ROM + { + if (DriveBoard.Init(driveROM)) + return FAIL; + } + else + DriveBoard.Init(NULL); + } + else + DriveBoard.Init(NULL); // disable + + // Security board encryption device + m_cryptoDevice.Init(Game->encryptionKey, std::bind(&CModel3::ReadSecurityRAM, this, std::placeholders::_1)); + + // Apply ROM patches + Patch(); + + // Print game information + std::set extraHw; + if (Game->mpegBoard) + extraHw.insert(Util::Format() << "Digital Sound Board (Type " << Game->mpegBoard << ")"); + if (Game->driveBoard) + extraHw.insert("Drive Board"); + if (Game->encryptionKey) + extraHw.insert("Security Board"); + printf(" Title: %s\n", Game->title); + printf(" ROM Set: %s\n", Game->id); + printf(" Developer: %s\n", Game->mfgName); + printf(" Year: %d\n", Game->year); + printf(" Step: %d.%d\n", (Game->step>>4)&0xF, Game->step&0xF); + printf(" Extra Hardware: %s\n", Util::Format(", ").Join(extraHw).str().c_str()); + printf("\n"); + + return OKAY; } void CModel3::AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr) { - TileGen.AttachRenderer(Render2DPtr); - GPU.AttachRenderer(Render3DPtr); + TileGen.AttachRenderer(Render2DPtr); + GPU.AttachRenderer(Render3DPtr); } void CModel3::AttachInputs(CInputs *InputsPtr) { - Inputs = InputsPtr; + Inputs = InputsPtr; - if (DriveBoard.IsAttached()) - DriveBoard.AttachInputs(Inputs, Game->inputFlags); + if (DriveBoard.IsAttached()) + DriveBoard.AttachInputs(Inputs, Game->inputFlags); - DebugLog("Model 3 attached inputs\n"); + DebugLog("Model 3 attached inputs\n"); } void CModel3::AttachOutputs(COutputs *OutputsPtr) { - Outputs = OutputsPtr; - Outputs->SetGame(Game); - Outputs->Attached(); + Outputs = OutputsPtr; + Outputs->SetGame(Game); + Outputs->Attached(); - if (DriveBoard.IsAttached()) - DriveBoard.AttachOutputs(Outputs); + if (DriveBoard.IsAttached()) + DriveBoard.AttachOutputs(Outputs); - DebugLog("Model 3 attached outputs\n"); + DebugLog("Model 3 attached outputs\n"); } // Model 3 initialization. Some initialization is deferred until ROMs are loaded in LoadROMSet() bool CModel3::Init(void) { - float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000; - - // Allocate all memory for ROMs and PPC RAM - memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE]; - if (NULL == memoryPool) - return ErrorLog("Insufficient memory for Model 3 object (needs %1.1f MB).", memSizeMB); - - // Set up pointers - ram = &memoryPool[OFFSET_RAM]; - crom = &memoryPool[OFFSET_CROM]; - vrom = &memoryPool[OFFSET_VROM]; - soundROM = &memoryPool[OFFSET_SOUNDROM]; - sampleROM = &memoryPool[OFFSET_SAMPLEROM]; - dsbROM = &memoryPool[OFFSET_DSBPROGROM]; - mpegROM = &memoryPool[OFFSET_DSBMPEGROM]; - backupRAM = &memoryPool[OFFSET_BACKUPRAM]; - securityRAM = &memoryPool[OFFSET_SECURITYRAM]; - driveROM = &memoryPool[OFFSET_DRIVEROM]; - SetCROMBank(0xFF); - - // Initialize other devices (PowerPC and DSB 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)) - return FAIL; - - PCIBridge.AttachPCIBus(&PCIBus); - PCIBus.AttachDevice(13,&GPU); - PCIBus.AttachDevice(14,&SCSI); - PCIBus.AttachDevice(16,this); - - DebugLog("Initialized Model 3 (allocated %1.1f MB)\n", memSizeMB); - - return OKAY; + float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000; + + // Allocate all memory for ROMs and PPC RAM + memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE]; + if (NULL == memoryPool) + return ErrorLog("Insufficient memory for Model 3 object (needs %1.1f MB).", memSizeMB); + + // Set up pointers + ram = &memoryPool[OFFSET_RAM]; + crom = &memoryPool[OFFSET_CROM]; + vrom = &memoryPool[OFFSET_VROM]; + soundROM = &memoryPool[OFFSET_SOUNDROM]; + sampleROM = &memoryPool[OFFSET_SAMPLEROM]; + dsbROM = &memoryPool[OFFSET_DSBPROGROM]; + mpegROM = &memoryPool[OFFSET_DSBMPEGROM]; + backupRAM = &memoryPool[OFFSET_BACKUPRAM]; + securityRAM = &memoryPool[OFFSET_SECURITYRAM]; + driveROM = &memoryPool[OFFSET_DRIVEROM]; + SetCROMBank(0xFF); + + // 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)) + return FAIL; + + PCIBridge.AttachPCIBus(&PCIBus); + PCIBus.AttachDevice(13,&GPU); + PCIBus.AttachDevice(14,&SCSI); + PCIBus.AttachDevice(16,this); + + DebugLog("Initialized Model 3 (allocated %1.1f MB)\n", memSizeMB); + + return OKAY; } CSoundBoard *CModel3::GetSoundBoard(void) { - return &SoundBoard; + return &SoundBoard; } CDriveBoard *CModel3::GetDriveBoard(void) { - return &DriveBoard; + return &DriveBoard; } CModel3::CModel3(void) { - // Initialize pointers so dtor can know whether to free them - memoryPool = NULL; - - // Various uninitialized pointers - Game = NULL; - Inputs = NULL; - Outputs = NULL; - ram = NULL; - crom = NULL; - vrom = NULL; - soundROM = NULL; - sampleROM = NULL; - dsbROM = NULL; - mpegROM = NULL; - cromBank = NULL; - backupRAM = NULL; - securityRAM = NULL; - - DSB = 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"); + // Initialize pointers so dtor can know whether to free them + memoryPool = NULL; + + // Various uninitialized pointers + Game = NULL; + Inputs = NULL; + Outputs = NULL; + ram = NULL; + crom = NULL; + vrom = NULL; + soundROM = NULL; + sampleROM = NULL; + dsbROM = NULL; + mpegROM = NULL; + cromBank = NULL; + backupRAM = NULL; + securityRAM = NULL; + + DSB = 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"); } +/* +// Dumps a memory region to a file for debugging purposes +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) + Reverse32(buf, size); + else if (reverse16) + Reverse16(buf, size); + fwrite(buf, sizeof(UINT8), size, fp); + fclose(fp); + printf("dumped %s\n", file); + } + else + printf("unable to dump %s\n", file); +} +*/ + CModel3::~CModel3(void) { - // Debug: dump some files -#if 0 - //Dump("ram", ram, 0x800000, true, false); - //Dump("vrom", vrom, 0x4000000, true, false); - Dump("crom", crom, 0x800000, true, false); - //Dump("bankedCrom", &crom[0x800000], 0x7000000, true, false); - //Dump("soundROM", soundROM, 0x80000, false, true); - //Dump("sampleROM", sampleROM, 0x800000, false, true); -#endif - - // Stop all threads - StopThreads(); - - // Free memory - if (memoryPool != NULL) - { - delete [] memoryPool; - memoryPool = NULL; - } - - if (DSB != NULL) - { - delete DSB; - DSB = NULL; - } - - Game = NULL; - Inputs = NULL; - Outputs = NULL; - ram = NULL; - crom = NULL; - vrom = NULL; - soundROM = NULL; - sampleROM = NULL; - dsbROM = NULL; - mpegROM = NULL; - cromBank = NULL; - backupRAM = NULL; - securityRAM = NULL; - - DebugLog("Destroyed Model 3\n"); + /* + // Debug: dump some files + //Dump("ram", ram, 0x800000, true, false); + //Dump("vrom", vrom, 0x4000000, true, false); + Dump("crom", crom, 0x800000, true, false); + //Dump("bankedCrom", &crom[0x800000], 0x7000000, true, false); + //Dump("soundROM", soundROM, 0x80000, false, true); + //Dump("sampleROM", sampleROM, 0x800000, false, true); + */ + + // Stop all threads + StopThreads(); + + // Free memory + if (memoryPool != NULL) + { + delete [] memoryPool; + memoryPool = NULL; + } + + if (DSB != NULL) + { + delete DSB; + DSB = NULL; + } + + Game = NULL; + Inputs = NULL; + Outputs = NULL; + ram = NULL; + crom = NULL; + vrom = NULL; + soundROM = NULL; + sampleROM = NULL; + dsbROM = NULL; + mpegROM = NULL; + cromBank = NULL; + backupRAM = NULL; + securityRAM = NULL; + + DebugLog("Destroyed Model 3\n"); } \ No newline at end of file diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h index a3123df..3f16d26 100644 --- a/Src/Model3/Model3.h +++ b/Src/Model3/Model3.h @@ -28,6 +28,7 @@ #ifndef INCLUDED_MODEL3_H #define INCLUDED_MODEL3_H +#include "Model3/Crypto.h" /* * FrameTimings @@ -36,13 +37,13 @@ */ struct FrameTimings { - UINT32 ppcTicks; - UINT32 syncSize; - UINT32 syncTicks; - UINT32 renderTicks; - UINT32 sndTicks; - UINT32 drvTicks; - UINT32 frameTicks; + UINT32 ppcTicks; + UINT32 syncSize; + UINT32 syncTicks; + UINT32 renderTicks; + UINT32 sndTicks; + UINT32 drvTicks; + UINT32 frameTicks; }; /* @@ -53,34 +54,34 @@ struct FrameTimings class CModel3Config { public: - bool multiThreaded; // Multi-threaded (enabled if true) - bool gpuMultiThreaded; // Multi-threaded rendering (enabled if true) - - // PowerPC clock frequency in MHz (minimum: 1 MHz) - inline void SetPowerPCFrequency(unsigned f) - { - if ((f<1) || (f>1000)) - { - ErrorLog("PowerPC frequency must be between 1 and 1000 MHz; setting to 50 MHz."); - f = 50; - } - ppcFrequency = f*1000000; - } - inline unsigned GetPowerPCFrequency(void) - { - return ppcFrequency/1000000; - } - - // Defaults - CModel3Config(void) - { - multiThreaded = true; // enable by default - gpuMultiThreaded = true; // enable by default - ppcFrequency = 50*1000000; // 50 MHz - } - + bool multiThreaded; // Multi-threaded (enabled if true) + bool gpuMultiThreaded; // Multi-threaded rendering (enabled if true) + + // PowerPC clock frequency in MHz (minimum: 1 MHz) + inline void SetPowerPCFrequency(unsigned f) + { + if ((f<1) || (f>1000)) + { + ErrorLog("PowerPC frequency must be between 1 and 1000 MHz; setting to 50 MHz."); + f = 50; + } + ppcFrequency = f*1000000; + } + inline unsigned GetPowerPCFrequency(void) + { + return ppcFrequency/1000000; + } + + // Defaults + CModel3Config(void) + { + multiThreaded = true; // enable by default + gpuMultiThreaded = true; // enable by default + ppcFrequency = 50*1000000; // 50 MHz + } + private: - unsigned ppcFrequency; // in Hz + unsigned ppcFrequency; // in Hz }; /* @@ -97,392 +98,393 @@ private: class CModel3: public CBus, public CPCIDevice { public: - /* - * ReadPCIConfigSpace(device, reg, bits, offset): - * - * Handles unknown PCI devices. See CPCIDevice definition for more details. - * - * Parameters: - * device Device number. - * reg Register number. - * bits Bit width of access (8, 16, or 32 only).; - * offset Byte offset within register, aligned to the specified bit - * width, and offset from the 32-bit aligned base of the - * register number. - * - * Returns: - * Register data. - */ - UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width); - - /* - * WritePCIConfigSpace(device, reg, bits, offset, data): - * - * Handles unknown PCI devices. See CPCIDevice definition for more details. - * - * Parameters: - * device Device number. - * reg Register number. - * bits Bit width of access (8, 16, or 32 only). - * offset Byte offset within register, aligned to the specified bit - * width, and offset from the 32-bit aligned base of the - * register number. - * data Data. - */ - void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data); + /* + * ReadPCIConfigSpace(device, reg, bits, offset): + * + * Handles unknown PCI devices. See CPCIDevice definition for more details. + * + * Parameters: + * device Device number. + * reg Register number. + * bits Bit width of access (8, 16, or 32 only).; + * offset Byte offset within register, aligned to the specified bit + * width, and offset from the 32-bit aligned base of the + * register number. + * + * Returns: + * Register data. + */ + UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width); + + /* + * WritePCIConfigSpace(device, reg, bits, offset, data): + * + * Handles unknown PCI devices. See CPCIDevice definition for more details. + * + * Parameters: + * device Device number. + * reg Register number. + * bits Bit width of access (8, 16, or 32 only). + * offset Byte offset within register, aligned to the specified bit + * width, and offset from the 32-bit aligned base of the + * register number. + * data Data. + */ + void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data); - /* - * Read8(addr): - * Read16(addr): - * Read32(addr): - * Read64(addr): - * - * Read a byte, 16-bit half word, 32-bit word, or 64-bit double word from - * the PowerPC address space. This implements the PowerPC address bus. Note - * that it is big endian, so when accessing from a little endian device, - * the byte order must be manually reversed. - * - * Parameters: - * addr Address to read. - * - * Returns: - * Data at the address. - */ - UINT8 Read8(UINT32 addr); - UINT16 Read16(UINT32 addr); - UINT32 Read32(UINT32 addr); - UINT64 Read64(UINT32 addr); - - /* - * Write8(addr, data): - * Write16(addr, data): - * Write32(addr, data): - * Write64(addr, data): - * - * Write a byte, half word, word, or double word to the PowerPC address - * space. Note that everything is stored in big endian form, so when - * accessing with a little endian device, the byte order must be manually - * reversed. - * - * Parameters: - * addr Address to write. - * data Data to write. - */ - void Write8(UINT32 addr, UINT8 data); - void Write16(UINT32 addr, UINT16 data); - void Write32(UINT32 addr, UINT32 data); - void Write64(UINT32 addr, UINT64 data); - - /* - * SaveState(SaveState): - * - * Saves an image of the current state. Must never be called while emulator - * is running (inside RunFrame()). - * - * Parameters: - * SaveState Block file to save state information to. - */ - void SaveState(CBlockFile *SaveState); + /* + * Read8(addr): + * Read16(addr): + * Read32(addr): + * Read64(addr): + * + * Read a byte, 16-bit half word, 32-bit word, or 64-bit double word from + * the PowerPC address space. This implements the PowerPC address bus. Note + * that it is big endian, so when accessing from a little endian device, + * the byte order must be manually reversed. + * + * Parameters: + * addr Address to read. + * + * Returns: + * Data at the address. + */ + UINT8 Read8(UINT32 addr); + UINT16 Read16(UINT32 addr); + UINT32 Read32(UINT32 addr); + UINT64 Read64(UINT32 addr); + + /* + * Write8(addr, data): + * Write16(addr, data): + * Write32(addr, data): + * Write64(addr, data): + * + * Write a byte, half word, word, or double word to the PowerPC address + * space. Note that everything is stored in big endian form, so when + * accessing with a little endian device, the byte order must be manually + * reversed. + * + * Parameters: + * addr Address to write. + * data Data to write. + */ + void Write8(UINT32 addr, UINT8 data); + void Write16(UINT32 addr, UINT16 data); + void Write32(UINT32 addr, UINT32 data); + void Write64(UINT32 addr, UINT64 data); + + /* + * SaveState(SaveState): + * + * Saves an image of the current state. Must never be called while emulator + * is running (inside RunFrame()). + * + * Parameters: + * SaveState Block file to save state information to. + */ + void SaveState(CBlockFile *SaveState); - /* - * LoadState(SaveState): - * - * Loads and resumes execution from a state image. Modifies data that may - * be used by multiple threads -- use with caution and ensure threads are - * not accessing data that will be touched, this can be done by calling - * PauseThreads beforehand. Must never be called while emulator is running - * (inside RunFrame()). - * - * Parameters: - * SaveState Block file to load state information from. - */ - void LoadState(CBlockFile *SaveState); - - /* - * SaveNVRAM(NVRAM): - * - * Saves an image of the current NVRAM state. - * - * Parameters: - * NVRAM Block file to save NVRAM to. - */ - void SaveNVRAM(CBlockFile *NVRAM); + /* + * LoadState(SaveState): + * + * Loads and resumes execution from a state image. Modifies data that may + * be used by multiple threads -- use with caution and ensure threads are + * not accessing data that will be touched, this can be done by calling + * PauseThreads beforehand. Must never be called while emulator is running + * (inside RunFrame()). + * + * Parameters: + * SaveState Block file to load state information from. + */ + void LoadState(CBlockFile *SaveState); + + /* + * SaveNVRAM(NVRAM): + * + * Saves an image of the current NVRAM state. + * + * Parameters: + * NVRAM Block file to save NVRAM to. + */ + void SaveNVRAM(CBlockFile *NVRAM); - /* - * LoadNVRAM(NVRAM): - * - * Loads an NVRAM image. - * - * Parameters: - * NVRAM Block file to load NVRAM state from. - */ - void LoadNVRAM(CBlockFile *NVRAM); - - /* - * ClearNVRAM(void): - * - * Clears all NVRAM (backup RAM and EEPROM). - */ - void ClearNVRAM(void); - - /* - * RunFrame(void): - * - * Runs one frame (assuming 60 Hz video refresh rate). - */ - void RunFrame(void); + /* + * LoadNVRAM(NVRAM): + * + * Loads an NVRAM image. + * + * Parameters: + * NVRAM Block file to load NVRAM state from. + */ + void LoadNVRAM(CBlockFile *NVRAM); + + /* + * ClearNVRAM(void): + * + * Clears all NVRAM (backup RAM and EEPROM). + */ + void ClearNVRAM(void); + + /* + * RunFrame(void): + * + * Runs one frame (assuming 60 Hz video refresh rate). + */ + void RunFrame(void); - /* - * Reset(void): - * - * Resets the system. Does not modify non-volatile memory. - */ - void Reset(void); - - /* - * GetGameInfo(void): - * - * Returns: - * A pointer to the presently loaded game's information structure (or - * NULL if no ROM set has yet been loaded). - */ - const struct GameInfo * GetGameInfo(void); - - /* - * LoadROMSet(GameList, zipFile): - * - * Loads a complete ROM set from the specified ZIP archive. - * - * NOTE: Command line settings will not have been applied here yet. - * - * Parameters: - * GameList List of all supported games and their ROMs. - * zipFile ZIP file to load from. - * - * Returns: - * OKAY if successful, FAIL otherwise. Prints errors. - */ - bool LoadROMSet(const struct GameInfo *GameList, const char *zipFile); - - /* - * AttachRenderers(Render2DPtr, Render3DPtr): - * - * Attaches the renderers to the appropriate device objects. - * - * Parameters: - * Render2DPtr Pointer to a tile renderer object. - * Render3DPtr Same as above but for a 3D renderer. - */ - void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr); - - /* - * AttachInputs(InputsPtr): - * - * Attaches OSD-managed inputs. - * - * Parameters: - * InputsPtr Pointer to the object containing input states. - */ - void AttachInputs(CInputs *InputsPtr); - - void AttachOutputs(COutputs *OutputsPtr); + /* + * Reset(void): + * + * Resets the system. Does not modify non-volatile memory. + */ + void Reset(void); + + /* + * GetGameInfo(void): + * + * Returns: + * A pointer to the presently loaded game's information structure (or + * NULL if no ROM set has yet been loaded). + */ + const struct GameInfo * GetGameInfo(void); + + /* + * LoadROMSet(GameList, zipFile): + * + * Loads a complete ROM set from the specified ZIP archive. + * + * NOTE: Command line settings will not have been applied here yet. + * + * Parameters: + * GameList List of all supported games and their ROMs. + * zipFile ZIP file to load from. + * + * Returns: + * OKAY if successful, FAIL otherwise. Prints errors. + */ + bool LoadROMSet(const struct GameInfo *GameList, const char *zipFile); + + /* + * AttachRenderers(Render2DPtr, Render3DPtr): + * + * Attaches the renderers to the appropriate device objects. + * + * Parameters: + * Render2DPtr Pointer to a tile renderer object. + * Render3DPtr Same as above but for a 3D renderer. + */ + void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr); + + /* + * AttachInputs(InputsPtr): + * + * Attaches OSD-managed inputs. + * + * Parameters: + * InputsPtr Pointer to the object containing input states. + */ + void AttachInputs(CInputs *InputsPtr); + + void AttachOutputs(COutputs *OutputsPtr); - /* - * Init(void): - * - * One-time initialization of the context. Must be called prior to all - * other members. Allocates memory and initializes device states. - * - * NOTE: Command line settings will not have been applied here yet. - * - * Returns: - * OKAY is successful, otherwise FAILED if a non-recoverable error - * occurred. Prints own error messages. - */ - bool Init(void); - - /* - * GetSoundBoard(void): - * - * Returns a reference to the sound board. - * - * Returns: - * Pointer to CSoundBoard object. - */ - CSoundBoard *GetSoundBoard(void); + /* + * Init(void): + * + * One-time initialization of the context. Must be called prior to all + * other members. Allocates memory and initializes device states. + * + * NOTE: Command line settings will not have been applied here yet. + * + * Returns: + * OKAY is successful, otherwise FAILED if a non-recoverable error + * occurred. Prints own error messages. + */ + bool Init(void); + + /* + * GetSoundBoard(void): + * + * Returns a reference to the sound board. + * + * Returns: + * Pointer to CSoundBoard object. + */ + CSoundBoard *GetSoundBoard(void); - /* - * GetDriveBoard(void): - * - * Returns a reference to the drive board. + /* + * GetDriveBoard(void): + * + * Returns a reference to the drive board. - * Returns: - * Pointer to CDriveBoard object. - */ - CDriveBoard *GetDriveBoard(void); + * Returns: + * Pointer to CDriveBoard object. + */ + CDriveBoard *GetDriveBoard(void); - /* - * PauseThreads(void): - * - * Flags that any running threads should pause and waits for them to do so. - * Should be used before invoking any method that accesses the internal state, eg LoadState or SaveState. - */ - bool PauseThreads(void); + /* + * PauseThreads(void): + * + * Flags that any running threads should pause and waits for them to do so. + * Should be used before invoking any method that accesses the internal state, eg LoadState or SaveState. + */ + bool PauseThreads(void); - /* - * ResumeThreads(void): - * - * Flags that any paused threads should resume running. - */ - bool ResumeThreads(void); + /* + * ResumeThreads(void): + * + * Flags that any paused threads should resume running. + */ + bool ResumeThreads(void); - /* - * DumpTimings(void): - * - * Prints all timings for the most recent frame to the console, for debugging purposes. - */ - void DumpTimings(void); + /* + * DumpTimings(void): + * + * Prints all timings for the most recent frame to the console, for debugging purposes. + */ + void DumpTimings(void); - /* - * GetTimings(void): - * - * Returns timings for the most recent frame, for debugging purposes. - */ - FrameTimings GetTimings(void); + /* + * GetTimings(void): + * + * Returns timings for the most recent frame, for debugging purposes. + */ + FrameTimings GetTimings(void); - /* - * CModel3(void): - * ~CModel3(void): - * - * Constructor and destructor for Model 3 class. Constructor performs a - * bare-bones initialization of object; does not perform any memory - * allocation or any actions that can fail. The destructor will deallocate - * memory and free resources used by the object (and its child objects). - */ - CModel3(void); - ~CModel3(void); + /* + * CModel3(void): + * ~CModel3(void): + * + * Constructor and destructor for Model 3 class. Constructor performs a + * bare-bones initialization of object; does not perform any memory + * allocation or any actions that can fail. The destructor will deallocate + * memory and free resources used by the object (and its child objects). + */ + CModel3(void); + ~CModel3(void); - /* - * Private Property. - * Tresspassers will be shot! ;) - */ + /* + * Private Property. + * Tresspassers will be shot! ;) + */ private: - // Private member functions - UINT8 ReadInputs(unsigned reg); - void WriteInputs(unsigned reg, UINT8 data); - UINT32 ReadSecurity(unsigned reg); - void WriteSecurity(unsigned reg, UINT32 data); - void SetCROMBank(unsigned idx); - UINT8 ReadSystemRegister(unsigned reg); - void WriteSystemRegister(unsigned reg, UINT8 data); - void Patch(void); + // Private member functions + UINT8 ReadInputs(unsigned reg); + void WriteInputs(unsigned reg, UINT8 data); + uint16_t ReadSecurityRAM(uint32_t addr); + UINT32 ReadSecurity(unsigned reg); + void WriteSecurity(unsigned reg, UINT32 data); + void SetCROMBank(unsigned idx); + UINT8 ReadSystemRegister(unsigned reg); + void WriteSystemRegister(unsigned reg, UINT8 data); + void Patch(void); - void RunMainBoardFrame(void); // Runs PPC main board for a frame - void SyncGPUs(void); // Sync's up GPUs in preparation for rendering - must be called when PPC is not running - void RenderFrame(void); // Renders current frame - bool RunSoundBoardFrame(void); // Runs sound board for a frame - void RunDriveBoardFrame(void); // Runs drive board for a frame + void RunMainBoardFrame(void); // Runs PPC main board for a frame + void SyncGPUs(void); // Sync's up GPUs in preparation for rendering - must be called when PPC is not running + void RenderFrame(void); // Renders current frame + bool RunSoundBoardFrame(void); // Runs sound board for a frame + void RunDriveBoardFrame(void); // Runs drive board for a frame - bool StartThreads(void); // Starts all threads - bool StopThreads(void); // Stops all threads - void DeleteThreadObjects(void); // Deletes all threads and synchronization objects + bool StartThreads(void); // Starts all threads + bool StopThreads(void); // Stops all threads + void DeleteThreadObjects(void); // Deletes all threads and synchronization objects - static int StartMainBoardThread(void *data); // Callback to start PPC main board thread - static int StartSoundBoardThread(void *data); // Callback to start sound board thread (unsync'd) - static int StartSoundBoardThreadSyncd(void *data); // Callback to start sound board thread (sync'd) - static int StartDriveBoardThread(void *data); // Callback to start drive board thread + static int StartMainBoardThread(void *data); // Callback to start PPC main board thread + static int StartSoundBoardThread(void *data); // Callback to start sound board thread (unsync'd) + static int StartSoundBoardThreadSyncd(void *data); // Callback to start sound board thread (sync'd) + static int StartDriveBoardThread(void *data); // Callback to start drive board thread - static void AudioCallback(void *data); // Audio buffer callback - - bool WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread (when not sync'd with render thread) - int RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread) - int RunSoundBoardThread(void); // Runs sound board thread (not sync'd in step with render thread, ie running at full speed) - int RunSoundBoardThreadSyncd(void); // Runs sound board thread (sync'd in step with render thread) - int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread) - - // Game and hardware information - const struct GameInfo *Game; - - // Game inputs - CInputs *Inputs; + static void AudioCallback(void *data); // Audio buffer callback + + bool WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread (when not sync'd with render thread) + int RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread) + int RunSoundBoardThread(void); // Runs sound board thread (not sync'd in step with render thread, ie running at full speed) + int RunSoundBoardThreadSyncd(void); // Runs sound board thread (sync'd in step with render thread) + int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread) + + // Game and hardware information + const struct GameInfo *Game; + + // Game inputs and outputs + CInputs *Inputs; + COutputs *Outputs; + + // Input registers (game controls) + UINT8 inputBank; + UINT8 serialFIFO1, serialFIFO2; + UINT8 gunReg; + int adcChannel; + + // MIDI port + UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior + + // Emulated core Model 3 memory regions + UINT8 *memoryPool; // single allocated region for all ROM and system RAM + UINT8 *ram; // 8 MB PowerPC RAM + UINT8 *crom; // 8+128 MB CROM (fixed CROM first, then 64MB of banked CROMs -- Daytona2 might need extra?) + UINT8 *vrom; // 64 MB VROM (video ROM, visible only to Real3D) + UINT8 *soundROM; // 512 KB sound ROM (68K program) + UINT8 *sampleROM; // 8 MB samples (68K) + UINT8 *dsbROM; // 128 KB DSB ROM (Z80 program) + UINT8 *mpegROM; // 8 MB DSB MPEG ROM + UINT8 *backupRAM; // 128 KB Backup RAM (battery backed) + UINT8 *securityRAM; // 128 KB Security Board RAM + UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional) + + // Banked CROM + UINT8 *cromBank; // currently mapped in CROM bank + unsigned cromBankReg; // the CROM bank register + + // Security device + bool m_securityFirstRead = true; + unsigned securityPtr; // pointer to current offset in security data + + // PowerPC + PPC_FETCH_REGION PPCFetchRegions[3]; - // Game outputs - COutputs *Outputs; - - // Input registers (game controls) - UINT8 inputBank; - UINT8 serialFIFO1, serialFIFO2; - UINT8 gunReg; - int adcChannel; - - // MIDI port - UINT8 midiCtrlPort; // controls MIDI (SCSP) IRQ behavior - - // Emulated core Model 3 memory regions - UINT8 *memoryPool; // single allocated region for all ROM and system RAM - UINT8 *ram; // 8 MB PowerPC RAM - UINT8 *crom; // 8+128 MB CROM (fixed CROM first, then 64MB of banked CROMs -- Daytona2 might need extra?) - UINT8 *vrom; // 64 MB VROM (video ROM, visible only to Real3D) - UINT8 *soundROM; // 512 KB sound ROM (68K program) - UINT8 *sampleROM; // 8 MB samples (68K) - UINT8 *dsbROM; // 128 KB DSB ROM (Z80 program) - UINT8 *mpegROM; // 8 MB DSB MPEG ROM - UINT8 *backupRAM; // 128 KB Backup RAM (battery backed) - UINT8 *securityRAM; // 128 KB Security Board RAM - UINT8 *driveROM; // 32 KB drive board ROM (Z80 program) (optional) - - // Banked CROM - UINT8 *cromBank; // currently mapped in CROM bank - unsigned cromBankReg; // the CROM bank register - - // Security device - unsigned securityPtr; // pointer to current offset in security data - - // PowerPC - PPC_FETCH_REGION PPCFetchRegions[3]; + // Multiple threading + bool gpusReady; // True if GPUs are ready to render + bool startedThreads; // True if threads have been created and started + bool pauseThreads; // True if threads should pause + bool stopThreads; // True if threads should stop + bool syncSndBrdThread; // True if sound board thread should be sync'd in step with render thread + CThread *ppcBrdThread; // PPC main board thread + CThread *sndBrdThread; // Sound board thread + CThread *drvBrdThread; // Drive board thread + bool ppcBrdThreadRunning; // Flag to indicate PPC main board thread is currently processing + bool ppcBrdThreadDone; // Flag to indicate PPC main board thread has finished processing + bool sndBrdThreadRunning; // Flag to indicate sound board thread is currently processing + bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing + bool sndBrdWakeNotify; // Flag to indicate that sound board thread has been woken by audio callback (when not sync'd with render thread) + bool drvBrdThreadRunning; // Flag to indicate drive board thread is currently processing + bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing - // Multiple threading - bool gpusReady; // True if GPUs are ready to render - bool startedThreads; // True if threads have been created and started - bool pauseThreads; // True if threads should pause - bool stopThreads; // True if threads should stop - bool syncSndBrdThread; // True if sound board thread should be sync'd in step with render thread - CThread *ppcBrdThread; // PPC main board thread - CThread *sndBrdThread; // Sound board thread - CThread *drvBrdThread; // Drive board thread - bool ppcBrdThreadRunning; // Flag to indicate PPC main board thread is currently processing - bool ppcBrdThreadDone; // Flag to indicate PPC main board thread has finished processing - bool sndBrdThreadRunning; // Flag to indicate sound board thread is currently processing - bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing - bool sndBrdWakeNotify; // Flag to indicate that sound board thread has been woken by audio callback (when not sync'd with render thread) - bool drvBrdThreadRunning; // Flag to indicate drive board thread is currently processing - bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing - - // Thread synchronization objects - CSemaphore *ppcBrdThreadSync; - CSemaphore *sndBrdThreadSync; - CMutex *sndBrdNotifyLock; - CCondVar *sndBrdNotifySync; - CSemaphore *drvBrdThreadSync; - CMutex *notifyLock; - CCondVar *notifySync; - - // Frame timings - FrameTimings timings; - - // Other devices - CIRQ IRQ; // Model 3 IRQ controller - CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller - CPCIBus PCIBus; // Model 3's PCI bus - C53C810 SCSI; // NCR 53C810 SCSI controller - CRTC72421 RTC; // Epson RTC-72421 real-time clock - C93C46 EEPROM; // 93C46 EEPROM - CTileGen TileGen; // Sega 2D tile generator - CReal3D GPU; // Real3D graphics hardware - CSoundBoard SoundBoard; // Sound board - CDSB *DSB; // Digital Sound Board (type determined dynamically at load time) - CDriveBoard DriveBoard; // Drive board + // Thread synchronization objects + CSemaphore *ppcBrdThreadSync; + CSemaphore *sndBrdThreadSync; + CMutex *sndBrdNotifyLock; + CCondVar *sndBrdNotifySync; + CSemaphore *drvBrdThreadSync; + CMutex *notifyLock; + CCondVar *notifySync; + + // Frame timings + FrameTimings timings; + + // Other devices + CIRQ IRQ; // Model 3 IRQ controller + CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller + CPCIBus PCIBus; // Model 3's PCI bus + C53C810 SCSI; // NCR 53C810 SCSI controller + CRTC72421 RTC; // Epson RTC-72421 real-time clock + C93C46 EEPROM; // 93C46 EEPROM + CTileGen TileGen; // Sega 2D tile generator + CReal3D GPU; // Real3D graphics hardware + CSoundBoard SoundBoard; // Sound board + CDSB *DSB; // Digital Sound Board (type determined dynamically at load time) + CDriveBoard DriveBoard; // Drive board + CCrypto m_cryptoDevice; // Encryption device }; -#endif // INCLUDED_MODEL3_H \ No newline at end of file +#endif // INCLUDED_MODEL3_H \ No newline at end of file diff --git a/Src/Util/Format.cpp b/Src/Util/Format.cpp new file mode 100644 index 0000000..941d40e --- /dev/null +++ b/Src/Util/Format.cpp @@ -0,0 +1,33 @@ +#include "Util/Format.h" + +namespace Util +{ + static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + const std::string Hex(uint32_t n, size_t num_digits) + { + Util::Format f; + f << "0x"; + for (size_t b = num_digits * 4; b; ) + { + b -= 4; + f << hex_digits[(n >> b) & 0xf]; + } + return f; + } + + const std::string Hex(uint32_t n) + { + return Hex(n, 8); + } + + const std::string Hex(uint16_t n) + { + return Hex(n, 4); + } + + const std::string Hex(uint8_t n) + { + return Hex(n, 2); + } +} // Util diff --git a/Src/Util/Format.h b/Src/Util/Format.h new file mode 100644 index 0000000..6397e96 --- /dev/null +++ b/Src/Util/Format.h @@ -0,0 +1,68 @@ +#ifndef INCLUDED_FORMAT_H +#define INCLUDED_FORMAT_H + +#include +#include +#include + +namespace Util +{ + class Format + { + public: + template + Format &operator<<(const T &data) + { + m_stream << data; + return *this; + } + + operator std::string() const + { + return str(); + } + + std::string str() const + { + return m_stream.str(); + } + + template + Format &Join(const T &collection) + { + std::string separator = m_stream.str(); + clear(); + for (auto it = collection.begin(); it != collection.end(); ) + { + m_stream << *it; + ++it; + if (it != collection.end()) + m_stream << separator; + } + return *this; + } + + Format(const std::string &str) + : m_stream(str) + { + } + + Format() + { + } + private: + std::stringstream m_stream; + + void clear() + { + m_stream.str(std::string()); + } + }; + + const std::string Hex(uint32_t n, size_t num_digits); + const std::string Hex(uint32_t n); + const std::string Hex(uint16_t n); + const std::string Hex(uint8_t n); +} // Util + +#endif // INCLUDED_FORMAT_H