mirror of https://github.com/bsnes-emu/bsnes.git
Update to v098r12 release.
byuu says: Changelog: - higan/video: added support for Emulator::Sprite - higan/resource: a new system for accessing embedded binary files inside the emulation cores; holds the sprites - higan/sfc/superscope,justifier: re-enabled display of crosshairs - higan/sfc/superscope: fixed turbo toggle (also shows different crosshair color when in turbo mode) - higan/sfc/ppu: always outputs at 512x480 resolution now - causes a slight speed-hit from ~127fps to ~125fps; - but allows high-resolution 32x32 cursors that look way better; - also avoids the need to implement sprite scaling logic Right now, the PPU code to always output at 480-height is a really gross hack. Don't worry, I'll make that nicer before release. Also, superscope.cpp and justifier.cpp are built around a 256x240 screen. But since we now have 512x480, we can make the cursor's movement much smoother by doubling the resolution on both axes. The actual games won't see any accuracy improvements when firing the light guns, but the cursors will animate nicer so I think it's still worth it. I'll work on that before the next release as well. The current 32x32 cursors are nicer, but we can do better now with full 24-bit color. So feel free to submit alternatives. I'll probably reject them, but you can always try :D The sprites don't support alpha blending, just color keying (0x00000000 = transparent; anything else is 0xff......). We can revisit that later if necessary. The way I have it designed, the only files that do anything with Emulator::Sprite at all are the superscope and justifier folders. I didn't have to add any hooks anywhere else. Rendering the sprite is a lot cleaner than the old code, too.
This commit is contained in:
parent
ae5d380d06
commit
7f3cfa17b9
|
@ -5,7 +5,7 @@ target := tomoko
|
||||||
# console := true
|
# console := true
|
||||||
|
|
||||||
flags += -I. -I.. -O3
|
flags += -I. -I.. -O3
|
||||||
objects := libco audio video
|
objects := libco audio video resource
|
||||||
|
|
||||||
# profile-guided optimization mode
|
# profile-guided optimization mode
|
||||||
# pgo := instrument
|
# pgo := instrument
|
||||||
|
@ -56,6 +56,7 @@ all: build;
|
||||||
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco/)
|
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco/)
|
||||||
obj/audio.o: audio/audio.cpp $(call rwildcard,audio/)
|
obj/audio.o: audio/audio.cpp $(call rwildcard,audio/)
|
||||||
obj/video.o: video/video.cpp $(call rwildcard,video/)
|
obj/video.o: video/video.cpp $(call rwildcard,video/)
|
||||||
|
obj/resource.o: resource/resource.cpp $(call rwildcard,resource/)
|
||||||
|
|
||||||
ui := target-$(target)
|
ui := target-$(target)
|
||||||
include $(ui)/GNUmakefile
|
include $(ui)/GNUmakefile
|
||||||
|
|
|
@ -5,10 +5,11 @@ using namespace nall;
|
||||||
|
|
||||||
#include <audio/audio.hpp>
|
#include <audio/audio.hpp>
|
||||||
#include <video/video.hpp>
|
#include <video/video.hpp>
|
||||||
|
#include <resource/resource.hpp>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "098.11";
|
static const string Version = "098.12";
|
||||||
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/";
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
all:
|
||||||
|
sourcery resource.bml resource.cpp resource.hpp
|
|
@ -0,0 +1,5 @@
|
||||||
|
namespace name=Resource
|
||||||
|
namespace name=Sprite
|
||||||
|
binary name=CrosshairRed file=sprite/crosshair-red.png
|
||||||
|
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
||||||
|
binary name=CrosshairBlue file=sprite/crosshair-blue.png
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include <nall/nall.hpp>
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
namespace Resource {
|
||||||
|
namespace Sprite {
|
||||||
|
const nall::vector<uint8_t> CrosshairRed = { //size: 342
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,248,73,68,65,84,88,133,205,87,65,14,196,32,8,132,102,255,255,101,246,176,177,139,
|
||||||
|
148,81,80,27,229,212,70,102,6,212,0,50,229,77,26,107,156,37,139,2,228,241,209,39,11,113,71,156,68,139,106,128,
|
||||||
|
56,255,198,175,203,223,114,16,79,68,253,138,90,99,141,113,112,80,231,131,196,11,83,52,19,43,196,53,135,147,7,38,
|
||||||
|
150,104,244,212,32,86,235,228,236,20,6,200,207,191,117,215,70,12,242,94,139,133,166,236,173,236,67,252,111,139,67,157,
|
||||||
|
237,71,48,27,192,244,142,93,228,23,148,144,184,228,131,96,254,3,164,4,176,213,108,37,52,5,208,53,47,227,81,28,
|
||||||
|
49,153,102,163,88,96,149,68,150,193,21,223,59,128,68,43,69,13,103,4,199,246,8,34,151,240,209,249,38,112,251,47,
|
||||||
|
97,177,209,74,152,246,95,93,9,211,51,160,181,99,142,128,104,115,55,124,59,136,115,7,146,237,51,33,2,71,166,226,
|
||||||
|
94,23,13,77,214,104,44,103,174,163,143,86,189,244,187,224,232,151,81,21,132,39,210,33,91,246,54,132,193,44,226,219,
|
||||||
|
107,95,57,136,120,253,172,254,16,23,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
const nall::vector<uint8_t> CrosshairGreen = { //size: 329
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,235,73,68,65,84,88,133,213,87,65,18,195,32,8,196,78,31,230,211,253,153,61,180,
|
||||||
|
52,18,145,1,193,97,178,39,141,44,139,24,69,11,216,209,133,177,98,117,166,37,92,162,77,176,170,118,223,26,163,78,
|
||||||
|
68,71,145,198,244,169,157,57,35,84,248,43,222,255,109,154,254,113,140,114,102,222,18,239,165,120,251,181,42,0,232,103,
|
||||||
|
114,217,85,226,163,27,124,232,163,87,142,115,153,82,137,71,98,233,247,21,44,228,194,169,217,171,252,159,22,95,234,164,
|
||||||
|
47,129,55,128,144,140,237,166,63,132,151,190,4,247,147,16,103,35,157,90,220,140,119,121,80,224,94,108,0,164,227,119,
|
||||||
|
182,221,229,13,182,82,193,225,176,42,56,59,188,105,9,52,5,3,109,58,243,205,202,203,255,9,17,251,91,202,169,227,
|
||||||
|
205,128,235,198,19,17,64,40,82,171,225,233,32,158,113,33,65,164,222,9,105,16,50,81,55,238,88,210,212,119,1,0,
|
||||||
|
238,241,241,126,143,125,62,216,173,151,209,35,222,134,235,96,98,252,229,226,3,112,72,179,236,202,138,114,18,0,0,0,
|
||||||
|
0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
const nall::vector<uint8_t> CrosshairBlue = { //size: 332
|
||||||
|
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||||
|
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,14,196,0,0,14,
|
||||||
|
196,1,149,43,14,27,0,0,0,238,73,68,65,84,88,133,213,87,91,18,195,32,8,196,78,15,232,81,189,161,253,9,
|
||||||
|
25,52,98,121,57,76,246,43,137,44,11,24,69,11,232,209,55,99,69,235,76,74,184,69,107,229,245,91,27,220,137,124,
|
||||||
|
75,140,58,21,165,34,181,246,199,251,100,167,174,200,32,124,137,119,124,134,177,252,116,108,224,44,120,44,190,156,56,102,
|
||||||
|
163,204,228,182,107,173,80,31,93,225,67,30,189,112,124,85,41,145,120,36,88,191,159,96,33,23,78,101,47,242,127,90,
|
||||||
|
156,213,73,159,2,111,0,33,21,179,150,63,132,151,62,5,243,78,136,217,236,118,173,85,198,86,30,20,152,154,13,192,
|
||||||
|
118,251,125,216,90,121,212,118,215,112,86,224,26,142,133,247,152,2,73,195,64,155,190,248,166,229,229,255,132,8,243,146,
|
||||||
|
242,234,120,43,224,58,241,68,4,16,138,212,110,120,58,136,119,28,72,16,169,103,194,33,136,63,68,209,184,103,74,83,
|
||||||
|
239,5,0,215,26,167,231,123,124,103,130,53,221,140,94,113,55,100,131,9,242,151,139,31,79,50,234,237,105,206,30,22,
|
||||||
|
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Resource {
|
||||||
|
namespace Sprite {
|
||||||
|
extern const nall::vector<uint8_t> CrosshairRed;
|
||||||
|
extern const nall::vector<uint8_t> CrosshairGreen;
|
||||||
|
extern const nall::vector<uint8_t> CrosshairBlue;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 332 B |
Binary file not shown.
After Width: | Height: | Size: 329 B |
Binary file not shown.
After Width: | Height: | Size: 342 B |
|
@ -13,6 +13,9 @@ Controller::Controller(bool port) : port(port) {
|
||||||
if(!thread) create(Controller::Enter, 1);
|
if(!thread) create(Controller::Enter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Controller::~Controller() {
|
||||||
|
}
|
||||||
|
|
||||||
auto Controller::Enter() -> void {
|
auto Controller::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.synchronize();
|
scheduler.synchronize();
|
||||||
|
|
|
@ -15,6 +15,7 @@ struct Controller : Cothread {
|
||||||
enum : bool { Port1 = 0, Port2 = 1 };
|
enum : bool { Port1 = 0, Port2 = 1 };
|
||||||
|
|
||||||
Controller(bool port);
|
Controller(bool port);
|
||||||
|
virtual ~Controller();
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
|
||||||
virtual auto main() -> void;
|
virtual auto main() -> void;
|
||||||
|
|
|
@ -3,17 +3,21 @@ Controller(port),
|
||||||
chained(chained),
|
chained(chained),
|
||||||
device(chained == false ? Device::Justifier : Device::Justifiers)
|
device(chained == false ? Device::Justifier : Device::Justifiers)
|
||||||
{
|
{
|
||||||
create(Controller::Enter, 21477272);
|
create(Controller::Enter, 21'477'272);
|
||||||
latched = 0;
|
latched = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
active = 0;
|
active = 0;
|
||||||
prev = 0;
|
prev = 0;
|
||||||
|
|
||||||
|
player1.sprite = Emulator::video.createSprite(32, 32);
|
||||||
|
player1.sprite->setPixels(Resource::Sprite::CrosshairGreen);
|
||||||
player1.x = 256 / 2;
|
player1.x = 256 / 2;
|
||||||
player1.y = 240 / 2;
|
player1.y = 240 / 2;
|
||||||
player1.trigger = false;
|
player1.trigger = false;
|
||||||
player2.start = false;
|
player2.start = false;
|
||||||
|
|
||||||
|
player2.sprite = Emulator::video.createSprite(32, 32);
|
||||||
|
player2.sprite->setPixels(Resource::Sprite::CrosshairRed);
|
||||||
player2.x = 256 / 2;
|
player2.x = 256 / 2;
|
||||||
player2.y = 240 / 2;
|
player2.y = 240 / 2;
|
||||||
player2.trigger = false;
|
player2.trigger = false;
|
||||||
|
@ -28,14 +32,19 @@ device(chained == false ? Device::Justifier : Device::Justifiers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Justifier::main() -> void {
|
Justifier::~Justifier() {
|
||||||
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
|
Emulator::video.removeSprite(player1.sprite);
|
||||||
|
Emulator::video.removeSprite(player2.sprite);
|
||||||
|
}
|
||||||
|
|
||||||
signed x = (active == 0 ? player1.x : player2.x), y = (active == 0 ? player1.y : player2.y);
|
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());
|
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= ppu.vdisp());
|
||||||
|
|
||||||
if(offscreen == false) {
|
if(!offscreen) {
|
||||||
unsigned target = y * 1364 + (x + 24) * 4;
|
uint target = y * 1364 + (x + 24) * 4;
|
||||||
if(next >= target && prev < target) {
|
if(next >= target && prev < target) {
|
||||||
//CRT raster detected, toggle iobit to latch counters
|
//CRT raster detected, toggle iobit to latch counters
|
||||||
iobit(0);
|
iobit(0);
|
||||||
|
@ -50,6 +59,8 @@ auto Justifier::main() -> void {
|
||||||
ny1 += player1.y;
|
ny1 += player1.y;
|
||||||
player1.x = max(-16, min(256 + 16, nx1));
|
player1.x = max(-16, min(256 + 16, nx1));
|
||||||
player1.y = max(-16, min(240 + 16, ny1));
|
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) {
|
if(next < prev && chained) {
|
||||||
|
@ -59,6 +70,8 @@ auto Justifier::main() -> void {
|
||||||
ny2 += player2.y;
|
ny2 += player2.y;
|
||||||
player2.x = max(-16, min(256 + 16, nx2));
|
player2.x = max(-16, min(256 + 16, nx2));
|
||||||
player2.y = max(-16, min(240 + 16, ny2));
|
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;
|
prev = next;
|
||||||
|
|
|
@ -4,6 +4,7 @@ struct Justifier : Controller {
|
||||||
};
|
};
|
||||||
|
|
||||||
Justifier(bool port, bool chained);
|
Justifier(bool port, bool chained);
|
||||||
|
~Justifier();
|
||||||
|
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto data() -> uint2;
|
auto data() -> uint2;
|
||||||
|
@ -11,14 +12,17 @@ struct Justifier : Controller {
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
const bool chained; //true if the second justifier is attached to the first
|
const bool chained; //true if the second justifier is attached to the first
|
||||||
const unsigned device;
|
const uint device;
|
||||||
bool latched;
|
bool latched;
|
||||||
unsigned counter;
|
uint counter;
|
||||||
unsigned prev;
|
uint prev;
|
||||||
|
|
||||||
bool active;
|
bool active;
|
||||||
struct Player {
|
struct Player {
|
||||||
signed x, y;
|
shared_pointer<Emulator::Sprite> sprite;
|
||||||
bool trigger, start;
|
int x;
|
||||||
|
int y;
|
||||||
|
bool trigger;
|
||||||
|
bool start;
|
||||||
} player1, player2;
|
} player1, player2;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
//Note that no commercial game ever utilizes a Super Scope in port 1.
|
//Note that no commercial game ever utilizes a Super Scope in port 1.
|
||||||
|
|
||||||
SuperScope::SuperScope(bool port) : Controller(port) {
|
SuperScope::SuperScope(bool port) : Controller(port) {
|
||||||
create(Controller::Enter, 21477272);
|
create(Controller::Enter, 21'477'272);
|
||||||
|
sprite = Emulator::video.createSprite(32, 32);
|
||||||
|
sprite->setPixels(Resource::Sprite::CrosshairGreen);
|
||||||
|
|
||||||
latched = 0;
|
latched = 0;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
|
|
||||||
|
@ -25,18 +28,22 @@ SuperScope::SuperScope(bool port) : Controller(port) {
|
||||||
pause = false;
|
pause = false;
|
||||||
offscreen = false;
|
offscreen = false;
|
||||||
|
|
||||||
turbolock = false;
|
oldturbo = false;
|
||||||
triggerlock = false;
|
triggerlock = false;
|
||||||
pauselock = false;
|
pauselock = false;
|
||||||
|
|
||||||
prev = 0;
|
prev = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperScope::main() -> void {
|
SuperScope::~SuperScope() {
|
||||||
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
|
Emulator::video.removeSprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
if(offscreen == false) {
|
auto SuperScope::main() -> void {
|
||||||
unsigned target = y * 1364 + (x + 24) * 4;
|
uint next = cpu.vcounter() * 1364 + cpu.hcounter();
|
||||||
|
|
||||||
|
if(!offscreen) {
|
||||||
|
uint target = y * 1364 + (x + 24) * 4;
|
||||||
if(next >= target && prev < target) {
|
if(next >= target && prev < target) {
|
||||||
//CRT raster detected, toggle iobit to latch counters
|
//CRT raster detected, toggle iobit to latch counters
|
||||||
iobit(0);
|
iobit(0);
|
||||||
|
@ -53,6 +60,8 @@ auto SuperScope::main() -> void {
|
||||||
x = max(-16, min(256 + 16, nx));
|
x = max(-16, min(256 + 16, nx));
|
||||||
y = max(-16, min(240 + 16, ny));
|
y = max(-16, min(240 + 16, ny));
|
||||||
offscreen = (x < 0 || y < 0 || x >= 256 || y >= ppu.vdisp());
|
offscreen = (x < 0 || y < 0 || x >= 256 || y >= ppu.vdisp());
|
||||||
|
sprite->setPosition(x * 2 - 16, y * 2 - 16);
|
||||||
|
sprite->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev = next;
|
prev = next;
|
||||||
|
@ -65,12 +74,11 @@ auto SuperScope::data() -> uint2 {
|
||||||
if(counter == 0) {
|
if(counter == 0) {
|
||||||
//turbo is a switch; toggle is edge sensitive
|
//turbo is a switch; toggle is edge sensitive
|
||||||
bool newturbo = interface->inputPoll(port, Device::SuperScope, Turbo);
|
bool newturbo = interface->inputPoll(port, Device::SuperScope, Turbo);
|
||||||
if(newturbo && !turbo) {
|
if(newturbo && !oldturbo) {
|
||||||
turbo = !turbo; //toggle state
|
turbo = !turbo; //toggle state
|
||||||
turbolock = true;
|
sprite->setPixels(turbo ? Resource::Sprite::CrosshairRed : Resource::Sprite::CrosshairGreen);
|
||||||
} else {
|
|
||||||
turbolock = false;
|
|
||||||
}
|
}
|
||||||
|
oldturbo = newturbo;
|
||||||
|
|
||||||
//trigger is a button
|
//trigger is a button
|
||||||
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
struct SuperScope : Controller {
|
struct SuperScope : Controller {
|
||||||
|
shared_pointer<Emulator::Sprite> sprite;
|
||||||
|
|
||||||
enum : uint {
|
enum : uint {
|
||||||
X, Y, Trigger, Cursor, Turbo, Pause,
|
X, Y, Trigger, Cursor, Turbo, Pause,
|
||||||
};
|
};
|
||||||
|
|
||||||
SuperScope(bool port);
|
SuperScope(bool port);
|
||||||
|
~SuperScope();
|
||||||
|
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto data() -> uint2;
|
auto data() -> uint2;
|
||||||
auto latch(bool data) -> void;
|
auto latch(bool data) -> void;
|
||||||
|
|
||||||
//private:
|
private:
|
||||||
bool latched;
|
bool latched;
|
||||||
unsigned counter;
|
uint counter;
|
||||||
|
|
||||||
signed x, y;
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
bool trigger;
|
bool trigger;
|
||||||
bool cursor;
|
bool cursor;
|
||||||
|
@ -21,9 +25,9 @@ struct SuperScope : Controller {
|
||||||
bool pause;
|
bool pause;
|
||||||
bool offscreen;
|
bool offscreen;
|
||||||
|
|
||||||
bool turbolock;
|
bool oldturbo;
|
||||||
bool triggerlock;
|
bool triggerlock;
|
||||||
bool pauselock;
|
bool pauselock;
|
||||||
|
|
||||||
unsigned prev;
|
uint prev;
|
||||||
};
|
};
|
||||||
|
|
|
@ -227,9 +227,9 @@ auto PPU::frame() -> void {
|
||||||
auto PPU::refresh() -> void {
|
auto PPU::refresh() -> void {
|
||||||
auto output = this->output;
|
auto output = this->output;
|
||||||
if(!overscan()) output -= 14 * 512;
|
if(!overscan()) output -= 14 * 512;
|
||||||
auto pitch = 1024 >> interlace();
|
auto pitch = 512;
|
||||||
auto width = 512;
|
auto width = 512;
|
||||||
auto height = !interlace() ? 240 : 480;
|
auto height = 480;
|
||||||
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ PPU::Screen::Screen(PPU& self) : self(self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::Screen::scanline() -> void {
|
auto PPU::Screen::scanline() -> void {
|
||||||
line = self.output + self.vcounter() * 1024;
|
lineA = self.output + self.vcounter() * 1024;
|
||||||
if(self.display.interlace && self.field()) line += 512;
|
lineB = lineA + (self.display.interlace ? 0 : 512);
|
||||||
|
if(self.display.interlace && self.field()) lineA += 512, lineB += 512;
|
||||||
|
|
||||||
//the first hires pixel of each scanline is transparent
|
//the first hires pixel of each scanline is transparent
|
||||||
//note: exact value initializations are not confirmed on hardware
|
//note: exact value initializations are not confirmed on hardware
|
||||||
|
@ -25,8 +26,8 @@ auto PPU::Screen::run() -> void {
|
||||||
auto sscolor = get_pixel_sub(hires);
|
auto sscolor = get_pixel_sub(hires);
|
||||||
auto mscolor = get_pixel_main();
|
auto mscolor = get_pixel_main();
|
||||||
|
|
||||||
*line++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor);
|
*lineA++ = *lineB++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor);
|
||||||
*line++ = (self.regs.display_brightness << 15) | (mscolor);
|
*lineA++ = *lineB++ = (self.regs.display_brightness << 15) | (mscolor);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::Screen::get_pixel_sub(bool hires) -> uint16 {
|
auto PPU::Screen::get_pixel_sub(bool hires) -> uint16 {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
struct Screen {
|
struct Screen {
|
||||||
uint32* line;
|
uint32* lineA;
|
||||||
|
uint32* lineB;
|
||||||
|
|
||||||
struct Regs {
|
struct Regs {
|
||||||
bool addsub_mode;
|
bool addsub_mode;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
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;
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
|
#include "sprite.cpp"
|
||||||
Video video;
|
Video video;
|
||||||
|
|
||||||
Video::~Video() {
|
Video::~Video() {
|
||||||
|
@ -10,6 +11,7 @@ Video::~Video() {
|
||||||
|
|
||||||
auto Video::reset() -> void {
|
auto Video::reset() -> void {
|
||||||
interface = nullptr;
|
interface = nullptr;
|
||||||
|
sprites.reset();
|
||||||
delete output;
|
delete output;
|
||||||
output = nullptr;
|
output = nullptr;
|
||||||
delete palette;
|
delete palette;
|
||||||
|
@ -85,6 +87,22 @@ auto Video::setEffect(Effect effect, const any& value) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
if(sprite == sprites[n]) {
|
||||||
|
sprites.remove(n);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
|
auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
|
||||||
if(this->width != width || this->height != height) {
|
if(this->width != width || this->height != height) {
|
||||||
delete output;
|
delete output;
|
||||||
|
@ -123,6 +141,23 @@ auto Video::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(auto& sprite : sprites) {
|
||||||
|
if(!sprite->visible) continue;
|
||||||
|
|
||||||
|
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] = 0xff000000 | pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface->videoRefresh(output, width * sizeof(uint32), width, height);
|
interface->videoRefresh(output, width * sizeof(uint32), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
struct Interface;
|
struct Interface;
|
||||||
|
struct Video;
|
||||||
|
struct Sprite;
|
||||||
|
|
||||||
struct Video {
|
struct Video {
|
||||||
enum class Effect : uint {
|
enum class Effect : uint {
|
||||||
|
@ -22,12 +24,18 @@ struct Video {
|
||||||
|
|
||||||
auto setEffect(Effect effect, const any& value) -> 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;
|
auto refresh(uint32* input, uint pitch, uint width, uint height) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Emulator::Interface* interface = nullptr;
|
Emulator::Interface* interface = nullptr;
|
||||||
|
vector<shared_pointer<Sprite>> sprites;
|
||||||
|
|
||||||
uint32* output = nullptr;
|
uint32* output = nullptr;
|
||||||
uint32* palette = nullptr;
|
uint32* palette = nullptr;
|
||||||
|
|
||||||
uint width = 0;
|
uint width = 0;
|
||||||
uint height = 0;
|
uint height = 0;
|
||||||
uint colors = 0;
|
uint colors = 0;
|
||||||
|
@ -40,6 +48,28 @@ private:
|
||||||
bool colorBleed = false;
|
bool colorBleed = false;
|
||||||
bool interframeBlending = false;
|
bool interframeBlending = false;
|
||||||
} effects;
|
} 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;
|
extern Video video;
|
||||||
|
|
Loading…
Reference in New Issue