// license:BSD-3-Clause
// copyright-holders:Olivier Galibert,Andreas Naive
#include "emu.h"
#include "awboard.h"
#include <algorithm>
/*
Atomiswave ROM board specs from Cah4e3 @ http://cah4e3.wordpress.com/2009/07/26/some-atomiswave-info/
AW_EPR_OFFSETL Register addres: 0x5f7000
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| EPR data offset low word |
+-------------------------------------------------------------------------------+
AW_EPR_OFFSETH Register addres: 0x5f7004
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| EPR data offset hi word |
+-------------------------------------------------------------------------------+
Both low and high words of 32-bit offset from start of EPR-ROM area. Used for
reading header and program code data, cannot be used for reading MPR-ROMs data.
During program code DMA transfer Romeo MCU perform data checksuming (decrypted data, in 8bit units),
result must match some CPLD / encryption key-specific value, otherwise MPR-ROM access
described below will not work correctly.
Game header (first 256 bytes of ROM) is not checksum protected.
AW_MPR_RECORD_INDEX Register addres: 0x5f700c
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| File system record index |
+-------------------------------------------------------------------------------+
This register contains index of MPR-ROM file system record (64-bytes in size) to
read throught DMA. Internal DMA offset register is assigned as AW_MPR_RECORD_INDEX<<6
from start of MPR-ROM area. Size of DMA transaction not limited, it is possible
to read any number of records or just part of it.
AW_MPR_FIRST_FILE_INDEX Register addres: 0x5f7010
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| First file record index |
+-------------------------------------------------------------------------------+
This register assign for internal cart circuit index of record in MPR-ROM file
system sub-area that contain information about first file of MPR-ROM files
sub-area. Internal circuit using this record to read absolute first file offset
from start of MPR-ROM area and calculate normal offset for each other file
requested, since MPR-ROM file data sub-area can be assighed only with relative
offsets from start of such sub-area.
AW_MPR_FILE_OFFSETL Register addres: 0x5f7014
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| MPR file offset low word |
+-------------------------------------------------------------------------------+
AW_MPR_FILE_OFFSETH Register addres: 0x5f7018
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| MPR file offset hi word |
+-------------------------------------------------------------------------------+
Both low and high words of 32-bit relative offset from start of MPR-ROM files
sub-area. Used by internal circuit to calculate absolute offset using data
from AW_MPR_FIRST_FILE_INDEX register. Cannot be used for reading EPR-ROM
data nor even MPR-ROM file system sub-area data.
AW_PIO_DATA Register addres: 0x5f7080
+-------------------------------------------------------------------------------+
| bit15-0 |
+-------------------------------------------------------------------------------+
| Read/Write word from/to ROM board address space |
+-------------------------------------------------------------------------------+
Using this register data can be read or written to ROM BD at AW_EPR_OFFSET directly,
decryption is not used, flash ROMs (re)programming via CFI commands possible.
Type 2 ROM BD have MPR_BANK register at AW_EPR_OFFSET 007fffff, which selects
1 of 4 mask ROM banks.
ROM board internal layouts:
Type 1:
00000000 - 00800000 IC18 flash ROM
00800000 - 01000000 IC10 or mirror of above
01000000 - 02000000 IC11 \
..... mask ROMs
07000000 - 08000000 IC17 /
Type 2:
00000000 - 00800000 FMEM1 flash ROM
00800000 - 01000000 mirror of above
01000000 - 01800000 FMEM2 flash ROM
01800000 - 02000000 mirror of above
02000000 - 04000000 MROM1 MROM4 MROM7 MROM10 \
04000000 - 06000000 MROM2 MROM5 MROM8 MROM11 banked mask ROMs
06000000 - 08000000 MROM3 MROM6 MROM9 MROM12 /
Type 3:
00000000 - 01000000 U3 flash ROM
01000000 - 02000000 U1 flash ROM
02000000 - 03000000 U4 flash ROM
03000000 - 04000000 U2 flash ROM
04000000 - 05000000 U15 flash ROM
05000000 - 06000000 U17 flash ROM
06000000 - 07000000 U14 flash ROM
07000000 - 08000000 U16 flash ROM
Development:
00000000 - 00800000 IC12 \
..... flash ROMs
07800000 - 08000000 IC27 /
In short:
EPR-ROM
+--------------+ 0x00000000
| |
| HEADER +- AW_EPR_OFFSET << 1
| |
+--------------+
| |
| CODE +- AW_EPR_OFFSET << 1
| |
| |
+--------------+ 0x007fffff
MPR-ROMS
+--------------+ 0x00000000
| FS_HEADER |
| FS_RECORD[1] +- (AW_MPR_RECORD_INDEX << 6)
| FS_RECORD[2] |
| FS_RECORD[3] +- (AW_MPR_FIRST_FILE_INDEX << 6)
| ... |
| FS_RECORD[N] |
+--------------+- FS_RECORD[AW_MPR_FIRST_FILE_INDEX].FILE_ABS_OFFSET
| FILE_0 |
| FILE_1 +- (AW_MPR_FILE_OFFSET << 1) + FS_RECORD[AW_MPR_FIRST_FILE_INDEX].FILE_ABS_OFFSET
| ... |
| FILE_N |
+--------------+ 0x07ffffff
*/
DEFINE_DEVICE_TYPE(AW_ROM_BOARD, aw_rom_board, "aw_rom_board", "Sammy Atomiswave ROM Board")
void aw_rom_board::submap(address_map &map)
{
map(0x00, 0x01).w(this, FUNC(aw_rom_board::epr_offsetl_w));
map(0x02, 0x03).w(this, FUNC(aw_rom_board::epr_offseth_w));
map(0x06, 0x07).w(this, FUNC(aw_rom_board::mpr_record_index_w));
map(0x08, 0x09).w(this, FUNC(aw_rom_board::mpr_first_file_index_w));
map(0x0a, 0x0b).w(this, FUNC(aw_rom_board::mpr_file_offsetl_w));
map(0x0c, 0x0d).w(this, FUNC(aw_rom_board::mpr_file_offseth_w));
map(0x40, 0x41).rw(this, FUNC(aw_rom_board::pio_r), FUNC(aw_rom_board::pio_w));
}
aw_rom_board::aw_rom_board(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: naomi_g1_device(mconfig, AW_ROM_BOARD, tag, owner, clock)
, m_region(*this, DEVICE_SELF)
, m_keyregion(*this, finder_base::DUMMY_TAG)
{
}
void aw_rom_board::static_set_keyregion(device_t &device, const char *keyregion)
{
aw_rom_board &dev = downcast<aw_rom_board &>(device);
dev.m_keyregion.set_tag(keyregion);
}
/*
We are using 20 bits keys with the following subfields' structure:
bits 0-15 is a 16-bits XOR
bits 17-16 is a index to the sboxes table
bits 19-18 is a index to the permutation table
These subfields could be differing from the "real" ones in the following ways:
- Every one of the index subfields could be suffering an arbitrary bitswap and XOR
- The 16-bits-XOR subfield could suffer an arbitrary XOR which could depend on the 4 index bits (that is: a different XOR per every index combination)
- Of course, the way in which we are mixing 3 subfields in one only key is arbitrary too.
The keys are stored as 32-bits big-endian values in a file.
*/
const int aw_rom_board::permutation_table[4][16] =
{
{8,10,1,3,7,4,11,2,5,15,6,0,12,13,9,14},
{4,5,9,6,1,13,7,11,10,0,14,12,8,15,2,3},
{12,7,11,2,0,5,15,6,1,8,14,4,9,13,3,10},
{14,1,11,15,7,3,8,13,0,4,2,12,6,10,5,9}
};
const aw_rom_board::sbox_set aw_rom_board::sboxes_table[4] =
{
{
{4,12,8,14,16,30,31,0,23,29,24,21,11,22,27,5,3,20,18,26,10,7,17,1,28,6,15,13,2,9,25,19},
{13,1,0,9,5,12,4,14,3,15,2,10,11,6,8,7},
{7,13,4,6,5,9,3,2,0,15,12,10,8,11,1,14},
{4,0,1,2,5,7,3,6}
},
{
{3,0,14,17,10,15,31,20,13,2,29,28,9,18,25,27,6,19,30,22,7,12,1,16,23,11,24,4,8,26,21,5},
{2,10,6,9,11,13,4,5,3,15,7,14,12,1,0,8},
{1,13,11,3,8,7,9,10,12,15,4,14,0,5,6,2},
{6,5,0,3,7,1,4,2}
},
{
{9,15,28,7,13,24,2,23,21,1,22,16,18,8,17,31,27,6,30,12,4,20,5,19,0,25,3,29,10,14,11,26},
{5,2,13,11,8,6,12,1,4,3,0,10,14,15,7,9},
{11,6,10,0,12,1,8,14,2,9,13,3,7,4,15,5},
{1,5,6,2,4,7,3,0}
},
{
{17,3,31,2,28,10,9,29,6,25,24,8,13,1,19,15,22,0,14,20,16,7,21,4,18,26,27,5,12,23,11,30},
{4,8,11,15,3,14,7,12,1,0,9,5,6,13,2,10},
{14,0,9,11,4,1,7,5,13,6,8,12,2,3,10,15},
{2,1,0,5,4,6,7,3}
},
};
uint16_t aw_rom_board::decrypt(uint16_t cipherText, uint32_t address, const uint32_t key)
{
uint8_t b0,b1,b2,b3;
uint16_t aux;
const int* pbox = permutation_table[key>>18];
const sbox_set* ss = &sboxes_table[(key>>16)&3];
aux = bitswap<16>(cipherText,
pbox[15],pbox[14],pbox[13],pbox[12],pbox[11],pbox[10],pbox[9],pbox[8],
pbox[7],pbox[6],pbox[5],pbox[4],pbox[3],pbox[2],pbox[1],pbox[0]);
aux = aux ^ bitswap<16>(address, 13,5,2, 14,10,9,4, 15,11,6,1, 12,8,7,3,0);
b0 = aux&0x1f;
b1 = (aux>>5)&0xf;
b2 = (aux>>9)&0xf;
b3 = aux>>13;
b0 = ss->S0[b0];
b1 = ss->S1[b1];
b2 = ss->S2[b2];
b3 = ss->S3[b3];
return ((b3<<13)|(b2<<9)|(b1<<5)|b0)^(key&0xffff);
}
void aw_rom_board::set_key()
{
if (!m_keyregion.found())
return;
if (m_keyregion->bytes() != 4)
throw emu_fatalerror("AW-ROM-BOARD: key region %s has incorrect size (%d, expected 4)\n", m_keyregion.finder_tag(), m_keyregion->bytes());
const uint8_t *krp = m_keyregion->base();
rombd_key = (krp[0] << 24) | (krp[1] << 16) | (krp[2] << 8) | krp[3];
}
void aw_rom_board::device_start()
{
naomi_g1_device::device_start();
set_key();
mpr_offset = decrypt16(0x58/2) | (decrypt16(0x5a/2) << 16);
save_item(NAME(epr_offset));
save_item(NAME(mpr_record_index));
save_item(NAME(mpr_first_file_index));
save_item(NAME(mpr_file_offset));
save_item(NAME(dma_offset));
save_item(NAME(dma_limit));
save_item(NAME(mpr_bank));
}
void aw_rom_board::device_reset()
{
naomi_g1_device::device_reset();
epr_offset = 0;
mpr_record_index = 0;
mpr_first_file_index = 0;
mpr_file_offset = 0;
mpr_bank = 0;
dma_offset = 0;
dma_limit = 0;
}
READ16_MEMBER(aw_rom_board::pio_r)
{
uint32_t roffset = epr_offset & 0x3ffffff;
if (roffset >= (mpr_offset / 2))
roffset += mpr_bank * 0x4000000;
uint16_t retval = (m_region->bytes() > (roffset * 2)) ? m_region->as_u16(roffset) : 0; // not endian-safe?
return retval;
}
WRITE16_MEMBER(aw_rom_board::pio_w)
{
// write to ROM board address space, including FlashROM programming using CFI (TODO)
if (epr_offset == 0x7fffff)
mpr_bank = data & 3;
}
WRITE16_MEMBER(aw_rom_board::epr_offsetl_w)
{
epr_offset = (epr_offset & 0xffff0000) | data;
recalc_dma_offset(EPR);
}
WRITE16_MEMBER(aw_rom_board::epr_offseth_w)
{
epr_offset = (epr_offset & 0x0000ffff) | (data << 16);
recalc_dma_offset(EPR);
}
WRITE16_MEMBER(aw_rom_board::mpr_record_index_w)
{
mpr_record_index = data;
recalc_dma_offset(MPR_RECORD);
}
WRITE16_MEMBER(aw_rom_board::mpr_first_file_index_w)
{
mpr_first_file_index = data;
recalc_dma_offset(MPR_FILE);
}
WRITE16_MEMBER(aw_rom_board::mpr_file_offsetl_w)
{
mpr_file_offset = (mpr_file_offset & 0xffff0000) | data;
recalc_dma_offset(MPR_FILE);
}
WRITE16_MEMBER(aw_rom_board::mpr_file_offseth_w)
{
mpr_file_offset = (mpr_file_offset & 0x0000ffff) | (data << 16);
recalc_dma_offset(MPR_FILE);
}
void aw_rom_board::recalc_dma_offset(int mode)
{
switch(mode) {
case EPR:
dma_offset = epr_offset * 2;
dma_limit = mpr_offset;
break;
case MPR_RECORD:
dma_offset = mpr_offset + mpr_record_index * 0x40;
dma_limit = std::min((uint32_t)0x8000000, m_region->bytes());
break;
case MPR_FILE: {
uint32_t filedata_offs = (mpr_bank * 0x8000000 + mpr_offset + mpr_first_file_index * 0x40 + 8) / 2;
dma_offset = decrypt16(filedata_offs) | (decrypt16(filedata_offs + 1) << 16);
dma_offset = (mpr_offset + dma_offset + mpr_file_offset * 2) & 0x7ffffff;
dma_limit = std::min((uint32_t)0x8000000, m_region->bytes());
break;
}
}
if (dma_offset >= mpr_offset) {
uint32_t bank_base = mpr_bank * 0x8000000;
dma_offset += bank_base;
dma_limit = std::min(dma_limit + bank_base, m_region->bytes());
}
}
void aw_rom_board::dma_get_position(uint8_t *&base, uint32_t &limit, bool to_mainram)
{
if(!to_mainram) {
limit = 0;
base = nullptr;
return;
}
uint32_t offset = dma_offset / 2;
for (int i = 0; i < 16; i++)
decrypted_buf[i] = decrypt16(offset + i);
base = (uint8_t*)decrypted_buf;
limit = 32;
}
void aw_rom_board::dma_advance(uint32_t size)
{
dma_offset += size;
}