mirror of
https://github.com/libretro/higan.git
synced 2025-02-20 00:41:36 +00:00
Update to v106r113 release.
byuu says: I've added a waveOut audio driver for Windows (no DRC), and hooked up a lot more neo geo pocket stuff (gamepad polling, APU/Z80 enable/disable, APU->CPU IRQs, VPU I/O registers, etc.) still not enough to get any further than we already were, sigh.
This commit is contained in:
parent
1a4acbf74b
commit
8b5293cd70
@ -37,7 +37,7 @@ using namespace nall;
|
||||
|
||||
namespace higan {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.112";
|
||||
static const string Version = "106.113";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
@ -3,10 +3,14 @@
|
||||
namespace higan::NeoGeoPocket {
|
||||
|
||||
APU apu;
|
||||
#include "memory.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto APU::main() -> void {
|
||||
step(1);
|
||||
if(!io.enable) return step(16);
|
||||
//static uint ctr=0;
|
||||
//if(++ctr<20) print(disassemble(r.pc), "\n");
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
@ -26,6 +30,21 @@ auto APU::power() -> void {
|
||||
while(true) scheduler.synchronize(), main();
|
||||
});
|
||||
ram.allocate(0x1000);
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto APU::enable() -> void {
|
||||
Thread::destroy();
|
||||
Z80::power();
|
||||
bus->grant(false);
|
||||
Thread::create(system.frequency() / 2.0, [&] {
|
||||
while(true) scheduler.synchronize(), main();
|
||||
});
|
||||
io.enable = true;
|
||||
}
|
||||
|
||||
auto APU::disable() -> void {
|
||||
io.enable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,16 +6,22 @@ struct APU : Z80, Z80::Bus, Thread {
|
||||
auto step(uint clocks) -> void override;
|
||||
auto synchronizing() const -> bool override;
|
||||
auto power() -> void;
|
||||
auto enable() -> void;
|
||||
auto disable() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto read(uint16 address) -> uint8 override { return 0; }
|
||||
auto write(uint16 address, uint8 data) -> void override {}
|
||||
auto read(uint16 address) -> uint8 override;
|
||||
auto write(uint16 address, uint8 data) -> void override;
|
||||
|
||||
auto in(uint8 address) -> uint8 override { return 0; }
|
||||
auto out(uint8 address, uint8 data) -> void override {}
|
||||
auto in(uint8 address) -> uint8 override;
|
||||
auto out(uint8 address, uint8 data) -> void override;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
uint1 enable;
|
||||
} io;
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
19
higan/ngp/apu/memory.cpp
Normal file
19
higan/ngp/apu/memory.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
auto APU::read(uint16 address) -> uint8 {
|
||||
if(address >= 0x0000 && address <= 0x0fff) return ram.read(address);
|
||||
if(address == 0x8000) return cpu.io.apuPort;
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::write(uint16 address, uint8 data) -> void {
|
||||
if(address >= 0x0000 && address <= 0x0fff) return ram.write(address, data);
|
||||
if(address == 0x8000) return (void)(cpu.io.apuPort = data);
|
||||
if(address == 0xc000) return cpu.setInterruptAPU(1);
|
||||
}
|
||||
|
||||
auto APU::in(uint8 address) -> uint8 {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto APU::out(uint8 address, uint8 data) -> void {
|
||||
cpu.setInterruptAPU(0); //todo: unconfirmed
|
||||
}
|
@ -63,14 +63,12 @@ auto Cartridge::power() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::read(uint1 chip, uint21 address) -> uint8 {
|
||||
if(!flash[0]) return 0xff;
|
||||
if(!flash[1]) chip = 0;
|
||||
if(!flash[chip]) return 0xff;
|
||||
return flash[chip].read(address);
|
||||
}
|
||||
|
||||
auto Cartridge::write(uint1 chip, uint21 address, uint8 data) -> void {
|
||||
if(!flash[0]) return;
|
||||
if(!flash[1]) chip = 0;
|
||||
if(!flash[chip]) return;
|
||||
return flash[chip].write(address, data);
|
||||
}
|
||||
|
||||
|
@ -2,13 +2,17 @@
|
||||
|
||||
namespace higan::NeoGeoPocket {
|
||||
|
||||
//bool tracing=false;
|
||||
|
||||
CPU cpu;
|
||||
#include "memory.cpp"
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::main() -> void {
|
||||
static uint ctr=0;
|
||||
if(++ctr<200) print(disassemble(), "\n");
|
||||
//static uint ctr=0;
|
||||
//if(tracing&&++ctr<4000) print(disassemble(), "\n");
|
||||
//else tracing=false;
|
||||
instruction();
|
||||
step(1);
|
||||
}
|
||||
@ -26,18 +30,33 @@ auto CPU::power() -> void {
|
||||
while(true) scheduler.synchronize(), main();
|
||||
});
|
||||
ram.allocate(0x3000);
|
||||
r.pc.b.b0 = read(0xffff00);
|
||||
r.pc.b.b1 = read(0xffff01);
|
||||
r.pc.b.b2 = read(0xffff02);
|
||||
r.pc.b.b3 = read(0xffff03);
|
||||
r.pc.l.l0 = 0xff1800;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto CPU::setInterruptAPU(boolean line) -> void {
|
||||
if(io.irq.apu != line) {
|
||||
io.irq.apu = line;
|
||||
if(line) interrupt(0xffff30);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::setInterruptHblank(boolean line) -> void {
|
||||
io.irq.hblank = line;
|
||||
//if(line) interrupt(0xffff0c);
|
||||
if(io.irq.hblank != line) {
|
||||
io.irq.hblank = line;
|
||||
//if(line) interrupt(0xffff0c);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::setInterruptVblank(boolean line) -> void {
|
||||
io.irq.vblank = line;
|
||||
if(line) interrupt(0xffff10);
|
||||
if(io.irq.vblank != line) {
|
||||
io.irq.vblank = line;
|
||||
if(line) interrupt(0xffff2c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ struct CPU : TLCS900H, Thread {
|
||||
auto step(uint clocks) -> void override;
|
||||
auto power() -> void;
|
||||
|
||||
auto setInterruptAPU(boolean line) -> void;
|
||||
auto setInterruptHblank(boolean line) -> void;
|
||||
auto setInterruptVblank(boolean line) -> void;
|
||||
|
||||
@ -13,15 +14,22 @@ struct CPU : TLCS900H, Thread {
|
||||
auto read(uint24 address) -> uint8 override;
|
||||
auto write(uint24 address, uint8 data) -> void override;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(uint8 address) -> uint8;
|
||||
auto writeIO(uint8 address, uint8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
//private:
|
||||
struct IO {
|
||||
struct IRQ {
|
||||
boolean apu;
|
||||
boolean hblank;
|
||||
boolean vblank;
|
||||
} irq;
|
||||
|
||||
uint8 apuPort;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
45
higan/ngp/cpu/io.cpp
Normal file
45
higan/ngp/cpu/io.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
auto CPU::readIO(uint8 address) -> uint8 {
|
||||
uint8 data = 0xff;
|
||||
|
||||
switch(address) {
|
||||
case 0xb0:
|
||||
controls.poll();
|
||||
data.bit(0) = controls.upLatch;
|
||||
data.bit(1) = controls.downLatch;
|
||||
data.bit(2) = controls.leftLatch;
|
||||
data.bit(3) = controls.rightLatch;
|
||||
data.bit(4) = controls.a->value;
|
||||
data.bit(5) = controls.b->value;
|
||||
data.bit(6) = controls.option->value;
|
||||
data.bit(7) = 0; //unused?
|
||||
break;
|
||||
|
||||
case 0xb1:
|
||||
data.bit(0) = 0; //power button
|
||||
data.bit(1) = 1; //sub battery (CR2032)
|
||||
break;
|
||||
|
||||
case 0xbc:
|
||||
data = io.apuPort;
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::writeIO(uint8 address, uint8 data) -> void {
|
||||
switch(address) {
|
||||
case 0xb9:
|
||||
if(data == 0x55) {
|
||||
apu.enable();
|
||||
}
|
||||
if(data == 0xaa) {
|
||||
apu.disable();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xbc:
|
||||
io.apuPort = data;
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,27 +1,20 @@
|
||||
auto CPU::read(uint24 address) -> uint8 {
|
||||
if(address < 0x000100) return 0xff;
|
||||
if(address < 0x004000) return 0xff;
|
||||
if(address < 0x007000) return cpu.ram.read((uint14)address);
|
||||
if(address < 0x008000) return apu.ram.read((uint12)address);
|
||||
if(address < 0x00c000) return vpu.ram.read((uint14)address);
|
||||
if(address < 0x200000) return 0xff;
|
||||
if(address < 0x400000) return cartridge.read(0, (uint21)address);
|
||||
if(address < 0x800000) return 0xff;
|
||||
if(address < 0xa00000) return cartridge.read(1, (uint21)address);
|
||||
if(address < 0xff0000) return 0xff;
|
||||
return system.bios.read((uint16)address);
|
||||
if(address >= 0x000000 && address <= 0x0000ff) return cpu.readIO(address);
|
||||
if(address >= 0x004000 && address <= 0x006fff) return cpu.ram.read(address);
|
||||
if(address >= 0x007000 && address <= 0x007fff) return apu.ram.read(address);
|
||||
if(address >= 0x008000 && address <= 0x00bfff) return vpu.read(address);
|
||||
if(address >= 0x200000 && address <= 0x3fffff) return cartridge.read(0, address);
|
||||
if(address >= 0x800000 && address <= 0x9fffff) return cartridge.read(1, address);
|
||||
if(address >= 0xff0000 && address <= 0xffffff) return system.bios.read(address);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto CPU::write(uint24 address, uint8 data) -> void {
|
||||
if(address < 0x000100) return;
|
||||
if(address < 0x004000) return;
|
||||
if(address < 0x007000) return cpu.ram.write((uint14)address, data);
|
||||
if(address < 0x008000) return apu.ram.write((uint12)address, data);
|
||||
if(address < 0x00c000) return vpu.ram.write((uint14)address, data);
|
||||
if(address < 0x200000) return;
|
||||
if(address < 0x400000) return cartridge.write(0, (uint21)address, data);
|
||||
if(address < 0x800000) return;
|
||||
if(address < 0xa00000) return cartridge.write(1, (uint21)address, data);
|
||||
if(address < 0xff0000) return;
|
||||
return system.bios.write((uint16)address, data);
|
||||
if(address >= 0x000000 && address <= 0x0000ff) return cpu.writeIO(address, data);
|
||||
if(address >= 0x004000 && address <= 0x006fff) return cpu.ram.write(address, data);
|
||||
if(address >= 0x007000 && address <= 0x007fff) return apu.ram.write(address, data);
|
||||
if(address >= 0x008000 && address <= 0x00bfff) return vpu.write(address, data);
|
||||
if(address >= 0x200000 && address <= 0x3fffff) return cartridge.write(0, address, data);
|
||||
if(address >= 0x800000 && address <= 0x9fffff) return cartridge.write(1, address, data);
|
||||
if(address >= 0xff0000 && address <= 0xffffff) return system.bios.write(address, data);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ struct Controls {
|
||||
auto load(Node::Object, Node::Object) -> void;
|
||||
auto poll() -> void;
|
||||
|
||||
private:
|
||||
//private:
|
||||
bool yHold = 0;
|
||||
bool upLatch = 0;
|
||||
bool downLatch = 0;
|
||||
|
149
higan/ngp/vpu/memory.cpp
Normal file
149
higan/ngp/vpu/memory.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
auto VPU::read(uint24 address) -> uint8 {
|
||||
address = 0x8000 | (uint14)address;
|
||||
if(address >= 0x8800 && address <= 0x88ff) return spriteRAM.read(address);
|
||||
if(address >= 0x8900 && address <= 0x8fff) return 0xff; //todo: does spriteRAM mirror here?
|
||||
if(address >= 0x9000 && address <= 0x9fff) return scrollRAM.read(address);
|
||||
if(address >= 0xa000 && address <= 0xbfff) return characterRAM.read(address);
|
||||
|
||||
uint8 data = 0xff;
|
||||
|
||||
switch(address) {
|
||||
case 0x8000:
|
||||
data.bit(6) = io.hblankEnableIRQ;
|
||||
data.bit(7) = io.vblankEnableIRQ;
|
||||
break;
|
||||
|
||||
case 0x8002: data = io.window.horigin; break;
|
||||
case 0x8003: data = io.window.vorigin; break;
|
||||
case 0x8004: data = io.window.hsize; break;
|
||||
case 0x8005: data = io.window.vsize; break;
|
||||
|
||||
case 0x8008: data = io.hcounter >> 1; break;
|
||||
case 0x8009: data = io.vcounter; break;
|
||||
|
||||
case 0x8010:
|
||||
data.bit(6) = io.vblankActive;
|
||||
data.bit(7) = io.characterOver;
|
||||
break;
|
||||
|
||||
case 0x8012:
|
||||
data.bits(0,2) = io.outsideWindowColor;
|
||||
data.bit(7) = io.negateScreen;
|
||||
break;
|
||||
|
||||
case 0x8020: data = io.sprite.hoffset; break;
|
||||
case 0x8021: data = io.sprite.voffset; break;
|
||||
|
||||
case 0x8030: data.bit(7) = io.planePriority; break;
|
||||
|
||||
case 0x8032: data = io.plane1.hscroll; break;
|
||||
case 0x8033: data = io.plane1.vscroll; break;
|
||||
case 0x8034: data = io.plane2.hscroll; break;
|
||||
case 0x8035: data = io.plane2.vscroll; break;
|
||||
|
||||
case 0x8100: break;
|
||||
case 0x8101: data.bit(0) = io.sprite.palette[0].bit(1); break;
|
||||
case 0x8102: data.bit(0) = io.sprite.palette[0].bit(2); break;
|
||||
case 0x8103: data.bit(0) = io.sprite.palette[0].bit(3); break;
|
||||
case 0x8104: break;
|
||||
case 0x8105: data.bit(0) = io.sprite.palette[1].bit(1); break;
|
||||
case 0x8106: data.bit(0) = io.sprite.palette[1].bit(2); break;
|
||||
case 0x8107: data.bit(0) = io.sprite.palette[1].bit(3); break;
|
||||
|
||||
case 0x8108: break;
|
||||
case 0x8109: data.bit(0) = io.plane1.palette[0].bit(1); break;
|
||||
case 0x810a: data.bit(0) = io.plane1.palette[0].bit(2); break;
|
||||
case 0x810b: data.bit(0) = io.plane1.palette[0].bit(3); break;
|
||||
case 0x810c: break;
|
||||
case 0x810d: data.bit(0) = io.plane1.palette[1].bit(1); break;
|
||||
case 0x810e: data.bit(0) = io.plane1.palette[1].bit(2); break;
|
||||
case 0x810f: data.bit(0) = io.plane1.palette[1].bit(3); break;
|
||||
|
||||
case 0x8110: break;
|
||||
case 0x8111: data.bit(0) = io.plane2.palette[0].bit(1); break;
|
||||
case 0x8112: data.bit(0) = io.plane2.palette[0].bit(2); break;
|
||||
case 0x8113: data.bit(0) = io.plane2.palette[0].bit(3); break;
|
||||
case 0x8114: break;
|
||||
case 0x8115: data.bit(0) = io.plane2.palette[1].bit(1); break;
|
||||
case 0x8116: data.bit(0) = io.plane2.palette[1].bit(2); break;
|
||||
case 0x8117: data.bit(0) = io.plane2.palette[1].bit(3); break;
|
||||
|
||||
case 0x8400: data = io.led.control; break;
|
||||
case 0x8402: data = io.led.frequency; break;
|
||||
|
||||
case 0x87fe: data = 0x3f; break; //input port register (reserved)
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto VPU::write(uint24 address, uint8 data) -> void {
|
||||
address = 0x8000 | (uint14)address;
|
||||
if(address >= 0x8800 && address <= 0x88ff) return spriteRAM.write(address, data);
|
||||
if(address >= 0x8900 && address <= 0x8fff) return; //todo: does spriteRAM mirror here?
|
||||
if(address >= 0x9000 && address <= 0x9fff) return scrollRAM.write(address, data);
|
||||
if(address >= 0xa000 && address <= 0xbfff) return characterRAM.write(address, data);
|
||||
|
||||
switch(address) {
|
||||
case 0x8000:
|
||||
io.hblankEnableIRQ = data.bit(6);
|
||||
io.vblankEnableIRQ = data.bit(7);
|
||||
cpu.setInterruptHblank(io.hblankActive & io.hblankEnableIRQ);
|
||||
cpu.setInterruptVblank(io.vblankActive & io.vblankEnableIRQ);
|
||||
break;
|
||||
|
||||
case 0x8002: io.window.horigin = data; break;
|
||||
case 0x8003: io.window.vorigin = data; break;
|
||||
case 0x8004: io.window.hsize = data; break;
|
||||
case 0x8005: io.window.vsize = data; break;
|
||||
|
||||
case 0x8012:
|
||||
io.outsideWindowColor = data.bits(0,2);
|
||||
io.negateScreen = data.bit(7);
|
||||
break;
|
||||
|
||||
case 0x8020: io.sprite.hoffset = data; break;
|
||||
case 0x8021: io.sprite.voffset = data; break;
|
||||
|
||||
case 0x8030: io.planePriority = data.bit(7); break;
|
||||
|
||||
case 0x8032: io.plane1.hscroll = data; break;
|
||||
case 0x8033: io.plane1.vscroll = data; break;
|
||||
case 0x8034: io.plane2.hscroll = data; break;
|
||||
case 0x8035: io.plane2.vscroll = data; break;
|
||||
|
||||
case 0x8100: break;
|
||||
case 0x8101: io.sprite.palette[0].bit(1) = data.bit(0); break;
|
||||
case 0x8102: io.sprite.palette[0].bit(2) = data.bit(0); break;
|
||||
case 0x8103: io.sprite.palette[0].bit(3) = data.bit(0); break;
|
||||
case 0x8104: break;
|
||||
case 0x8105: io.sprite.palette[1].bit(1) = data.bit(0); break;
|
||||
case 0x8106: io.sprite.palette[1].bit(2) = data.bit(0); break;
|
||||
case 0x8107: io.sprite.palette[1].bit(3) = data.bit(0); break;
|
||||
|
||||
case 0x8108: break;
|
||||
case 0x8109: io.plane1.palette[0].bit(1) = data.bit(0); break;
|
||||
case 0x810a: io.plane1.palette[0].bit(2) = data.bit(0); break;
|
||||
case 0x810b: io.plane1.palette[0].bit(3) = data.bit(0); break;
|
||||
case 0x810c: break;
|
||||
case 0x810d: io.plane1.palette[1].bit(1) = data.bit(0); break;
|
||||
case 0x810e: io.plane1.palette[1].bit(2) = data.bit(0); break;
|
||||
case 0x810f: io.plane1.palette[1].bit(3) = data.bit(0); break;
|
||||
|
||||
case 0x8110: break;
|
||||
case 0x8111: io.plane2.palette[0].bit(1) = data.bit(0); break;
|
||||
case 0x8112: io.plane2.palette[0].bit(2) = data.bit(0); break;
|
||||
case 0x8113: io.plane2.palette[0].bit(3) = data.bit(0); break;
|
||||
case 0x8114: break;
|
||||
case 0x8115: io.plane2.palette[1].bit(1) = data.bit(0); break;
|
||||
case 0x8116: io.plane2.palette[1].bit(2) = data.bit(0); break;
|
||||
case 0x8117: io.plane2.palette[1].bit(3) = data.bit(0); break;
|
||||
|
||||
case 0x8400: io.led.control.bits(3,7) = data.bits(3,7); break;
|
||||
case 0x8402: io.led.frequency = data; break;
|
||||
|
||||
case 0x87e0:
|
||||
if(data == 0x52) io = {};
|
||||
break;
|
||||
}
|
||||
}
|
@ -3,36 +3,42 @@
|
||||
namespace higan::NeoGeoPocket {
|
||||
|
||||
VPU vpu;
|
||||
#include "memory.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto VPU::main() -> void {
|
||||
cpu.setInterruptHblank(0);
|
||||
for(uint hclock : range(480)) {
|
||||
io.hcounter++;
|
||||
step(1);
|
||||
}
|
||||
if(io.vcounter <= 150) {
|
||||
if(ram[0x0000].bit(6)) cpu.setInterruptHblank(1);
|
||||
io.hblankActive = 1;
|
||||
cpu.setInterruptHblank(io.hblankEnableIRQ);
|
||||
}
|
||||
for(uint hclock : range(35)) {
|
||||
io.hcounter++;
|
||||
step(1);
|
||||
}
|
||||
cpu.setInterruptHblank(0);
|
||||
|
||||
io.hcounter = 0;
|
||||
io.hblankActive = 0;
|
||||
cpu.setInterruptHblank(0);
|
||||
|
||||
io.vcounter++;
|
||||
if(io.vcounter == 152) {
|
||||
ram[0x0010].bit(6) = 1;
|
||||
if(ram[0x0000].bit(7)) cpu.setInterruptVblank(1);
|
||||
io.vblankActive = 1;
|
||||
cpu.setInterruptVblank(io.vblankEnableIRQ);
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
if(io.vcounter == 198) {
|
||||
if(ram[0x0000].bit(6)) cpu.setInterruptHblank(1);
|
||||
io.hblankActive = 1;
|
||||
cpu.setInterruptHblank(io.hblankEnableIRQ);
|
||||
}
|
||||
if(io.vcounter == 199) {
|
||||
ram[0x0010].bit(6) = 0;
|
||||
cpu.setInterruptVblank(0);
|
||||
io.vcounter = 0;
|
||||
io.vblankActive = 0;
|
||||
io.characterOver = 0;
|
||||
cpu.setInterruptVblank(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,8 +48,9 @@ auto VPU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto VPU::refresh() -> void {
|
||||
for(uint address : range(0x4000)) buffer[address] = ram[address];
|
||||
for(uint address : range(0x1f00)) buffer[address + 0x4000] = cpu.ram[address + 0x3000 - 0x1f00];
|
||||
for(uint address : range(0x1000)) buffer[address] = scrollRAM[address];
|
||||
for(uint address : range(0x2000)) buffer[address + 0x1000] = characterRAM[address];
|
||||
for(uint address : range(0x2f00)) buffer[address + 0x3000] = cpu.ram[address + 0x100];
|
||||
display.screen->refresh(buffer, 160 * sizeof(uint32), 160, 152);
|
||||
}
|
||||
|
||||
@ -51,7 +58,9 @@ auto VPU::power() -> void {
|
||||
Thread::create(system.frequency(), [&] {
|
||||
while(true) scheduler.synchronize(), main();
|
||||
});
|
||||
ram.allocate(0x4000);
|
||||
spriteRAM.allocate(0x100);
|
||||
scrollRAM.allocate(0x1000);
|
||||
characterRAM.allocate(0x2000);
|
||||
io = {};
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
//K2GE (Neo Geo Pocket Color)
|
||||
|
||||
struct VPU : Thread {
|
||||
Memory::Writable<uint8> ram;
|
||||
Memory::Writable<uint8> spriteRAM;
|
||||
Memory::Writable<uint8> scrollRAM;
|
||||
Memory::Writable<uint8> characterRAM;
|
||||
|
||||
//vpu.cpp
|
||||
auto main() -> void;
|
||||
@ -10,15 +12,55 @@ struct VPU : Thread {
|
||||
auto refresh() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto read(uint24 address) -> uint8;
|
||||
auto write(uint24 address, uint8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
//private:
|
||||
uint32 buffer[160 * 152];
|
||||
|
||||
struct IO {
|
||||
uint8 vcounter;
|
||||
uint10 hcounter;
|
||||
|
||||
uint1 hblankEnableIRQ = 1; //todo: should be 0
|
||||
uint1 vblankEnableIRQ = 1; //todo: should be 0
|
||||
uint1 hblankActive;
|
||||
uint1 vblankActive;
|
||||
|
||||
uint3 outsideWindowColor;
|
||||
uint1 negateScreen;
|
||||
|
||||
uint1 characterOver;
|
||||
|
||||
uint1 planePriority; //0 = plane1 > plane2; 1 = plane2 > plane1
|
||||
|
||||
struct Plane {
|
||||
uint8 hscroll;
|
||||
uint8 vscroll;
|
||||
uint4 palette[2];
|
||||
} plane1, plane2;
|
||||
|
||||
struct Sprite {
|
||||
uint8 hoffset;
|
||||
uint8 voffset;
|
||||
uint4 palette[2];
|
||||
} sprite;
|
||||
|
||||
struct Window {
|
||||
uint8 horigin;
|
||||
uint8 vorigin;
|
||||
uint8 hsize;
|
||||
uint8 vsize;
|
||||
} window;
|
||||
|
||||
struct LED {
|
||||
uint8 control = 0x07;
|
||||
uint8 frequency = 0x80;
|
||||
} led;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
@ -57,6 +57,8 @@ auto Emulator::create(shared_pointer<higan::Interface> instance, string location
|
||||
}
|
||||
|
||||
auto Emulator::main() -> void {
|
||||
if(Application::modal()) return (void)usleep(20 * 1000);
|
||||
|
||||
inputManager.poll();
|
||||
hotkeys.poll();
|
||||
|
||||
|
@ -5,7 +5,6 @@ namespace nall {
|
||||
auto image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) -> void {
|
||||
source.transform(_endian, _depth, _alpha.mask(), _red.mask(), _green.mask(), _blue.mask());
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < sourceHeight; y++) {
|
||||
const uint8_t* sp = source._data + source.pitch() * (sourceY + y) + source.stride() * sourceX;
|
||||
uint8_t* dp = _data + pitch() * (targetY + y) + stride() * targetX;
|
||||
|
@ -27,7 +27,6 @@ auto image::scaleLinearWidth(unsigned outputWidth) -> void {
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
uint64_t xstride = ((uint64_t)(_width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint64_t xfraction = 0;
|
||||
|
||||
@ -63,7 +62,6 @@ auto image::scaleLinearHeight(unsigned outputHeight) -> void {
|
||||
uint8_t* outputData = allocate(_width, outputHeight, stride());
|
||||
uint64_t ystride = ((uint64_t)(_height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
uint64_t yfraction = 0;
|
||||
|
||||
@ -102,7 +100,6 @@ auto image::scaleLinear(unsigned outputWidth, unsigned outputHeight) -> void {
|
||||
uint64_t xstride = ((uint64_t)(_width - 1) << 32) / max(1u, outputWidth - 1);
|
||||
uint64_t ystride = ((uint64_t)(_height - 1) << 32) / max(1u, outputHeight - 1);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
uint64_t yfraction = ystride * y;
|
||||
uint64_t xfraction = 0;
|
||||
@ -147,7 +144,6 @@ auto image::scaleNearest(unsigned outputWidth, unsigned outputHeight) -> void {
|
||||
uint64_t xstride = ((uint64_t)_width << 32) / outputWidth;
|
||||
uint64_t ystride = ((uint64_t)_height << 32) / outputHeight;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
uint64_t yfraction = ystride * y;
|
||||
uint64_t xfraction = 0;
|
||||
|
@ -74,7 +74,6 @@ auto image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsig
|
||||
uint8_t* outputData = allocate(outputWidth, outputHeight, stride());
|
||||
unsigned outputPitch = outputWidth * stride();
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
const uint8_t* sp = _data + pitch() * (outputY + y) + stride() * outputX;
|
||||
uint8_t* dp = outputData + outputPitch * y;
|
||||
@ -97,7 +96,6 @@ auto image::alphaBlend(uint64_t alphaColor) -> void {
|
||||
uint64_t alphaG = (alphaColor & _green.mask()) >> _green.shift();
|
||||
uint64_t alphaB = (alphaColor & _blue.mask() ) >> _blue.shift();
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
@ -123,7 +121,6 @@ auto image::alphaBlend(uint64_t alphaColor) -> void {
|
||||
auto image::alphaMultiply() -> void {
|
||||
unsigned divisor = (1 << _alpha.depth()) - 1;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
uint8_t* dp = _data + pitch() * y;
|
||||
for(unsigned x = 0; x < _width; x++) {
|
||||
@ -154,7 +151,6 @@ auto image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAl
|
||||
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
||||
output.allocate(_width, _height);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < _height; y++) {
|
||||
const uint8_t* sp = _data + pitch() * y;
|
||||
uint8_t* dp = output._data + output.pitch() * y;
|
||||
|
@ -1,7 +1,7 @@
|
||||
ifeq ($(ruby),)
|
||||
ifeq ($(platform),windows)
|
||||
ruby += video.wgl video.direct3d video.directdraw video.gdi
|
||||
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound
|
||||
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound audio.waveout
|
||||
ruby += input.windows
|
||||
else ifeq ($(platform),macos)
|
||||
ruby += video.cgl
|
||||
@ -42,6 +42,7 @@ ruby.options += $(if $(findstring audio.directsound,$(ruby)),-ldsound -luuid)
|
||||
ruby.options += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse)
|
||||
ruby.options += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
|
||||
ruby.options += $(if $(findstring audio.wasapi,$(ruby)),-lavrt -luuid)
|
||||
ruby.options += $(if $(findstring audio.waveout,$(ruby)),-lwinmm)
|
||||
ruby.options += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
|
||||
|
||||
ruby.options += $(if $(findstring input.sdl,$(ruby)),$(shell sdl2-config --libs))
|
||||
|
@ -34,6 +34,10 @@
|
||||
#include <ruby/audio/wasapi.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
#include <ruby/audio/waveout.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
#include <ruby/audio/xaudio2.cpp>
|
||||
#endif
|
||||
@ -174,6 +178,10 @@ auto Audio::create(string driver) -> bool {
|
||||
if(driver == "WASAPI") self.instance = new AudioWASAPI(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
if(driver == "waveOut") self.instance = new AudioWaveOut(*this);
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_XAUDIO2)
|
||||
if(driver == "XAudio 2.1") self.instance = new AudioXAudio2(*this);
|
||||
#endif
|
||||
@ -202,6 +210,10 @@ auto Audio::hasDrivers() -> vector<string> {
|
||||
"DirectSound 7.0",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
"waveOut",
|
||||
#endif
|
||||
|
||||
#if defined(AUDIO_ALSA)
|
||||
"ALSA",
|
||||
#endif
|
||||
@ -238,6 +250,8 @@ auto Audio::optimalDriver() -> string {
|
||||
return "XAudio 2.1";
|
||||
#elif defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound 7.0";
|
||||
#elif defined(AUDIO_WAVEOUT)
|
||||
return "waveOut";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
@ -256,7 +270,9 @@ auto Audio::optimalDriver() -> string {
|
||||
}
|
||||
|
||||
auto Audio::safestDriver() -> string {
|
||||
#if defined(AUDIO_DIRECTSOUND)
|
||||
#if defined(AUDIO_WAVEOUT)
|
||||
return "waveOut";
|
||||
#elif defined(AUDIO_DIRECTSOUND)
|
||||
return "DirectSound 7.0";
|
||||
#elif defined(AUDIO_WASAPI)
|
||||
return "WASAPI";
|
||||
|
124
ruby/audio/waveout.cpp
Normal file
124
ruby/audio/waveout.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include <mmsystem.h>
|
||||
|
||||
auto CALLBACK waveOutCallback(HWAVEOUT handle, UINT message, DWORD_PTR userData, DWORD_PTR, DWORD_PTR) -> void;
|
||||
|
||||
struct AudioWaveOut : AudioDriver {
|
||||
AudioWaveOut& self = *this;
|
||||
AudioWaveOut(Audio& super) : AudioDriver(super) {}
|
||||
~AudioWaveOut() { terminate(); }
|
||||
|
||||
auto create() -> bool override {
|
||||
super.setChannels(2);
|
||||
super.setFrequency(44100);
|
||||
super.setLatency(0);
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "waveOut"; }
|
||||
auto ready() -> bool override { return true; }
|
||||
|
||||
auto hasDevices() -> vector<string> override {
|
||||
vector<string> devices{"Default"};
|
||||
for(uint index : range(waveOutGetNumDevs())) {
|
||||
WAVEOUTCAPS caps{};
|
||||
if(waveOutGetDevCaps(index, &caps, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR) {
|
||||
devices.append((const char*)utf8_t(caps.szPname));
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasFrequencies() -> vector<uint> override { return {44100}; }
|
||||
auto hasLatencies() -> vector<uint> override { return {0}; }
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override { return true; }
|
||||
|
||||
auto clear() -> void override {
|
||||
for(auto& header : headers) {
|
||||
memory::fill(header.lpData, frameCount * 4);
|
||||
}
|
||||
}
|
||||
|
||||
auto output(const double samples[]) -> void override {
|
||||
uint16_t lsample = sclamp<16>(samples[0] * 32767.0);
|
||||
uint16_t rsample = sclamp<16>(samples[1] * 32767.0);
|
||||
|
||||
auto block = (uint32_t*)headers[blockIndex].lpData;
|
||||
block[frameIndex] = lsample << 0 | rsample << 16;
|
||||
|
||||
if(++frameIndex >= frameCount) {
|
||||
frameIndex = 0;
|
||||
while(true) {
|
||||
auto result = waveOutWrite(handle, &headers[blockIndex], sizeof(WAVEHDR));
|
||||
if(!self.blocking || result != WAVERR_STILLPLAYING) break;
|
||||
InterlockedIncrement(&blockQueue);
|
||||
}
|
||||
if(++blockIndex >= blockCount) {
|
||||
blockIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
|
||||
auto deviceIndex = hasDevices().find(self.device);
|
||||
if(!deviceIndex) deviceIndex = 0;
|
||||
|
||||
WAVEFORMATEX format{};
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.nChannels = 2;
|
||||
format.nSamplesPerSec = 44100;
|
||||
format.nBlockAlign = 4;
|
||||
format.wBitsPerSample = 16;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0; //not sizeof(WAVEFORMAT); size of extra information after WAVEFORMATEX
|
||||
//-1 = default; 0+ = specific device; subtract -1 as hasDevices() includes "Default" entry
|
||||
waveOutOpen(&handle, (int)*deviceIndex - 1, &format, (DWORD_PTR)waveOutCallback, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
|
||||
headers.resize(blockCount);
|
||||
for(auto& header : headers) {
|
||||
memory::fill(&header, sizeof(WAVEHDR));
|
||||
header.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, frameCount * 4);
|
||||
header.dwBufferLength = frameCount * 4;
|
||||
waveOutPrepareHeader(handle, &header, sizeof(WAVEHDR));
|
||||
}
|
||||
|
||||
frameIndex = 0;
|
||||
blockIndex = 0;
|
||||
|
||||
waveOutSetVolume(handle, 0xffff); //100% volume (255 left, 255 right)
|
||||
waveOutRestart(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
if(!handle) return;
|
||||
waveOutPause(handle);
|
||||
waveOutReset(handle);
|
||||
for(auto& header : headers) {
|
||||
waveOutUnprepareHeader(handle, &header, sizeof(WAVEHDR));
|
||||
LocalFree(header.lpData);
|
||||
}
|
||||
waveOutClose(handle);
|
||||
handle = nullptr;
|
||||
headers.reset();
|
||||
}
|
||||
|
||||
HWAVEOUT handle = nullptr;
|
||||
vector<WAVEHDR> headers;
|
||||
const uint frameCount = 256;
|
||||
const uint blockCount = 8;
|
||||
uint frameIndex = 0;
|
||||
uint blockIndex = 0;
|
||||
|
||||
public:
|
||||
LONG blockQueue = 0;
|
||||
};
|
||||
|
||||
auto CALLBACK waveOutCallback(HWAVEOUT handle, UINT message, DWORD_PTR userData, DWORD_PTR, DWORD_PTR) -> void {
|
||||
auto instance = (AudioWaveOut*)userData;
|
||||
InterlockedDecrement(&instance->blockQueue);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user