diff --git a/Assets/dll/MSXHawk.dll b/Assets/dll/MSXHawk.dll index d2fbb50341..452a419eab 100644 Binary files a/Assets/dll/MSXHawk.dll and b/Assets/dll/MSXHawk.dll differ diff --git a/Assets/gamedb/gamedb_msx_msx1_rom.txt b/Assets/gamedb/gamedb_msx_msx1_rom.txt index 3662fcd346..531912eff2 100644 --- a/Assets/gamedb/gamedb_msx_msx1_rom.txt +++ b/Assets/gamedb/gamedb_msx_msx1_rom.txt @@ -1246,7 +1246,7 @@ D2560E3B7D4E137BDBBA8426030D2F44 Rotors (1984)(ASCII)(JP) MSX JP 324E6E87465ECF49AF0C613A87467E10 R-Type v2 (1988)(IREM)(JP)[a] MSX JP 0F03B2306E11C5182351DB3B7AA30533 Saimazoom (2005)(Karoshi)[RK708] MSX B0D4FB9A9A88FB65C12D2B567B567171 Saimazoom (2005)(Karoshi)[RK708EN] MSX -EC869B375D7517A7BA6161B10F5DEE06 Salamander - Operation X (1987)(Konami)(JP)[RC-758] MSX JP +EC869B375D7517A7BA6161B10F5DEE06 Salamander - Operation X (1987)(Konami)(JP)[RC-758] MSX mapper=2 JP 59767E46E226F5C69469694817839189 Salamander - Operation X (1987)(Konami)(JP)[SCC][RC-758] MSX JP 0DA25C28FCE818D6FA7231CB6A914783 Salamander - Operation X (1988)(Zemina)(KR)[RC-758] MSX KR 305BF5B78B03D28B92DEF20AD89A4E4B Sangokushi. Romance of Three Kingdoms (1986)(Koei)(JP) MSX JP diff --git a/libHawk/MSXHawk/MSXHawk/AY_3_8910.h b/libHawk/MSXHawk/MSXHawk/AY_3_8910.h index 000d70f212..9af6ebbd30 100644 --- a/libHawk/MSXHawk/MSXHawk/AY_3_8910.h +++ b/libHawk/MSXHawk/MSXHawk/AY_3_8910.h @@ -43,6 +43,7 @@ namespace MSXHawk uint32_t noise = 0x1; int32_t old_sample; + int32_t current_sample; // non stated if only on frame boundaries bool sound_out_A; @@ -51,11 +52,6 @@ namespace MSXHawk uint8_t Clock_Divider; - int32_t current_sample; - uint32_t sampleclock; - uint32_t num_samples; - int32_t samples[9000] = {}; - void Reset() { clock_A = clock_B = clock_C = 0x1000; @@ -168,7 +164,7 @@ namespace MSXHawk } } - void generate_sound() + bool generate_sound() { // there are 8 cpu cycles for every psg cycle clock_A--; @@ -282,13 +278,9 @@ namespace MSXHawk current_sample *= 2; - if ((current_sample != old_sample) && (num_samples < 4500)) - { - samples[num_samples * 2] = sampleclock; - samples[num_samples * 2 + 1] = current_sample - old_sample; - num_samples++; - old_sample = current_sample; - } + if (current_sample != old_sample) { return true; } + + return false; } #pragma endregion diff --git a/libHawk/MSXHawk/MSXHawk/Core.h b/libHawk/MSXHawk/MSXHawk/Core.h index c907a69075..ba5cf9e82d 100644 --- a/libHawk/MSXHawk/MSXHawk/Core.h +++ b/libHawk/MSXHawk/MSXHawk/Core.h @@ -5,6 +5,7 @@ #include "Z80A.h" #include "AY_3_8910.h" +#include "SCC.h" #include "TMS9918A.h" #include "Memory.h" @@ -18,19 +19,31 @@ namespace MSXHawk MemMap.cpu_pntr = &cpu; MemMap.vdp_pntr = &vdp; MemMap.psg_pntr = &psg; + MemMap.SCC_1_pntr = &SCC_1; + MemMap.SCC_2_pntr = &SCC_2; cpu.mem_ctrl = &MemMap; vdp.IRQ_PTR = &cpu.FlagI; vdp.SHOW_BG = vdp.SHOW_SPRITES = true; psg.Clock_Divider = 16; sl_case = 0; + SCC_1.page_pointer = &MemMap.SCC_1_page[0]; + SCC_2.page_pointer = &MemMap.SCC_2_page[0]; }; TMS9918A vdp; Z80A cpu; AY_3_8910 psg; + SCC SCC_1; + SCC SCC_2; MemoryManager MemMap; uint8_t sl_case = 0; + bool new_sample = false; + + // combine audio from all sources + uint32_t sampleclock; + uint32_t num_samples; + int32_t samples[9000] = {}; void Load_BIOS(uint8_t* bios, uint8_t* basic) { @@ -65,8 +78,8 @@ namespace MSXHawk uint32_t scanlinesPerFrame = 262; vdp.SpriteLimit = true; - psg.num_samples = 0; - psg.sampleclock = 0; + num_samples = 0; + sampleclock = 0; for (uint32_t i = 0; i < scanlinesPerFrame; i++) { @@ -87,55 +100,76 @@ namespace MSXHawk for (int i = 0; i < 14; i++) { cpu.ExecuteOne(16); - psg.sampleclock+=16; - psg.generate_sound(); + sampleclock+=16; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } } cpu.ExecuteOne(4); - psg.sampleclock += 4; + sampleclock += 4; sl_case = 1; break; case 1: cpu.ExecuteOne(12); - psg.sampleclock += 12; - psg.generate_sound(); + sampleclock += 12; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } for (int i = 0; i < 13; i++) { cpu.ExecuteOne(16); - psg.sampleclock += 16; - psg.generate_sound(); + sampleclock += 16; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } } cpu.ExecuteOne(8); - psg.sampleclock += 8; + sampleclock += 8; sl_case = 2; break; case 2: cpu.ExecuteOne(8); - psg.sampleclock += 8; - psg.generate_sound(); + sampleclock += 8; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } for (int i = 0; i < 13; i++) { cpu.ExecuteOne(16); - psg.sampleclock += 16; - psg.generate_sound(); + sampleclock += 16; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } } cpu.ExecuteOne(12); - psg.sampleclock += 12; + sampleclock += 12; sl_case = 3; break; case 3: cpu.ExecuteOne(4); - psg.sampleclock += 4; - psg.generate_sound(); + sampleclock += 4; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } for (int i = 0; i < 14; i++) { cpu.ExecuteOne(16); - psg.sampleclock += 16; - psg.generate_sound(); + sampleclock += 16; + new_sample |= psg.generate_sound(); + new_sample |= SCC_1.generate_sound(); + //new_sample |= SCC_2.generate_sound(); + if (new_sample) { Add_Audio_Sample(); } } sl_case = 0; break; @@ -145,6 +179,23 @@ namespace MSXHawk return MemMap.lagged; } + void Add_Audio_Sample() + { + if (num_samples < 4500) + { + samples[num_samples * 2] = sampleclock; + samples[num_samples * 2 + 1] = psg.current_sample - psg.old_sample; + samples[num_samples * 2 + 1] += SCC_1.current_sample - SCC_1.old_sample; + //samples[num_samples * 2 + 1] = SCC_2.current_sample - SCC_2.old_sample; + num_samples++; + psg.old_sample = psg.current_sample; + SCC_1.old_sample = SCC_1.current_sample; + //SCC_2.old_sample = SCC_2.current_sample; + + new_sample = false; + } + } + void GetVideo(uint32_t* dest) { uint32_t* src = vdp.FrameBuffer; @@ -155,13 +206,13 @@ namespace MSXHawk uint32_t GetAudio(int32_t* dest, int32_t* n_samp) { - int32_t* src = psg.samples; + int32_t* src = samples; int32_t* dst = dest; - std::memcpy(dst, src, sizeof int32_t * psg.num_samples * 2); - n_samp[0] = psg.num_samples; + std::memcpy(dst, src, sizeof int32_t * num_samples * 2); + n_samp[0] = num_samples; - return psg.sampleclock; + return sampleclock; } int GetMessageLength() @@ -182,8 +233,11 @@ namespace MSXHawk saver = vdp.SaveState(saver); saver = cpu.SaveState(saver); saver = psg.SaveState(saver); + saver = SCC_1.SaveState(saver); + saver = SCC_2.SaveState(saver); saver = MemMap.SaveState(saver); + *saver = (uint8_t)(new_sample ? 1 : 0); saver++; *saver = sl_case; saver++; } @@ -192,8 +246,11 @@ namespace MSXHawk loader = vdp.LoadState(loader); loader = cpu.LoadState(loader); loader = psg.LoadState(loader); + loader = SCC_1.LoadState(loader); + loader = SCC_2.LoadState(loader); loader = MemMap.LoadState(loader); + new_sample = *loader == 1; loader++; sl_case = *loader; loader++; } diff --git a/libHawk/MSXHawk/MSXHawk/MSXHawk.vcxproj b/libHawk/MSXHawk/MSXHawk/MSXHawk.vcxproj index ad03d7a570..1de05a3dfc 100644 --- a/libHawk/MSXHawk/MSXHawk/MSXHawk.vcxproj +++ b/libHawk/MSXHawk/MSXHawk/MSXHawk.vcxproj @@ -160,6 +160,7 @@ + diff --git a/libHawk/MSXHawk/MSXHawk/Memory.cpp b/libHawk/MSXHawk/MSXHawk/Memory.cpp index 310d20e5f1..e1148e7770 100644 --- a/libHawk/MSXHawk/MSXHawk/Memory.cpp +++ b/libHawk/MSXHawk/MSXHawk/Memory.cpp @@ -7,6 +7,7 @@ #include "Z80A.h" #include "TMS9918A.h" #include "AY_3_8910.h" +#include "SCC.h" using namespace std; @@ -300,6 +301,72 @@ namespace MSXHawk } } } + else if (rom_mapper_1 == 2) // konami mapper with SCC + { + if (base_addr == 0) + { + if (segment < 8) + { + if (SCC_1_enabled) + { + return &unmapped[0]; + } + return &rom_1[rom1_konami_page_2 * 0x2000 + (0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_1[rom1_konami_page_3 * 0x2000 + (0x400 * segment)]; + } + } + else if (base_addr == 0x4000) + { + if (segment < 8) + { + return &rom_1[(0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_1[rom1_konami_page_1 * 0x2000 + (0x400 * segment)]; + } + } + else if (base_addr == 0x8000) + { + if (segment < 8) + { + if (SCC_1_enabled) + { + if (segment < 6) + { + return &unmapped[0]; + } + else + { + return &SCC_1_page[0]; + } + } + return &rom_1[rom1_konami_page_2 * 0x2000 + (0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_1[rom1_konami_page_3 * 0x2000 + (0x400 * segment)]; + } + } + else + { + if (segment < 8) + { + return &rom_1[(0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_1[rom1_konami_page_1 * 0x2000 + (0x400 * segment)]; + } + } + } else { return &unmapped[0]; @@ -363,9 +430,126 @@ namespace MSXHawk } } } + else if (rom_mapper_2 == 2) // konami mapper with SCC + { + if (base_addr == 0) + { + if (segment < 8) + { + if (SCC_2_enabled) + { + return &unmapped[0]; + } + return &rom_2[rom2_konami_page_2 * 0x2000 + (0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_2[rom2_konami_page_3 * 0x2000 + (0x400 * segment)]; + } + } + else if (base_addr == 0x4000) + { + if (segment < 8) + { + return &rom_2[(0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_2[rom2_konami_page_1 * 0x2000 + (0x400 * segment)]; + } + } + else if (base_addr == 0x8000) + { + if (segment < 8) + { + if (SCC_2_enabled) + { + if (segment < 6) + { + return &unmapped[0]; + } + else + { + return &SCC_2_page[0]; + } + } + return &rom_2[rom2_konami_page_2 * 0x2000 + (0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_2[rom2_konami_page_3 * 0x2000 + (0x400 * segment)]; + } + } + else + { + if (segment < 8) + { + return &rom_2[(0x400 * segment)]; + } + else + { + segment -= 8; + return &rom_2[rom2_konami_page_1 * 0x2000 + (0x400 * segment)]; + } + } + } else { return &unmapped[0]; } } + + void MemoryManager::MemoryWrite(uint32_t addr, uint8_t value) + { + // Konami addresses without SCC + if (rom_mapper_1 == 1) + { + if (addr >= 0x6000 && addr < 0x8000 && slot_1_has_rom == 1) { rom1_konami_page_1 = (uint8_t)(value & rom_size_1); remap(); } + if (addr >= 0x8000 && addr < 0xA000 && slot_2_has_rom == 1) { rom1_konami_page_2 = (uint8_t)(value & rom_size_1); remap(); } + if (addr >= 0xA000 && addr < 0xC000 && slot_2_has_rom == 1) { rom1_konami_page_3 = (uint8_t)(value & rom_size_1); remap(); } + } + if (rom_mapper_2 == 1) + { + if (addr >= 0x6000 && addr < 0x8000 && slot_1_has_rom == 2) { rom2_konami_page_1 = (uint8_t)(value & rom_size_2); remap(); } + if (addr >= 0x8000 && addr < 0xA000 && slot_2_has_rom == 2) { rom2_konami_page_2 = (uint8_t)(value & rom_size_2); remap(); } + if (addr >= 0xA000 && addr < 0xC000 && slot_2_has_rom == 2) { rom2_konami_page_3 = (uint8_t)(value & rom_size_2); remap(); } + } + + // Konami addresses with SCC + if (rom_mapper_1 == 2) + { + if (addr >= 0x5000 && addr < 0x5800 && slot_1_has_rom == 1) { rom1_konami_page_0 = (uint8_t)(value & rom_size_1); remap(); } + if (addr >= 0x7000 && addr < 0x7800 && slot_1_has_rom == 1) { rom1_konami_page_1 = (uint8_t)(value & rom_size_1); remap(); } + if (addr >= 0x9000 && addr < 0x9800 && slot_2_has_rom == 1) + { + if ((value & 0xFF) == 0x3F) { SCC_1_enabled = true; } + else { SCC_1_enabled = false; } + rom1_konami_page_2 = (uint8_t)(value & rom_size_1); remap(); + } + if (addr >= 0x9800 && addr < 0xA000 && slot_2_has_rom == 1 && SCC_1_enabled) + { + SCC_1_pntr->WriteReg(value & 0xFF); + } + if (addr >= 0xB000 && addr < 0xB800 && slot_2_has_rom == 1) { rom1_konami_page_3 = (uint8_t)(value & rom_size_1); remap(); } + } + if (rom_mapper_2 == 2) + { + if (addr >= 0x5000 && addr < 0x5800 && slot_1_has_rom == 2) { rom2_konami_page_0 = (uint8_t)(value & rom_size_2); remap(); } + if (addr >= 0x7000 && addr < 0x7800 && slot_1_has_rom == 2) { rom2_konami_page_1 = (uint8_t)(value & rom_size_2); remap(); } + if (addr >= 0x9000 && addr < 0x9800 && slot_2_has_rom == 2) + { + if ((value & 0xFF) == 0x3F) { SCC_2_enabled = true; } + else { SCC_2_enabled = false; } + rom2_konami_page_2 = (uint8_t)(value & rom_size_2); remap(); + } + if (addr >= 0x9800 && addr < 0xA000 && slot_2_has_rom == 1 && SCC_2_enabled) + { + SCC_2_pntr->WriteReg(value & 0xFF); + } + if (addr >= 0xB000 && addr < 0xB800 && slot_2_has_rom == 2) { rom2_konami_page_3 = (uint8_t)(value & rom_size_2); remap(); } + } + } } \ No newline at end of file diff --git a/libHawk/MSXHawk/MSXHawk/Memory.h b/libHawk/MSXHawk/MSXHawk/Memory.h index 6e2a97b344..60c084767b 100644 --- a/libHawk/MSXHawk/MSXHawk/Memory.h +++ b/libHawk/MSXHawk/MSXHawk/Memory.h @@ -10,6 +10,7 @@ namespace MSXHawk class Z80A; class TMS9918A; class AY_3_8910; + class SCC; class MemoryManager { @@ -17,6 +18,8 @@ namespace MSXHawk TMS9918A* vdp_pntr = nullptr; AY_3_8910* psg_pntr = nullptr; + SCC* SCC_1_pntr = nullptr; + SCC* SCC_2_pntr = nullptr; Z80A* cpu_pntr = nullptr; uint8_t* rom_1 = nullptr; uint8_t* rom_2 = nullptr; @@ -44,6 +47,16 @@ namespace MSXHawk uint8_t ram[0x10000] = {}; uint8_t cart_ram[0x8000] = {}; uint8_t unmapped[0x400] = {}; + uint8_t SCC_1_page[0x400] = {}; + uint8_t SCC_2_page[0x400] = {}; + + // mapper support and variables + uint8_t slot_0_has_rom, slot_1_has_rom, slot_2_has_rom, slot_3_has_rom; + uint8_t rom1_konami_page_0, rom1_konami_page_1, rom1_konami_page_2, rom1_konami_page_3; + uint8_t rom2_konami_page_0, rom2_konami_page_1, rom2_konami_page_2, rom2_konami_page_3; + + bool SCC_1_enabled = false; + bool SCC_2_enabled = false; MemoryManager() { @@ -59,6 +72,8 @@ namespace MSXHawk void HardwareWrite(uint32_t addr, uint8_t value); + void MemoryWrite(uint32_t addr, uint8_t value); + void remap(); uint8_t* remap_rom1(uint32_t, uint32_t); @@ -83,20 +98,24 @@ namespace MSXHawk memcpy(rom_1, ext_rom_1, ext_rom_size_1); memcpy(rom_2, ext_rom_2, ext_rom_size_2); - rom_size_1 = ext_rom_size_1 / 0x400 - 1; rom_mapper_1 = ext_rom_mapper_1; - - rom_size_2 = ext_rom_size_2 / 0x400 - 1; rom_mapper_2 = ext_rom_mapper_2; - if (rom_mapper_1 == 1) + // page size 0x2000 for konami games + if (rom_mapper_1 == 1 || rom_mapper_1 == 2) { rom_size_1 = ext_rom_size_1 / 0x2000 - 1; } + if (rom_mapper_2 == 1 || rom_mapper_2 == 2) { rom_size_2 = ext_rom_size_2 / 0x2000 - 1; } + + + if (rom_mapper_1 == 1 || rom_mapper_1 == 2) { + rom1_konami_page_0 = 0; rom1_konami_page_1 = 1; rom1_konami_page_2 = 2; rom1_konami_page_3 = 3; } - if (rom_mapper_2 == 1) + if (rom_mapper_2 == 1 || rom_mapper_2 == 2) { + rom2_konami_page_0 = 0; rom2_konami_page_1 = 1; rom2_konami_page_2 = 2; rom2_konami_page_3 = 3; @@ -108,28 +127,6 @@ namespace MSXHawk remap(); } - // mapper support and variables - uint8_t slot_0_has_rom, slot_1_has_rom, slot_2_has_rom, slot_3_has_rom; - uint8_t rom1_konami_page_1, rom1_konami_page_2, rom1_konami_page_3; - uint8_t rom2_konami_page_1, rom2_konami_page_2, rom2_konami_page_3; - - void MemoryWrite(uint32_t addr, uint8_t value) - { - if (rom_mapper_1 == 1) - { - if (addr >= 0x6000 && addr < 0x8000 && slot_1_has_rom == 1) { rom1_konami_page_1 = (uint8_t)(value & rom_size_1); remap(); } - if (addr >= 0x8000 && addr < 0xA000 && slot_2_has_rom == 1) { rom1_konami_page_2 = (uint8_t)(value & rom_size_1); remap(); } - if (addr >= 0xA000 && addr < 0xC000 && slot_2_has_rom == 1) { rom1_konami_page_3 = (uint8_t)(value & rom_size_1); remap(); } - } - - if (rom_mapper_2 == 1) - { - if (addr >= 0x6000 && addr < 0x8000 && slot_1_has_rom == 2) { rom2_konami_page_1 = (uint8_t)(value & rom_size_2); remap(); } - if (addr >= 0x8000 && addr < 0xA000 && slot_2_has_rom == 2) { rom2_konami_page_2 = (uint8_t)(value & rom_size_2); remap(); } - if (addr >= 0xA000 && addr < 0xC000 && slot_2_has_rom == 2) { rom2_konami_page_3 = (uint8_t)(value & rom_size_2); remap(); } - } - } - #pragma region State Save / Load uint8_t* SaveState(uint8_t* saver) @@ -137,6 +134,8 @@ namespace MSXHawk *saver = (uint8_t)(PortDEEnabled ? 1 : 0); saver++; *saver = (uint8_t)(lagged ? 1 : 0); saver++; *saver = (uint8_t)(start_pressed ? 1 : 0); saver++; + *saver = (uint8_t)(SCC_1_enabled ? 1 : 0); saver++; + *saver = (uint8_t)(SCC_2_enabled ? 1 : 0); saver++; *saver = kb_rows_sel; saver++; *saver = PortA8; saver++; @@ -150,15 +149,19 @@ namespace MSXHawk *saver = slot_2_has_rom; saver++; *saver = slot_3_has_rom; saver++; + *saver = rom1_konami_page_0; saver++; *saver = rom1_konami_page_1; saver++; *saver = rom1_konami_page_2; saver++; *saver = rom1_konami_page_3; saver++; + *saver = rom2_konami_page_0; saver++; *saver = rom2_konami_page_1; saver++; *saver = rom2_konami_page_2; saver++; *saver = rom2_konami_page_3; saver++; std::memcpy(saver, &ram, 0x10000); saver += 0x10000; std::memcpy(saver, &cart_ram, 0x8000); saver += 0x8000; + std::memcpy(saver, &SCC_1_page, 0x400); saver += 0x400; + std::memcpy(saver, &SCC_2_page, 0x400); saver += 0x400; return saver; } @@ -168,6 +171,8 @@ namespace MSXHawk PortDEEnabled = *loader == 1; loader++; lagged = *loader == 1; loader++; start_pressed = *loader == 1; loader++; + SCC_1_enabled = *loader == 1; loader++; + SCC_2_enabled = *loader == 1; loader++; kb_rows_sel = *loader; loader++; PortA8 = *loader; loader++; @@ -181,15 +186,19 @@ namespace MSXHawk slot_2_has_rom = *loader; loader++; slot_3_has_rom = *loader; loader++; + rom1_konami_page_0 = *loader; loader++; rom1_konami_page_1 = *loader; loader++; rom1_konami_page_2 = *loader; loader++; rom1_konami_page_3 = *loader; loader++; + rom2_konami_page_0 = *loader; loader++; rom2_konami_page_1 = *loader; loader++; rom2_konami_page_2 = *loader; loader++; rom2_konami_page_3 = *loader; loader++; std::memcpy(&ram, loader, 0x10000); loader += 0x10000; std::memcpy(&cart_ram, loader, 0x8000); loader += 0x8000; + std::memcpy(&SCC_1_page, loader, 0x400); loader += 0x400; + std::memcpy(&SCC_2_page, loader, 0x400); loader += 0x400; remap(); diff --git a/libHawk/MSXHawk/MSXHawk/SCC.h b/libHawk/MSXHawk/MSXHawk/SCC.h new file mode 100644 index 0000000000..ffd536261f --- /dev/null +++ b/libHawk/MSXHawk/MSXHawk/SCC.h @@ -0,0 +1,437 @@ +#include +#include +#include +#include + +using namespace std; + +namespace MSXHawk +{ + class SCC + { + public: + +#pragma region SCC + + SCC() + { + Reset(); + } + + uint8_t* page_pointer = nullptr; + + bool A_on, B_on, C_on; + bool A_up, B_up, C_up; + bool A_noise, B_noise, C_noise; + bool env_vol_A, env_vol_B, env_vol_C; + + uint8_t env_shape; + uint8_t port_sel; + uint8_t vol_A, vol_B, vol_C; + uint8_t Register[16] = {}; + + uint32_t psg_clock; + uint32_t sq_per_A, sq_per_B, sq_per_C; + uint32_t clock_A, clock_B, clock_C; + + uint32_t env_per; + uint32_t env_clock; + + int32_t env_E; + int32_t E_up_down; + + uint32_t noise_clock; + uint32_t noise_per; + uint32_t noise = 0x1; + + int32_t old_sample; + int32_t current_sample; + + // non stated if only on frame boundaries + bool sound_out_A; + bool sound_out_B; + bool sound_out_C; + + uint8_t Clock_Divider; + + void Reset() + { + clock_A = clock_B = clock_C = 0x1000; + noise_clock = 0x20; + port_sel = 0; + + for (int i = 0; i < 16; i++) + { + Register[i] = 0x0; + } + sync_psg_state(); + } + + short Sample() + { + return current_sample; + } + + const uint32_t VolumeTable[16] = + { + 0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA, + 0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA + }; + + // returns do not occur in this iplementation, they come from the core + uint8_t ReadReg() + { + + } + + void sync_psg_state() + { + sq_per_A = (Register[0] & 0xFF) | (((Register[1] & 0xF) << 8)); + if (sq_per_A == 0) + { + sq_per_A = 0x1000; + } + + sq_per_B = (Register[2] & 0xFF) | (((Register[3] & 0xF) << 8)); + if (sq_per_B == 0) + { + sq_per_B = 0x1000; + } + + sq_per_C = (Register[4] & 0xFF) | (((Register[5] & 0xF) << 8)); + if (sq_per_C == 0) + { + sq_per_C = 0x1000; + } + + env_per = (Register[11] & 0xFF) | (((Register[12] & 0xFF) << 8)); + if (env_per == 0) + { + env_per = 0x10000; + } + + env_per *= 2; + + A_on = (Register[7] & 0x1) > 0; + B_on = (Register[7] & 0x2) > 0; + C_on = (Register[7] & 0x4) > 0; + A_noise = (Register[7] & 0x8) > 0; + B_noise = (Register[7] & 0x10) > 0; + C_noise = (Register[7] & 0x20) > 0; + + noise_per = Register[6] & 0x1F; + if (noise_per == 0) + { + noise_per = 0x20; + } + + uint8_t shape_select = Register[13] & 0xF; + + if (shape_select < 4) { env_shape = 0; } + else if (shape_select < 8) { env_shape = 1; } + else { env_shape = 2 + (shape_select - 8); } + + vol_A = Register[8] & 0xF; + env_vol_A = ((Register[8] >> 4) & 0x1) > 0; + + vol_B = Register[9] & 0xF; + env_vol_B = ((Register[9] >> 4) & 0x1) > 0; + + vol_C = Register[10] & 0xF; + env_vol_C = ((Register[10] >> 4) & 0x1) > 0; + } + + void WriteReg(uint8_t value) + { + value &= 0xFF; + + if (port_sel != 0xE) { Register[port_sel] = value; } + + + sync_psg_state(); + + if (port_sel == 13) + { + env_clock = env_per; + + if (env_shape == 0 || env_shape == 2 || env_shape == 3 || env_shape == 4 || env_shape == 5) + { + env_E = 15; + E_up_down = -1; + } + else + { + env_E = 0; + E_up_down = 1; + } + } + } + + bool generate_sound() + { + // there are 8 cpu cycles for every psg cycle + clock_A--; + clock_B--; + clock_C--; + + noise_clock--; + env_clock--; + + // clock noise + if (noise_clock == 0) + { + noise = (noise >> 1) ^ (((noise & 0x1) > 0) ? 0x10004 : 0); + noise_clock = noise_per; + } + + if (env_clock == 0) + { + env_clock = env_per; + + env_E += E_up_down; + + if (env_E == 16 || env_E == -1) + { + // we just completed a period of the envelope, determine what to do now based on the envelope shape + if (env_shape == 0 || env_shape == 1 || env_shape == 3 || env_shape == 9) + { + E_up_down = 0; + env_E = 0; + } + else if (env_shape == 5 || env_shape == 7) + { + E_up_down = 0; + env_E = 15; + } + else if (env_shape == 4 || env_shape == 8) + { + if (env_E == 16) + { + env_E = 15; + E_up_down = -1; + } + else + { + env_E = 0; + E_up_down = 1; + } + } + else if (env_shape == 2) + { + env_E = 15; + } + else + { + env_E = 0; + } + } + } + + if (clock_A == 0) + { + A_up = !A_up; + clock_A = sq_per_A; + } + + if (clock_B == 0) + { + B_up = !B_up; + clock_B = sq_per_B; + } + + if (clock_C == 0) + { + C_up = !C_up; + clock_C = sq_per_C; + } + + sound_out_A = (((noise & 0x1) > 0) | A_noise) & (A_on | A_up); + sound_out_B = (((noise & 0x1) > 0) | B_noise) & (B_on | B_up); + sound_out_C = (((noise & 0x1) > 0) | C_noise) & (C_on | C_up); + + // now calculate the volume of each channel and add them together + current_sample = 0; + + if (env_vol_A) + { + current_sample = (sound_out_A ? VolumeTable[env_E] : 0); + } + else + { + current_sample = (sound_out_A ? VolumeTable[vol_A] : 0); + } + + if (env_vol_B) + { + current_sample += (sound_out_B ? VolumeTable[env_E] : 0); + } + else + { + current_sample += (sound_out_B ? VolumeTable[vol_B] : 0); + } + + if (env_vol_C) + { + current_sample += (sound_out_C ? VolumeTable[env_E] : 0); + } + else + { + current_sample += (sound_out_C ? VolumeTable[vol_C] : 0); + } + + current_sample *= 2; + + if (current_sample != old_sample) { return true; } + + return false; + } + +#pragma endregion + +#pragma region State Save / Load + + uint8_t* SaveState(uint8_t* saver) + { + *saver = (uint8_t)(A_on ? 1 : 0); saver++; + *saver = (uint8_t)(B_on ? 1 : 0); saver++; + *saver = (uint8_t)(C_on ? 1 : 0); saver++; + *saver = (uint8_t)(A_up ? 1 : 0); saver++; + *saver = (uint8_t)(B_up ? 1 : 0); saver++; + *saver = (uint8_t)(C_up ? 1 : 0); saver++; + *saver = (uint8_t)(A_noise ? 1 : 0); saver++; + *saver = (uint8_t)(B_noise ? 1 : 0); saver++; + *saver = (uint8_t)(C_noise ? 1 : 0); saver++; + *saver = (uint8_t)(env_vol_A ? 1 : 0); saver++; + *saver = (uint8_t)(env_vol_B ? 1 : 0); saver++; + *saver = (uint8_t)(env_vol_C ? 1 : 0); saver++; + + *saver = env_shape; saver++; + *saver = port_sel; saver++; + *saver = vol_A; saver++; + *saver = vol_B; saver++; + *saver = vol_C; saver++; + + for (int i = 0; i < 16; i++) { *saver = Register[i]; saver++; } + + *saver = (uint8_t)(psg_clock & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 8) & 0xFF); saver++; + *saver = (uint8_t)((psg_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((psg_clock >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(sq_per_A & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 8) & 0xFF); saver++; + *saver = (uint8_t)((sq_per_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_A >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(sq_per_B & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 8) & 0xFF); saver++; + *saver = (uint8_t)((sq_per_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_B >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(sq_per_C & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 8) & 0xFF); saver++; + *saver = (uint8_t)((sq_per_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((sq_per_C >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(clock_A & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 8) & 0xFF); saver++; + *saver = (uint8_t)((clock_A >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_A >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(clock_B & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 8) & 0xFF); saver++; + *saver = (uint8_t)((clock_B >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_B >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(clock_C & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 8) & 0xFF); saver++; + *saver = (uint8_t)((clock_C >> 16) & 0xFF); saver++; *saver = (uint8_t)((clock_C >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(env_per & 0xFF); saver++; *saver = (uint8_t)((env_per >> 8) & 0xFF); saver++; + *saver = (uint8_t)((env_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_per >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(env_clock & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 8) & 0xFF); saver++; + *saver = (uint8_t)((env_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_clock >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(env_E & 0xFF); saver++; *saver = (uint8_t)((env_E >> 8) & 0xFF); saver++; + *saver = (uint8_t)((env_E >> 16) & 0xFF); saver++; *saver = (uint8_t)((env_E >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(E_up_down & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 8) & 0xFF); saver++; + *saver = (uint8_t)((E_up_down >> 16) & 0xFF); saver++; *saver = (uint8_t)((E_up_down >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(noise_clock & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 8) & 0xFF); saver++; + *saver = (uint8_t)((noise_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_clock >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(noise_per & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 8) & 0xFF); saver++; + *saver = (uint8_t)((noise_per >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise_per >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(noise & 0xFF); saver++; *saver = (uint8_t)((noise >> 8) & 0xFF); saver++; + *saver = (uint8_t)((noise >> 16) & 0xFF); saver++; *saver = (uint8_t)((noise >> 24) & 0xFF); saver++; + + *saver = (uint8_t)(old_sample & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 8) & 0xFF); saver++; + *saver = (uint8_t)((old_sample >> 16) & 0xFF); saver++; *saver = (uint8_t)((old_sample >> 24) & 0xFF); saver++; + + return saver; + } + + uint8_t* LoadState(uint8_t* loader) + { + A_on = *loader == 1; loader++; + B_on = *loader == 1; loader++; + C_on = *loader == 1; loader++; + A_up = *loader == 1; loader++; + B_up = *loader == 1; loader++; + C_up = *loader == 1; loader++; + A_noise = *loader == 1; loader++; + B_noise = *loader == 1; loader++; + C_noise = *loader == 1; loader++; + env_vol_A = *loader == 1; loader++; + env_vol_B = *loader == 1; loader++; + env_vol_C = *loader == 1; loader++; + + env_shape = *loader; loader++; + port_sel = *loader; loader++; + vol_A = *loader; loader++; + vol_B = *loader; loader++; + vol_C = *loader; loader++; + + for (int i = 0; i < 16; i++) { Register[i] = *loader; loader++; } + + psg_clock = *loader; loader++; psg_clock |= (*loader << 8); loader++; + psg_clock |= (*loader << 16); loader++; psg_clock |= (*loader << 24); loader++; + + sq_per_A = *loader; loader++; sq_per_A |= (*loader << 8); loader++; + sq_per_A |= (*loader << 16); loader++; sq_per_A |= (*loader << 24); loader++; + + sq_per_B = *loader; loader++; sq_per_B |= (*loader << 8); loader++; + sq_per_B |= (*loader << 16); loader++; sq_per_B |= (*loader << 24); loader++; + + sq_per_C = *loader; loader++; sq_per_C |= (*loader << 8); loader++; + sq_per_C |= (*loader << 16); loader++; sq_per_C |= (*loader << 24); loader++; + + clock_A = *loader; loader++; clock_A |= (*loader << 8); loader++; + clock_A |= (*loader << 16); loader++; clock_A |= (*loader << 24); loader++; + + clock_B = *loader; loader++; clock_B |= (*loader << 8); loader++; + clock_B |= (*loader << 16); loader++; clock_B |= (*loader << 24); loader++; + + clock_C = *loader; loader++; clock_C |= (*loader << 8); loader++; + clock_C |= (*loader << 16); loader++; clock_C |= (*loader << 24); loader++; + + env_per = *loader; loader++; env_per |= (*loader << 8); loader++; + env_per |= (*loader << 16); loader++; env_per |= (*loader << 24); loader++; + + env_clock = *loader; loader++; env_clock |= (*loader << 8); loader++; + env_clock |= (*loader << 16); loader++; env_clock |= (*loader << 24); loader++; + + env_E = *loader; loader++; env_E |= (*loader << 8); loader++; + env_E |= (*loader << 16); loader++; env_E |= (*loader << 24); loader++; + + E_up_down = *loader; loader++; E_up_down |= (*loader << 8); loader++; + E_up_down |= (*loader << 16); loader++; E_up_down |= (*loader << 24); loader++; + + noise_clock = *loader; loader++; noise_clock |= (*loader << 8); loader++; + noise_clock |= (*loader << 16); loader++; noise_clock |= (*loader << 24); loader++; + + noise_per = *loader; loader++; noise_per |= (*loader << 8); loader++; + noise_per |= (*loader << 16); loader++; noise_per |= (*loader << 24); loader++; + + noise = *loader; loader++; noise |= (*loader << 8); loader++; + noise |= (*loader << 16); loader++; noise |= (*loader << 24); loader++; + + old_sample = *loader; loader++; old_sample |= (*loader << 8); loader++; + old_sample |= (*loader << 16); loader++; old_sample |= (*loader << 24); loader++; + + return loader; + } + +#pragma endregion + }; +} diff --git a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs index 14ec692691..3f09ddea66 100644 --- a/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs +++ b/src/BizHawk.Emulation.Cores/Computers/MSX/MSX.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using BizHawk.Common; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.MSX @@ -16,7 +17,13 @@ namespace BizHawk.Emulation.Cores.Computers.MSX SyncSettings = (MSXSyncSettings)syncSettings ?? new MSXSyncSettings(); RomData = new byte[rom.Length]; - + + // look up game in db before transforming ROM + var hash_md5 = MD5Checksum.ComputePrefixedHex(rom); + var gi = Database.CheckDatabase(hash_md5); + var dict = gi.GetOptions(); + string s_mapper; + for (int i = 0; i < rom.Length; i++) { RomData[i] = rom[i]; @@ -38,8 +45,25 @@ namespace BizHawk.Emulation.Cores.Computers.MSX } else { - mapper_1 = 1; - Console.WriteLine("Konami Mapper"); + // Assume default konami style mapper + if (gi == null) + { + + } + else if (!dict.TryGetValue("mapper", out s_mapper)) + { + mapper_1 = 1; + Console.WriteLine("Using Konami Mapper"); + } + else + { + if (s_mapper == "2") + { + mapper_1 = 2; + Console.WriteLine("Using Konami Mapper with SCC"); + } + } + } // if the original was not 64 or 48 k, move it (may need to do this case by case) @@ -135,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Computers.MSX } private IntPtr MSX_Pntr { get; set; } = IntPtr.Zero; - private byte[] MSX_core = new byte[0x20000]; + private byte[] MSX_core = new byte[0x28000]; public static byte[] Bios = null; public static byte[] Basic;