From 4c3f9b93e7277a7901f50556a65bdd1b24826ee6 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 27 Feb 2017 19:45:51 +1100 Subject: [PATCH] Update to v102r12 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: Changelog: - MD/PSG: fixed 68K bus Z80 status read address location - MS, GG, MD/PSG: channels post-decrement their counters, not pre-decrement [Cydrak]¹ - MD/VDP: cache screen width registers once per scanline; screen height registers once per frame - MD/VDP: support 256-width display mode (used in Shining Force, etc) - MD/YM2612: implemented timers² - MD/YM2612: implemented 8-bit PCM DAC² - 68000: TRAP instruction should index the vector location by 32 (eg by 128 bytes), fixes Shining Force - nall: updated hex(), octal(), binary() functions to take uintmax instead of template parameter³ ¹: this one makes an incredible difference. Sie noticed that lots of games set a period of 0, which would end up being a really long period with pre-decrement. By fixing this, noise shows up in many more games, and sounds way better in games even where it did before. You can hear extra sound on Lunar - Sanposuru Gakuen's title screen, the noise in Sonic The Hedgehog (Mega Drive) sounds better, etc. ²: this also really helps sound. The timers allow PSG music to play back at the correct speed instead of playing back way too quickly. And the PCM DAC lets you hear a lot of drum effects, as well as the "Sega!!" sound at the start of Sonic the Hedgehog, and the infamous, "Rise from your grave!" line from Altered Beast. Still, most music on the Mega Drive comes from the FM channels, so there's still not a whole lot to listen to. I didn't implement Cydrak's $02c test register just yet. Sie wasn't 100% certain on how the extended DAC bit worked, so I'd like to play it a little conservative and get sound working, then I'll go back and add a toggle or something to enable undocumented registers, that way we can use that to detect any potential problems they might be causing. ³: unfortunately we lose support for using hex() on nall/arithmetic types. If I have a const Pair& version of the function, then the compiler gets confused on whether Natural<32> should use uintmax or const Pair&, because compilers are stupid, and you can't have explicit arguments in overloaded functions. So even though either function would work, it just decides to error out instead >_> This is actually really annoying, because I want hex() to be useful for printing out nall/crypto keys and hashes directly. But ... this change had to be made. Negative signed integers would crash programs, and that was taking out my 68000 disassembler. --- higan/emulator/emulator.hpp | 2 +- higan/md/bus/bus.cpp | 2 +- higan/md/psg/noise.cpp | 2 +- higan/md/psg/psg.cpp | 1 + higan/md/psg/tone.cpp | 2 +- higan/md/vdp/io.cpp | 2 +- higan/md/vdp/render.cpp | 14 ++++-- higan/md/vdp/vdp.cpp | 6 ++- higan/md/vdp/vdp.hpp | 17 +++++-- higan/md/ym2612/io.cpp | 56 +++++++++++++++++++++++- higan/md/ym2612/timer.cpp | 33 ++++++++++++++ higan/md/ym2612/ym2612.cpp | 19 +++++++- higan/md/ym2612/ym2612.hpp | 35 +++++++++++++++ higan/ms/psg/noise.cpp | 2 +- higan/ms/psg/tone.cpp | 2 +- higan/processor/lr35902/disassembler.cpp | 4 -- higan/processor/m68k/instructions.cpp | 2 +- higan/processor/r65816/disassembler.cpp | 4 -- nall/arithmetic/natural.hpp | 13 ++++++ nall/string.hpp | 8 ++-- nall/string/format.hpp | 27 ++++-------- 21 files changed, 205 insertions(+), 48 deletions(-) create mode 100644 higan/md/ym2612/timer.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 34cf8ee2..9c185fc0 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.11"; + static const string Version = "102.12"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/bus/bus.cpp b/higan/md/bus/bus.cpp index 0247edda..7b74cc7e 100644 --- a/higan/md/bus/bus.cpp +++ b/higan/md/bus/bus.cpp @@ -74,7 +74,7 @@ auto BusCPU::readIO(uint24 addr) -> uint16 { case 0xa1000a: return peripherals.controllerPort2->readControl(); case 0xa1000c: return peripherals.extensionPort->readControl(); - case 0xa11000: return !busAPU.granted(); + case 0xa11100: return !busAPU.granted(); } return 0x0000; diff --git a/higan/md/psg/noise.cpp b/higan/md/psg/noise.cpp index 978ecc89..128eec08 100644 --- a/higan/md/psg/noise.cpp +++ b/higan/md/psg/noise.cpp @@ -1,5 +1,5 @@ auto PSG::Noise::run() -> void { - if(--counter) return; + if(counter--) return; if(rate == 0) counter = 0x10; if(rate == 1) counter = 0x20; diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index 8e5a3da7..9d03ae7a 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -34,6 +34,7 @@ auto PSG::main() -> void { auto PSG::step(uint clocks) -> void { Thread::step(clocks); synchronize(cpu); + synchronize(apu); } auto PSG::power() -> void { diff --git a/higan/md/psg/tone.cpp b/higan/md/psg/tone.cpp index bae975d4..caa1af3b 100644 --- a/higan/md/psg/tone.cpp +++ b/higan/md/psg/tone.cpp @@ -1,5 +1,5 @@ auto PSG::Tone::run() -> void { - if(--counter) return; + if(counter--) return; counter = pitch; output ^= 1; diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index 6e5daab7..9eadb9d2 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -223,7 +223,7 @@ auto VDP::writeControlPort(uint16 data) -> void { //mode register 4 case 0x0c: { - io.tileWidth = data.bit(0) | data.bit(7) << 1; + io.displayWidth = data.bit(0) | data.bit(7) << 1; io.interlaceMode = data.bits(1,2); io.shadowHighlightEnable = data.bit(3); io.externalColorEnable = data.bit(4); diff --git a/higan/md/vdp/render.cpp b/higan/md/vdp/render.cpp index eaecb283..accb14cc 100644 --- a/higan/md/vdp/render.cpp +++ b/higan/md/vdp/render.cpp @@ -1,6 +1,14 @@ +auto VDP::frame() -> void { + latch.overscan = io.overscan; +} + auto VDP::scanline() -> void { - state.x = 0; if(++state.y >= 262) state.y = 0; + if(state.y == 0) frame(); + state.x = 0; + state.hcounter = 0; + + latch.displayWidth = io.displayWidth; if(state.y < screenHeight()) { planeA.scanline(state.y); @@ -36,9 +44,9 @@ auto VDP::run() -> void { } auto VDP::outputPixel(uint9 color) -> void { - for(auto n : range(4)) { + for(auto n : range(pixelWidth())) { state.output[ 0 + n] = color; state.output[1280 + n] = color; } - state.output += 4; + state.output += pixelWidth(); } diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 52d7e5e1..21d64989 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -21,9 +21,9 @@ auto VDP::main() -> void { cpu.lower(CPU::Interrupt::VerticalBlank); } cpu.lower(CPU::Interrupt::HorizontalBlank); - for(uint x : range(320)) { + while(state.hcounter < 1280) { run(); - step(4); + step(pixelWidth()); } if(io.horizontalBlankInterruptEnable) { cpu.raise(CPU::Interrupt::HorizontalBlank); @@ -42,6 +42,7 @@ auto VDP::main() -> void { } auto VDP::step(uint clocks) -> void { + state.hcounter += clocks; while(clocks--) { dma.run(); Thread::step(1); @@ -58,6 +59,7 @@ auto VDP::power() -> void { create(VDP::Enter, system.colorburst() * 15.0 / 2.0); memory::fill(&io, sizeof(IO)); + memory::fill(&latch, sizeof(Latch)); memory::fill(&state, sizeof(State)); planeA.power(); diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index 9ade08be..6c289089 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -38,6 +38,7 @@ struct VDP : Thread { } dma; //render.cpp + auto frame() -> void; auto scanline() -> void; auto run() -> void; auto outputPixel(uint9 color) -> void; @@ -128,8 +129,9 @@ struct VDP : Thread { Sprite sprite; private: - auto screenWidth() const -> uint { return io.tileWidth ? 320 : 256; } - auto screenHeight() const -> uint { return io.overscan ? 240 : 224; } + auto pixelWidth() const -> uint { return latch.displayWidth ? 4 : 5; } + auto screenWidth() const -> uint { return latch.displayWidth ? 320 : 256; } + auto screenHeight() const -> uint { return latch.overscan ? 240 : 224; } //video RAM struct VRAM { @@ -193,7 +195,7 @@ private: uint1 externalInterruptEnable; //$0c mode register 4 - uint2 tileWidth; + uint2 displayWidth; uint2 interlaceMode; uint1 shadowHighlightEnable; uint1 externalColorEnable; @@ -208,8 +210,17 @@ private: uint8 dataIncrement; } io; + struct Latch { + //per-frame + uint1 overscan; + + //per-scanline + uint2 displayWidth; + } latch; + struct State { uint32* output = nullptr; + uint hcounter; uint x; uint y; } state; diff --git a/higan/md/ym2612/io.cpp b/higan/md/ym2612/io.cpp index 4423fdaa..08c2477b 100644 --- a/higan/md/ym2612/io.cpp +++ b/higan/md/ym2612/io.cpp @@ -1,9 +1,63 @@ auto YM2612::readStatus() -> uint8 { - return nall::random(); + //d7 = busy + return timerA.line << 0 | timerB.line << 1; } auto YM2612::writeAddress(uint9 data) -> void { + io.address = data; } auto YM2612::writeData(uint8 data) -> void { + switch(io.address) { + + //timer A period (high) + case 0x024: { + timerA.period.bits(2,9) = data.bits(0,7); + break; + } + + //timer A period (low) + case 0x025: { + timerA.period.bits(0,1) = data.bits(0,1); + break; + } + + //timer B period + case 0x026: { + timerB.period.bits(0,7) = data.bits(0,7); + break; + } + + //timer control + case 0x027: { + //d6,d7 = mode (unimplemented; treated as mode 0 always) + + //reload period on 0->1 transition + if(!timerA.enable && data.bit(0)) timerA.counter = timerA.period; + if(!timerB.enable && data.bit(1)) timerB.counter = timerB.period; + + timerA.enable = data.bit(0); + timerB.enable = data.bit(1); + timerA.irq = data.bit(2); + timerB.irq = data.bit(3); + + if(data.bit(4)) timerA.line = 0; + if(data.bit(5)) timerB.line = 0; + + break; + } + + //DAC sample + case 0x2a: { + dac.sample = data; + break; + } + + //DAC enable + case 0x2b: { + dac.enable = data.bit(7); + break; + } + + } } diff --git a/higan/md/ym2612/timer.cpp b/higan/md/ym2612/timer.cpp new file mode 100644 index 00000000..d8441f16 --- /dev/null +++ b/higan/md/ym2612/timer.cpp @@ -0,0 +1,33 @@ +auto YM2612::TimerA::run() -> void { + if(!enable) return; + if(++counter) return; + + counter = period; + line |= irq; +} + +auto YM2612::TimerA::power() -> void { + enable = 0; + irq = 0; + line = 0; + period = 0; + counter = 0; +} + +auto YM2612::TimerB::run() -> void { + if(!enable) return; + if(++divider) return; + if(++counter) return; + + counter = period; + line |= irq; +} + +auto YM2612::TimerB::power() -> void { + enable = 0; + irq = 0; + line = 0; + period = 0; + counter = 0; + divider = 0; +} diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index 87e9705a..27c95b7d 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -4,24 +4,41 @@ namespace MegaDrive { YM2612 ym2612; #include "io.cpp" +#include "timer.cpp" auto YM2612::Enter() -> void { while(true) scheduler.synchronize(), ym2612.main(); } auto YM2612::main() -> void { - stream->sample(0.0, 0.0); + timerA.run(); + timerB.run(); + + int output = 0; + if(dac.enable) output += dac.sample; + output <<= 5; + + stream->sample(output / 32768.0, output / 32768.0); step(1); } auto YM2612::step(uint clocks) -> void { Thread::step(clocks); synchronize(cpu); + synchronize(apu); } auto YM2612::power() -> void { create(YM2612::Enter, system.colorburst() * 15.0 / 7.0 / 144.0); stream = Emulator::audio.createStream(2, frequency()); + + memory::fill(&io, sizeof(IO)); + + timerA.power(); + timerB.power(); + + dac.enable = 0; + dac.sample = 0; } } diff --git a/higan/md/ym2612/ym2612.hpp b/higan/md/ym2612/ym2612.hpp index 328d1da5..63dbbae6 100644 --- a/higan/md/ym2612/ym2612.hpp +++ b/higan/md/ym2612/ym2612.hpp @@ -13,6 +13,41 @@ struct YM2612 : Thread { auto readStatus() -> uint8; auto writeAddress(uint9 data) -> void; auto writeData(uint8 data) -> void; + +private: + struct IO { + uint9 address; + } io; + + struct TimerA { + //timer.cpp + auto run() -> void; + auto power() -> void; + + uint1 enable; + uint1 irq; + uint1 line; + uint10 period; + uint10 counter; + } timerA; + + struct TimerB { + //timer.cpp + auto run() -> void; + auto power() -> void; + + uint1 enable; + uint1 irq; + uint1 line; + uint8 period; + uint8 counter; + uint4 divider; + } timerB; + + struct DAC { + uint1 enable; + uint8 sample; + } dac; }; extern YM2612 ym2612; diff --git a/higan/ms/psg/noise.cpp b/higan/ms/psg/noise.cpp index 8b22453e..a8ac734e 100644 --- a/higan/ms/psg/noise.cpp +++ b/higan/ms/psg/noise.cpp @@ -1,5 +1,5 @@ auto PSG::Noise::run() -> void { - if(--counter) return; + if(counter--) return; if(rate == 0) counter = 0x10; if(rate == 1) counter = 0x20; diff --git a/higan/ms/psg/tone.cpp b/higan/ms/psg/tone.cpp index 552f703b..914e3627 100644 --- a/higan/ms/psg/tone.cpp +++ b/higan/ms/psg/tone.cpp @@ -1,5 +1,5 @@ auto PSG::Tone::run() -> void { - if(--counter) return; + if(counter--) return; counter = pitch; output ^= 1; diff --git a/higan/processor/lr35902/disassembler.cpp b/higan/processor/lr35902/disassembler.cpp index 68fefd09..8cc962f8 100644 --- a/higan/processor/lr35902/disassembler.cpp +++ b/higan/processor/lr35902/disassembler.cpp @@ -1,7 +1,3 @@ -static auto hex(uintmax value, long precision = 0, char padchar = '0') -> string { - return nall::hex(value, precision, padchar); -} - auto LR35902::disassemble(uint16 pc) -> string { char output[80]; memset(output, ' ', sizeof output); diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index 492c4eab..e6c1c5fa 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -1131,7 +1131,7 @@ auto M68K::instructionTAS(EffectiveAddress with) -> void { } auto M68K::instructionTRAP(uint4 vector) -> void { - exception(Exception::Trap, vector); + exception(Exception::Trap, 32 + vector); } auto M68K::instructionTRAPV() -> void { diff --git a/higan/processor/r65816/disassembler.cpp b/higan/processor/r65816/disassembler.cpp index c7c0c8af..991e8a1b 100644 --- a/higan/processor/r65816/disassembler.cpp +++ b/higan/processor/r65816/disassembler.cpp @@ -1,7 +1,3 @@ -static auto hex(uintmax value, long precision = 0, char padchar = '0') -> string { - return nall::hex(value, precision, padchar); -} - auto R65816::dreadb(uint24 addr) -> uint8 { if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) { //$00-3f|80-bf:2000-5fff diff --git a/nall/arithmetic/natural.hpp b/nall/arithmetic/natural.hpp index fb5fff4a..a49527f4 100644 --- a/nall/arithmetic/natural.hpp +++ b/nall/arithmetic/natural.hpp @@ -343,6 +343,19 @@ inline auto to_vector(Pair value) -> vector { return result; } +/* +inline auto hex(const Pair& value, long precision = 0, char padchar = '0') -> string { + string text; + if(!upper(value)) { + text.append(hex(lower(value))); + } else { + text.append(hex(upper(value))); + text.append(hex(lower(value), TypeBits / 4, '0')); + } + return pad(text, precision, padchar); +} +*/ + } #undef ConcatenateType diff --git a/nall/string.hpp b/nall/string.hpp index 522398e2..00136cf5 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -63,11 +63,11 @@ template struct stringify; template inline auto print(P&&...) -> void; template inline auto print(FILE*, P&&...) -> void; template inline auto pad(const T& value, long precision = 0, char padchar = ' ') -> string; -template inline auto hex(T value, long precision = 0, char padchar = '0') -> string; -template inline auto octal(T value, long precision = 0, char padchar = '0') -> string; -template inline auto binary(T value, long precision = 0, char padchar = '0') -> string; -template inline auto pointer(const T* value, long precision = 0) -> string; +inline auto hex(uintmax value, long precision = 0, char padchar = '0') -> string; +inline auto octal(uintmax value, long precision = 0, char padchar = '0') -> string; +inline auto binary(uintmax value, long precision = 0, char padchar = '0') -> string; inline auto pointer(uintptr value, long precision = 0) -> string; +template inline auto pointer(const T* value, long precision = 0) -> string; //match.hpp inline auto tokenize(const char* s, const char* p) -> bool; diff --git a/nall/string/format.hpp b/nall/string/format.hpp index 536164be..1d10a67e 100644 --- a/nall/string/format.hpp +++ b/nall/string/format.hpp @@ -84,9 +84,9 @@ template auto pad(const T& value, long precision, char padchar) -> s return buffer; } -template auto hex(T value, long precision, char padchar) -> string { +auto hex(uintmax value, long precision, char padchar) -> string { string buffer; - buffer.resize(sizeof(T) * 2); + buffer.resize(sizeof(uintmax) * 2); char* p = buffer.get(); uint size = 0; @@ -101,9 +101,9 @@ template auto hex(T value, long precision, char padchar) -> string { return buffer; } -template auto octal(T value, long precision, char padchar) -> string { +auto octal(uintmax value, long precision, char padchar) -> string { string buffer; - buffer.resize(sizeof(T) * 3); + buffer.resize(sizeof(uintmax) * 3); char* p = buffer.get(); uint size = 0; @@ -117,9 +117,9 @@ template auto octal(T value, long precision, char padchar) -> string return buffer; } -template auto binary(T value, long precision, char padchar) -> string { +auto binary(uintmax value, long precision, char padchar) -> string { string buffer; - buffer.resize(sizeof(T) * 8); + buffer.resize(sizeof(uintmax) * 8); char* p = buffer.get(); uint size = 0; @@ -133,23 +133,14 @@ template auto binary(T value, long precision, char padchar) -> strin return buffer; } -template auto pointer(const T* value, long precision) -> string { - if(value == nullptr) return "(nullptr)"; - return {"0x", hex((uintptr)value, precision)}; -} - auto pointer(uintptr value, long precision) -> string { if(value == 0) return "(nullptr)"; return {"0x", hex(value, precision)}; } -/* -auto real(long double value) -> string { - string temp; - temp.resize(fromReal(nullptr, value)); - fromReal(temp.get(), value); - return temp; +template auto pointer(const T* value, long precision) -> string { + if(value == nullptr) return "(nullptr)"; + return {"0x", hex((uintptr)value, precision)}; } -*/ }