diff --git a/hash/megadriv.xml b/hash/megadriv.xml index 12974ca35be33..896f5f9c4be6e 100644 --- a/hash/megadriv.xml +++ b/hash/megadriv.xml @@ -37525,4 +37525,21 @@ Tries to jump to jsr $a385b86.l (?) at PC=0x38b0, must be using its own mapper r + + + Paprium + 2020 + WaterMelon + + + + + + + + + + + + diff --git a/src/devices/bus/megadrive/md_carts.cpp b/src/devices/bus/megadrive/md_carts.cpp index c5be97a54f419..3e67fc0886288 100644 --- a/src/devices/bus/megadrive/md_carts.cpp +++ b/src/devices/bus/megadrive/md_carts.cpp @@ -87,4 +87,5 @@ void md_cart(device_slot_interface &device) device.option_add_internal("rom_nbajam_alt", MD_EEPROM_NBAJAM_ALT); + device.option_add_internal("rom_paprium", MD_ROM_PAPRIUM); } diff --git a/src/devices/bus/megadrive/md_slot.cpp b/src/devices/bus/megadrive/md_slot.cpp index a7b4d2a20ac3a..b0d615ed9b3dc 100644 --- a/src/devices/bus/megadrive/md_slot.cpp +++ b/src/devices/bus/megadrive/md_slot.cpp @@ -274,7 +274,8 @@ static const md_slot slot_list[] = { TOPFIGHTER, "rom_topf" }, { SEGA_SRAM_FULLPATH, "rom_sram" }, - { SEGA_SRAM_FALLBACK, "rom_sramsafe" } + { SEGA_SRAM_FALLBACK, "rom_sramsafe" }, + { PAPRIUM, "rom_paprium" } }; static int md_get_pcb_id(const char *slot) @@ -666,6 +667,9 @@ void base_md_cart_slot_device::setup_nvram() case NBA_JAM_ALT: m_cart->nvram_alloc(0x100); break; + case PAPRIUM: + m_cart->nvram_alloc(0x2000); + break; } } @@ -865,6 +869,8 @@ int base_md_cart_slot_device::get_cart_type(const uint8_t *ROM, uint32_t len) case 0x800000: if (!memcmp((char *)&ROM[0x0180], "GM T-574023-", 12)) // Pier Solar type = PSOLAR; + if (!memcmp((char *)&ROM[0x0120], "PAPRIUM", 7)) // Paprium + type = PAPRIUM; break; default: diff --git a/src/devices/bus/megadrive/md_slot.h b/src/devices/bus/megadrive/md_slot.h index 82c8b09e8af8e..1af4cfae5a8f7 100644 --- a/src/devices/bus/megadrive/md_slot.h +++ b/src/devices/bus/megadrive/md_slot.h @@ -42,6 +42,7 @@ enum EA_NHLPA, /* NHLPA Hockey 93 / Rings of Power */ BRIAN_LARA, /* Brian Lara Cricket 96 */ PSOLAR, /* Pier Solar (STM95 EEPROM) */ + PAPRIUM, /* Paprium (EEPROM gated behind FPGA+STM32) */ // J-Cart CM_JCART, /* Pete Sampras Tennis */ diff --git a/src/devices/bus/megadrive/rom.cpp b/src/devices/bus/megadrive/rom.cpp index 6c624f2b515d3..86f58976ef225 100644 --- a/src/devices/bus/megadrive/rom.cpp +++ b/src/devices/bus/megadrive/rom.cpp @@ -65,6 +65,7 @@ DEFINE_DEVICE_TYPE(MD_ROM_BEGGARP, md_rom_beggarp_device, "md_rom_beggarp", " DEFINE_DEVICE_TYPE(MD_ROM_WUKONG, md_rom_wukong_device, "md_rom_wukong", "MD Legend of Wukong") DEFINE_DEVICE_TYPE(MD_ROM_STARODYS, md_rom_starodys_device, "md_rom_starodys", "MD Star Odyssey") DEFINE_DEVICE_TYPE(MD_ROM_SRAM_ARG96, md_rom_sram_arg96_device, "md_rom_sram_arg96", "MD Futbol Argentino 96") +DEFINE_DEVICE_TYPE(MD_ROM_PAPRIUM, md_rom_paprium_device, "md_rom_paprium", "MD Paprium") md_std_rom_device::md_std_rom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) @@ -1667,3 +1668,944 @@ void md_rom_sram_arg96_device::write(offs_t offset, uint16_t data, uint16_t mem_ logerror("unhandled write at offset %08x %04x %04x\n", offset, data, mem_mask); } } + +/*------------------------------------------------- + Paprium + Cortex-M4 CPU code isn't dumped, we have to make + up for the missing features with best guesses of + internal workings. + + Current issues: + - First boot will launch a fake minigame, this is normal + when save is empty. + - Still WIP/incomplete, no sound, reset not working, hacks, etc... + - Implementation is a bit too much pointer happy + for save state atm. + -------------------------------------------------*/ + + +#define PPM_DEBUG 0 + +md_rom_paprium_device::md_rom_paprium_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : md_rom_sram_device(mconfig, MD_ROM_PAPRIUM, tag, owner, clock) +{ +} + +void md_rom_paprium_device::device_start() +{ + logerror("sizeof(ppm_interface_struct): 0x%x\n", (unsigned int)sizeof(ppm_interface_struct)); + ppm_interface = (ppm_interface_struct *)ppm_dual_port_ram; + + save_item(NAME(ppm_dual_port_ram)); + save_item(NAME(ppm_sdram)); + + // save_item(NAME(ppm_interface)); + + save_item(NAME(ppm_scale_stamp)); + + // save_item(NAME(ppm_sdram_pointer)); + save_item(NAME(ppm_sdram_window_enabled)); + + // save_item(NAME(ppm_object_handles)); + save_pointer((uint16_t *)&ppm_object_handles[0], "ppm_object_handles", 0); + save_item(NAME(ppm_drawList)); + // save_item(NAME(ppm_drawListPtr)); + + // save_item(NAME(ppm_vram_slots)); + save_pointer((uint16_t *)&ppm_vram_slots[0], "ppm_vram_slots", 0); + save_item(NAME(ppm_vram_max_slot)); + save_item(NAME(ppm_block_unpack_addr)); + + save_item(NAME(ppm_anim_data_base_addr)); + // save_item(NAME(ppm_anim_data)); + save_item(NAME(ppm_anim_max_index)); + + save_item(NAME(ppm_unk_data_addr)); + save_item(NAME(ppm_unk_data2_addr)); + + // save_item(NAME(ppm_bgm_tracks_offsets)); + save_item(NAME(ppm_bgm_tracks_base_addr)); + save_item(NAME(ppm_bgm_unpack_addr)); + + // save_item(NAME(ppm_sfx_data)); + save_item(NAME(ppm_sfx_base_addr)); + + // save_item(NAME(ppm_gfx_blocks_offsets)); + save_item(NAME(ppm_gfx_blocks_base_addr)); + save_item(NAME(ppm_max_gfx_block)); + + save_item(NAME(ppm_audio_bgm_volume)); + save_item(NAME(ppm_audio_sfx_volume)); + save_item(NAME(ppm_audio_config)); +} + +void md_rom_paprium_device::device_reset() +{ + // currently loading MAX10 data over rom area then copies it to ram block, + // this should be changed later on to add proper dataarea for this to load into + + // did we decode yet? + if (m_rom[0x8000 / 2]) + { + uint16_t key1 = m_rom[0x8000 / 2]; // 0x0000 reset stack ptr + uint16_t key2 = m_rom[0xbd000 / 2]; // blank area + + // top 8K overlayed with MAX10 dual port ram module + // for (uint32_t addr = 0; addr < 0x2000 / 2; addr++) + // m_rom[addr] ^= (key2 | bitswap<16>(addr & 0xff, 15, 1, 14, 6, 13, 2, 12, 0, 11, 3, 10, 4, 9, 7, 8, 5)); + for (uint32_t addr = 0x2000 / 2; addr < 0x10000 / 2; addr++) + m_rom[addr] ^= (key1 | bitswap<16>(addr & 0xff, 15, 1, 14, 6, 13, 2, 12, 0, 11, 3, 10, 4, 9, 7, 8, 5)); + for (uint32_t addr = 0x10000 / 2; addr < 0x800000 / 2; addr++) + m_rom[addr] ^= (key2 | bitswap<16>(addr & 0xff, 15, 1, 14, 6, 13, 2, 12, 0, 11, 3, 10, 4, 9, 7, 8, 5)); + + // copy data to dual port ram + for (uint16_t x = 0; x < 0x2000 / 2; x++) + ppm_dual_port_ram[x] = m_rom[x]; + + // check ROM version & patch a few things + // (hold B on startup to display version ingame) + switch (m_rom[0x1000a / 2]) + { + case 0x2e7f: + // boot check + ppm_dual_port_ram[0x1d1c / 2] = 0x0004; // 0xc00004 + ppm_dual_port_ram[0x1d2c / 2] |= 0x0100; // switch to beq.s (0x670e) + + // post splash jump location + ppm_dual_port_ram[0x1562 / 2] = 0x0001; + ppm_dual_port_ram[0x1564 / 2] = 0x0100; + + // emulator check + m_rom[0x81104 / 2] = 0x4e71; + + // mame pretends to be version 1 megadrive but does not implement + // proper TMSS register & rom, this will fix the issue being detected + m_rom[0xbca5e / 2] = 0x6006; // bra.s to the correct location + break; + default: + printf("UNKNOWN PAPRIUM VERSION 0x%04X\n", m_rom[0x1000a / 2]); + break; + } + } + + // clear/setup registers + ppm_interface->reg_command = 0; + ppm_interface->reg_status_1 = 0; + ppm_interface->reg_status_2.word = 0; + ppm_interface->reg_status_2.bits.megawire_status = 7; // let's pretend MW is plugged & connected + + ppm_sdram_pointer = ppm_sdram; + ppm_sdram_window_enabled = false; + + ppm_vram_max_slot = 0; +} + +uint16_t md_rom_paprium_device::read(offs_t offset) +{ + if (offset < sizeof(ppm_dual_port_ram) / 2) + return ppm_dual_port_ram[offset]; + + if ((offset >= 0xc000 / 2) && (offset < 0x10000 / 2) && ppm_sdram_window_enabled) + return *ppm_sdram_pointer++; + + if (offset < 0x400000 / 2) + return m_rom[offset]; + else + return 0xffff; +} + +void md_rom_paprium_device::write(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + // can only write to dual port ram + if (offset < sizeof(ppm_dual_port_ram) / 2) + { + ppm_dual_port_ram[offset] = (ppm_dual_port_ram[offset] & ~mem_mask) | (data & mem_mask); + switch (offset) + { + case offsetof(ppm_interface_struct, reg_status_1) / 2: + printf("Status register 1 write? (0x%04x)\n", data); + break; + case offsetof(ppm_interface_struct, reg_status_2) / 2: + printf("Status register 2 write? (0x%04x)\n", data); + break; + case offsetof(ppm_interface_struct, reg_command) / 2: + ppm_process_command(); + break; + case offsetof(ppm_interface_struct, reg_watchdog) / 2: + if (data != 0x9494) + printf("Watchdog? / unknown value: 0x%04x\n", data); + break; + case offsetof(ppm_interface_struct, reg_unk0) / 2: + case offsetof(ppm_interface_struct, reg_unk1) / 2: + case offsetof(ppm_interface_struct, reg_unk4) / 2: + case offsetof(ppm_interface_struct, reg_unk7) / 2: + case offsetof(ppm_interface_struct, reg_unk8) / 2: + case offsetof(ppm_interface_struct, reg_unk9) / 2: + case offsetof(ppm_interface_struct, reg_unkA) / 2: + case offsetof(ppm_interface_struct, reg_unkB) / 2: + case offsetof(ppm_interface_struct, reg_unkC) / 2: + case offsetof(ppm_interface_struct, reg_unkD) / 2: + case offsetof(ppm_interface_struct, reg_unkE) / 2: + case offsetof(ppm_interface_struct, reg_unkF) / 2: + printf("Unknown register write? (0x%04x, 0x%04x)\n", offset * 2, data); + break; + } + } +} + +void md_rom_paprium_device::ppm_process_command() +{ + uint8_t command_id = ppm_interface->reg_command >> 8; + uint8_t command_arg = ppm_interface->reg_command; // & 0xff + + switch (command_id) + { + // special challenge command used on startup. + // command 0x0055 takes quite some time to execute + case 0x00: + switch (command_arg) + { + case 0xaa: + ppm_interface->reg_command = 0x00ff; + return; + case 0x55: + ppm_interface->reg_command = 0x0000; // explicit 0 return + return; + default: + #if PPM_DEBUG + printf("CMD 0x00, unknown challenge 0x%02x\n", command_arg); + #endif + break; + } + break; + // initial startup/reset? - 3 0w8100 writes on startup + // finding arcade mode sends 0x810f + case 0x81: + #if PPM_DEBUG + printf("CMD 0x%04x\n", ppm_interface->reg_command); + #endif + ppm_sdram_window_enabled = true; + // removed, as command 0xb0 was added + // ppm_vram_set_budget(0); + // ppm_obj_reset(); // spr system reset + break; + // unk startup thing + // 8300/8302 toggle *12 (24 total writes) on startup + // couple toggles between stages + case 0x83: + #if PPM_DEBUG + printf("CMD 0x%04x\n", ppm_interface->reg_command); + #endif + break; + // disable sdram window + case 0x84: + ppm_sdram_window_enabled = false; + break; + // set audio config + case 0x88: + ppm_audio_config = command_arg; + if (command_arg & ~0x0b) // setting unknown bits ? + logerror("audio config: unknown bits (0x%02x)\n", command_arg); + break; + // bgm play, check & 0x80 + case 0x8c: + logerror("BGM play 0x%02x (code %02x)\n", command_arg & 0x7f, command_arg); + if (command_arg & 0x80) + { + // ? + } + ppm_unpack(ppm_bgm_addr(command_arg & 0x7f), ppm_bgm_unpack_addr); + break; + + // case 0x8d: + // // sound related + + // case 0x8e: + // // bgm stop/pause? + // break; + + // (bgm related) + case 0x95: + break; + // (bgm related) + case 0x96: + break; + + // megawire settings + case 0xa4: + switch (command_arg) + { + default: + logerror("Megawire unk. option (0x%02x)\n", command_arg); + case 0x00: // reset conn. + case 0x40: // ?delete credentials? + case 0x80: // power profile off + case 0x81: // power profile auto + case 0x82: // power profile forced + break; + } + break; + // a858 MW reboot (update screen) + // a900 MW interrupt/reset? sub_9B738 + + // add object to draw list + case 0xad: + ppm_obj_add(command_arg); + break; + // frame begin + case 0xae: + ppm_obj_frame_start(); + break; + // frame finish + case 0xaf: + // af01: no scaling ongoing + // af02: must reserve bandwith for scaling buffer? + ppm_obj_frame_end(); + break; + // ?unk? + // case 0xb0: + // break; + // sent by waitvbl under condition, instead of 0xaf + // end frame without obj rendering ? + case 0xb0: + // reset SPR engine? (used at startup & test screen) + ppm_obj_reset(); + break; + case 0xb1: + // seems used to keep previous SAT data in VRAM, do nothing? + break; + // post scale command + case 0xb6: + // likely instructs to restore boot code to allow back HW reset + break; + // load/setup base data + case 0xc6: + ppm_setup_data( + swapshorts(ppm_interface->command_args_long[0]), + swapshorts(ppm_interface->command_args_long[1]), + swapshorts(ppm_interface->command_args_long[2]), + swapshorts(ppm_interface->command_args_long[3]), + swapshorts(ppm_interface->command_args_long[4]), + swapshorts(ppm_interface->command_args_long[5]), + swapshorts(ppm_interface->command_args_long[6])); + break; + // set BGM volume + case 0xc9: + ppm_audio_bgm_volume = command_arg; + break; + // set SFX volume + case 0xca: + ppm_audio_sfx_volume = command_arg; + break; + + // d0 + + // SFX play, 4 additional args + case 0xd1: + #if PPM_DEBUG + printf("SFX 0x%02x - 0x%04x / 0x%04x / 0x%04x / 0x%04x\n", command_arg, ppm_interface->command_args[0], ppm_interface->command_args[1], ppm_interface->command_args[2], ppm_interface->command_args[3]); + #endif + break; + // ?unk? + // case 0xd2: + // 0xd240 0xd280 + // break; + + // d6 + case 0xd6: + #if PPM_DEBUG + printf("CMD 0x%04x - 0x%04x\n", ppm_interface->reg_command, ppm_interface->command_args[0]); + #endif + break; + + // unpack request + case 0xda: + logerror("Unpacking 0x%06x @0x%04x (0x%02x)\n", (ppm_interface->command_args[1] << 16) + ppm_interface->command_args[2], ppm_interface->command_args[0], command_arg); + ppm_unpack((ppm_interface->command_args[1] << 16) + ppm_interface->command_args[2], ppm_interface->command_args[0]); + ppm_sdram_pointer = &ppm_sdram[ppm_interface->command_args[0] / 2]; // optional ? + ppm_interface->reg_status_1 &= ~0x0004; + ppm_interface->reg_status_2.bits.busy = 0; + break; + // setup sdram pointer for read + case 0xdb: + logerror("Setup sdram read @0x%06x, size 0x%04x (0x%02x)\n", swapshorts(ppm_interface->command_args_long[0]), ppm_interface->command_args[2], command_arg); + ppm_sdram_pointer = &ppm_sdram[swapshorts(ppm_interface->command_args_long[0]) / 2]; + // read size = ppm_interface->command_args[2]; + break; + // eeprom load + case 0xdf: + switch (command_arg) + { + case 1: + case 2: + case 3: + memcpy(&ppm_dual_port_ram[ppm_interface->command_args[0] / 2], &m_nvram[(0x200 + command_arg * 0x200) / 2], 0x100); + break; + case 4: + memcpy(&ppm_dual_port_ram[ppm_interface->command_args[0] / 2], &m_nvram[0], 0x200); + break; + } + break; + // eeprom save (command_args[0]=0xbeef) + case 0xe0: + switch (command_arg) + { + case 1: + case 2: + case 3: + memcpy(&m_nvram[(0x200 + command_arg * 0x200) / 2], &ppm_dual_port_ram[ppm_interface->command_args[1] / 2], 0x100); + break; + case 4: + memcpy(&m_nvram[0], &ppm_dual_port_ram[ppm_interface->command_args[1] / 2], 0x200); + break; + } + ppm_interface->reg_status_2.bits.eeprom_error1 = 0; + ppm_interface->reg_status_2.bits.eeprom_error2 = 0; + break; + + case 0xe7: // send some data over network + #if PPM_DEBUG + printf("CMD 0x%04x (0x%04x / 0x%04x):", ppm_interface->reg_command, ppm_interface->command_args[0], ppm_interface->command_args[1]); + for (int i = 0; i < (ppm_interface->command_args[1] + 1) / 2; i++) + printf(" 0x%04x", ppm_interface->command_args[2 + i]); + printf("\n"); + #endif + ppm_interface->reg_status_2.bits.megawire_data_in = 1; // pretend data is in + ppm_interface->network_data[0x10 / 2] = ppm_interface->command_args[0] + 16; // pretend 16 bytes in? + // 0x24: ranking fetch + // 0x82: list access points + // 0x84: set account/password (16 bytes each, null terminated if less than 16) + break; + + // set vram block budget + case 0xec: + ppm_vram_set_budget(ppm_interface->command_args[1]); + if (ppm_interface->command_args[0]) + logerror("PPM command 0xec: unk arg 0 (0x%04x)", ppm_interface->command_args[0]); + break; + // load single block (used in obj viewer) + case 0xf2: + // todo: check block range + ppm_unpack(ppm_block_addr(ppm_interface->command_args[0]), 0x9000); + ppm_unpack(ppm_block_addr(ppm_interface->command_args[0]), 0x9200); + ppm_sdram_pointer = &ppm_sdram[0x9000 / 2]; + break; + // load a stamp for scaling + case 0xf4: + ppm_unpack(swapshorts(ppm_interface->command_args_long[0]), 0, true); + break; + // scale current stamp to desired size + case 0xf5: + // logerror("stamp resize - 0x%04x / 0x%04x / 0x%04x / 0x%04x\n", ppm_interface->command_args[0], ppm_interface->command_args[1], ppm_interface->command_args[2], ppm_interface->command_args[3]); + ppm_stamp_rescale(ppm_interface->command_args[0], ppm_interface->command_args[1], ppm_interface->command_args[2], ppm_interface->command_args[3]); + break; + default: + #if PPM_DEBUG + printf("Unprocessed command 0x%04x\n", ppm_interface->reg_command); + #endif + break; + } + // ack command + // cart returns 0 tho only MSB is checked by the game + ppm_interface->reg_command = 0; +} + +uint32_t md_rom_paprium_device::ppm_sdram_read_u32(uint32_t addr) +{ + // fetching non aligned U32 + return ((ppm_sdram[addr / 2 + 1] << 16) + ppm_sdram[addr / 2]); +} + +uint32_t md_rom_paprium_device::ppm_unpack(uint32_t source_addr, uint32_t dest_addr, bool is_scaling_stamp) +{ + // packed data is byte width, ^1 on all addresses to fix endian mumbo jumbo + uint8_t code, count, data_byte; + uint32_t copy_addr; + uint32_t initial_dest_addr = dest_addr; + uint8_t *packed_data = (uint8_t *)m_rom; + uint8_t *unpacked_data = is_scaling_stamp ? (uint8_t *)ppm_scale_stamp : (uint8_t *)ppm_sdram; + + switch (packed_data[source_addr++ ^ 1]) + { + case 0x80: + while ((code = packed_data[source_addr++ ^ 1])) + { + switch (count = code & 0x3f, code >> 6) + { + case 0: + while (count--) + unpacked_data[dest_addr++ ^ 1] = packed_data[source_addr++ ^ 1]; + break; + case 1: + data_byte = packed_data[source_addr++ ^ 1]; + while (count--) + unpacked_data[dest_addr++ ^ 1] = data_byte; + break; + case 2: + copy_addr = dest_addr - packed_data[source_addr++ ^ 1]; + while (count--) + unpacked_data[dest_addr++ ^ 1] = unpacked_data[copy_addr++ ^ 1]; + break; + case 3: + while (count--) + unpacked_data[dest_addr++ ^ 1] = 0; + break; + } + } + break; + case 0x81: + uint16_t copy_size, literal_size; + + while ((code = packed_data[source_addr++ ^ 1]) != 0x11) // unconfirmed end code + { + switch (code >> 4) + { + case 0: + copy_size = 0; + literal_size = code ? (3 + (code & 0x1f)) : (0x12 + packed_data[source_addr++ ^ 1]); + break; + case 1: + if ((copy_size = 2 + (code & 0x7)) == 2) + copy_size = 9 + packed_data[source_addr++ ^ 1]; + literal_size = packed_data[source_addr ^ 1] & 0x3; + copy_addr = dest_addr - 0x4000 - (((packed_data[(source_addr + 1) ^ 1] << 8) + packed_data[source_addr ^ 1]) >> 2); + source_addr += 2; + break; + case 2: + case 3: + if ((copy_size = (code & 0x1f))) + copy_size += 2; + else + { + copy_size = 0x21; + while (!packed_data[source_addr++ ^ 1]) + copy_size += 0xff; + copy_size += packed_data[(source_addr - 1) ^ 1]; + } + literal_size = packed_data[source_addr ^ 1] & 0x3; + copy_addr = dest_addr - 1 - (((packed_data[(source_addr + 1) ^ 1] << 8) + packed_data[source_addr ^ 1]) >> 2); + source_addr += 2; + break; + default: + copy_size = (code >> 5) + 1; + literal_size = code & 0x3; + copy_addr = dest_addr - 1 - (((code >> 2) & 0x7) + (packed_data[source_addr ^ 1] << 3)); + source_addr++; + break; + } + while (copy_size--) + unpacked_data[dest_addr++ ^ 1] = unpacked_data[copy_addr++ ^ 1]; + while (literal_size--) + unpacked_data[dest_addr++ ^ 1] = packed_data[source_addr++ ^ 1]; + } + break; + default: + logerror("unknown packer format, wrong address? (0x%06x)\n", source_addr - 1); + break; + } + return dest_addr - initial_dest_addr; +} + +void md_rom_paprium_device::ppm_setup_data(uint32_t bgm_file, uint32_t unk1_file, uint32_t smp_file, uint32_t unk2_file, uint32_t sfx_file, uint32_t anm_file, uint32_t blk_file) +{ + uint32_t unpack_addr = PPM_DATA_START_ADDR; + + logerror("=== data setup ===\n"); + // setup misc data (unpack some of thoses to sdram): + // bgm data + logerror("BGM data: 0x%06x\n", bgm_file); + ppm_bgm_tracks_base_addr = bgm_file; + ppm_bgm_tracks_offsets = (uint32_t *)&m_rom[bgm_file / 2]; + // ?? data + logerror("uk1 data: 0x%06x > 0x%06x\n", unk1_file, unpack_addr); + ppm_unk_data_addr = unpack_addr; + unpack_addr += ppm_unpack(unk1_file, unpack_addr); + ++unpack_addr &= 0xfffffffeu; // word align + // samples data (WavPack file) + logerror("SMP data: 0x%06x > 0x%06x\n", smp_file, unpack_addr); + // unpacked data is about 1.4MB + // ?? data + logerror("uk2 data: 0x%06x > 0x%06x\n", unk2_file, unpack_addr); + ppm_unk_data2_addr = unpack_addr; + unpack_addr += ppm_unpack(unk2_file, unpack_addr); + ++unpack_addr &= 0xfffffffeu; // word align + // sfx data + logerror("SFX data: 0x%06x\n", sfx_file); + ppm_sfx_base_addr = sfx_file; + ppm_sfx_data = (ppm_sfx_struct *)&m_rom[sfx_file / 2]; + ppm_sfx_struct *fx = &ppm_sfx_data[1]; + for (int x = 1; x <= ppm_sfx_data[0].length; fx++, x++) + logerror("\tSFX 0x%02x: 0x%06x / 0x%04x / 0x%04x\n", x, swapshorts(fx->offset), fx->attributes, fx->length); + // animation data + logerror("ANM data: 0x%06x > 0x%06x\n", anm_file, unpack_addr); + ppm_anim_data_base_addr = unpack_addr; + unpack_addr += ppm_unpack(anm_file, unpack_addr); + ++unpack_addr &= 0xfffffffeu; // word align + ppm_anim_data = (uint32_t *)&ppm_sdram[ppm_anim_data_base_addr / 2]; + logerror("anim data for 0x%x objs\n", ppm_anim_data[0]); + for (uint16_t x = 1; x <= ppm_anim_data[0]; x++) + { + uint32_t anim_offset = ppm_anim_data[x]; + uint16_t anim_count = 0; + while (ppm_anim_data[anim_count + (anim_offset / 4)] != 0xffffffffu) + anim_count++; + ppm_anim_max_index[x - 1] = anim_count - 1; + logerror("\tobj 0x%02x (0x%06x): %d anims\n", x, anim_offset, anim_count); + if (anim_offset & 0x3) + printf("(unaligned pointers)\n"); + } + // blocks data + logerror("BLK data: 0x%06x\n", blk_file); + ppm_gfx_blocks_base_addr = blk_file; + ppm_gfx_blocks_offsets = (uint32_t *)&m_rom[blk_file / 2]; + ppm_max_gfx_block = swapshorts(ppm_gfx_blocks_offsets[0]) - 1; + logerror("blocks addr: %06x, count: %d\n", ppm_gfx_blocks_base_addr, swapshorts(ppm_gfx_blocks_offsets[0])); + + logerror("unpack addr finish: 0x%06x / free: 0x%06x\n", unpack_addr, 0x200000 - unpack_addr); + ppm_bgm_unpack_addr = unpack_addr; +} + +void md_rom_paprium_device::ppm_vram_set_budget(uint16_t blocks) +{ + // temp failsafe + if (blocks > 0x35) + { + logerror("Allocation error (0x%04x blocks)\n", blocks); + blocks = 0x35; + } + ppm_vram_reset_blocks((ppm_vram_max_slot = blocks)); +} + +void md_rom_paprium_device::ppm_vram_reset_blocks(uint16_t last_block) +{ + ppm_vram_slot_struct *slot = &ppm_vram_slots[last_block]; + for (uint16_t i = last_block; i < PPM_MAX_VRAM_SLOTS; slot++, i++) + { + slot->block_num = 0; + slot->usage = 0; + slot->age = 0; + } +} + +uint16_t md_rom_paprium_device::ppm_vram_find_block(uint16_t num) +{ // return tile index + ppm_vram_slot_struct *slot = ppm_vram_slots; + for (uint16_t x = 0; x < ppm_vram_max_slot; slot++, x++) + if (slot->block_num == num) + return ((x + (x <= 0x30 ? 1 : 0x4b)) << 4); + return 0; +} + +uint16_t md_rom_paprium_device::ppm_vram_load_block(uint16_t num) +{ + if (!num) + return 0; + + // is block already in VRAM? + ppm_vram_slot_struct *slot = ppm_vram_slots; + for (uint16_t x = 0; x < ppm_vram_max_slot; slot++, x++) + if (slot->block_num == num) + { + slot->usage++; + slot->age = 0; + return ((x + (x <= 0x30 ? 1 : 0x4b)) << 4); + } + + // enough DMA budget? + if (ppm_interface->dma_remaining < 0x110) + return 0; + + // find oldest slot to load into + uint32_t max_age = 0; + uint16_t block_index = 0xffff; + slot = ppm_vram_slots; + + for (uint16_t x = 0; x < ppm_vram_max_slot; slot++, x++) + if ((!slot->usage) && (slot->age > max_age)) + { + max_age = slot->age; + block_index = x; + } + + // no slot available? + if (block_index == 0xffff) + return 0; + + // found slot & have budget, unpack and DMA the block + slot = &ppm_vram_slots[block_index]; + slot->block_num = num; + slot->usage++; + slot->age = 0; + + ppm_unpack(ppm_block_addr(num), ppm_block_unpack_addr); + ppm_block_unpack_addr += 0x200; + + ppm_dma_command_struct *dma_entry = &ppm_interface->dma_commands[ppm_interface->dma_commands_count++]; + ppm_interface->dma_remaining -= 0x110; + dma_entry->autoinc = 0x8f02; + dma_entry->lenH = 0x9401; + dma_entry->lenL = 0x9300; // 0x200 words size + dma_entry->srcH = 0x9700; + dma_entry->srcM = 0x9660; + dma_entry->srcL = 0x9500; // 0xc000 source + + block_index += (block_index <= 0x30 ? 1 : 0x4b); // translate index + uint32_t command = (((block_index << 25) | (block_index >> 5)) & 0x3fff0003) | 0x40000080; + dma_entry->cmdH = command >> 16; + dma_entry->cmdL = command; + + return (block_index << 4); +} + +void md_rom_paprium_device::ppm_obj_reset() +{ + ppm_vram_reset_blocks(0); + + memset(ppm_interface->obj_data, 0, sizeof(ppm_interface->obj_data)); + ppm_drawListPtr = ppm_drawList; +} + +void md_rom_paprium_device::ppm_obj_add(uint8_t num) +{ + *ppm_drawListPtr++ = num; +} + +void md_rom_paprium_device::ppm_obj_frame_start() +{ + ppm_drawListPtr = ppm_drawList; + + // all blocks unused + ppm_vram_slot_struct *slot = ppm_vram_slots; + for (uint16_t x = 0; x < PPM_MAX_VRAM_SLOTS; slot++, x++) + slot->usage = 0; +} + +void md_rom_paprium_device::ppm_obj_frame_end() +{ + ppm_block_unpack_addr = 0x9000; + ppm_interface->dma_remaining = ppm_interface->dma_budget - ppm_interface->dma_total; + + uint8_t *ptr = ppm_drawList; + while (ptr != ppm_drawListPtr) + ppm_obj_render(*ptr++); + + ppm_vram_slot_struct *slot = ppm_vram_slots; + for (uint16_t x = 0; x < PPM_MAX_VRAM_SLOTS; slot++, x++) + if (!slot->usage) + slot->age++; + + ppm_close_sprite_table(); + ppm_sdram_pointer = &ppm_sdram[0x9000 / 2]; +} + +void md_rom_paprium_device::ppm_close_sprite_table() +{ + ppm_sat_item_struct *sat_entry = &ppm_interface->sat_data[ppm_interface->sat_count]; + + if (!ppm_interface->sat_count) + { + sat_entry->posY = 0x10; + sat_entry->sizeNext = 0; + sat_entry->attrs = 0; + sat_entry->posX = 0x10; + ppm_interface->sat_count++; + } + else + (--sat_entry)->sizeNext &= 0xff00; + + ppm_dma_command_struct *dma_entry = &ppm_interface->dma_commands[ppm_interface->dma_commands_count++]; + dma_entry->autoinc = 0x8f02; + + uint16_t word_size = ppm_interface->sat_count * (sizeof(ppm_sat_item_struct) / 2); + dma_entry->lenH = 0x9400 + (word_size >> 8); + dma_entry->lenL = 0x9300 + (word_size & 0xff); + + uint32_t sat_addr = offsetof(ppm_interface_struct, sat_data) / 2; + dma_entry->srcH = 0x9700 + ((sat_addr >> 16) & 0xff); + dma_entry->srcM = 0x9600 + ((sat_addr >> 8) & 0xff); + dma_entry->srcL = 0x9500 + (sat_addr & 0xff); + + // xfer to SAT location in VRAM (0xf000) + dma_entry->cmdH = 0x7000; + dma_entry->cmdL = 0x0083; +} + +void md_rom_paprium_device::ppm_obj_render(uint16_t obj_slot) +{ + ppm_intf_object_struct *intf_obj = &ppm_interface->obj_data[obj_slot]; // interface obj + ppm_obj_struct *handle = &ppm_object_handles[obj_slot]; // internal handle + + if ((intf_obj->anim & 0xff) > ppm_anim_max_index[intf_obj->objID & 0xff]) + { + logerror("anim over for ID %02x: 0x%04x/0x%04x\n", intf_obj->objID & 0xff, intf_obj->anim & 0xff, ppm_anim_max_index[intf_obj->objID & 0xff]); + return; + } + + uint32_t offset, data_offset; + uint32_t previous_offset = handle->anim_offset; + uint16_t previous_counter = handle->counter; + + // printf("Slot 0x%02x (ID 0x%04x): ", obj_slot, intf_obj->objID); + + // set / update animation + if ((intf_obj->objID & 0x8000) || (intf_obj->anim != handle->crtAnim) || (intf_obj->animCounter != handle->counter)) + { + // fresh obj? + if (intf_obj->objID & 0x8000) + previous_offset = 0, previous_counter = 1; + + offset = ppm_anim_data[(intf_obj->objID & 0xff) + 1]; // obj offset + offset = ppm_anim_data[(offset >> 2) + (intf_obj->anim & 0xff)]; // anim offset + data_offset = ppm_anim_data[offset >> 2] & 0xffffffu; // data offset + + handle->anim_offset = offset; + handle->crtAnim = intf_obj->anim; + handle->counter = intf_obj->animCounter; + } + else + { + // move to next frame + + // get current offset (previous frame) + if (!(offset = handle->anim_offset)) + return; + // read data pointer + data_offset = ppm_anim_data[offset >> 2]; + + if (data_offset & 0x80000000u) + { + // previous frame was not last, just move to next + offset += 4; + } + else + { + // anim is over, do we have fallback anim? + if (intf_obj->nextAnim != 0xffff) + { + // yes, switch anims + intf_obj->anim = intf_obj->nextAnim; + intf_obj->nextAnim = 0xffff; // unverified + ppm_obj_render(obj_slot); + return; + } + else + { + // no, take anim loop + offset = ppm_anim_data[(offset + 4) >> 2] & 0xffffffu; + } + } + + if (!(handle->anim_offset = offset)) // store offset + return; + data_offset = ppm_anim_data[offset >> 2] & 0xffffffu; + intf_obj->animCounter++; + handle->counter++; + } + + // render sprite to SAT + // > data might not be 4 bytes aligned, access via ppm_sdram < + ppm_spr_data_header_struct *spr_info = (ppm_spr_data_header_struct *)&ppm_sdram[(ppm_anim_data_base_addr + data_offset) >> 1]; + ppm_sat_item_struct *satEntry = &ppm_interface->sat_data[ppm_interface->sat_count]; + int16_t posX = intf_obj->posX; + int16_t posY = intf_obj->posY; + + bool blocks_available = true; + // load new spr data + ppm_spr_data_struct *spr_data = &spr_info->sprites[0]; + for (uint16_t x = 0; x < spr_info->count; spr_data++, x++) + { + if (!spr_data->blockNum) + continue; + if (!ppm_vram_load_block(spr_data->blockNum)) + { + blocks_available = false; + // break; need to iterate all to keep resevations? + } + } + + if (!blocks_available) + { + #if PPM_DEBUG + printf(">>> block budget out / "); + #endif + if (previous_offset) + { + // restore offset/counter + handle->anim_offset = previous_offset; + handle->counter = previous_counter; + intf_obj->animCounter = previous_counter; + data_offset = ppm_anim_data[previous_offset >> 2] & 0xffffff; + spr_info = (ppm_spr_data_header_struct *)&ppm_sdram[(ppm_anim_data_base_addr + data_offset) >> 1]; + #if PPM_DEBUG + printf("Obj #%02x: keep previous frame\n", obj_slot); + #endif + } + else + { + #if PPM_DEBUG + printf("Obj #%02x: no render\n", obj_slot); + #endif + return; + } + } + + spr_data = &spr_info->sprites[0]; + for (uint16_t x = 0; x < spr_info->count; spr_data++, x++) + { + posX += (intf_obj->attrs & 0x0800) ? spr_data->flipPosX : spr_data->posX; + posY += spr_data->posY; + if (!spr_data->blockNum) + continue; + + // clipping + if ((posX >= 320 + 128) || + (posY >= 240 + 128) || + (posX < 128 - ((((spr_data->size >> 2) & 0x3) + 1) * 8)) || + (posY < 128 - (((spr_data->size & 0x3) + 1) * 8))) + continue; + + ppm_interface->sat_count++; + satEntry->posX = posX & 0x1ff; + satEntry->posY = posY & 0x3ff; + satEntry->sizeNext = ((spr_data->size & 0xf) << 8) + (ppm_interface->sat_count & 0xff); + // attributes aren't quite right yet, some objects have wrong palette +//# satEntry->attrs = ((spr_data->attrs & 0x98) << 8) ^ intf_obj->attrs ^ (ppm_vram_find_block(spr_data->blockNum) + spr_data->offset); // whole attr word? + satEntry->attrs = ((spr_data->attrs & 0xf8) << 8) ^ intf_obj->attrs ^ (ppm_vram_find_block(spr_data->blockNum) + spr_data->offset); // whole attr word? + // satEntry->attrs = ppm_dual_port_ram[0] ^ (spr_data->attrs << 8) ^ intf_obj->attrs ^ (ppm_vram_find_block(spr_data->blockNum) + spr_data->offset); // debug + // satEntry->attrs = ((spr_data->attrs & ppm_dual_port_ram[0]) << 8) ^ intf_obj->attrs ^ (ppm_vram_find_block(spr_data->blockNum) + spr_data->offset); //whole attr word? + // satEntry->attrs ^= (spr_info->flags & ppm_dual_port_ram[1]) << 8; + satEntry++; + } + intf_obj->objID &= 0x7fff; +} + +void md_rom_paprium_device::ppm_stamp_rescale(uint16_t window_start, uint16_t window_end, uint16_t factor, uint16_t stamp_offset) +{ + // prepare temp buffer + uint8_t scaled_stamp[128][32]; + memset(scaled_stamp, 0, sizeof(scaled_stamp)); + + // scale the stamp in temp buffer + float offset = (float)stamp_offset; + float adder = (float)factor / 64; + for (uint16_t y = window_start; y < window_end; offset += adder, y++) + memcpy(&scaled_stamp[y][0], &ppm_scale_stamp[(int)offset][0], 32); + + // translate data: 128*32px block, as 4*32 px strips + // layout is weird because... reasons? + for (uint16_t s = 0; s < 32; s++) // 4px strips + { + // uint16_t clmn = ((s << 4) & 0xffe0) + (s & 1 ? 0x200 : 0); + uint16_t clmn = ((s & 0xfe) << 4) + ((s & 1) << 9); + for (uint16_t y = 0; y < 32; y++) + { + // 1 word contains 4 pixels + ppm_interface->scaling_buffer[clmn + y] = + (((scaled_stamp[(s << 2) + 0][y ^ 1] & 0xf0) | + (scaled_stamp[(s << 2) + 1][y ^ 1] & 0x0f)) + << 8) + + (((scaled_stamp[(s << 2) + 2][y ^ 1] & 0xf0) | + (scaled_stamp[(s << 2) + 3][y ^ 1] & 0x0f))); + } + } +} diff --git a/src/devices/bus/megadrive/rom.h b/src/devices/bus/megadrive/rom.h index b98469d3155e3..5dd151c27f09e 100644 --- a/src/devices/bus/megadrive/rom.h +++ b/src/devices/bus/megadrive/rom.h @@ -637,6 +637,246 @@ class md_rom_starodys_device : public md_std_rom_device }; +// ======================> md_rom_paprium_device + +// helpers +constexpr uint32_t swapshorts(uint32_t val) { return (((val) & 0xffff0000u) >> 16) | (((val) & 0xffffu) << 16); } +#define ppm_block_addr(num) (ppm_gfx_blocks_base_addr+swapshorts(ppm_gfx_blocks_offsets[(num)])) +#define ppm_bgm_addr(num) (ppm_bgm_tracks_base_addr+swapshorts(ppm_bgm_tracks_offsets[(num)])) + +// dual port ram data +typedef struct ppm_sat_item_struct +{ //VDP specs + uint16_t posY; + uint16_t sizeNext; + uint16_t attrs; + uint16_t posX; +} ppm_sat_item_struct; + +typedef struct ppm_intf_object_struct +{ + uint16_t anim; + uint16_t nextAnim; + uint16_t objID; //b15 = fresh assign ? + uint16_t field_6; + uint16_t attrs; + uint16_t animCounter; + int16_t posX; + int16_t posY; +} ppm_intf_object_struct; + +typedef struct ppm_dma_command_struct +{ + uint16_t autoinc; + uint16_t lenH; + uint16_t lenL; + uint16_t srcH; + uint16_t srcM; + uint16_t srcL; + uint16_t cmdH; + uint16_t cmdL; +} ppm_dma_command_struct; + +typedef struct ppm_interface_struct +{ + uint16_t vectors[0x80]; // 68k vectors table + uint16_t rom_header[0x80]; // megadrive header data + uint16_t scaling_buffer[0x300]; + uint16_t save_buffer[0x100]; // also part of scaling data + uint16_t _padding[0x80]; + ppm_sat_item_struct sat_data[144]; // sprite allocation table sits here + ppm_intf_object_struct obj_data[64]; // objects table + uint16_t _padding2[0x40]; + // 0x1400 + ppm_dma_command_struct dma_commands[121]; + uint16_t audio_data[0x70 / 2]; + uint16_t network_data[0x108]; + union + { + uint16_t command_args[128]; + uint32_t command_args_long[64]; + }; + uint16_t dma_total; // total size in words + uint16_t dma_budget; // per frame budget (depends on system) + uint16_t dma_remaining; // remaining budget for current frame + uint16_t dma_commands_count; + uint16_t sat_count; // # of items present in SAT table + uint16_t unknown_1f1a; + uint16_t unknown_1f1c; + uint16_t unknown_1f1e; + uint16_t buffer[0x60]; + // main registers + uint16_t reg_unk0; // 1fe0 + uint16_t reg_unk1; // 1fe2 + uint16_t reg_status_1; // 1fe4 + struct + { + union + { + uint16_t word; + struct + { + uint16_t megawire_status: 3; + uint16_t bit_3: 1; + // sub_9B738 + uint16_t bit_4: 1; // MW? send a900 if set + uint16_t megawire_data_in: 1; // MW ack ? + uint16_t bit_6: 1; + uint16_t bit_7: 1; + + uint16_t eeprom_error1: 1; // b8 sum error? + uint16_t eeprom_error2: 1; // b9 eepr error? + uint16_t bit_10: 1; + uint16_t bit_11: 1; + + uint16_t bit_12: 1; + uint16_t bit_13: 1; + uint16_t busy: 1; // b14 task busy + uint16_t bit_15: 1; + } bits; + }; + } reg_status_2; // 1fe6 + uint16_t reg_unk4; // 1fe8 + uint16_t reg_command; + uint16_t reg_watchdog; // unconfirmed + uint16_t reg_unk7; // 1fee + uint16_t reg_unk8; // 1ff0 + uint16_t reg_unk9; // 1ff2 + uint16_t reg_unkA; // 1ff4 + uint16_t reg_unkB; // 1ff6 + uint16_t reg_unkC; // 1ff8 + uint16_t reg_unkD; // 1ffa + uint16_t reg_unkE; // 1ffc + uint16_t reg_unkF; // 1ffe +} ppm_interface_struct; + +// sdram data +#define PPM_DATA_START_ADDR 0x10000 // base sdram unpack address +#define PPM_MAX_VRAM_SLOTS 64 +#define PPM_OBJECTS_COUNT 64 +#define PPM_MAX_OBJ_BLOCKS 32 + +typedef struct ppm_sfx_struct +{ + uint32_t offset; + uint16_t attributes; + uint16_t length; +} ppm_sfx_struct; + +typedef struct ppm_vram_slot_struct +{ + uint16_t block_num; + uint16_t usage; + uint16_t age; +} ppm_vram_slot_struct; + +typedef struct ppm_obj_struct +{ + uint32_t anim_offset; // + uint32_t lastDisplayedOffset; // + uint16_t crtAnim; + uint16_t counter; +} ppm_obj_struct; + +typedef struct ppm_spr_data_struct +{ //byteswapped (endianess fix) + int8_t posY; + int8_t posX; + int8_t flipPosX; + uint8_t size; //lower nibble + uint16_t blockNum; + uint8_t offset; //block offset + uint8_t attrs; //flip posY? +} ppm_spr_data_struct; + +typedef struct ppm_spr_data_header_struct +{ //byteswapped (endianess fix) + uint8_t flags; + uint8_t count; + ppm_spr_data_struct sprites[]; +} ppm_spr_data_header_struct; + +// class md_rom_paprium_device : public md_std_rom_device +class md_rom_paprium_device : public md_rom_sram_device +{ +public: + // construction/destruction + md_rom_paprium_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // reading and writing + virtual uint16_t read(offs_t offset) override; + virtual void write(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override; + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + +private: + // memory interfaces + uint16_t ppm_dual_port_ram[0x2000 / 2]; + ppm_interface_struct *ppm_interface; + uint16_t ppm_sdram[0x200000 / 2]; + virtual uint32_t ppm_sdram_read_u32(uint32_t addr); + + // commands processing + virtual void ppm_process_command(); + + // scaling items + uint8_t ppm_scale_stamp[64][32]; + virtual void ppm_stamp_rescale(uint16_t window_start, uint16_t window_end, uint16_t factor, uint16_t stamp_offset); + + // unpack & related data + virtual uint32_t ppm_unpack(uint32_t from, uint32_t to, bool is_scaling_stamp = false); + virtual void ppm_setup_data(uint32_t bgm_file, uint32_t unk1_file, uint32_t smp_file, uint32_t unk2_file, uint32_t sfx_file, uint32_t anm_file, uint32_t blk_file); + uint16_t *ppm_sdram_pointer; + bool ppm_sdram_window_enabled; + + // objects setup/render + virtual void ppm_obj_reset(); + virtual void ppm_obj_add(uint8_t num); + virtual void ppm_obj_frame_start(); + virtual void ppm_obj_frame_end(); + virtual void ppm_close_sprite_table(); + virtual void ppm_obj_render(uint16_t obj_slot); + ppm_obj_struct ppm_object_handles[PPM_OBJECTS_COUNT]; // matching intf objs + uint8_t ppm_drawList[PPM_OBJECTS_COUNT]; // contains slot # + uint8_t *ppm_drawListPtr; + + // block data + virtual void ppm_vram_set_budget(uint16_t blocks); + virtual void ppm_vram_reset_blocks(uint16_t last_block); + virtual uint16_t ppm_vram_find_block(uint16_t num); + virtual uint16_t ppm_vram_load_block(uint16_t num); + ppm_vram_slot_struct ppm_vram_slots[PPM_MAX_VRAM_SLOTS]; + uint16_t ppm_vram_max_slot; + uint16_t ppm_block_unpack_addr; + + // keeping track of various data locations + uint32_t ppm_anim_data_base_addr; + uint32_t *ppm_anim_data; + uint16_t ppm_anim_max_index[256]; + + uint32_t ppm_unk_data_addr; + uint32_t ppm_unk_data2_addr; + + uint32_t *ppm_bgm_tracks_offsets; + uint32_t ppm_bgm_tracks_base_addr; + uint32_t ppm_bgm_unpack_addr; + + ppm_sfx_struct *ppm_sfx_data; + uint32_t ppm_sfx_base_addr; + + uint32_t *ppm_gfx_blocks_offsets; + uint32_t ppm_gfx_blocks_base_addr; + uint32_t ppm_max_gfx_block; + + // audio related (unsupported) + uint8_t ppm_audio_bgm_volume; + uint8_t ppm_audio_sfx_volume; + uint8_t ppm_audio_config; +}; + // device type definition DECLARE_DEVICE_TYPE(MD_STD_ROM, md_std_rom_device) @@ -676,5 +916,6 @@ DECLARE_DEVICE_TYPE(MD_ROM_RADICA, md_rom_radica_device) DECLARE_DEVICE_TYPE(MD_ROM_BEGGARP, md_rom_beggarp_device) DECLARE_DEVICE_TYPE(MD_ROM_WUKONG, md_rom_wukong_device) DECLARE_DEVICE_TYPE(MD_ROM_STARODYS, md_rom_starodys_device) +DECLARE_DEVICE_TYPE(MD_ROM_PAPRIUM, md_rom_paprium_device) #endif // MAME_BUS_MEGADRIVE_ROM_H