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:
Tim Allen
2013-12-20 22:40:39 +11:00
parent 926a39d701
commit 84fab07756
46 changed files with 270 additions and 118 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -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>

View File

@@ -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() {

View File

@@ -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
View 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
View 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;

View 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);
}

View File

@@ -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;
}
}

View File

@@ -4,7 +4,6 @@ struct PPU : Thread, MMIO {
#include "registers.hpp"
#include "state.hpp"
uint32* output;
uint16* blur;
static void Enter();
void main();

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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);
}
}
}

View File

@@ -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() {

View File

@@ -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();