mirror of https://github.com/bsnes-emu/bsnes.git
Update to v102r11 release.
byuu says: Changelog: - MD: connected 32KB cartridge RAM up to every Genesis game under 2MB loaded¹ - MS, GG, MD: improved PSG noise channel emulation, hopefully² - MS, GG, MD: lowered PSG volume so that the lowpass doesn't clamp samples³ - MD: added read/write handlers for VRAM, VSRAM, CRAM - MD: block VRAM copy when CD4 is clear⁴ - MD: rewrote VRAM fill, VRAM copy to be byte-based⁵ - MD: VRAM fill byte set should fall through to regular data port write handler⁶ ¹: the header parsing for backup RAM is really weird. It's spaces when not used, and seems to be 0x02000001-0x02003fff for the Shining games. I don't understand why it starts at 0x02000001 instead of 0x02000000. So I'm just forcing every game to have 32KB of RAM for now. There's also special handling for ROMs > 2MB that also have RAM (Phantasy Star IV, etc) where there's a toggle to switch between ROM and RAM. For now, that's not emulated. I was hoping the Shining games would run after this, but they're still dead-locking on me :( ²: Cydrak pointed out some flaws in my attempt to implement what he had. I was having trouble understanding what he meant, so I went back and read the docs on the sound chip and tried implementing the counter the way the docs describe. Hopefully I have this right, but I don't know of any good test ROMs to make sure my noise emulation is correct. The docs say the shifted-out value goes to the output instead of the low bit of the LFSR, so I made that change as well. I think I hear the noise I'm supposed to in Sonic Marble Zone now, but it seems like it's not correct in Green Hill Zone, adding a bit of an annoying buzz to the background music. Maybe it sounds better with the YM2612, but more likely, I still screwed something up :/ ³: it's set to 50% range for both cores right now. For the MD, it will need to be 25% once YM2612 emulation is in. ⁴: technically, this deadlocks the VDP until a hard reset. I could emulate this, but for now I just don't do the VRAM copy in this case. ⁵: VSRAM fill and CRAM fill not supported in this new mode. They're technically undocumented, and I don't have good notes on how they work. I've been seeing conflicting notes on whether the VRAM fill buffer is 8-bits or 16-bits (I chose 8-bits), and on whether you write the low byte and then high byte of each words, or the high byte and then low byte (I chose the latter.) The VRAM copy improvements fix the opening text in Langrisser II, so that's great. ⁶: Langrisser II sets the transfer length to one less than needed to fill the background letter tile on the scenario overview screen. After moving to byte-sized transfers, a black pixel was getting stuck there. So effectively, VRAM fill length becomes DMA length + 1, and the first byte uses the data port so it writes a word value instead of just a byte value. Hopefully this is all correct, although it probably gets way more complicated with the VDP FIFO.
This commit is contained in:
parent
68f04c3bb8
commit
1cab2dfeb8
|
@ -12,7 +12,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "102.10";
|
static const string Version = "102.11";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -60,19 +60,28 @@ auto Cartridge::save() -> void {
|
||||||
auto Cartridge::unload() -> void {
|
auto Cartridge::unload() -> void {
|
||||||
delete[] rom.data;
|
delete[] rom.data;
|
||||||
delete[] ram.data;
|
delete[] ram.data;
|
||||||
rom = Memory();
|
rom = {};
|
||||||
ram = Memory();
|
ram = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::power() -> void {
|
auto Cartridge::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::read(uint24 addr) -> uint16 {
|
auto Cartridge::read(uint24 addr) -> uint16 {
|
||||||
uint16 data = rom.data[addr + 0 & rom.mask] << 8;
|
if(addr.bit(21) && ram.size) {
|
||||||
return data | rom.data[addr + 1 & rom.mask] << 0;
|
uint16 data = ram.data[addr + 0 & ram.mask] << 8;
|
||||||
|
return data | ram.data[addr + 1 & ram.mask] << 0;
|
||||||
|
} else {
|
||||||
|
uint16 data = rom.data[addr + 0 & rom.mask] << 8;
|
||||||
|
return data | rom.data[addr + 1 & rom.mask] << 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::write(uint24 addr, uint16 data) -> void {
|
auto Cartridge::write(uint24 addr, uint16 data) -> void {
|
||||||
|
if(addr.bit(21) && ram.size) {
|
||||||
|
ram.data[addr + 0 & ram.mask] = data >> 8;
|
||||||
|
ram.data[addr + 1 & ram.mask] = data >> 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ auto PSG::write(uint8 data) -> void {
|
||||||
case 4: {
|
case 4: {
|
||||||
if(l) tone2.pitch.bits(0,3) = data.bits(0,3);
|
if(l) tone2.pitch.bits(0,3) = data.bits(0,3);
|
||||||
else tone2.pitch.bits(4,9) = data.bits(0,5);
|
else tone2.pitch.bits(4,9) = data.bits(0,5);
|
||||||
|
noise.pitch = tone2.pitch;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
auto PSG::Noise::run() -> void {
|
auto PSG::Noise::run() -> void {
|
||||||
auto latch = clock;
|
if(--counter) return;
|
||||||
|
|
||||||
counter++;
|
if(rate == 0) counter = 0x10;
|
||||||
if(rate == 0) output ^= !counter.bits(0,3);
|
if(rate == 1) counter = 0x20;
|
||||||
if(rate == 1) output ^= !counter.bits(0,4);
|
if(rate == 2) counter = 0x40;
|
||||||
if(rate == 2) output ^= !counter.bits(0,5);
|
if(rate == 3) counter = pitch; //shared with tone2
|
||||||
if(rate == 3) output ^= psg.tone2.clock;
|
|
||||||
|
|
||||||
if(!latch && clock) {
|
if(clock ^= 1) { //0->1 transition
|
||||||
|
output = lfsr.bit(0);
|
||||||
auto eor = enable ? ~lfsr >> 3 : 0;
|
auto eor = enable ? ~lfsr >> 3 : 0;
|
||||||
lfsr = (lfsr ^ eor) << 15 | lfsr >> 1;
|
lfsr = (lfsr ^ eor) << 15 | lfsr >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
output = lfsr.bit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PSG::Noise::power() -> void {
|
auto PSG::Noise::power() -> void {
|
||||||
volume = ~0;
|
volume = ~0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
pitch = 0;
|
||||||
enable = 0;
|
enable = 0;
|
||||||
rate = 0;
|
rate = 0;
|
||||||
lfsr = 0x8000;
|
lfsr = 0x8000;
|
||||||
|
|
|
@ -43,7 +43,7 @@ auto PSG::power() -> void {
|
||||||
select = 0;
|
select = 0;
|
||||||
lowpass = 0;
|
lowpass = 0;
|
||||||
for(auto n : range(15)) {
|
for(auto n : range(15)) {
|
||||||
levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5;
|
levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5;
|
||||||
}
|
}
|
||||||
levels[15] = 0;
|
levels[15] = 0;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ private:
|
||||||
uint4 volume;
|
uint4 volume;
|
||||||
uint10 counter;
|
uint10 counter;
|
||||||
uint10 pitch;
|
uint10 pitch;
|
||||||
uint1 clock;
|
|
||||||
uint1 output;
|
uint1 output;
|
||||||
} tone0, tone1, tone2;
|
} tone0, tone1, tone2;
|
||||||
|
|
||||||
|
@ -31,7 +30,8 @@ private:
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
uint4 volume;
|
uint4 volume;
|
||||||
uint6 counter;
|
uint10 counter;
|
||||||
|
uint10 pitch;
|
||||||
uint1 enable;
|
uint1 enable;
|
||||||
uint2 rate;
|
uint2 rate;
|
||||||
uint16 lfsr;
|
uint16 lfsr;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
auto PSG::Tone::run() -> void {
|
auto PSG::Tone::run() -> void {
|
||||||
clock = 0;
|
|
||||||
if(--counter) return;
|
if(--counter) return;
|
||||||
|
|
||||||
clock = 1;
|
|
||||||
counter = pitch;
|
counter = pitch;
|
||||||
output ^= 1;
|
output ^= 1;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +9,5 @@ auto PSG::Tone::power() -> void {
|
||||||
volume = ~0;
|
volume = ~0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
pitch = 0;
|
pitch = 0;
|
||||||
clock = 0;
|
|
||||||
output = 0;
|
output = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ auto VDP::Background::updateHorizontalScroll(uint y) -> void {
|
||||||
address += (y & mask[io.horizontalScrollMode]) << 1;
|
address += (y & mask[io.horizontalScrollMode]) << 1;
|
||||||
address += id == ID::PlaneB;
|
address += id == ID::PlaneB;
|
||||||
|
|
||||||
state.horizontalScroll = vdp.vram[address].bits(0,9);
|
state.horizontalScroll = vdp.vram.read(address).bits(0,9);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void {
|
auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void {
|
||||||
|
@ -22,7 +22,7 @@ auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void {
|
||||||
auto address = (x >> 4 & 0 - io.verticalScrollMode) << 1;
|
auto address = (x >> 4 & 0 - io.verticalScrollMode) << 1;
|
||||||
address += id == ID::PlaneB;
|
address += id == ID::PlaneB;
|
||||||
|
|
||||||
state.verticalScroll = vdp.vsram[address];
|
state.verticalScroll = vdp.vsram.read(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::Background::nametableAddress() -> uint15 {
|
auto VDP::Background::nametableAddress() -> uint15 {
|
||||||
|
@ -62,13 +62,13 @@ auto VDP::Background::run(uint x, uint y) -> void {
|
||||||
auto address = nametableAddress();
|
auto address = nametableAddress();
|
||||||
address += (tileY * width + tileX) & 0x0fff;
|
address += (tileY * width + tileX) & 0x0fff;
|
||||||
|
|
||||||
uint16 tileAttributes = vdp.vram[address];
|
uint16 tileAttributes = vdp.vram.read(address);
|
||||||
uint15 tileAddress = tileAttributes.bits(0,10) << 4;
|
uint15 tileAddress = tileAttributes.bits(0,10) << 4;
|
||||||
uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0);
|
uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0);
|
||||||
uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0);
|
uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0);
|
||||||
tileAddress += pixelY << 1 | pixelX >> 2;
|
tileAddress += pixelY << 1 | pixelX >> 2;
|
||||||
|
|
||||||
uint16 tileData = vdp.vram[tileAddress];
|
uint16 tileData = vdp.vram.read(tileAddress);
|
||||||
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
||||||
if(color) {
|
if(color) {
|
||||||
output.color = tileAttributes.bits(13,14) << 4 | color;
|
output.color = tileAttributes.bits(13,14) << 4 | color;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
auto VDP::DMA::run() -> void {
|
auto VDP::DMA::run() -> void {
|
||||||
if(!io.enable || io.wait) return;
|
if(!io.enable || io.wait) return;
|
||||||
if(!vdp.io.command.bit(5)) return;
|
|
||||||
|
|
||||||
|
if(!vdp.io.command.bit(5)) return;
|
||||||
if(io.mode <= 1) return load();
|
if(io.mode <= 1) return load();
|
||||||
if(io.mode == 2) return fill();
|
if(io.mode == 2) return fill();
|
||||||
|
if(!vdp.io.command.bit(4)) return;
|
||||||
if(io.mode == 3) return copy();
|
if(io.mode == 3) return copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,21 +21,26 @@ auto VDP::DMA::load() -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//todo: supposedly, this can also write to VSRAM and CRAM (undocumented)
|
||||||
auto VDP::DMA::fill() -> void {
|
auto VDP::DMA::fill() -> void {
|
||||||
auto data = io.fill;
|
if(vdp.io.command.bits(0,3) == 1) {
|
||||||
vdp.writeDataPort(data << 8 | data << 0);
|
vdp.vram.writeByte(vdp.io.address, io.fill);
|
||||||
|
}
|
||||||
|
|
||||||
io.source.bits(0,15)++;
|
io.source.bits(0,15)++;
|
||||||
|
vdp.io.address += vdp.io.dataIncrement;
|
||||||
if(--io.length == 0) {
|
if(--io.length == 0) {
|
||||||
vdp.io.command.bit(5) = 0;
|
vdp.io.command.bit(5) = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//note: this can only copy to VRAM
|
||||||
auto VDP::DMA::copy() -> void {
|
auto VDP::DMA::copy() -> void {
|
||||||
auto data = vdp.vram[io.source.bits(0,14)];
|
auto data = vdp.vram.readByte(io.source);
|
||||||
vdp.writeDataPort(data);
|
vdp.vram.writeByte(vdp.io.address, data);
|
||||||
|
|
||||||
io.source.bits(0,15)++;
|
io.source.bits(0,15)++;
|
||||||
|
vdp.io.address += vdp.io.dataIncrement;
|
||||||
if(--io.length == 0) {
|
if(--io.length == 0) {
|
||||||
vdp.io.command.bit(5) = 0;
|
vdp.io.command.bit(5) = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ auto VDP::readDataPort() -> uint16 {
|
||||||
//VRAM read
|
//VRAM read
|
||||||
if(io.command.bits(0,3) == 0) {
|
if(io.command.bits(0,3) == 0) {
|
||||||
auto address = io.address.bits(1,15);
|
auto address = io.address.bits(1,15);
|
||||||
auto data = vram[address];
|
auto data = vram.read(address);
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,7 @@ auto VDP::readDataPort() -> uint16 {
|
||||||
//VSRAM read
|
//VSRAM read
|
||||||
if(io.command.bits(0,3) == 4) {
|
if(io.command.bits(0,3) == 4) {
|
||||||
auto address = io.address.bits(1,6);
|
auto address = io.address.bits(1,6);
|
||||||
if(address >= 40) return 0x0000;
|
auto data = vsram.read(address);
|
||||||
auto data = vsram[address];
|
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,7 @@ auto VDP::readDataPort() -> uint16 {
|
||||||
//CRAM read
|
//CRAM read
|
||||||
if(io.command.bits(0,3) == 8) {
|
if(io.command.bits(0,3) == 8) {
|
||||||
auto address = io.address.bits(1,6);
|
auto address = io.address.bits(1,6);
|
||||||
auto data = cram[address];
|
auto data = cram.read(address);
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return data.bits(0,2) << 1 | data.bits(3,5) << 2 | data.bits(6,8) << 3;
|
return data.bits(0,2) << 1 | data.bits(3,5) << 2 | data.bits(6,8) << 3;
|
||||||
}
|
}
|
||||||
|
@ -76,17 +75,15 @@ auto VDP::writeDataPort(uint16 data) -> void {
|
||||||
//DMA VRAM fill
|
//DMA VRAM fill
|
||||||
if(dma.io.wait.lower()) {
|
if(dma.io.wait.lower()) {
|
||||||
dma.io.fill = data >> 8;
|
dma.io.fill = data >> 8;
|
||||||
return;
|
//falls through to memory write
|
||||||
|
//causes extra transfer to occur on VRAM fill operations
|
||||||
}
|
}
|
||||||
|
|
||||||
//VRAM write
|
//VRAM write
|
||||||
if(io.command.bits(0,3) == 1) {
|
if(io.command.bits(0,3) == 1) {
|
||||||
auto address = io.address.bits(1,15);
|
auto address = io.address.bits(1,15);
|
||||||
if(io.address.bit(0)) data = data >> 8 | data << 8;
|
if(io.address.bit(0)) data = data >> 8 | data << 8;
|
||||||
vram[address] = data;
|
vram.write(address, data);
|
||||||
if(address >= sprite.io.attributeAddress && address < sprite.io.attributeAddress + 320) {
|
|
||||||
sprite.write(address, data);
|
|
||||||
}
|
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -94,9 +91,8 @@ auto VDP::writeDataPort(uint16 data) -> void {
|
||||||
//VSRAM write
|
//VSRAM write
|
||||||
if(io.command.bits(0,3) == 5) {
|
if(io.command.bits(0,3) == 5) {
|
||||||
auto address = io.address.bits(1,6);
|
auto address = io.address.bits(1,6);
|
||||||
if(address >= 40) return;
|
|
||||||
//data format: ---- --yy yyyy yyyy
|
//data format: ---- --yy yyyy yyyy
|
||||||
vsram[address] = data.bits(0,9);
|
vsram.write(address, data.bits(0,9));
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +101,7 @@ auto VDP::writeDataPort(uint16 data) -> void {
|
||||||
if(io.command.bits(0,3) == 3) {
|
if(io.command.bits(0,3) == 3) {
|
||||||
auto address = io.address.bits(1,6);
|
auto address = io.address.bits(1,6);
|
||||||
//data format: ---- bbb- ggg- rrr-
|
//data format: ---- bbb- ggg- rrr-
|
||||||
cram[address] = data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6;
|
cram.write(address, data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6);
|
||||||
io.address += io.dataIncrement;
|
io.address += io.dataIncrement;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
auto VDP::VRAM::read(uint15 address) const -> uint16 {
|
||||||
|
return memory[address];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::VRAM::write(uint15 address, uint16 data) -> void {
|
||||||
|
memory[address] = data;
|
||||||
|
if(address < vdp.sprite.io.attributeAddress) return;
|
||||||
|
if(address > vdp.sprite.io.attributeAddress + 319) return;
|
||||||
|
vdp.sprite.write(address, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::VRAM::readByte(uint16 address) const -> uint8 {
|
||||||
|
return read(address >> 1).byte(!address.bit(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::VRAM::writeByte(uint16 address, uint8 data) -> void {
|
||||||
|
auto word = read(address >> 1);
|
||||||
|
word.byte(!address.bit(0)) = data;
|
||||||
|
write(address >> 1, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::VSRAM::read(uint6 address) const -> uint10 {
|
||||||
|
if(address >= 40) return 0x0000;
|
||||||
|
return memory[address];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::VSRAM::write(uint6 address, uint10 data) -> void {
|
||||||
|
if(address >= 40) return;
|
||||||
|
memory[address] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::CRAM::read(uint6 address) const -> uint9 {
|
||||||
|
return memory[address];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VDP::CRAM::write(uint6 address, uint9 data) -> void {
|
||||||
|
memory[address] = data;
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ auto VDP::run() -> void {
|
||||||
if(planeA.output.priority) if(auto color = planeA.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;
|
if(sprite.output.priority) if(auto color = sprite.output.color) output = color;
|
||||||
|
|
||||||
outputPixel(cram[output]);
|
outputPixel(cram.read(output));
|
||||||
state.x++;
|
state.x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ auto VDP::Sprite::run(uint x, uint y) -> void {
|
||||||
uint pixelY = objectY & 7;
|
uint pixelY = objectY & 7;
|
||||||
tileAddress += pixelY << 1 | pixelX >> 2;
|
tileAddress += pixelY << 1 | pixelX >> 2;
|
||||||
|
|
||||||
uint16 tileData = vdp.vram[tileAddress];
|
uint16 tileData = vdp.vram.read(tileAddress);
|
||||||
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
|
||||||
if(color) {
|
if(color) {
|
||||||
output.color = o.palette << 4 | color;
|
output.color = o.palette << 4 | color;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace MegaDrive {
|
namespace MegaDrive {
|
||||||
|
|
||||||
VDP vdp;
|
VDP vdp;
|
||||||
|
#include "memory.cpp"
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
#include "dma.cpp"
|
#include "dma.cpp"
|
||||||
#include "render.cpp"
|
#include "render.cpp"
|
||||||
|
|
|
@ -131,9 +131,38 @@ private:
|
||||||
auto screenWidth() const -> uint { return io.tileWidth ? 320 : 256; }
|
auto screenWidth() const -> uint { return io.tileWidth ? 320 : 256; }
|
||||||
auto screenHeight() const -> uint { return io.overscan ? 240 : 224; }
|
auto screenHeight() const -> uint { return io.overscan ? 240 : 224; }
|
||||||
|
|
||||||
uint16 vram[32768];
|
//video RAM
|
||||||
uint9 cram[64];
|
struct VRAM {
|
||||||
uint10 vsram[40];
|
//memory.cpp
|
||||||
|
auto read(uint15 address) const -> uint16;
|
||||||
|
auto write(uint15 address, uint16 data) -> void;
|
||||||
|
|
||||||
|
auto readByte(uint16 address) const -> uint8;
|
||||||
|
auto writeByte(uint16 address, uint8 data) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16 memory[32768];
|
||||||
|
} vram;
|
||||||
|
|
||||||
|
//vertical scroll RAM
|
||||||
|
struct VSRAM {
|
||||||
|
//memory.cpp
|
||||||
|
auto read(uint6 address) const -> uint10;
|
||||||
|
auto write(uint6 address, uint10 data) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint10 memory[40];
|
||||||
|
} vsram;
|
||||||
|
|
||||||
|
//color RAM
|
||||||
|
struct CRAM {
|
||||||
|
//memory.cpp
|
||||||
|
auto read(uint6 address) const -> uint9;
|
||||||
|
auto write(uint6 address, uint9 data) -> void;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint9 memory[64];
|
||||||
|
} cram;
|
||||||
|
|
||||||
struct IO {
|
struct IO {
|
||||||
//command
|
//command
|
||||||
|
|
|
@ -29,6 +29,7 @@ auto PSG::write(uint8 data) -> void {
|
||||||
case 4: {
|
case 4: {
|
||||||
if(l) tone2.pitch.bits(0,3) = data.bits(0,3);
|
if(l) tone2.pitch.bits(0,3) = data.bits(0,3);
|
||||||
else tone2.pitch.bits(4,9) = data.bits(0,5);
|
else tone2.pitch.bits(4,9) = data.bits(0,5);
|
||||||
|
noise.pitch = tone2.pitch;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
auto PSG::Noise::run() -> void {
|
auto PSG::Noise::run() -> void {
|
||||||
auto latch = clock;
|
if(--counter) return;
|
||||||
|
|
||||||
counter++;
|
if(rate == 0) counter = 0x10;
|
||||||
if(rate == 0) output ^= !counter.bits(0,3);
|
if(rate == 1) counter = 0x20;
|
||||||
if(rate == 1) output ^= !counter.bits(0,4);
|
if(rate == 2) counter = 0x40;
|
||||||
if(rate == 2) output ^= !counter.bits(0,5);
|
if(rate == 3) counter = pitch; //shared with tone2
|
||||||
if(rate == 3) output ^= psg.tone2.clock;
|
|
||||||
|
|
||||||
if(!latch && clock) {
|
if(clock ^= 1) { //0->1 transition
|
||||||
|
output = lfsr.bit(0);
|
||||||
auto eor = enable ? ~lfsr >> 3 : 0;
|
auto eor = enable ? ~lfsr >> 3 : 0;
|
||||||
lfsr = (lfsr ^ eor) << 15 | lfsr >> 1;
|
lfsr = (lfsr ^ eor) << 15 | lfsr >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
output = lfsr.bit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PSG::Noise::power() -> void {
|
auto PSG::Noise::power() -> void {
|
||||||
volume = ~0;
|
volume = ~0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
pitch = 0;
|
||||||
enable = 0;
|
enable = 0;
|
||||||
rate = 0;
|
rate = 0;
|
||||||
lfsr = 0x8000;
|
lfsr = 0x8000;
|
||||||
|
|
|
@ -57,7 +57,7 @@ auto PSG::power() -> void {
|
||||||
lowpassLeft = 0;
|
lowpassLeft = 0;
|
||||||
lowpassRight = 0;
|
lowpassRight = 0;
|
||||||
for(auto n : range(15)) {
|
for(auto n : range(15)) {
|
||||||
levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5;
|
levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5;
|
||||||
}
|
}
|
||||||
levels[15] = 0;
|
levels[15] = 0;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ private:
|
||||||
uint4 volume;
|
uint4 volume;
|
||||||
uint10 counter;
|
uint10 counter;
|
||||||
uint10 pitch;
|
uint10 pitch;
|
||||||
uint1 clock;
|
|
||||||
uint1 output;
|
uint1 output;
|
||||||
|
|
||||||
uint1 left;
|
uint1 left;
|
||||||
|
@ -44,7 +43,8 @@ private:
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint4 volume;
|
uint4 volume;
|
||||||
uint6 counter;
|
uint10 counter;
|
||||||
|
uint10 pitch;
|
||||||
uint1 enable;
|
uint1 enable;
|
||||||
uint2 rate;
|
uint2 rate;
|
||||||
uint16 lfsr;
|
uint16 lfsr;
|
||||||
|
|
|
@ -16,7 +16,6 @@ auto PSG::Tone::serialize(serializer& s) -> void {
|
||||||
s.integer(volume);
|
s.integer(volume);
|
||||||
s.integer(counter);
|
s.integer(counter);
|
||||||
s.integer(pitch);
|
s.integer(pitch);
|
||||||
s.integer(clock);
|
|
||||||
s.integer(output);
|
s.integer(output);
|
||||||
|
|
||||||
s.integer(left);
|
s.integer(left);
|
||||||
|
@ -26,6 +25,7 @@ auto PSG::Tone::serialize(serializer& s) -> void {
|
||||||
auto PSG::Noise::serialize(serializer& s) -> void {
|
auto PSG::Noise::serialize(serializer& s) -> void {
|
||||||
s.integer(volume);
|
s.integer(volume);
|
||||||
s.integer(counter);
|
s.integer(counter);
|
||||||
|
s.integer(pitch);
|
||||||
s.integer(enable);
|
s.integer(enable);
|
||||||
s.integer(rate);
|
s.integer(rate);
|
||||||
s.integer(lfsr);
|
s.integer(lfsr);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
auto PSG::Tone::run() -> void {
|
auto PSG::Tone::run() -> void {
|
||||||
clock = 0;
|
|
||||||
if(--counter) return;
|
if(--counter) return;
|
||||||
|
|
||||||
clock = 1;
|
|
||||||
counter = pitch;
|
counter = pitch;
|
||||||
output ^= 1;
|
output ^= 1;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +9,6 @@ auto PSG::Tone::power() -> void {
|
||||||
volume = ~0;
|
volume = ~0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
pitch = 0;
|
pitch = 0;
|
||||||
clock = 0;
|
|
||||||
output = 0;
|
output = 0;
|
||||||
|
|
||||||
left = 1;
|
left = 1;
|
||||||
|
|
|
@ -11,6 +11,8 @@ struct MegaDriveCartridge {
|
||||||
MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) {
|
MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) {
|
||||||
manifest.append("board\n");
|
manifest.append("board\n");
|
||||||
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
|
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||||
|
if(size <= 0x200000)
|
||||||
|
manifest.append(" ram name=save.ram size=0x8000\n");
|
||||||
manifest.append("\n");
|
manifest.append("\n");
|
||||||
manifest.append("information\n");
|
manifest.append("information\n");
|
||||||
manifest.append(" title: ", Location::prefix(location), "\n");
|
manifest.append(" title: ", Location::prefix(location), "\n");
|
||||||
|
|
Loading…
Reference in New Issue