mirror of https://github.com/bsnes-emu/bsnes.git
v107.14
* fix support for light guns (Super Scope, Justifier(s))
This commit is contained in:
parent
42e5bcc604
commit
c2a181dbc5
|
@ -1,7 +1,5 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
|
||||
#include <emulator/audio/audio.cpp>
|
||||
#include <emulator/video/video.cpp>
|
||||
#include <emulator/resource/resource.cpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
|
|
@ -22,20 +22,20 @@
|
|||
using namespace nall;
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <emulator/bits.hpp>
|
||||
#include <emulator/types.hpp>
|
||||
#include <emulator/memory/readable.hpp>
|
||||
#include <emulator/memory/writable.hpp>
|
||||
#include <emulator/audio/audio.hpp>
|
||||
#include <emulator/video/video.hpp>
|
||||
#include <emulator/resource/resource.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "bsnes";
|
||||
static const string Version = "107.13";
|
||||
static const string Version = "107.14";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
static const string Website = "https://byuu.org";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "107.3";
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
Sprite::Sprite(uint width, uint height) : width(width), height(height) {
|
||||
pixels = new uint32[width * height]();
|
||||
}
|
||||
|
||||
Sprite::~Sprite() {
|
||||
delete[] pixels;
|
||||
}
|
||||
|
||||
auto Sprite::setPixels(const nall::image& image) -> void {
|
||||
memory::copy(this->pixels, width * height * sizeof(uint32), image.data(), image.size());
|
||||
}
|
||||
|
||||
auto Sprite::setVisible(bool visible) -> void {
|
||||
this->visible = visible;
|
||||
}
|
||||
|
||||
auto Sprite::setPosition(int x, int y) -> void {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
namespace Emulator {
|
||||
|
||||
#include "sprite.cpp"
|
||||
Video video;
|
||||
|
||||
Video::~Video() {
|
||||
reset(nullptr);
|
||||
}
|
||||
|
||||
auto Video::reset(Interface* interface) -> void {
|
||||
this->interface = interface;
|
||||
|
||||
sprites.reset();
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
delete rotate;
|
||||
rotate = nullptr;
|
||||
delete palette;
|
||||
palette = nullptr;
|
||||
width = 0;
|
||||
height = 0;
|
||||
effects.colorBleed = false;
|
||||
}
|
||||
|
||||
auto Video::setPalette() -> void {
|
||||
if(!interface) return;
|
||||
|
||||
delete palette;
|
||||
colors = interface->display().colors;
|
||||
palette = new uint32[colors];
|
||||
for(auto index : range(colors)) {
|
||||
uint64 color = interface->color(index);
|
||||
uint16 b = bits(color, 0-15);
|
||||
uint16 g = bits(color,16-31);
|
||||
uint16 r = bits(color,32-47);
|
||||
uint16 a = 0xffff;
|
||||
|
||||
if(saturation != 1.0) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double inverse = max(0.0, 1.0 - saturation);
|
||||
r = uclamp<16>(r * saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(gamma != 1.0) {
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
|
||||
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
|
||||
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
|
||||
}
|
||||
|
||||
if(luminance != 1.0) {
|
||||
r = uclamp<16>(r * luminance);
|
||||
g = uclamp<16>(g * luminance);
|
||||
b = uclamp<16>(b * luminance);
|
||||
}
|
||||
|
||||
switch(depth) {
|
||||
case 24: palette[index] = r >> 8 << 16 | g >> 8 << 8 | b >> 8 << 0; break;
|
||||
case 30: palette[index] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::setDepth(uint depth) -> void {
|
||||
this->depth = depth;
|
||||
}
|
||||
|
||||
auto Video::setSaturation(double saturation) -> void {
|
||||
this->saturation = saturation;
|
||||
}
|
||||
|
||||
auto Video::setGamma(double gamma) -> void {
|
||||
this->gamma = gamma;
|
||||
}
|
||||
|
||||
auto Video::setLuminance(double luminance) -> void {
|
||||
this->luminance = luminance;
|
||||
}
|
||||
|
||||
auto Video::setEffect(Effect effect, const any& value) -> void {
|
||||
if(effect == Effect::ColorBleed && value.is<bool>()) {
|
||||
effects.colorBleed = value.get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
auto Video::createSprite(uint width, uint height) -> shared_pointer<Sprite> {
|
||||
shared_pointer<Sprite> sprite = new Sprite{width, height};
|
||||
sprites.append(sprite);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
auto Video::removeSprite(shared_pointer<Sprite> sprite) -> bool {
|
||||
for(uint n : range(sprites.size())) {
|
||||
if(sprite == sprites[n]) {
|
||||
sprites.remove(n);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
|
||||
if(this->width != width || this->height != height) {
|
||||
delete buffer;
|
||||
delete rotate;
|
||||
buffer = new uint32[width * height]();
|
||||
rotate = new uint32[height * width]();
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
auto output = buffer;
|
||||
pitch >>= 2; //bytes to words
|
||||
|
||||
for(uint y : range(height)) {
|
||||
auto source = input + y * pitch;
|
||||
auto target = output + y * width;
|
||||
|
||||
for(uint x : range(width)) {
|
||||
auto color = palette[*source++];
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
if(effects.colorBleed) {
|
||||
uint32 mask = depth == 30 ? 0x40100401 : 0x01010101;
|
||||
for(uint y : range(height)) {
|
||||
auto target = output + y * width;
|
||||
for(uint x : range(width)) {
|
||||
auto a = target[x];
|
||||
auto b = target[x + (x != width - 1)];
|
||||
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& sprite : sprites) {
|
||||
if(!sprite->visible) continue;
|
||||
|
||||
uint32 opaqueAlpha = depth == 30 ? 0xc0000000 : 0xff000000;
|
||||
for(int y : range(sprite->height)) {
|
||||
for(int x : range(sprite->width)) {
|
||||
int pixelY = sprite->y + y;
|
||||
if(pixelY < 0 || pixelY >= height) continue;
|
||||
|
||||
int pixelX = sprite->x + x;
|
||||
if(pixelX < 0 || pixelX >= width) continue;
|
||||
|
||||
auto pixel = sprite->pixels[y * sprite->width + x];
|
||||
if(pixel) output[pixelY * width + pixelX] = opaqueAlpha | pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//platform->videoFrame((const uint16*)output, width * sizeof(uint32), width, height);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface;
|
||||
struct Video;
|
||||
struct Sprite;
|
||||
|
||||
struct Video {
|
||||
enum class Effect : uint {
|
||||
ColorBleed,
|
||||
};
|
||||
|
||||
~Video();
|
||||
auto reset(Interface* interface) -> void;
|
||||
|
||||
auto setPalette() -> void;
|
||||
auto setDepth(uint depth) -> void;
|
||||
auto setSaturation(double saturation) -> void;
|
||||
auto setGamma(double gamma) -> void;
|
||||
auto setLuminance(double luminance) -> void;
|
||||
|
||||
auto setEffect(Effect effect, const any& value) -> void;
|
||||
|
||||
auto createSprite(uint width, uint height) -> shared_pointer<Sprite>;
|
||||
auto removeSprite(shared_pointer<Sprite> sprite) -> bool;
|
||||
|
||||
auto refresh(uint32* input, uint pitch, uint width, uint height) -> void;
|
||||
|
||||
private:
|
||||
Interface* interface = nullptr;
|
||||
vector<shared_pointer<Sprite>> sprites;
|
||||
|
||||
uint32* buffer = nullptr;
|
||||
uint32* rotate = nullptr;
|
||||
uint32* palette = nullptr;
|
||||
|
||||
uint width = 0;
|
||||
uint height = 0;
|
||||
uint colors = 0;
|
||||
|
||||
uint depth = 24;
|
||||
double saturation = 1.0;
|
||||
double gamma = 1.0;
|
||||
double luminance = 1.0;
|
||||
|
||||
struct Effects {
|
||||
bool colorBleed = false;
|
||||
} effects;
|
||||
|
||||
friend class Sprite;
|
||||
};
|
||||
|
||||
struct Sprite {
|
||||
Sprite(uint width, uint height);
|
||||
~Sprite();
|
||||
|
||||
auto setPixels(const nall::image& image) -> void;
|
||||
auto setVisible(bool visible) -> void;
|
||||
auto setPosition(int x, int y) -> void;
|
||||
|
||||
private:
|
||||
const uint width;
|
||||
const uint height;
|
||||
uint32* pixels = nullptr;
|
||||
|
||||
bool visible = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
friend class Video;
|
||||
};
|
||||
|
||||
extern Video video;
|
||||
|
||||
}
|
|
@ -19,6 +19,8 @@ struct Controller {
|
|||
auto iobit(bool data) -> void;
|
||||
virtual auto data() -> uint2 { return 0; }
|
||||
virtual auto latch(bool data) -> void {}
|
||||
virtual auto latch() -> void {} //light guns
|
||||
virtual auto draw(uint16_t* output, uint pitch, uint width, uint height) -> void {} //light guns
|
||||
|
||||
const uint port;
|
||||
};
|
||||
|
|
|
@ -8,15 +8,11 @@ device(!chained ? ID::Device::Justifier : ID::Device::Justifiers)
|
|||
active = 0;
|
||||
prev = 0;
|
||||
|
||||
player1.sprite = Emulator::video.createSprite(32, 32);
|
||||
player1.sprite->setPixels(Resource::Sprite::CrosshairGreen);
|
||||
player1.x = 256 / 2;
|
||||
player1.y = 240 / 2;
|
||||
player1.trigger = false;
|
||||
player2.start = false;
|
||||
|
||||
player2.sprite = Emulator::video.createSprite(32, 32);
|
||||
player2.sprite->setPixels(Resource::Sprite::CrosshairRed);
|
||||
player2.x = 256 / 2;
|
||||
player2.y = 240 / 2;
|
||||
player2.trigger = false;
|
||||
|
@ -31,55 +27,6 @@ device(!chained ? ID::Device::Justifier : ID::Device::Justifiers)
|
|||
}
|
||||
}
|
||||
|
||||
Justifier::~Justifier() {
|
||||
Emulator::video.removeSprite(player1.sprite);
|
||||
Emulator::video.removeSprite(player2.sprite);
|
||||
}
|
||||
|
||||
/*
|
||||
auto Justifier::main() -> void {
|
||||
uint next = cpu.vcounter() * 1364 + cpu.hcounter();
|
||||
|
||||
int x = (active == 0 ? player1.x : player2.x), y = (active == 0 ? player1.y : player2.y);
|
||||
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= ppu.vdisp());
|
||||
|
||||
if(!offscreen) {
|
||||
uint target = y * 1364 + (x + 24) * 4;
|
||||
if(next >= target && prev < target) {
|
||||
//CRT raster detected, toggle iobit to latch counters
|
||||
iobit(0);
|
||||
iobit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(next < prev) {
|
||||
int nx1 = platform->inputPoll(port, device, 0 + X);
|
||||
int ny1 = platform->inputPoll(port, device, 0 + Y);
|
||||
nx1 += player1.x;
|
||||
ny1 += player1.y;
|
||||
player1.x = max(-16, min(256 + 16, nx1));
|
||||
player1.y = max(-16, min(240 + 16, ny1));
|
||||
player1.sprite->setPosition(player1.x * 2 - 16, player1.y * 2 - 16);
|
||||
player1.sprite->setVisible(true);
|
||||
}
|
||||
|
||||
if(next < prev && chained) {
|
||||
int nx2 = platform->inputPoll(port, device, 4 + X);
|
||||
int ny2 = platform->inputPoll(port, device, 4 + Y);
|
||||
nx2 += player2.x;
|
||||
ny2 += player2.y;
|
||||
player2.x = max(-16, min(256 + 16, nx2));
|
||||
player2.y = max(-16, min(240 + 16, ny2));
|
||||
player2.sprite->setPosition(player2.x * 2 - 16, player2.y * 2 - 16);
|
||||
player2.sprite->setVisible(true);
|
||||
}
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
*/
|
||||
|
||||
auto Justifier::data() -> uint2 {
|
||||
if(counter >= 32) return 1;
|
||||
|
||||
|
@ -141,3 +88,75 @@ auto Justifier::latch(bool data) -> void {
|
|||
counter = 0;
|
||||
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
|
||||
}
|
||||
|
||||
auto Justifier::latch() -> void {
|
||||
/* active value is inverted here ... */
|
||||
|
||||
if(active != 0) {
|
||||
int nx = platform->inputPoll(port, device, 0 + X);
|
||||
int ny = platform->inputPoll(port, device, 0 + Y);
|
||||
player1.x = max(-16, min(256 + 16, nx + player1.x));
|
||||
player1.y = max(-16, min((int)ppu.vdisp() + 16, ny + player1.y));
|
||||
bool offscreen = (player1.x < 0 || player1.y < 0 || player1.x >= 256 || player1.y >= (int)ppu.vdisp());
|
||||
if(!offscreen) ppu.latchCounters(player1.x, player1.y);
|
||||
}
|
||||
|
||||
if(active != 1) {
|
||||
int nx = platform->inputPoll(port, device, 4 + X);
|
||||
int ny = platform->inputPoll(port, device, 4 + Y);
|
||||
player2.x = max(-16, min(256 + 16, nx + player2.x));
|
||||
player2.y = max(-16, min((int)ppu.vdisp() + 16, ny + player2.y));
|
||||
bool offscreen = (player2.x < 0 || player2.y < 0 || player2.x >= 256 || player2.y >= (int)ppu.vdisp());
|
||||
if(!offscreen) ppu.latchCounters(player2.x, player2.y);
|
||||
}
|
||||
}
|
||||
|
||||
auto Justifier::draw(uint16_t* data, uint pitch, uint width, uint height) -> void {
|
||||
pitch >>= 1;
|
||||
float scaleX = (float)width / 256.0;
|
||||
float scaleY = (float)height / (float)ppu.vdisp();
|
||||
int length = (float)width / 256.0 * 4.0;
|
||||
|
||||
auto plot = [&](int x, int y, uint16_t color) -> void {
|
||||
if(x >= 0 && y >= 0 && x < (int)width && y < (int)height) {
|
||||
data[y * pitch + x] = color;
|
||||
}
|
||||
};
|
||||
|
||||
{ int x = player1.x * scaleX;
|
||||
int y = player1.y * scaleY;
|
||||
|
||||
uint16_t color = 0x03e0;
|
||||
uint16_t black = 0x0000;
|
||||
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y - 1, black);
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y + 1, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x - 1, py, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x + 1, py, black);
|
||||
plot(x - length - 1, y, black);
|
||||
plot(x + length + 1, y, black);
|
||||
plot(x, y - length - 1, black);
|
||||
plot(x, y + length + 1, black);
|
||||
for(int px = x - length; px <= x + length; px++) plot(px, y, color);
|
||||
for(int py = y - length; py <= y + length; py++) plot(x, py, color);
|
||||
}
|
||||
|
||||
if(chained)
|
||||
{ int x = player2.x * scaleX;
|
||||
int y = player2.y * scaleY;
|
||||
|
||||
uint16_t color = 0x7c00;
|
||||
uint16_t black = 0x0000;
|
||||
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y - 1, black);
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y + 1, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x - 1, py, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x + 1, py, black);
|
||||
plot(x - length - 1, y, black);
|
||||
plot(x + length + 1, y, black);
|
||||
plot(x, y - length - 1, black);
|
||||
plot(x, y + length + 1, black);
|
||||
for(int px = x - length; px <= x + length; px++) plot(px, y, color);
|
||||
for(int py = y - length; py <= y + length; py++) plot(x, py, color);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ struct Justifier : Controller {
|
|||
};
|
||||
|
||||
Justifier(uint port, bool chained);
|
||||
~Justifier();
|
||||
|
||||
auto data() -> uint2;
|
||||
auto latch(bool data) -> void;
|
||||
auto latch() -> void override;
|
||||
auto draw(uint16_t* data, uint pitch, uint width, uint height) -> void override;
|
||||
|
||||
//private:
|
||||
const bool chained; //true if the second justifier is attached to the first
|
||||
|
@ -18,7 +19,6 @@ struct Justifier : Controller {
|
|||
|
||||
bool active;
|
||||
struct Player {
|
||||
shared_pointer<Emulator::Sprite> sprite;
|
||||
int x;
|
||||
int y;
|
||||
bool trigger;
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
//Note that no commercial game ever utilizes a Super Scope in port 1.
|
||||
|
||||
SuperScope::SuperScope(uint port) : Controller(port) {
|
||||
sprite = Emulator::video.createSprite(32, 32);
|
||||
sprite->setPixels(Resource::Sprite::CrosshairGreen);
|
||||
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
|
||||
|
@ -34,42 +31,6 @@ SuperScope::SuperScope(uint port) : Controller(port) {
|
|||
prev = 0;
|
||||
}
|
||||
|
||||
SuperScope::~SuperScope() {
|
||||
Emulator::video.removeSprite(sprite);
|
||||
}
|
||||
|
||||
/*
|
||||
auto SuperScope::main() -> void {
|
||||
uint next = cpu.vcounter() * 1364 + cpu.hcounter();
|
||||
|
||||
if(!offscreen) {
|
||||
uint target = y * 1364 + (x + 24) * 4;
|
||||
if(next >= target && prev < target) {
|
||||
//CRT raster detected, toggle iobit to latch counters
|
||||
iobit(0);
|
||||
iobit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(next < prev) {
|
||||
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
|
||||
int nx = platform->inputPoll(port, ID::Device::SuperScope, X);
|
||||
int ny = platform->inputPoll(port, ID::Device::SuperScope, Y);
|
||||
nx += x;
|
||||
ny += y;
|
||||
x = max(-16, min(256 + 16, nx));
|
||||
y = max(-16, min(240 + 16, ny));
|
||||
offscreen = (x < 0 || y < 0 || x >= 256 || y >= ppu.vdisp());
|
||||
sprite->setPosition(x * 2 - 16, y * 2 - 16);
|
||||
sprite->setVisible(true);
|
||||
}
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
*/
|
||||
|
||||
auto SuperScope::data() -> uint2 {
|
||||
if(counter >= 8) return 1;
|
||||
|
||||
|
@ -78,7 +39,6 @@ auto SuperScope::data() -> uint2 {
|
|||
bool newturbo = platform->inputPoll(port, ID::Device::SuperScope, Turbo);
|
||||
if(newturbo && !oldturbo) {
|
||||
turbo = !turbo; //toggle state
|
||||
sprite->setPixels(turbo ? (image)Resource::Sprite::CrosshairRed : (image)Resource::Sprite::CrosshairGreen);
|
||||
}
|
||||
oldturbo = newturbo;
|
||||
|
||||
|
@ -128,3 +88,42 @@ auto SuperScope::latch(bool data) -> void {
|
|||
latched = data;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
auto SuperScope::latch() -> void {
|
||||
int nx = platform->inputPoll(port, ID::Device::SuperScope, X);
|
||||
int ny = platform->inputPoll(port, ID::Device::SuperScope, Y);
|
||||
x = max(-16, min(256 + 16, nx + x));
|
||||
y = max(-16, min((int)ppu.vdisp() + 16, ny + y));
|
||||
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (int)ppu.vdisp());
|
||||
if(!offscreen) ppu.latchCounters(x, y);
|
||||
}
|
||||
|
||||
auto SuperScope::draw(uint16_t* data, uint pitch, uint width, uint height) -> void {
|
||||
pitch >>= 1;
|
||||
float scaleX = (float)width / 256.0;
|
||||
float scaleY = (float)height / (float)ppu.vdisp();
|
||||
int length = (float)width / 256.0 * 4.0;
|
||||
|
||||
int x = this->x * scaleX;
|
||||
int y = this->y * scaleY;
|
||||
|
||||
auto plot = [&](int x, int y, uint16_t color) -> void {
|
||||
if(x >= 0 && y >= 0 && x < (int)width && y < (int)height) {
|
||||
data[y * pitch + x] = color;
|
||||
}
|
||||
};
|
||||
|
||||
uint16_t color = turbo ? 0x7c00 : 0x03e0;
|
||||
uint16_t black = 0x0000;
|
||||
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y - 1, black);
|
||||
for(int px = x - length - 1; px <= x + length + 1; px++) plot(px, y + 1, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x - 1, py, black);
|
||||
for(int py = y - length - 1; py <= y + length + 1; py++) plot(x + 1, py, black);
|
||||
plot(x - length - 1, y, black);
|
||||
plot(x + length + 1, y, black);
|
||||
plot(x, y - length - 1, black);
|
||||
plot(x, y + length + 1, black);
|
||||
for(int px = x - length; px <= x + length; px++) plot(px, y, color);
|
||||
for(int py = y - length; py <= y + length; py++) plot(x, py, color);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
struct SuperScope : Controller {
|
||||
shared_pointer<Emulator::Sprite> sprite;
|
||||
|
||||
enum : uint {
|
||||
X, Y, Trigger, Cursor, Turbo, Pause,
|
||||
};
|
||||
|
||||
SuperScope(uint port);
|
||||
~SuperScope();
|
||||
|
||||
auto data() -> uint2;
|
||||
auto latch(bool data) -> void;
|
||||
auto latch() -> void override;
|
||||
auto draw(uint16_t* data, uint pitch, uint width, uint height) -> void override;
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
auto PPU::latchCounters(uint hcounter, uint vcounter) -> void {
|
||||
io.hcounter = hcounter;
|
||||
io.vcounter = vcounter;
|
||||
latch.counters = 1;
|
||||
}
|
||||
|
||||
auto PPU::latchCounters() -> void {
|
||||
io.hcounter = cpu.hdot();
|
||||
io.vcounter = cpu.vcounter();
|
||||
|
|
|
@ -109,8 +109,9 @@ auto PPU::scanline() -> void {
|
|||
latch.ss |= io.bgMode == 7 && hdScale() > 1 && hdSupersample() == 1;
|
||||
}
|
||||
|
||||
if(vcounter() == vdisp() && !io.displayDisable) {
|
||||
oamAddressReset();
|
||||
if(vcounter() == vdisp()) {
|
||||
if(auto device = controllerPort2.device) device->latch(); //light guns
|
||||
if(!io.displayDisable) oamAddressReset();
|
||||
}
|
||||
|
||||
if(vcounter() == 240) {
|
||||
|
@ -144,6 +145,7 @@ auto PPU::refresh() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
if(auto device = controllerPort2.device) device->draw(output, pitch * sizeof(uint16), width, height);
|
||||
platform->videoFrame(output, pitch * sizeof(uint16), width, height, hd() ? hdScale() : 1);
|
||||
|
||||
frame.pitch = pitch;
|
||||
|
|
|
@ -236,6 +236,7 @@ public:
|
|||
};
|
||||
|
||||
//io.cpp
|
||||
auto latchCounters(uint hcounter, uint vcounter) -> void;
|
||||
auto latchCounters() -> void;
|
||||
alwaysinline auto vramAddress() const -> uint;
|
||||
alwaysinline auto readVRAM() -> uint16;
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
auto PPU::latchCounters(uint hcounter, uint vcounter) -> void {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.latchCounters(hcounter, vcounter);
|
||||
}
|
||||
|
||||
io.hcounter = hcounter;
|
||||
io.vcounter = vcounter;
|
||||
latch.counters = 1;
|
||||
}
|
||||
|
||||
auto PPU::latchCounters() -> void {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.latchCounters();
|
||||
|
|
|
@ -231,6 +231,10 @@ auto PPU::scanline() -> void {
|
|||
window.scanline();
|
||||
screen.scanline();
|
||||
|
||||
if(vcounter() == vdisp()) {
|
||||
if(auto device = controllerPort2.device) device->latch(); //light guns
|
||||
}
|
||||
|
||||
if(vcounter() == 240) {
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
|
@ -245,6 +249,7 @@ auto PPU::refresh() -> void {
|
|||
auto pitch = 512;
|
||||
auto width = 512;
|
||||
auto height = 480;
|
||||
if(auto device = controllerPort2.device) device->draw(output, pitch * sizeof(uint16), width, height);
|
||||
platform->videoFrame(output, pitch * sizeof(uint16), width, height, /* scale = */ 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ struct PPU : Thread, PPUcounter {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto latchCounters(uint hcounter, uint vcounter) -> void;
|
||||
auto latchCounters() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -92,9 +92,6 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
Emulator::video.reset(interface);
|
||||
Emulator::video.setPalette();
|
||||
|
||||
Emulator::audio.reset(interface);
|
||||
|
||||
random.entropy(Random::Entropy::Low);
|
||||
|
|
|
@ -49,8 +49,6 @@ auto Program::updateVideoFormat() -> void {
|
|||
settings.video.format = video.format();
|
||||
}
|
||||
video.setFormat(settings.video.format);
|
||||
Emulator::video.setDepth(settings.video.format == "RGB30" ? 30 : 24);
|
||||
Emulator::video.setPalette();
|
||||
}
|
||||
|
||||
auto Program::updateVideoShader() -> void {
|
||||
|
|
Loading…
Reference in New Issue