mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-31 23:11:45 +02:00
Update to v093r10 release.
byuu says: Changelog: - Game Boy (Color): STAT OAM+HBlank IRQs only trigger during LY=0-143 with display enabled - fixes backgrounds and text in Wacky Races - Game Boy (Color): fixed underflow in window clamping - fixes Wacky Races, Prehistorik Man, Alleyway, etc - Game Boy (Color): LCD OAM DMA was running too slow - fixes Shin Megami Tensei - Devichil - Kuro no Sho - Game Boy Advance: removed built-in frame blending; display emulation shaders will handle this going forward - Game Boy Advance: added Game Boy Player emulation - currently the screen is tinted red during rumble, no actual gamepad rumble support yet - this is going to be slow, as we have to hash the frame to detect the GBP logo, it'll be optional later on - Emulator::Interface::Palette can now output a raw palette (for Display Emulation shaders only) - color channels are not yet split up, it's just the raw packed value
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
gba_objects := gba-memory gba-interface gba-scheduler gba-system
|
||||
gba_objects += gba-video gba-cartridge
|
||||
gba_objects += gba-video gba-cartridge gba-player
|
||||
gba_objects += gba-cpu gba-ppu gba-apu
|
||||
objects += $(gba_objects)
|
||||
|
||||
@@ -9,6 +9,7 @@ obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/sche
|
||||
obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system)
|
||||
obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video)
|
||||
obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge)
|
||||
obj/gba-player.o: $(gba)/player/player.cpp $(call rwildcard,$(gba)/player)
|
||||
obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu)
|
||||
obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu)
|
||||
obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu)
|
||||
|
@@ -47,8 +47,9 @@ uint8 CPU::read(uint32 addr) {
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
auto& data = regs.serial.data[(addr >> 1) & 3];
|
||||
if(auto data = player.read()) return data() >> ((addr & 3) << 3);
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
auto& data = regs.serial.data[(addr >> 1) & 3];
|
||||
return data >> shift;
|
||||
}
|
||||
|
||||
@@ -62,11 +63,13 @@ uint8 CPU::read(uint32 addr) {
|
||||
|
||||
//KEYINPUT
|
||||
case 0x04000130:
|
||||
if(auto result = player.keyinput()) return result() >> 0;
|
||||
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, n) << n;
|
||||
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
|
||||
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
|
||||
return result ^ 0xff;
|
||||
case 0x04000131:
|
||||
if(auto result = player.keyinput()) return result() >> 8;
|
||||
result |= interface->inputPoll(0, 0, 8) << 0;
|
||||
result |= interface->inputPoll(0, 0, 9) << 1;
|
||||
return result ^ 0x03;
|
||||
@@ -241,6 +244,7 @@ void CPU::write(uint32 addr, uint8 byte) {
|
||||
case 0x04000122: case 0x04000123:
|
||||
case 0x04000124: case 0x04000125:
|
||||
case 0x04000126: case 0x04000127: {
|
||||
player.write(byte, addr & 3);
|
||||
auto& data = regs.serial.data[(addr >> 1) & 3];
|
||||
unsigned shift = (addr & 1) * 8;
|
||||
data = (data & ~(255 << shift)) | (byte << shift);
|
||||
|
@@ -53,6 +53,7 @@ namespace GameBoyAdvance {
|
||||
#include <gba/scheduler/scheduler.hpp>
|
||||
#include <gba/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
#include <gba/player/player.hpp>
|
||||
#include <gba/cpu/cpu.hpp>
|
||||
#include <gba/ppu/ppu.hpp>
|
||||
#include <gba/apu/apu.hpp>
|
||||
|
@@ -109,8 +109,8 @@ bool Interface::unserialize(serializer& s) {
|
||||
return system.unserialize(s);
|
||||
}
|
||||
|
||||
void Interface::paletteUpdate(bool colorEmulation) {
|
||||
video.generate_palette(colorEmulation);
|
||||
void Interface::paletteUpdate(PaletteMode mode) {
|
||||
video.generate_palette(mode);
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
|
@@ -43,7 +43,7 @@ struct Interface : Emulator::Interface {
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void paletteUpdate(bool colorEmulation);
|
||||
void paletteUpdate(PaletteMode mode);
|
||||
|
||||
Interface();
|
||||
|
||||
|
93
gba/player/player.cpp
Normal file
93
gba/player/player.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
//Game Boy Player emulation
|
||||
|
||||
#include "serialization.cpp"
|
||||
Player player;
|
||||
|
||||
void Player::power() {
|
||||
status.enable = false;
|
||||
status.rumble = false;
|
||||
|
||||
status.logoDetected = false;
|
||||
status.logoCounter = 0;
|
||||
|
||||
status.packet = 0;
|
||||
status.send = 0;
|
||||
status.recv = 0;
|
||||
}
|
||||
|
||||
void Player::frame() {
|
||||
uint32 hash = crc32_calculate((const uint8*)ppu.output, 240 * 160 * sizeof(uint32));
|
||||
status.logoDetected = (hash == 0x7776eb55);
|
||||
|
||||
if(status.logoDetected) {
|
||||
status.enable = true;
|
||||
status.logoCounter = (status.logoCounter + 1) % 3;
|
||||
status.packet = 0;
|
||||
}
|
||||
|
||||
if(status.enable == false) return;
|
||||
|
||||
if(cpu.regs.joybus.settings == 0x0000 && cpu.regs.serial.control == 0x5088) {
|
||||
status.packet = (status.packet + 1) % 17;
|
||||
switch(status.packet) {
|
||||
case 0: status.send = 0x0000494e; break;
|
||||
case 1: status.send = 0xb6b1494e; break;
|
||||
case 2: status.send = 0xb6b1494e; break;
|
||||
case 3: status.send = 0xb6b1544e; break;
|
||||
case 4: status.send = 0xabb1544e; break;
|
||||
case 5: status.send = 0xabb14e45; break;
|
||||
case 6: status.send = 0xb1ba4e45; break;
|
||||
case 7: status.send = 0xb1ba4f44; break;
|
||||
case 8: status.send = 0xb0bb4f44; break;
|
||||
case 9: status.send = 0xb0bb8002; break;
|
||||
case 10: status.send = 0x10000010; break;
|
||||
case 11: status.send = 0x20000013; break;
|
||||
case 12: status.send = 0x30000003; break;
|
||||
case 13: status.send = 0x30000003; break;
|
||||
case 14: status.send = 0x30000003; break;
|
||||
case 15: status.send = 0x30000003; break;
|
||||
case 16: status.send = 0x30000003; break;
|
||||
}
|
||||
cpu.regs.irq.flag.serial = true;
|
||||
}
|
||||
|
||||
if(status.rumble) {
|
||||
//todo: support actual gamepad rumble; for now, color screen red during rumble
|
||||
for(unsigned n = 0; n < 240 * 160; n++) ppu.output[n] &= 0x001f;
|
||||
}
|
||||
}
|
||||
|
||||
optional<uint16> Player::keyinput() {
|
||||
if(status.logoDetected == false) return false;
|
||||
|
||||
switch(status.logoCounter) {
|
||||
case 0: return {true, 0x03ff};
|
||||
case 1: return {true, 0x03ff};
|
||||
case 2: return {true, 0x030f};
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
optional<uint32> Player::read() {
|
||||
if(status.enable == false) return false;
|
||||
|
||||
return {true, status.send};
|
||||
}
|
||||
|
||||
void Player::write(uint8 byte, uint2 addr) {
|
||||
if(status.enable == false) return;
|
||||
|
||||
unsigned shift = addr << 3;
|
||||
status.recv &= ~(255 << shift);
|
||||
status.recv |= byte << shift;
|
||||
|
||||
if(addr == 3 && status.packet == 15) {
|
||||
status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
gba/player/player.hpp
Normal file
24
gba/player/player.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
struct Player {
|
||||
struct Status {
|
||||
bool enable;
|
||||
bool rumble;
|
||||
|
||||
bool logoDetected;
|
||||
unsigned logoCounter;
|
||||
|
||||
unsigned packet;
|
||||
uint32 send;
|
||||
uint32 recv;
|
||||
} status;
|
||||
|
||||
void power();
|
||||
void frame();
|
||||
|
||||
optional<uint16> keyinput();
|
||||
optional<uint32> read();
|
||||
void write(uint8 byte, uint2 addr);
|
||||
|
||||
void serialize(serializer& s);
|
||||
};
|
||||
|
||||
extern Player player;
|
11
gba/player/serialization.cpp
Normal file
11
gba/player/serialization.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
void Player::serialize(serializer& s) {
|
||||
s.integer(status.enable);
|
||||
s.integer(status.rumble);
|
||||
|
||||
s.integer(status.logoDetected);
|
||||
s.integer(status.logoCounter);
|
||||
|
||||
s.integer(status.packet);
|
||||
s.integer(status.send);
|
||||
s.integer(status.recv);
|
||||
}
|
@@ -44,7 +44,7 @@ void PPU::step(unsigned clocks) {
|
||||
void PPU::power() {
|
||||
create(PPU::Enter, 16777216);
|
||||
|
||||
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0, blur[n] = 0;
|
||||
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0;
|
||||
|
||||
for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
|
||||
for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
|
||||
@@ -149,12 +149,12 @@ void PPU::scanline() {
|
||||
}
|
||||
|
||||
void PPU::frame() {
|
||||
player.frame();
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
output = new uint32[240 * 160];
|
||||
blur = new uint16[240 * 160];
|
||||
|
||||
regs.bg[0].id = BG0;
|
||||
regs.bg[1].id = BG1;
|
||||
@@ -164,7 +164,6 @@ PPU::PPU() {
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] output;
|
||||
delete[] blur;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ struct PPU : Thread, MMIO {
|
||||
#include "registers.hpp"
|
||||
#include "state.hpp"
|
||||
uint32* output;
|
||||
uint16* blur;
|
||||
|
||||
static void Enter();
|
||||
void main();
|
||||
|
@@ -1,15 +1,12 @@
|
||||
void PPU::render_forceblank() {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
uint16* last = blur + regs.vcounter * 240;
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
line[x] = video.palette[(0x7fff + last[x] - ((0x7fff ^ last[x]) & 0x0421)) >> 1];
|
||||
last[x] = 0x7fff;
|
||||
line[x] = 0x7fff;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::render_screen() {
|
||||
uint32* line = output + regs.vcounter * 240;
|
||||
uint16* last = blur + regs.vcounter * 240;
|
||||
|
||||
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
|
||||
if(regs.bg[1].control.mosaic) render_mosaic_background(BG1);
|
||||
@@ -58,9 +55,8 @@ void PPU::render_screen() {
|
||||
color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy);
|
||||
}
|
||||
|
||||
//output pixel; blend with previous pixel to simulate GBA LCD blur
|
||||
line[x] = video.palette[(color + last[x] - ((color ^ last[x]) & 0x0421)) >> 1];
|
||||
last[x] = color;
|
||||
//output pixel
|
||||
line[x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,7 @@ void System::serialize_all(serializer& s) {
|
||||
ppu.serialize(s);
|
||||
apu.serialize(s);
|
||||
bus.serialize(s);
|
||||
player.serialize(s);
|
||||
}
|
||||
|
||||
void System::serialize_init() {
|
||||
|
@@ -15,6 +15,7 @@ void System::term() {
|
||||
|
||||
void System::power() {
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
@@ -39,7 +40,7 @@ void System::run() {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
|
||||
}
|
||||
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
|
||||
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
|
||||
}
|
||||
|
||||
void System::runtosave() {
|
||||
@@ -62,7 +63,7 @@ void System::runthreadtosave() {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
|
||||
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,14 +4,22 @@ namespace GameBoyAdvance {
|
||||
|
||||
Video video;
|
||||
|
||||
void Video::generate_palette(bool color_emulation) {
|
||||
//todo: implement LCD color emulation
|
||||
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
|
||||
for(unsigned color = 0; color < (1 << 15); color++) {
|
||||
if(mode == Emulator::Interface::PaletteMode::None) {
|
||||
palette[color] = color;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned B = (color >> 10) & 31;
|
||||
unsigned G = (color >> 5) & 31;
|
||||
unsigned R = (color >> 0) & 31;
|
||||
|
||||
if(color_emulation) {
|
||||
if(mode == Emulator::Interface::PaletteMode::Standard) {
|
||||
R = R << 11 | R << 6 | R << 1 | R >> 4;
|
||||
G = G << 11 | G << 6 | G << 1 | G >> 4;
|
||||
B = B << 11 | B << 6 | B << 1 | B >> 4;
|
||||
} else {
|
||||
R = curve[R];
|
||||
G = curve[G];
|
||||
B = curve[B];
|
||||
@@ -47,10 +55,6 @@ void Video::generate_palette(bool color_emulation) {
|
||||
R = R << 8 | R;
|
||||
G = G << 8 | G;
|
||||
B = B << 8 | B;
|
||||
} else {
|
||||
R = R << 11 | R << 6 | R << 1 | R >> 4;
|
||||
G = G << 11 | G << 6 | G << 1 | G >> 4;
|
||||
B = B << 11 | B << 6 | B << 1 | B >> 4;
|
||||
}
|
||||
|
||||
palette[color] = interface->videoColor(color, R, G, B);
|
||||
@@ -58,7 +62,7 @@ void Video::generate_palette(bool color_emulation) {
|
||||
}
|
||||
|
||||
Video::Video() {
|
||||
palette = new uint32[1 << 15]();
|
||||
palette = new uint32_t[1 << 15]();
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
struct Video {
|
||||
unsigned* palette;
|
||||
void generate_palette(bool color_emulation);
|
||||
uint32_t* palette = nullptr;
|
||||
void generate_palette(Emulator::Interface::PaletteMode mode);
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
Reference in New Issue
Block a user