From ffd150735b52a281e33e1195828ecde30714f9dc Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 15 Aug 2016 14:56:38 +1000 Subject: [PATCH] Update to v101r07 release. byuu says: Added VDP sprite rendering. Can't get any games far enough in to see if it actually works. So in other words, it doesn't work at all and is 100% completely broken. Also added 68K exceptions and interrupts. So far only the VDP interrupt is present. It definitely seems to be firing in commercial games, so that's promising. But the implementation is almost certainly completely wrong. There is fuck all of nothing for documentation on how interrupts actually work. I had to find out the interrupt vector numbers from reading the comments from the Sonic the Hedgehog disassembly. I have literally no fucking clue what I0-I2 (3-bit integer priority value in the status register) is supposed to do. I know that Vblank=6, Hblank=4, Ext(gamepad)=2. I know that at reset, SR.I=7. I don't know if I'm supposed to block interrupts when I is >, >=, <, <= to the interrupt level. I don't know what level CPU exceptions are supposed to be. Also implemented VDP regular DMA. No idea if it works correctly since none of the commercial games run far enough to use it. So again, it's horribly broken for usre. Also improved VDP fill mode. But I don't understand how it takes byte-lengths when the bus is 16-bit. The transfer times indicate it's actually transferring at the same speed as the 68K->VDP copy, strongly suggesting it's actually doing 16-bit transfers at a time. In which case, what happens when you set an odd transfer length? Also, both DMA modes can now target VRAM, VSRAM, CRAM. Supposedly there's all kinds of weird shit going on when you target VSRAM, CRAM with VDP fill/copy modes, but whatever. Get to that later. Also implemented a very lazy preliminary wait mechanism to to stall out a processor while another processor exerts control over the bus. This one's going to be a major work in progress. For one, it totally breaks the model I use to do save states with libco. For another, I don't know if a 68K->VDP DMA instantly locks the CPU, or if it the CPU could actually keep running if it was executing out of RAM when it started the DMA transfer from ROM (eg it's a bus busy stall, not a hard chip stall.) That'll greatly change how I handle the waiting. Also, the OSS driver now supports Audio::Latency. Sound should be even lower latency now. On FreeBSD when set to 0ms, it's absolutely incredible. Cannot detect latency whatsoever. The Mario jump sound seems to happen at the very instant I hear my cherry blue keyswitch activate. --- higan/emulator/emulator.hpp | 2 +- higan/md/cpu/cpu.cpp | 37 ++++++++- higan/md/cpu/cpu.hpp | 16 +++- higan/md/md.hpp | 9 +++ higan/md/vdp/background.cpp | 8 +- higan/md/vdp/dma.cpp | 36 ++++++--- higan/md/vdp/io.cpp | 16 ++-- higan/md/vdp/render.cpp | 14 ++++ higan/md/vdp/sprite.cpp | 76 +++++++++++++++++++ higan/md/vdp/vdp.cpp | 17 ++++- higan/md/vdp/vdp.hpp | 54 ++++++++++--- higan/processor/m68k/m68k.cpp | 10 +++ higan/processor/m68k/m68k.hpp | 17 +++-- .../presentation/presentation.cpp | 59 ++++++-------- .../presentation/presentation.hpp | 2 +- higan/target-tomoko/program/medium.cpp | 4 +- higan/target-tomoko/program/program.cpp | 2 +- nall/array.hpp | 53 +++++++++++++ nall/nall.hpp | 1 + ruby/audio/oss.cpp | 24 +++--- 20 files changed, 364 insertions(+), 93 deletions(-) create mode 100644 higan/md/vdp/sprite.cpp create mode 100644 nall/array.hpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 1a4131ad..e3c29ab8 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 = "101.06"; + static const string Version = "101.07"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index 435d95ed..765e48b3 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -21,17 +21,51 @@ auto CPU::main() -> void { fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n"); #endif + if(state.interruptPending) { + if(state.interruptPending.bit((uint)Interrupt::HorizontalBlank)) { + state.interruptPending.bit((uint)Interrupt::HorizontalBlank) = 0; + r.i = 4; + return exception(Exception::Interrupt, Vector::HorizontalBlank); + } + + if(state.interruptPending.bit((uint)Interrupt::VerticalBlank)) { + state.interruptPending.bit((uint)Interrupt::VerticalBlank) = 0; + r.i = 6; + return exception(Exception::Interrupt, Vector::VerticalBlank); + } + } + instruction(); } auto CPU::step(uint clocks) -> void { + while(wait) { + Thread::step(1); + synchronize(); + } + Thread::step(clocks); + synchronize(); +} + +auto CPU::synchronize() -> void { synchronize(apu); synchronize(vdp); synchronize(psg); synchronize(ym2612); } +auto CPU::raise(Interrupt interrupt) -> void { + if(!state.interruptLine.bit((uint)interrupt)) { + state.interruptLine.bit((uint)interrupt) = 1; + state.interruptPending.bit((uint)interrupt) = 1; + } +} + +auto CPU::lower(Interrupt interrupt) -> void { + state.interruptLine.bit((uint)interrupt) = 0; +} + auto CPU::power() -> void { M68K::power(); @@ -41,7 +75,8 @@ auto CPU::power() -> void { auto CPU::reset() -> void { M68K::reset(); create(CPU::Enter, system.colorburst() * 15.0 / 7.0); - cycles = 0; + + memory::fill(&state, sizeof(State)); } auto CPU::readByte(uint24 addr) -> uint8 { diff --git a/higan/md/cpu/cpu.hpp b/higan/md/cpu/cpu.hpp index 602795aa..d7a79232 100644 --- a/higan/md/cpu/cpu.hpp +++ b/higan/md/cpu/cpu.hpp @@ -1,10 +1,21 @@ //Motorola 68000 struct CPU : Processor::M68K, Thread { + enum class Interrupt : uint { + HorizontalBlank, + VerticalBlank, + }; + + using Thread::synchronize; + static auto Enter() -> void; auto boot() -> void; auto main() -> void; auto step(uint clocks) -> void override; + auto synchronize() -> void; + + auto raise(Interrupt) -> void; + auto lower(Interrupt) -> void; auto power() -> void; auto reset() -> void; @@ -17,7 +28,10 @@ struct CPU : Processor::M68K, Thread { private: uint8 ram[64 * 1024]; - uint cycles = 0; + struct State { + uint32 interruptLine; + uint32 interruptPending; + } state; }; extern CPU cpu; diff --git a/higan/md/md.hpp b/higan/md/md.hpp index aa5bd036..43590e35 100644 --- a/higan/md/md.hpp +++ b/higan/md/md.hpp @@ -15,15 +15,24 @@ namespace MegaDrive { using Scheduler = Emulator::Scheduler; extern Scheduler scheduler; + struct Wait { + enum : uint { + VDP_DMA = 1 << 0, + }; + }; + struct Thread : Emulator::Thread { auto create(auto (*entrypoint)() -> void, double frequency) -> void { Emulator::Thread::create(entrypoint, frequency); scheduler.append(*this); + wait = 0; } inline auto synchronize(Thread& thread) -> void { if(clock() >= thread.clock()) scheduler.resume(thread); } + + uint wait = 0; }; #include diff --git a/higan/md/vdp/background.cpp b/higan/md/vdp/background.cpp index e572721d..2e7ab26d 100644 --- a/higan/md/vdp/background.cpp +++ b/higan/md/vdp/background.cpp @@ -1,4 +1,4 @@ -auto VDP::Background::scanline() -> void { +auto VDP::Background::scanline(uint y) -> void { } auto VDP::Background::run(uint x, uint y) -> void { @@ -17,9 +17,9 @@ auto VDP::Background::run(uint x, uint y) -> void { tileAddress += pixelY << 1 | pixelX >> 2; uint16 tileData = vdp.vram[tileAddress]; - uint4 palette = tileData >> (((pixelX & 3) ^ 3) << 2); - if(palette) { - output.color = tileAttributes.bits(13,14) << 4 | palette; + uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); + if(color) { + output.color = tileAttributes.bits(13,14) << 4 | color; output.priority = tileAttributes.bit(15); } } diff --git a/higan/md/vdp/dma.cpp b/higan/md/vdp/dma.cpp index ec8bb6e3..1e01b1d7 100644 --- a/higan/md/vdp/dma.cpp +++ b/higan/md/vdp/dma.cpp @@ -1,18 +1,36 @@ auto VDP::dmaRun() -> void { if(!io.dmaEnable) return; - if(!io.dmaActive) return; + if(io.command.bits(4,5) != 2) return; - if(io.dmaMode == 2) return dmaFillVRAM(); + if(io.dmaMode <= 1) return dmaLoad(); + if(io.dmaMode == 2) return dmaFill(); + if(io.dmaMode == 3) return dmaCopy(); } -auto VDP::dmaFillVRAM() -> void { - auto address = io.address.bits(1,15); - auto data = io.dmaFillWord; - if(io.address.bit(0)) data = data >> 8 | data << 8; - vram[address] = data; - io.address += io.dataIncrement; +auto VDP::dmaLoad() -> void { + cpu.wait |= Wait::VDP_DMA; + auto data = cpu.readWord(io.dmaSource); + writeDataPort(data); + + io.dmaSource.bits(0,15) += 2; if(--io.dmaLength == 0) { - io.dmaActive = false; + io.command.bit(5) = 0; + cpu.wait &=~ Wait::VDP_DMA; } } + +auto VDP::dmaFill() -> void { + if(io.dmaFillWait) return; + + auto data = io.dmaFillWord; + writeDataPort(data); + + io.dmaSource.bits(0,15) += 2; + if(--io.dmaLength == 0 || --io.dmaLength == 0) { + io.command.bit(5) = 0; + } +} + +auto VDP::dmaCopy() -> void { +} diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index c2d3d3f0..2cf5c945 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -72,8 +72,7 @@ auto VDP::writeDataPort(uint16 data) -> void { io.commandPending = false; //DMA VRAM fill - if(io.command.bits(4,5) == 2) { - io.dmaActive = true; + if(io.dmaFillWait.lower()) { io.dmaFillWord = data; return; } @@ -113,7 +112,10 @@ auto VDP::readControlPort() -> uint16 { io.commandPending = false; uint16 result = 0b0011'0100'0000'0000; - result |= io.dmaActive << 1; + result |= 1 << 9; //FIFO empty + result |= (state.y >= 240) << 3; //vertical blank + result |= (state.y >= 240 || state.x >= 320) << 2; //horizontal blank + result |= io.command.bit(5) << 1; //DMA active return result; } @@ -126,7 +128,7 @@ auto VDP::writeControlPort(uint16 data) -> void { io.command.bits(2,5) = data.bits(4,7); io.address.bits(14,15) = data.bits(0,1); - + io.dmaFillWait = io.dmaMode == 2 && io.command.bits(4,5) == 2; return; } @@ -147,7 +149,7 @@ auto VDP::writeControlPort(uint16 data) -> void { case 0x00: { io.displayOverlayEnable = data.bit(0); io.counterLatch = data.bit(1); - io.horizontalInterruptEnable = data.bit(4); + io.horizontalBlankInterruptEnable = data.bit(4); io.leftColumnBlank = data.bit(5); return; } @@ -186,13 +188,13 @@ auto VDP::writeControlPort(uint16 data) -> void { //sprite attribute table location case 0x05: { - io.attrtableSprite = data.bits(0,7); + sprite.io.attributeAddress = data.bits(0,7) << 8; return; } //sprite pattern base address case 0x06: { - io.nametableBaseSprite = data.bit(5); + sprite.io.nametableAddressBase = data.bit(5); return; } diff --git a/higan/md/vdp/render.cpp b/higan/md/vdp/render.cpp index 571fc2a3..08867c6e 100644 --- a/higan/md/vdp/render.cpp +++ b/higan/md/vdp/render.cpp @@ -3,6 +3,17 @@ auto VDP::scanline() -> void { if(++state.y >= 262) state.y = 0; if(state.y == 0) scheduler.exit(Scheduler::Event::Frame); + if(state.y == 0) { + sprite.frame(); + } + + if(state.y < 240) { + planeA.scanline(state.y); + window.scanline(state.y); + planeB.scanline(state.y); + sprite.scanline(state.y); + } + state.output = buffer + (state.y * 2 + 0) * 1280; } @@ -16,12 +27,15 @@ auto VDP::run() -> void { planeA.run(state.x, state.y); planeB.run(state.x, state.y); + sprite.run(state.x, state.y); auto output = io.backgroundColor; if(auto color = planeB.output.color) output = color; if(auto color = planeA.output.color) output = color; + if(auto color = sprite.output.color) output = color; if(planeB.output.priority) if(auto color = planeB.output.color) output = color; if(planeA.output.priority) if(auto color = planeA.output.color) output = color; + if(sprite.output.priority) if(auto color = sprite.output.color) output = color; outputPixel(cram[output]); state.x++; diff --git a/higan/md/vdp/sprite.cpp b/higan/md/vdp/sprite.cpp new file mode 100644 index 00000000..667084c5 --- /dev/null +++ b/higan/md/vdp/sprite.cpp @@ -0,0 +1,76 @@ +auto VDP::Sprite::frame() -> void { + uint15 address = io.attributeAddress; + uint7 link = 0; + + oam.reset(); + while(oam.size() < 80) { + uint64 attributes; + attributes |= (uint64)vdp.vram[address + (link << 2) + 0] << 48; + attributes |= (uint64)vdp.vram[address + (link << 2) + 1] << 32; + attributes |= (uint64)vdp.vram[address + (link << 2) + 2] << 16; + attributes |= (uint64)vdp.vram[address + (link << 2) + 3] << 0; + + auto& object = oam.append(); + object.x = attributes.bits( 0, 9) - 128; + object.address = attributes.bits(16,26) << 4; + object.horizontalFlip = attributes.bit (27); + object.verticalFlip = attributes.bit (28); + object.palette = attributes.bits(29,30); + object.priority = attributes.bit (31); + object.height = attributes.bits(40,41) << 3; + object.width = attributes.bits(42,43) << 3; + object.y = attributes.bits(48,57) - 128; + + link = attributes.bits(32,38); + if(!link) break; + } +} + +auto VDP::Sprite::scanline(uint y) -> void { + object.reset(); + for(auto& o : oam) { + if((uint9)(o.y + o.height - 1) < y) continue; + if((uint9)(y + o.height - 1) < o.y) continue; + if(o.x == 0) break; + + object.append(o); + if(object.size() >= object.capacity()) break; + } +} + +auto VDP::Sprite::run(uint x, uint y) -> void { + output.priority = 0; + output.color = 0; + + for(auto& o : object) { + if((uint9)(o.x + o.width - 1) < x) continue; + if((uint9)(y + o.width - 1) < o.x) continue; + + auto objectX = (uint9)(x - o.x); + auto objectY = (uint9)(y - o.y); + if(o.horizontalFlip) objectX = (o.width - 1) - objectX; + if(o.verticalFlip) objectY = (o.height - 1) - objectY; + + uint tileX = objectX >> 3; + uint tileY = objectY >> 3; + uint tileNumber = tileX * (o.width >> 3) + tileY; + uint15 tileAddress = o.address + (tileNumber << 4); + uint pixelX = objectX & 7; + uint pixelY = objectY & 7; + tileAddress += pixelY << 1 | pixelX >> 2; + + uint16 tileData = vdp.vram[tileAddress]; + uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2); + if(color) { + output.color = o.palette << 4 | color; + output.priority = o.priority; + } + } +} + +auto VDP::Sprite::power() -> void { +} + +auto VDP::Sprite::reset() -> void { + memory::fill(&io, sizeof(IO)); +} diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 3cde2ef1..8c8dd36d 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -10,6 +10,7 @@ VDP vdp; #include "dma.cpp" #include "render.cpp" #include "background.cpp" +#include "sprite.cpp" auto VDP::Enter() -> void { while(true) scheduler.synchronize(), vdp.main(); @@ -18,14 +19,26 @@ auto VDP::Enter() -> void { auto VDP::main() -> void { scanline(); if(state.y < 240) { + if(state.y == 0) { + cpu.lower(CPU::Interrupt::VerticalBlank); + } + cpu.lower(CPU::Interrupt::HorizontalBlank); for(uint x : range(320)) { run(); step(1); } + if(io.horizontalBlankInterruptEnable) { + cpu.raise(CPU::Interrupt::HorizontalBlank); + } + step(22); } else { + if(state.y == 240) { + if(io.verticalBlankInterruptEnable) { + cpu.raise(CPU::Interrupt::VerticalBlank); + } + } step(342); } - step(22); } auto VDP::step(uint clocks) -> void { @@ -44,6 +57,7 @@ auto VDP::power() -> void { planeA.power(); window.power(); planeB.power(); + sprite.power(); } auto VDP::reset() -> void { @@ -54,6 +68,7 @@ auto VDP::reset() -> void { planeA.reset(); window.reset(); planeB.reset(); + sprite.reset(); } } diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index f298f7eb..14e2c9ba 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -24,7 +24,9 @@ struct VDP : Thread { //dma.cpp auto dmaRun() -> void; - auto dmaFillVRAM() -> void; + auto dmaLoad() -> void; + auto dmaFill() -> void; + auto dmaCopy() -> void; //render.cpp auto scanline() -> void; @@ -33,7 +35,7 @@ struct VDP : Thread { //background.cpp struct Background { - auto scanline() -> void; + auto scanline(uint y) -> void; auto run(uint x, uint y) -> void; auto power() -> void; @@ -54,15 +56,51 @@ struct VDP : Thread { Background window; Background planeB; + //sprite.cpp + struct Sprite { + auto frame() -> void; + auto scanline(uint y) -> void; + auto run(uint x, uint y) -> void; + + auto power() -> void; + auto reset() -> void; + + struct IO { + uint15 attributeAddress; + uint1 nametableAddressBase; + } io; + + struct Object { + uint10 x; + uint10 y; + uint width; + uint height; + bool horizontalFlip; + bool verticalFlip; + uint2 palette; + uint1 priority; + uint15 address; + }; + + struct Output { + uint6 color; + boolean priority; + } output; + + array oam; + array object; + }; + Sprite sprite; + +private: uint16 vram[32768]; uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware uint9 cram[64]; uint10 vsram[40]; -private: struct IO { //internal state - boolean dmaActive; + boolean dmaFillWait; uint8 dmaFillWord; //command @@ -73,7 +111,7 @@ private: //$00 mode register 1 uint1 displayOverlayEnable; uint1 counterLatch; - uint1 horizontalInterruptEnable; + uint1 horizontalBlankInterruptEnable; uint1 leftColumnBlank; //$01 mode register 2 @@ -84,12 +122,6 @@ private: uint1 displayEnable; uint1 externalVRAM; - //$05 sprite attribute table location - uint8 attrtableSprite; - - //$06 sprite pattern base address - uint1 nametableBaseSprite; - //$07 background color uint6 backgroundColor; diff --git a/higan/processor/m68k/m68k.cpp b/higan/processor/m68k/m68k.cpp index a6cd699a..90d712e5 100644 --- a/higan/processor/m68k/m68k.cpp +++ b/higan/processor/m68k/m68k.cpp @@ -46,6 +46,16 @@ auto M68K::supervisor() -> bool { } auto M68K::exception(uint exception, uint vector) -> void { + auto pc = r.pc; + auto sr = readSR(); + + r.s = 1; + r.t = 0; + + push(pc); + push(sr); + + r.pc = read(vector << 2); } } diff --git a/higan/processor/m68k/m68k.hpp b/higan/processor/m68k/m68k.hpp index 81b17a44..7b26e5b1 100644 --- a/higan/processor/m68k/m68k.hpp +++ b/higan/processor/m68k/m68k.hpp @@ -32,16 +32,19 @@ struct M68K { Unprivileged, Trap, + Interrupt, };}; struct Vector { enum : uint { - Illegal = 4, - DivisionByZero = 5, - BoundsCheck = 6, - Overflow = 7, - Unprivileged = 8, - IllegalLineA = 10, - IllegalLineF = 11, + Illegal = 4, + DivisionByZero = 5, + BoundsCheck = 6, + Overflow = 7, + Unprivileged = 8, + IllegalLineA = 10, + IllegalLineF = 11, + HorizontalBlank = 28, + VerticalBlank = 30, };}; M68K(); diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index 88e7240e..af4c3bb0 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -199,7 +199,28 @@ auto Presentation::updateEmulator() -> void { emulator->set("Scanline Emulation", scanlineEmulation.checked()); } +auto Presentation::clearViewport() -> void { + if(!video) return; + + uint32_t* output; + uint length = 0; + uint width = viewport.geometry().width(); + uint height = viewport.geometry().height(); + if(video->lock(output, length, width, height)) { + for(uint y : range(height)) { + auto dp = output + y * (length >> 2); + for(uint x : range(width)) *dp++ = 0xff000000; + } + + video->unlock(); + video->refresh(); + } +} + auto Presentation::resizeViewport() -> void { + //clear video area before resizing to avoid seeing distorted video momentarily + clearViewport(); + uint scale = 2; if(settings["Video/Scale"].text() == "Small" ) scale = 2; if(settings["Video/Scale"].text() == "Medium") scale = 3; @@ -219,7 +240,6 @@ auto Presentation::resizeViewport() -> void { if(!emulator) { viewport.setGeometry({0, 0, windowWidth, windowHeight}); - draw(Resource::Logo::higan); } else { auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection); viewport.setGeometry({ @@ -227,6 +247,9 @@ auto Presentation::resizeViewport() -> void { videoSize.width, videoSize.height }); } + + //clear video area again to ensure entire viewport area has been painted in + clearViewport(); } auto Presentation::toggleFullScreen() -> void { @@ -243,43 +266,9 @@ auto Presentation::toggleFullScreen() -> void { menuBar.setVisible(true); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); } - - Application::processEvents(); resizeViewport(); } -auto Presentation::draw(image logo) -> void { - if(!video) return; - - uint32_t* output; - uint length = 0; - uint width = viewport.geometry().width(); - uint height = viewport.geometry().height(); - if(video->lock(output, length, width, height)) { - uint cx = (width - logo.width()) - 10; - uint cy = (height - logo.height()) - 10; - - image backdrop; - backdrop.allocate(width, height); - if(logo && !program->hasQuit) { - backdrop.sphericalGradient(0xff0000bf, 0xff000000, logo.width(), logo.height() / 2, width, height); - backdrop.impose(image::blend::sourceAlpha, cx, cy, logo, 0, 0, logo.width(), logo.height()); - } else { - backdrop.fill(0xff000000); - } - - auto data = (uint32_t*)backdrop.data(); - for(auto y : range(height)) { - auto dp = output + y * (length >> 2); - auto sp = data + y * width; - for(auto x : range(width)) *dp++ = *sp++; - } - - video->unlock(); - video->refresh(); - } -} - auto Presentation::loadShaders() -> void { auto pathname = locate("Video Shaders/"); diff --git a/higan/target-tomoko/presentation/presentation.hpp b/higan/target-tomoko/presentation/presentation.hpp index e55a5412..169a41d6 100644 --- a/higan/target-tomoko/presentation/presentation.hpp +++ b/higan/target-tomoko/presentation/presentation.hpp @@ -11,9 +11,9 @@ struct AboutWindow : Window { struct Presentation : Window { Presentation(); auto updateEmulator() -> void; + auto clearViewport() -> void; auto resizeViewport() -> void; auto toggleFullScreen() -> void; - auto draw(image logo = {}) -> void; auto loadShaders() -> void; MenuBar menuBar{this}; diff --git a/higan/target-tomoko/program/medium.cpp b/higan/target-tomoko/program/medium.cpp index 1f4825fd..afb13f91 100644 --- a/higan/target-tomoko/program/medium.cpp +++ b/higan/target-tomoko/program/medium.cpp @@ -29,7 +29,6 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa } updateAudioDriver(); updateAudioEffects(); - presentation->draw(); emulator->power(); presentation->resizeViewport(); @@ -45,14 +44,13 @@ auto Program::loadMedium(Emulator::Interface& interface, const Emulator::Interfa auto Program::unloadMedium() -> void { if(!emulator) return; - presentation->draw(); + presentation->clearViewport(); toolsManager->cheatEditor.saveCheats(); emulator->unload(); emulator = nullptr; mediumPaths.reset(); presentation->resizeViewport(); - presentation->draw(Resource::Logo::higan); presentation->setTitle({"higan v", Emulator::Version}); presentation->systemMenu.setVisible(false); presentation->toolsMenu.setVisible(false); diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 59b65b14..5cbba905 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -31,7 +31,7 @@ Program::Program(string_vector args) { video->set(Video::Synchronize, settings["Video/Synchronize"].boolean()); if(!video->init()) video = Video::create("None"); - presentation->draw(Resource::Logo::higan); + presentation->clearViewport(); audio = Audio::create(settings["Audio/Driver"].text()); audio->set(Audio::Device, settings["Audio/Device"].text()); diff --git a/nall/array.hpp b/nall/array.hpp new file mode 100644 index 00000000..37b4b5c4 --- /dev/null +++ b/nall/array.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include + +namespace nall { + +template +struct array { + auto capacity() const -> uint { return Capacity; } + auto size() const -> uint { return _size; } + + auto reset() -> void { + for(uint n : range(_size)) _pool.t[n].~T(); + _size = 0; + } + + auto operator[](uint index) -> T& { + return _pool.t[index]; + } + + auto operator[](uint index) const -> const T& { + return _pool.t[index]; + } + + auto append() -> T& { + new(_pool.t + _size) T; + return _pool.t[_size++]; + } + + auto append(const T& value) -> void { + new(_pool.t + _size++) T(value); + } + + auto append(T&& value) -> void { + new(_pool.t + _size++) T(move(value)); + } + + auto begin() { return &_pool.t[0]; } + auto end() { return &_pool.t[_size]; } + + auto begin() const { return &_pool.t[0]; } + auto end() const { return &_pool.t[_size]; } + +private: + union U { + U() {} + ~U() {} + T t[Capacity]; + } _pool; + uint _size = 0; +}; + +} diff --git a/nall/nall.hpp b/nall/nall.hpp index 648da7f1..66560adb 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/ruby/audio/oss.cpp b/ruby/audio/oss.cpp index eb9628cb..9e077f36 100644 --- a/ruby/audio/oss.cpp +++ b/ruby/audio/oss.cpp @@ -3,12 +3,7 @@ #include #include -//OSS4 soundcard.h includes below SNDCTL defines, but OSS3 does not -//However, OSS4 soundcard.h does not reside in -//Therefore, attempt to manually define SNDCTL values if using OSS3 header -//Note that if the defines below fail to work on any specific platform, one can point soundcard.h -//above to the correct location for OSS4 (usually /usr/lib/oss/include/sys/soundcard.h) -//Failing that, one can disable OSS4 ioctl calls inside init() and remove the below defines +//OSSv4 features: define fallbacks for OSSv3 (where these ioctls are ignored) #ifndef SNDCTL_DSP_COOKEDMODE #define SNDCTL_DSP_COOKEDMODE _IOW('P', 30, int) @@ -31,12 +26,14 @@ struct AudioOSS : Audio { string device = "/dev/dsp"; bool synchronize = true; uint frequency = 48000; + uint latency = 60; } settings; auto cap(const string& name) -> bool { if(name == Audio::Device) return true; if(name == Audio::Synchronize) return true; if(name == Audio::Frequency) return true; + if(name == Audio::Latency) return true; return false; } @@ -44,6 +41,7 @@ struct AudioOSS : Audio { if(name == Audio::Device) return settings.device; if(name == Audio::Synchronize) return settings.synchronize; if(name == Audio::Frequency) return settings.frequency; + if(name == Audio::Latency) return settings.latency; return {}; } @@ -66,6 +64,12 @@ struct AudioOSS : Audio { return true; } + if(name == Audio::Latency && value.is()) { + settings.latency = value.get(); + if(device.fd >= 0) init(); + return true; + } + return false; } @@ -81,13 +85,11 @@ struct AudioOSS : Audio { device.fd = open(settings.device, O_WRONLY, O_NONBLOCK); if(device.fd < 0) return false; - #if 1 //SOUND_VERSION >= 0x040000 - //attempt to enable OSS4-specific features regardless of version - //OSS3 ioctl calls will silently fail, but sound will still work - int cooked = 1, policy = 4; //policy should be 0 - 10, lower = less latency, more CPU usage + int cooked = 1; ioctl(device.fd, SNDCTL_DSP_COOKEDMODE, &cooked); + //policy: 0 = minimum latency (higher CPU usage); 10 = maximum latency (lower CPU usage) + int policy = min(10, settings.latency / 20); //note: latency measurement isn't exact ioctl(device.fd, SNDCTL_DSP_POLICY, &policy); - #endif int frequency = settings.frequency; ioctl(device.fd, SNDCTL_DSP_CHANNELS, &device.channels); ioctl(device.fd, SNDCTL_DSP_SETFMT, &device.format);