Naomi: DMA on-the-fly decompression+XOR support [Deunan Knute, R. Belmont]

This commit is contained in:
R. Belmont 2011-02-27 17:55:07 +00:00
parent 6f24fc7670
commit 70ef5cc21c
2 changed files with 240 additions and 81 deletions

View File

@ -36,17 +36,10 @@ ToDo:
- all games that uses YUV just updates one frame then dies, why?
- all or almost all games seems to have issues with the sound loops, for example sfz3ugd keeps to say "This-This-This ..." at start-up
instead of completing the phrase;
- suchie3: used to boot (at least go over the I/O test), it doesn't now, might be protection related.
- SH to ARM sound streaming doesn't work (used by ADX compression system)
- ngdup23a, ngdup23c: missing DIMM emulation, hence they can't possibly work, emulate the DIMM means to add an extra SH-4 ...
- totd: emulation exits at some point during attract mode without any output.
- spkrbtl: ARM CPU crashes sometimes? Also, it crashes when entering into the gameplay.
---------------------------------------------------------------------------------------------------------------------------------------------------
Guru's Readme
@ -1347,7 +1340,7 @@ Sega Bass Fishing Challenge
#include "sound/aica.h"
#include "includes/dc.h"
#define CPU_CLOCK (200000000)
#define CPU_CLOCK (70000000)
static UINT32 *dc_sound_ram;
static macronix_29l001mc_device *awflash;
@ -3578,16 +3571,6 @@ ROM_START( mvsc2 )
ROM_LOAD("mpr-23061.ic30", 0x7000000, 0x0800000, CRC(fb1844c4) SHA1(1d1571516a6dbed0c4ded3b80efde9cc9281f66f) )
ROM_LOAD("mpr-23083.ic31", 0x7800000, 0x0400000, CRC(c61d2dfe) SHA1(a05fb979ed7c8040de91716fc8814e6bd995efa2) )
ROM_LOAD("mpr-23084.ic32", 0x8000000, 0x0400000, CRC(e228cdfd) SHA1(d02a2e3557bd24cf34c5ddb42d41ca15e78ae885) )
// DMA protection data
ROM_LOAD("88000000.bin", 0x8800000, 0x025f00, CRC(77d79823) SHA1(2545d28eee47114e8ffb9bc6d7a910e90fc48420) )
ROM_LOAD("88026440.bin", 0x8830000, 0x016520, CRC(dad9ebbd) SHA1(39c0697caa2b5ee11d99e75726e92ed86a23f10b) )
ROM_LOAD("8803bda0.bin", 0x8850000, 0x01e5e0, CRC(9e0b8202) SHA1(729bed557c1a00da13c990603bbadab38d90285e) )
ROM_LOAD("8805a560.bin", 0x8870000, 0x0017a0, CRC(7bc27482) SHA1(6ce6074cf47989f42af02deb7aac52883912784a) )
ROM_LOAD("8805b720.bin", 0x8880000, 0x02b5a0, CRC(485d0aef) SHA1(853f4b49b489cc512c906edcaf3cd8b5bf4c64c0) )
ROM_LOAD("8808b7e0.bin", 0x88a0000, 0x013ec0, CRC(0fc8f363) SHA1(dad30d43cef89d01ce80301b1d796aabad755de6) )
ROM_END
/* toy fighter - 1999 sega */
@ -4033,9 +4016,6 @@ ROM_START( qmegamis )
ROM_LOAD32_WORD("mpr-23226.ic32", 0x8000002, 0x0800000, CRC(cd5da506) SHA1(2e76c8892c1d389b0f12a0046213f43d2ab07d78) ) //ic 17
ROM_COPY( "user1", 0x1400000, 0x0800000, 0x0800000 ) // mirror data for IC1 (no test on this game, but layout is the same as gram2000)
// trojaned protection data (must be in the "user1" region because it's accessed via DMA)
ROM_LOAD( "81452000_dma.bin", 0x9000000, 0x000120, CRC(96049488) SHA1(e2b98e8986f8cbf026db50a652300081a8e470b0) )
ROM_END
/*

View File

@ -2,8 +2,8 @@
Naomi plug-in board emulator
emulator by Samuele Zannoli
protection chip by R. Belmont, reverse engineering by Deunan Knute
emulator by Samuele Zannoli and R. Belmont
reverse engineering by ElSemi, Deunan Knute, Andreas Naive, Olivier Galibert, and Cah4e3
**************************************************************************/
@ -61,10 +61,9 @@
In other words, disable DMA once before using PIO (most games using both access types do that when the DMA terminates).
This bit is also used to reset the chip's internal protection mechanism on "Oh! My Goddess" to a known state.
* bit 29 (mode bit 1) is address shuffle bit
* bit 29 (mode bit 1) is "M1" compression bit on Actel carts, other functions on others
It's actually the opposite, when set the addressing is following the chip layout and when cleared the protection chip will have it's fun
with address lines 10 to 23(?). It's not a simple swap function, rather a lookup table and one with repeating results too.
The few games I got to work never made any use of that bit, it's always set for all normal reads.
doing a decompression + XOR on the data for Actel carts. Non-Actel carts may ignore this bit or remap the address space.
* bit 28 (mode bit 0) is unused (so far)
Or it could really be the last address bit to allow up to 512MB of data on a cart?
@ -179,7 +178,7 @@ Atomiswave ROM board specs from Cah4e3 @ http://cah4e3.wordpress.com/2009/07/26/
#define NAOMIBD_FLAG_AUTO_ADVANCE (8) // address auto-advances on read
#define NAOMIBD_FLAG_SPECIAL_MODE (4) // used to access protection registers
#define NAOMIBD_FLAG_ADDRESS_SHUFFLE (2) // 0 to let protection chip en/decrypt, 1 for normal
#define NAOMIBD_FLAG_DMA_COMPRESSION (2) // 0 protection chip decompresses DMA data, 1 for normal DMA reads
#define NAOMIBD_PRINTF_PROTECTION (0) // 1 to printf protection access details
@ -189,8 +188,6 @@ Atomiswave ROM board specs from Cah4e3 @ http://cah4e3.wordpress.com/2009/07/26/
*
*************************************/
#define MAX_PROT_REGIONS (32)
#define PACK_BUF_SIZE (32768)
enum
@ -218,6 +215,11 @@ struct _naomibd_prot
UINT8 pak_byte, cmd_byte;
int seed;
UINT8 pak_buf[PACK_BUF_SIZE];
UINT8 *s_input;
UINT8 s_xor[4];
UINT8 s_dict[111];
int s_subst, s_out_len, s_out_cnt, s_shift, s_bits, s_in_len, s_in_pos;
};
typedef struct _naomibd_state naomibd_state;
@ -237,13 +239,9 @@ struct _naomibd_state
// live decrypt vars
UINT32 dc_gamekey, dc_seqkey, dc_dmakey;
UINT8 dc_cart_ram[128*1024]; // internal cartridge RAM
UINT8 dc_cart_ram[256*1024]; // internal cartridge RAM
INT32 dc_m3_ptr;
#if NAOMIBD_PRINTF_PROTECTION
int prot_pio_count;
#endif
naomibd_prot prot;
};
@ -342,8 +340,6 @@ INLINE naomibd_state *get_safe_token(device_t *device)
int naomibd_interrupt_callback(device_t *device, naomibd_interrupt_func callback)
{
naomibd_config *config = (naomibd_config *)downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config();
//naomibd_state *v = get_safe_token(device);
config->interrupt = callback;
return 0;
}
@ -354,10 +350,168 @@ int naomibd_get_type(device_t *device)
return v->type;
}
void *naomibd_get_memory(device_t *device)
// M1 decryption/decompression
static UINT8 naomibd_m1dec_readbyte(naomibd_state *naomibd)
{
return get_safe_token(device)->memory;
UINT8 v;
switch (naomibd->prot.s_in_pos & 3)
{
case 0:
v = naomibd->prot.s_input[naomibd->prot.s_in_pos + 3];
v ^= naomibd->prot.s_input[naomibd->prot.s_in_pos + 1];
break;
case 1:
v = naomibd->prot.s_input[naomibd->prot.s_in_pos + 1];
v ^= naomibd->prot.s_input[naomibd->prot.s_in_pos - 1];
break;
case 2:
v = naomibd->prot.s_input[naomibd->prot.s_in_pos - 1];
break;
case 3:
v = naomibd->prot.s_input[naomibd->prot.s_in_pos - 3];
break;
}
v ^= naomibd->prot.s_xor[naomibd->prot.s_in_pos & 3];
naomibd->prot.s_in_pos++;
return v;
}
static void naomibd_m1dec_storebyte (naomibd_state *naomibd, UINT8 b)
{
if (naomibd->prot.s_subst && naomibd->prot.s_out_cnt >= 2)
{
b = naomibd->dc_cart_ram[naomibd->prot.s_out_cnt - 2] - b;
}
naomibd->dc_cart_ram[naomibd->prot.s_out_cnt] = b;
naomibd->prot.s_out_cnt++;
if (naomibd->prot.s_out_cnt >= (256*1024))
{
fatalerror("naomibd: M1 decode exceeds buffer size!\n");
}
}
static void naomibd_m1dec_shiftin(naomibd_state *naomibd)
{
naomibd->prot.s_shift <<= 8;
naomibd->prot.s_shift |= naomibd_m1dec_readbyte(naomibd);
naomibd->prot.s_bits += 8;
}
static void naomibd_m1_decode(naomibd_state *naomibd)
{
int i, eos;
naomibd->prot.s_xor [0] = (UINT8)naomibd->dc_dmakey;
naomibd->prot.s_xor [1] = (UINT8)(naomibd->dc_dmakey >> 8);
naomibd->prot.s_xor [2] = (UINT8)(naomibd->dc_dmakey >> 16);
naomibd->prot.s_xor [3] = (UINT8)(naomibd->dc_dmakey >> 24);
#if NAOMIBD_PRINTF_PROTECTION
printf("M1 decode: dma offset %x, key %x\n", naomibd->dma_offset, naomibd->dc_dmakey);
#endif
naomibd->prot.s_input = naomibd->memory + naomibd->dma_offset;
naomibd->prot.s_in_pos = 0;
// read in the dictionary
for (i = 0; i < 111; i++)
{
naomibd->prot.s_dict [i] = naomibd_m1dec_readbyte(naomibd);
}
// control bits
naomibd->prot.s_subst = (naomibd->prot.s_dict [0] & 64) ? 1 : 0;
// command stream
naomibd->prot.s_out_cnt = 0, eos = 0;
naomibd->prot.s_shift = 0, naomibd->prot.s_bits = 0;
while (!eos)
{
int code, addr, t;
if (naomibd->prot.s_bits < 2)
naomibd_m1dec_shiftin(naomibd);
code = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 2)) & 3;
switch (code)
{
case 0:
// 00-aa
if (naomibd->prot.s_bits < 4)
naomibd_m1dec_shiftin(naomibd);
addr = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 4)) & 3;
naomibd->prot.s_bits -= 4;
if (addr == 0)
{
// quotation
if (naomibd->prot.s_bits < 8)
naomibd_m1dec_shiftin (naomibd);
t = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 8)) & 255;
naomibd->prot.s_bits -= 8;
naomibd_m1dec_storebyte(naomibd, t);
break;
}
naomibd_m1dec_storebyte(naomibd, naomibd->prot.s_dict [addr]);
break;
case 1:
if (naomibd->prot.s_bits < 5)
naomibd_m1dec_shiftin (naomibd);
t = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 3)) & 1;
if (t == 0)
{
// 010-aa
addr = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 5)) & 3;
addr += 4;
naomibd->prot.s_bits -= 5;
}
else
{
// 011-aaa
if (naomibd->prot.s_bits < 6)
naomibd_m1dec_shiftin (naomibd);
addr = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 6)) & 7;
addr += 8;
naomibd->prot.s_bits -= 6;
}
naomibd_m1dec_storebyte(naomibd, naomibd->prot.s_dict [addr]);
break;
case 2:
if (naomibd->prot.s_bits < 7)
naomibd_m1dec_shiftin(naomibd);
// 10-aaaaa
addr = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 7)) & 31;
addr += 16;
naomibd->prot.s_bits -= 7;
naomibd_m1dec_storebyte(naomibd, naomibd->prot.s_dict [addr]);
break;
case 3:
if (naomibd->prot.s_bits < 8)
naomibd_m1dec_shiftin(naomibd);
// 11-aaaaaa
addr = (naomibd->prot.s_shift >> (naomibd->prot.s_bits - 8)) & 63;
addr += 48;
naomibd->prot.s_bits -= 8;
if (addr == 111)
{
// end of stream
eos = 1;
}
else
{
naomibd_m1dec_storebyte(naomibd, naomibd->prot.s_dict [addr]);
}
break;
}
}
}
// Streaming M2/M3 protection and decompression
@ -367,7 +521,7 @@ INLINE UINT16 bswap16(UINT16 in)
return ((in>>8) | (in<<8));
}
UINT16 naomibd_get_decrypted_stream(naomibd_state *naomibd)
static UINT16 naomibd_get_decrypted_stream(naomibd_state *naomibd)
{
UINT16 wordn = bswap16(naomibd->prot.ptr[naomibd->prot.count++]);
@ -378,7 +532,7 @@ UINT16 naomibd_get_decrypted_stream(naomibd_state *naomibd)
return wordn;
}
void naomibd_init_stream(naomibd_state *naomibd)
static void naomibd_init_stream(naomibd_state *naomibd)
{
naomibd->prot.last_word = 0;
@ -395,7 +549,7 @@ void naomibd_init_stream(naomibd_state *naomibd)
}
}
UINT16 naomibd_get_compressed_bit(naomibd_state *naomibd)
static UINT16 naomibd_get_compressed_bit(naomibd_state *naomibd)
{
if(naomibd->prot.pak_bit == 0)
{
@ -410,7 +564,7 @@ UINT16 naomibd_get_compressed_bit(naomibd_state *naomibd)
return naomibd->prot.pak_word >> 15;
}
UINT16 naomibd_get_decompressed_stream(naomibd_state *naomibd)
static UINT16 naomibd_get_decompressed_stream(naomibd_state *naomibd)
{
/* node format
0xxxxxxx - next node index
@ -558,7 +712,7 @@ UINT16 naomibd_get_decompressed_stream(naomibd_state *naomibd)
// stream read protected PIO hook
//-----------------------------------------------------------
UINT16 naomibd_get_data_stream(naomibd_state *naomibd)
static UINT16 naomibd_get_data_stream(naomibd_state *naomibd)
{
UINT16 wordn;
@ -574,8 +728,31 @@ UINT16 naomibd_get_data_stream(naomibd_state *naomibd)
return wordn;
}
void *naomibd_get_memory(device_t *device)
{
naomibd_state *naomibd = get_safe_token(device);
// for M1 decodes, return the buffer we'll DMA from
if (!(naomibd->dma_offset_flags & NAOMIBD_FLAG_DMA_COMPRESSION) && (naomibd->type == ROM_BOARD) && (naomibd->dc_dmakey != 0))
{
// perform the M1 decode
naomibd_m1_decode(naomibd);
// return the pointer to our output buffer
return naomibd->dc_cart_ram;
}
if (!(naomibd->dma_offset_flags & NAOMIBD_FLAG_DMA_COMPRESSION) && (naomibd->type == ROM_BOARD))
{
logerror("Unhandled M1 DMA with key %x, flags %x, offset %x\n", naomibd->dc_dmakey, naomibd->dma_offset_flags, naomibd->dma_offset);
}
return get_safe_token(device)->memory;
}
offs_t naomibd_get_dmaoffset(device_t *device)
{
naomibd_state *naomibd = get_safe_token(device);
offs_t result = 0;
#if NAOMIBD_PRINTF_PROTECTION
@ -583,38 +760,17 @@ offs_t naomibd_get_dmaoffset(device_t *device)
#endif
// if the flag is cleared that lets the protection chip go,
// we need to handle this specially. but not on DIMM boards.
if (!(get_safe_token(device)->dma_offset_flags & NAOMIBD_FLAG_ADDRESS_SHUFFLE) && (get_safe_token(device)->type == ROM_BOARD))
// we need to handle this specially. but not on DIMM boards or if there's no key.
if (!(naomibd->dma_offset_flags & NAOMIBD_FLAG_DMA_COMPRESSION) && (naomibd->type == ROM_BOARD) && (naomibd->dc_dmakey != 0))
{
if (!strcmp(device->machine->gamedrv->name, "qmegamis"))
{
result = 0x9000000;
return result;
}
else if (!strcmp(device->machine->gamedrv->name, "mvsc2"))
{
switch (get_safe_token(device)->dma_offset)
{
case 0x08000000: result = 0x8800000; break;
case 0x08026440: result = 0x8830000; break;
case 0x0803bda0: result = 0x8850000; break;
case 0x0805a560: result = 0x8870000; break;
case 0x0805b720: result = 0x8880000; break;
case 0x0808b7e0: result = 0x88a0000; break;
default:
result = get_safe_token(device)->dma_offset;
break;
}
return result;
}
else
{
logerror("Protected DMA not handled for this game (dma_offset %x)\n", get_safe_token(device)->dma_offset);
}
// no offset, start at the beginning of cart ram for M1 transfers
result = 0;
}
else
{
result = get_safe_token(device)->dma_offset;
}
result = get_safe_token(device)->dma_offset;
return result;
}
@ -650,6 +806,32 @@ static void init_save_state(device_t *device)
device->save_item(NAME(v->aw_offset));
device->save_item(NAME(v->aw_file_base));
device->save_item(NAME(v->aw_file_offset));
device->save_item(NAME(v->dc_m3_ptr));
device->save_item(NAME(v->dc_cart_ram));
device->save_item(NAME(v->prot.last_word));
device->save_item(NAME(v->prot.aux_word));
device->save_item(NAME(v->prot.pak_word));
device->save_item(NAME(v->prot.heading_word));
device->save_item(NAME(v->prot.count));
device->save_item(NAME(v->prot.pak_bit));
device->save_item(NAME(v->prot.control_bits));
device->save_item(NAME(v->prot.pak_state));
device->save_item(NAME(v->prot.dec_count));
device->save_item(NAME(v->prot.pak_buf_size));
device->save_item(NAME(v->prot.pak_buf_pos));
device->save_item(NAME(v->prot.pak_fetch_ofs));
device->save_item(NAME(v->prot.pak_byte));
device->save_item(NAME(v->prot.cmd_byte));
device->save_item(NAME(v->prot.seed));
device->save_item(NAME(v->prot.s_xor));
device->save_item(NAME(v->prot.s_dict));
device->save_item(NAME(v->prot.s_subst));
device->save_item(NAME(v->prot.s_out_len));
device->save_item(NAME(v->prot.s_out_cnt));
device->save_item(NAME(v->prot.s_shift));
device->save_item(NAME(v->prot.s_bits));
device->save_item(NAME(v->prot.s_in_len));
device->save_item(NAME(v->prot.s_in_pos));
}
@ -802,7 +984,7 @@ WRITE64_DEVICE_HANDLER( naomibd_w )
v->aw_offset &= 0xffff;
v->aw_offset |= ((data>>16) & 0xffff0000);
v->dma_offset = v->aw_offset*2;
v->dma_offset_flags = NAOMIBD_FLAG_ADDRESS_SHUFFLE|NAOMIBD_FLAG_AUTO_ADVANCE; // force normal DMA mode
v->dma_offset_flags = NAOMIBD_FLAG_DMA_COMPRESSION|NAOMIBD_FLAG_AUTO_ADVANCE; // force normal DMA mode
//printf("EPR_OFFSETH = %x, dma_offset %x\n", (UINT32)(data>>32), v->dma_offset);
}
@ -895,7 +1077,7 @@ WRITE64_DEVICE_HANDLER( naomibd_w )
// DMA_OFFSETH
v->dma_offset &= 0xffff;
v->dma_offset |= (data >> 16) & 0x1fff0000;
v->dma_offset_flags = (data>>28);
v->dma_offset_flags = (data>>(28+16));
}
if(ACCESSING_BITS_0_15)
{
@ -917,8 +1099,6 @@ WRITE64_DEVICE_HANDLER( naomibd_w )
#if NAOMIBD_PRINTF_PROTECTION
printf("Protection: set up read @ %x, key %x (PIO %x DMA %x) [%s]\n", v->prot_offset*2, v->prot_key, v->rom_offset, v->dma_offset, device->machine->describe_context());
v->prot_pio_count = 0;
#endif
// if dc_gamekey isn't -1, we can live-decrypt this one
@ -1843,9 +2023,8 @@ static DEVICE_START( naomibd )
/* store a pointer back to the device */
v->device = device;
#if NAOMIBD_PRINTF_PROTECTION
v->prot_pio_count = 0;
#endif
v->dc_dmakey = 0;
for (i=0; i<ARRAY_LENGTH(naomibd_translate_tbl); i++)
{
if (!strcmp(device->machine->gamedrv->name, naomibd_translate_tbl[i].name))