bsnes/higan/ms/vdp/vdp.cpp

131 lines
3.1 KiB
C++
Raw Normal View History

#include <ms/ms.hpp>
namespace MasterSystem {
VDP vdp;
#include "io.cpp"
#include "background.cpp"
#include "sprite.cpp"
Update to v102r10 release. byuu says: Changelog: - removed Emulator::Interface::Capabilities¹ - MS: improved the PSG emulation a bit - MS: added cheat code support - MS: added save state support² - MD: emulated the PSG³ ¹: there's really no point to it anymore. I intend to add cheat codes to the GBA core, as well as both cheat codes and save states to the Mega Drive core. I no longer intend to emulate any new systems, so these values will always be true. Further, the GUI doesn't respond to these values to disable those features anymore ever since the hiro rewrite, so they're double useless. ²: right now, the Z80 core is using a pointer for HL-\>(IX,IY) overrides. But I can't reliably serialize pointers, so I need to convert the Z80 core to use an integer here. The save states still appear to work fine, but there's the potential for an instruction to execute incorrectly if you're incredibly unlucky, so this needs to be fixed as soon as possible. Further, I still need a way to serialize array<T, Size> objects, and I should also add nall::Boolean serialization support. ³: I don't have a system in place to share identical sound chips. But this chip is so incredibly simple that it's not really much trouble to duplicate it. Further, I can strip out the stereo sound support code from the Game Gear portion, so it's even tinier. Note that the Mega Drive only just barely uses the PSG. Not at all in Altered Beast, and only for a tiny part of the BGM music on Sonic 1, plus his jump sound effect.
2017-02-22 21:25:01 +00:00
#include "serialization.cpp"
auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main();
}
auto VDP::main() -> void {
if(io.vcounter <= vlines()) {
if(io.lcounter-- == 0) {
io.lcounter = io.lineCounter;
io.intLine = 1;
}
} else {
io.lcounter = io.lineCounter;
}
if(io.vcounter == vlines() + 1) {
io.intFrame = 1;
}
//684 clocks/scanline
uint y = io.vcounter;
sprite.setup(y);
if(y < vlines()) {
uint32* screen = buffer + (24 + y) * 256;
for(uint x : range(256)) {
background.run(x, y);
sprite.run(x, y);
step(2);
uint12 color = palette(16 | io.backdropColor);
if(!io.leftClip || x >= 8) {
if(background.output.priority || !sprite.output.color) {
color = palette(background.output.palette << 4 | background.output.color);
} else if(sprite.output.color) {
color = palette(16 | sprite.output.color);
}
}
if(!io.displayEnable) color = 0;
*screen++ = color;
}
} else {
//Vblank
step(512);
}
step(172);
if(io.vcounter == 240) scheduler.exit(Scheduler::Event::Frame);
}
auto VDP::step(uint clocks) -> void {
while(clocks--) {
if(++io.hcounter == 684) {
io.hcounter = 0;
Update to v102r28 release. byuu says: Changelog: - higan: `Emulator::<Platform::load>()` now returns a struct containing both a path ID and a string option - higan: `Emulator::<Platform::load>()` now takes an optional final argument of string options - fc: added PAL emulation (finally, only took six years) - md: added PAL emulation - md: fixed address parameter to `VDP::Sprite::write()`; fixes missing sprites in Super Street Fighter II - md: emulated HIRQ counter; fixes many games - Super Street Fighter II - status bar - Altered Beast - status bar - Sonic the Hedgehog - Labyrinth Zone - water effect - etc. - ms: added PAL emulation - sfc: added the ability to override the default region auto-detection - sfc: removed "system.region" override setting from `Super Famicom.sys` - tomoko: added options list to game folder load dialog window - tomoko: added the ability to specify game folder load options on the command-line So, basically ... Sega forced a change with the way region detection works. You end up with games that can run on multiple regions, and the content changes accordingly. Bare Knuckle in NTSC-J mode will become Streets of Rage in NTSC-U mode. Some games can even run in both NTSC and PAL mode. In my view, there should be a separate ROM for each region a game was released in, even if the ROM content were identical. But unfortunately that's not how things were done by anyone else. So to support this, the higan load dialog now has a drop-down at the bottom-right, where you can choose the region to load games from. On the SNES, it defaults to "Auto", which will pull the region setting from the manifest, or fall back on NTSC. On the Mega Drive ... unfortunately, I can't auto-detect the region from the ROM header. $1f0 is supposed to contain a string like "JUE", but instead you get games like Maui Mallard that put an "A" there, and other such nonsense. Sega was far more lax than Nintendo with the ROM header validity. So for now at least, you have to manually select your region every time you play a Mega Drive game, thus you have "NTSC-J", "NTSC-U", and "PAL". The same goes for the Master System for the same reason, but there's only "NTSC" and "PAL" here. I'm not sure if games have a way to detect domestic vs international consoles. And for now ... the Famicom is the same as well, with no auto-detection. I'd sincerely hope iNES has a header bit for the region, but I didn't bother with updating icarus to support that yet. The way to pass these parameters on the command-line is to prefix the game path with "option:", so for example:    higan "PAL:/path/to/Sonic the Hedgehog (USA, Europe).md" If you don't provide a prefix, it uses the default (NTSC-J, NTSC, or Auto.) Obviously, it's not possible to pass parameters with drag-and-drop, so you will always get the default option in said case.
2017-06-20 12:34:50 +00:00
if(++io.vcounter == (Region::NTSC() ? 262 : 312)) {
io.vcounter = 0;
}
}
cpu.setINT((io.lineInterrupts && io.intLine) || (io.frameInterrupts && io.intFrame));
Thread::step(1);
synchronize(cpu);
}
}
auto VDP::refresh() -> void {
if(Model::SG1000() || Model::SC3000()) {
uint32* screen = buffer;
screen += 24 * 256;
Emulator::video.refresh(screen, 256 * sizeof(uint32), 256, 192);
}
if(Model::MasterSystem()) {
//center the video output vertically in the viewport
uint32* screen = buffer;
if(vlines() == 224) screen += 16 * 256;
if(vlines() == 240) screen += 24 * 256;
Emulator::video.refresh(screen, 256 * sizeof(uint32), 256, 240);
}
if(Model::GameGear()) {
Emulator::video.refresh(buffer + 48 * 256 + 48, 256 * sizeof(uint32), 160, 144);
}
}
auto VDP::vlines() -> uint {
switch(io.mode) {
default: return 192;
case 0b1011: return 224;
case 0b1110: return 240;
}
}
auto VDP::vblank() -> bool {
return io.vcounter >= vlines();
}
auto VDP::power() -> void {
create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
memory::fill<uint32>(buffer, 256 * 264);
io = {};
background.power();
sprite.power();
}
auto VDP::palette(uint5 index) -> uint12 {
if(Model::SG1000() || Model::SC3000()) return index.bits(0,3);
//Master System and Game Gear approximate TMS9918A colors by converting to RGB6 palette colors
static uint6 palette[16] = {
0x00, 0x00, 0x08, 0x0c, 0x10, 0x30, 0x01, 0x3c,
0x02, 0x03, 0x05, 0x0f, 0x04, 0x33, 0x15, 0x3f,
};
if(!io.mode.bit(3)) return palette[index.bits(0,3)];
if(Model::MasterSystem()) return cram[index];
if(Model::GameGear()) return cram[index * 2 + 0] << 0 | cram[index * 2 + 1] << 8;
return 0;
}
}