mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r32 release.
byuu says: Changelog: - bsnes-accuracy emulates reset vector properly[1] - bsnes-balanced compiles once more - bsnes-performance compiles once more The balanced and performance profiles are fixed for the last time. They will be removed for v098r01. Please test this WIP as much as you can. I intend to release v098 soon. I know save states are a little unstable for the WS/WSC, but they work well enough for a release. If I can't figure it out soon, I'm going to post v098 anyway. [1] this one's been a really long time coming, but ... one of the bugs I found when I translated Tekkaman Blade was that my translation patch would crash every now and again when you hit the reset button on a real SNES, but it always worked upon power on. Turns out that while power-on initializes the stack register to $01ff, reset does things a little bit differently. Reset actually triggers the reset interrupt vector after putting the CPU into emulation mode, but it doesn't initialize the stack pointer. The net effect is that the stack high byte is set to $01, and the low byte is left as it was. And then the reset vector runs, which pushes the low 16-bits of the program counter, plus the processor flags, onto the stack frame. So you can actually tell where the game was at when the system was reset ... sort of. It's a really weird behavior to be sure. But here's the catch: say you're hacking a game, and so you hook the reset vector with jsl showMyTranslationCreditsSplashScreen, and inside this new subroutine, you then perform whatever bytes you hijacked, and then initialize the stack frame to go about your business drawing the screen, and when you're done, you return via rtl. Generally, this works fine. But if S={0100, 0101, or 0102}, then the stack will wrap due to being in emulation mode at reset. So it will write to {0100, 01ff, 01fe}. But now in your subroutine, you enable native mode. So when you return from your subroutine hijack, it reads the return address from {01ff, 0200, 0201} instead of the expected {01ff, 0100, 0101}. Thus, you get an invalid address back, and you "return" to the wrong location, and your program dies. The odds of this happening depend on how the game handles S, but generally speaking, it's a ~1:85 chance. By emulating this behavior, I'll likely expose this bug in many ROM hacks that do splash screen hooks like this, including my own Tekkaman Blade translation. And it's also very possible that there are commercial games that screw this up as well. But, it's what the real system does. So if any crashes start happening as of this WIP upon resetting the game, well ... it'd happen on real hardware, too.
This commit is contained in:
parent
25eaaa82f4
commit
06d44b4878
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097.31";
|
||||
static const string Version = "097.32";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -30,7 +30,7 @@ auto CPU::synchronizeSMP() -> void {
|
|||
if(SMP::Threaded == true) {
|
||||
if(smp.clock < 0) co_switch(smp.thread);
|
||||
} else {
|
||||
while(smp.clock < 0) smp.enter();
|
||||
while(smp.clock < 0) smp.main();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ auto CPU::synchronizePPU() -> void {
|
|||
if(PPU::Threaded == true) {
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
} else {
|
||||
while(ppu.clock < 0) ppu.enter();
|
||||
while(ppu.clock < 0) ppu.main();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,29 +54,24 @@ auto CPU::synchronizeDevices() -> void {
|
|||
if(device.controllerPort2->clock < 0) co_switch(device.controllerPort2->thread);
|
||||
}
|
||||
|
||||
auto CPU::Enter() -> void { cpu.enter(); }
|
||||
auto CPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
|
||||
auto CPU::enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(status.nmi_pending) {
|
||||
status.nmi_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
|
||||
op_irq();
|
||||
}
|
||||
|
||||
if(status.irq_pending) {
|
||||
status.irq_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
|
||||
op_irq();
|
||||
}
|
||||
|
||||
op_exec();
|
||||
auto CPU::main() -> void {
|
||||
if(status.nmi_pending) {
|
||||
status.nmi_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
|
||||
interrupt();
|
||||
}
|
||||
|
||||
if(status.irq_pending) {
|
||||
status.irq_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
|
||||
interrupt();
|
||||
}
|
||||
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::enable() -> void {
|
||||
|
|
|
@ -11,17 +11,19 @@ struct CPU : Processor::R65816, Thread, public PPUcounter {
|
|||
|
||||
auto pio() -> uint8;
|
||||
auto joylatch() -> bool;
|
||||
auto interrupt_pending() -> bool;
|
||||
auto interruptPending() const -> bool;
|
||||
auto port_read(uint8 port) -> uint8;
|
||||
auto port_write(uint8 port, uint8 data) -> void;
|
||||
auto dmaPortRead(uint24 addr, uint8 data) -> uint8;
|
||||
auto dmaPortWrite(uint24 addr, uint8 data) -> void;
|
||||
auto mmio_read(uint addr, uint8 data) -> uint8;
|
||||
auto mmio_write(uint addr, uint8 data) -> void;
|
||||
|
||||
auto op_io() -> void;
|
||||
auto op_read(uint addr) -> uint8;
|
||||
auto op_write(uint addr, uint8 data) -> void;
|
||||
auto io() -> void;
|
||||
auto read(uint24 addr) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
auto enable() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
@ -37,7 +39,7 @@ private:
|
|||
|
||||
//timing
|
||||
auto queue_event(uint id) -> void;
|
||||
auto last_cycle() -> void;
|
||||
auto lastCycle() -> void;
|
||||
auto add_clocks(uint clocks) -> void;
|
||||
auto scanline() -> void;
|
||||
auto run_auto_joypad_poll() -> void;
|
||||
|
@ -89,8 +91,8 @@ private:
|
|||
uint8 source_bank;
|
||||
|
||||
union {
|
||||
uint16 transfer_size;
|
||||
uint16 indirect_addr;
|
||||
uint16_t transfer_size;
|
||||
uint16_t indirect_addr;
|
||||
};
|
||||
|
||||
uint8 indirect_bank;
|
||||
|
|
|
@ -28,7 +28,7 @@ auto CPU::dma_transfer(bool direction, uint8 bbus, uint abus) -> void {
|
|||
add_clocks(8);
|
||||
dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, data);
|
||||
} else {
|
||||
uint8 data = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus, regs.mdr) : 0x00;
|
||||
uint8 data = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus, regs.mdr) : (uint8)0x00;
|
||||
add_clocks(8);
|
||||
dma_write(dma_addr_valid(abus), abus, data);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ auto CPU::joylatch() -> bool {
|
|||
return status.joypad_strobe_latch;
|
||||
}
|
||||
|
||||
auto CPU::interrupt_pending() -> bool {
|
||||
auto CPU::interruptPending() const -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -18,17 +18,17 @@ auto CPU::port_write(uint8 port, uint8 data) -> void {
|
|||
port_data[port & 3] = data;
|
||||
}
|
||||
|
||||
auto CPU::op_io() -> void {
|
||||
auto CPU::io() -> void {
|
||||
add_clocks(6);
|
||||
}
|
||||
|
||||
auto CPU::op_read(uint addr) -> uint8 {
|
||||
auto CPU::read(uint24 addr) -> uint8 {
|
||||
regs.mdr = bus.read(addr, regs.mdr);
|
||||
add_clocks(speed(addr));
|
||||
return regs.mdr;
|
||||
}
|
||||
|
||||
auto CPU::op_write(uint addr, uint8 data) -> void {
|
||||
auto CPU::write(uint24 addr, uint8 data) -> void {
|
||||
add_clocks(speed(addr));
|
||||
bus.write(addr, regs.mdr = data);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
auto CPU::dmaPortRead(uint24 addr, uint8 data) -> uint8 {
|
||||
return mmio_read(addr, data);
|
||||
}
|
||||
|
||||
auto CPU::dmaPortWrite(uint24 addr, uint8 data) -> void {
|
||||
return mmio_write(addr, data);
|
||||
}
|
||||
|
||||
auto CPU::mmio_read(uint addr, uint8 data) -> uint8 {
|
||||
if((addr & 0xffc0) == 0x2140) {
|
||||
synchronizeSMP();
|
||||
|
@ -186,7 +194,7 @@ auto CPU::mmio_write(uint addr, uint8 data) -> void {
|
|||
case 0x4206: {
|
||||
status.wrdivb = data;
|
||||
status.rddiv = status.wrdivb ? status.wrdiva / status.wrdivb : 0xffff;
|
||||
status.rdmpy = status.wrdivb ? status.wrdiva % status.wrdivb : status.wrdiva;
|
||||
status.rdmpy = status.wrdivb ? status.wrdiva % status.wrdivb : (uint)status.wrdiva;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ auto CPU::queue_event(uint id) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::last_cycle() -> void {
|
||||
auto CPU::lastCycle() -> void {
|
||||
if(status.irq_lock) {
|
||||
status.irq_lock = false;
|
||||
return;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
|
||||
#include <sfc/dsp/audio.cpp>
|
||||
DSP dsp;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
@ -17,13 +18,13 @@ auto DSP::step(uint clocks) -> void {
|
|||
|
||||
auto DSP::synchronizeSMP() -> void {
|
||||
if(SMP::Threaded == true) {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(smp.thread);
|
||||
} else {
|
||||
while(clock >= 0) smp.enter();
|
||||
while(clock >= 0) smp.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto DSP::enter() -> void {
|
||||
auto DSP::main() -> void {
|
||||
spc_dsp.run(1);
|
||||
step(24);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <sfc/dsp/audio.hpp>
|
||||
|
||||
#include "SPC_DSP.h"
|
||||
|
||||
struct DSP : Thread {
|
||||
|
@ -12,7 +14,7 @@ struct DSP : Thread {
|
|||
auto read(uint8 addr) -> uint8;
|
||||
auto write(uint8 addr, uint8 data) -> void;
|
||||
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
@ -22,7 +24,7 @@ struct DSP : Thread {
|
|||
|
||||
private:
|
||||
SPC_DSP spc_dsp;
|
||||
int16 samplebuffer[8192];
|
||||
int16_t samplebuffer[8192];
|
||||
bool channel_enabled[8];
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
|
||||
#include <sfc/ppu/video.cpp>
|
||||
PPU ppu;
|
||||
|
||||
#include "memory/memory.cpp"
|
||||
|
@ -49,55 +50,50 @@ auto PPU::step(uint clocks) -> void {
|
|||
|
||||
auto PPU::synchronizeCPU() -> void {
|
||||
if(CPU::Threaded == true) {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
} else {
|
||||
while(clock >= 0) cpu.enter();
|
||||
while(clock >= 0) cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::Enter() -> void { ppu.enter(); }
|
||||
auto PPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ppu.main();
|
||||
}
|
||||
|
||||
auto PPU::enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
auto PPU::main() -> void {
|
||||
//H = 0 (initialize)
|
||||
scanline();
|
||||
add_clocks(10);
|
||||
|
||||
//H = 10 (cache mode7 registers + OAM address reset)
|
||||
cache.m7_hofs = regs.m7_hofs;
|
||||
cache.m7_vofs = regs.m7_vofs;
|
||||
cache.m7a = regs.m7a;
|
||||
cache.m7b = regs.m7b;
|
||||
cache.m7c = regs.m7c;
|
||||
cache.m7d = regs.m7d;
|
||||
cache.m7x = regs.m7x;
|
||||
cache.m7y = regs.m7y;
|
||||
if(vcounter() == (!overscan() ? 225 : 240)) {
|
||||
if(regs.display_disabled == false) {
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
|
||||
//H = 0 (initialize)
|
||||
scanline();
|
||||
add_clocks(10);
|
||||
|
||||
//H = 10 (cache mode7 registers + OAM address reset)
|
||||
cache.m7_hofs = regs.m7_hofs;
|
||||
cache.m7_vofs = regs.m7_vofs;
|
||||
cache.m7a = regs.m7a;
|
||||
cache.m7b = regs.m7b;
|
||||
cache.m7c = regs.m7c;
|
||||
cache.m7d = regs.m7d;
|
||||
cache.m7x = regs.m7x;
|
||||
cache.m7y = regs.m7y;
|
||||
if(vcounter() == (!overscan() ? 225 : 240)) {
|
||||
if(regs.display_disabled == false) {
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
}
|
||||
add_clocks(502);
|
||||
|
||||
//H = 512 (render)
|
||||
render_scanline();
|
||||
add_clocks(640);
|
||||
|
||||
//H = 1152 (cache OBSEL)
|
||||
if(cache.oam_basesize != regs.oam_basesize) {
|
||||
cache.oam_basesize = regs.oam_basesize;
|
||||
sprite_list_valid = false;
|
||||
}
|
||||
cache.oam_nameselect = regs.oam_nameselect;
|
||||
cache.oam_tdaddr = regs.oam_tdaddr;
|
||||
add_clocks(lineclocks() - 1152); //seek to start of next scanline
|
||||
|
||||
}
|
||||
add_clocks(502);
|
||||
|
||||
//H = 512 (render)
|
||||
render_scanline();
|
||||
add_clocks(640);
|
||||
|
||||
//H = 1152 (cache OBSEL)
|
||||
if(cache.oam_basesize != regs.oam_basesize) {
|
||||
cache.oam_basesize = regs.oam_basesize;
|
||||
sprite_list_valid = false;
|
||||
}
|
||||
cache.oam_nameselect = regs.oam_nameselect;
|
||||
cache.oam_tdaddr = regs.oam_tdaddr;
|
||||
add_clocks(lineclocks() - 1152); //seek to start of next scanline
|
||||
}
|
||||
|
||||
auto PPU::add_clocks(uint clocks) -> void {
|
||||
|
@ -131,7 +127,8 @@ auto PPU::scanline() -> void {
|
|||
}
|
||||
|
||||
if(line == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
video.refresh();
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,6 +398,8 @@ auto PPU::reset() -> void {
|
|||
regs.bg_y[1] = 0;
|
||||
regs.bg_y[2] = 0;
|
||||
regs.bg_y[3] = 0;
|
||||
|
||||
video.reset();
|
||||
}
|
||||
|
||||
auto PPU::layer_enable(uint layer, uint priority, bool enable) -> void {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <sfc/ppu/video.hpp>
|
||||
|
||||
struct PPU : Thread, public PPUcounter {
|
||||
enum : bool { Threaded = true };
|
||||
|
||||
|
@ -23,7 +25,7 @@ struct PPU : Thread, public PPUcounter {
|
|||
auto scanline() -> void;
|
||||
auto render_scanline() -> void;
|
||||
auto frame() -> void;
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
auto enable() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
|
|
@ -5,7 +5,7 @@ auto PPU::update_bg_info() -> void {
|
|||
|
||||
for(unsigned bg = 0; bg < 4; bg++) {
|
||||
bg_info[bg].th = (regs.bg_tilesize[bg] ? 4 : 3);
|
||||
bg_info[bg].tw = (hires ? 4 : bg_info[bg].th);
|
||||
bg_info[bg].tw = (hires ? 4 : (uint)bg_info[bg].th);
|
||||
|
||||
bg_info[bg].mx = (bg_info[bg].th == 4 ? (width << 1) : width);
|
||||
bg_info[bg].my = bg_info[bg].mx;
|
||||
|
@ -96,7 +96,7 @@ auto PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) -> void {
|
|||
bool mirror_x, mirror_y;
|
||||
|
||||
const uint8* tile_ptr;
|
||||
const uint16* mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0];
|
||||
const uint16* mtable = mosaic_table[regs.mosaic_enabled[bg] ? (uint)regs.mosaic_size : 0];
|
||||
const bool is_opt_mode = (mode == 2 || mode == 4 || mode == 6);
|
||||
const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (mode == 3 || mode == 4));
|
||||
|
||||
|
|
|
@ -118,12 +118,12 @@ auto PPU::flush_pixel_cache() -> void {
|
|||
}
|
||||
|
||||
auto PPU::alloc_tiledata_cache() -> void {
|
||||
bg_tiledata[TILE_2BIT] = new uint8_t[262144]();
|
||||
bg_tiledata[TILE_4BIT] = new uint8_t[131072]();
|
||||
bg_tiledata[TILE_8BIT] = new uint8_t[ 65536]();
|
||||
bg_tiledata_state[TILE_2BIT] = new uint8_t[ 4096]();
|
||||
bg_tiledata_state[TILE_4BIT] = new uint8_t[ 2048]();
|
||||
bg_tiledata_state[TILE_8BIT] = new uint8_t[ 1024]();
|
||||
bg_tiledata[TILE_2BIT] = new uint8[262144]();
|
||||
bg_tiledata[TILE_4BIT] = new uint8[131072]();
|
||||
bg_tiledata[TILE_8BIT] = new uint8[ 65536]();
|
||||
bg_tiledata_state[TILE_2BIT] = new uint8[ 4096]();
|
||||
bg_tiledata_state[TILE_4BIT] = new uint8[ 2048]();
|
||||
bg_tiledata_state[TILE_8BIT] = new uint8[ 1024]();
|
||||
}
|
||||
|
||||
//marks all tiledata cache entries as dirty
|
||||
|
|
|
@ -44,13 +44,13 @@ auto PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) -> void {
|
|||
uint16* mtable_x;
|
||||
uint16* mtable_y;
|
||||
if(bg == BG1) {
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? (uint)regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? (uint)regs.mosaic_size : 0];
|
||||
} else { //bg == BG2
|
||||
//Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic,
|
||||
//and BG2 mosaic enable to control horizontal mosaic...
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? (uint)regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? (uint)regs.mosaic_size : 0];
|
||||
}
|
||||
|
||||
int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8);
|
||||
|
@ -108,7 +108,7 @@ auto PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) -> void {
|
|||
|
||||
if(!palette) continue;
|
||||
|
||||
_x = (regs.mode7_hflip == false) ? (x) : (255 - x);
|
||||
_x = (regs.mode7_hflip == false) ? ((uint)x) : (255 - x);
|
||||
|
||||
uint32 col;
|
||||
if(regs.direct_color == true && bg == BG1) {
|
||||
|
|
|
@ -69,7 +69,7 @@ auto PPU::is_sprite_on_scanline() -> bool {
|
|||
sprite_item* spr = &sprite_list[active_sprite];
|
||||
if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false;
|
||||
|
||||
int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1);
|
||||
int spr_height = (regs.oam_interlace == false) ? ((uint)spr->height) : (spr->height >> 1);
|
||||
if(line >= spr->y && line < (spr->y + spr_height)) return true;
|
||||
if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true;
|
||||
return false;
|
||||
|
|
|
@ -111,7 +111,7 @@ auto PPU::Background::render() -> void {
|
|||
hscroll = regs.hoffset;
|
||||
vscroll = regs.voffset;
|
||||
|
||||
uint y = (regs.mosaic == 0 ? self.vcounter() : mosaic_voffset);
|
||||
uint y = (regs.mosaic == 0 ? (uint)self.vcounter() : mosaic_voffset);
|
||||
if(hires) {
|
||||
hscroll <<= 1;
|
||||
if(self.regs.interlace) y = (y << 1) + self.field();
|
||||
|
|
|
@ -14,7 +14,7 @@ auto PPU::Background::render_mode7() -> void {
|
|||
int hofs = sclip<13>(self.regs.mode7_hoffset);
|
||||
int vofs = sclip<13>(self.regs.mode7_voffset);
|
||||
|
||||
int y = (self.regs.mode7_vflip == false ? self.vcounter() : 255 - self.vcounter());
|
||||
int y = (self.regs.mode7_vflip == false ? (uint)self.vcounter() : 255 - self.vcounter());
|
||||
|
||||
uint16* mosaic_x;
|
||||
uint16* mosaic_y;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
|
||||
#include <sfc/ppu/video.cpp>
|
||||
PPU ppu;
|
||||
|
||||
#include "mmio/mmio.cpp"
|
||||
|
@ -39,28 +40,24 @@ auto PPU::step(uint clocks) -> void {
|
|||
|
||||
auto PPU::synchronizeCPU() -> void {
|
||||
if(CPU::Threaded == true) {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
} else {
|
||||
while(clock >= 0) cpu.enter();
|
||||
while(clock >= 0) cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::Enter() -> void { ppu.enter(); }
|
||||
auto PPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ppu.main();
|
||||
}
|
||||
|
||||
auto PPU::enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
scanline();
|
||||
if(vcounter() < display.height && vcounter()) {
|
||||
add_clocks(512);
|
||||
render_scanline();
|
||||
add_clocks(lineclocks() - 512);
|
||||
} else {
|
||||
add_clocks(lineclocks());
|
||||
}
|
||||
auto PPU::main() -> void {
|
||||
scanline();
|
||||
if(vcounter() < display.height && vcounter()) {
|
||||
add_clocks(512);
|
||||
render_scanline();
|
||||
add_clocks(lineclocks() - 512);
|
||||
} else {
|
||||
add_clocks(lineclocks());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +90,8 @@ auto PPU::scanline() -> void {
|
|||
if(vcounter() == display.height && regs.display_disable == false) sprite.address_reset();
|
||||
|
||||
if(vcounter() == 241) {
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
video.refresh();
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +124,7 @@ auto PPU::reset() -> void {
|
|||
mmio_reset();
|
||||
display.interlace = false;
|
||||
display.overscan = false;
|
||||
video.reset();
|
||||
}
|
||||
|
||||
auto PPU::layer_enable(uint layer, uint priority, bool enable) -> void {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <sfc/ppu/video.hpp>
|
||||
|
||||
struct PPU : Thread, public PPUcounter {
|
||||
enum : bool { Threaded = true };
|
||||
|
||||
|
@ -11,7 +13,7 @@ struct PPU : Thread, public PPUcounter {
|
|||
auto interlace() const -> bool;
|
||||
auto overscan() const -> bool;
|
||||
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
auto enable() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
|
|
@ -21,7 +21,7 @@ auto SMP::synchronizeCPU() -> void {
|
|||
if(CPU::Threaded == true) {
|
||||
//if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
} else {
|
||||
while(clock >= 0) cpu.enter();
|
||||
while(clock >= 0) cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,11 +29,11 @@ auto SMP::synchronizeDSP() -> void {
|
|||
if(DSP::Threaded == true) {
|
||||
//if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread);
|
||||
} else {
|
||||
while(dsp.clock < 0) dsp.enter();
|
||||
while(dsp.clock < 0) dsp.main();
|
||||
}
|
||||
}
|
||||
|
||||
auto SMP::enter() -> void {
|
||||
auto SMP::main() -> void {
|
||||
while(clock < 0) op_step();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ struct SMP : Thread {
|
|||
auto mmio_read(uint addr) -> uint;
|
||||
auto mmio_write(uint addr, uint data) -> void;
|
||||
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
@ -69,8 +69,8 @@ struct SMP : Thread {
|
|||
uint16 pc;
|
||||
uint8 sp;
|
||||
union {
|
||||
uint16 ya;
|
||||
struct { uint8 order_lsb2(a, y); };
|
||||
uint16_t ya;
|
||||
struct { uint8_t order_lsb2(a, y); };
|
||||
};
|
||||
uint8 x;
|
||||
Flags p;
|
||||
|
|
|
@ -66,16 +66,21 @@ auto CPU::main() -> void {
|
|||
status.interrupt_pending = false;
|
||||
if(status.nmi_pending) {
|
||||
status.nmi_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
|
||||
regs.vector = !regs.e ? 0xffea : 0xfffa;
|
||||
interrupt();
|
||||
debugger.op_nmi();
|
||||
} else if(status.irq_pending) {
|
||||
status.irq_pending = false;
|
||||
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
|
||||
regs.vector = !regs.e ? 0xffee : 0xfffe;
|
||||
interrupt();
|
||||
debugger.op_irq();
|
||||
} else if(status.reset_pending) {
|
||||
status.reset_pending = false;
|
||||
addClocks(132);
|
||||
regs.vector = 0xfffc;
|
||||
interrupt();
|
||||
} else if(status.power_pending) {
|
||||
status.power_pending = false;
|
||||
addClocks(186);
|
||||
regs.pc.l = bus.read(0xfffc, regs.mdr);
|
||||
regs.pc.h = bus.read(0xfffd, regs.mdr);
|
||||
|
@ -146,6 +151,9 @@ auto CPU::power() -> void {
|
|||
channel.line_counter = 0xff;
|
||||
channel.unknown = 0xff;
|
||||
}
|
||||
|
||||
status.power_pending = true;
|
||||
status.interrupt_pending = true;
|
||||
}
|
||||
|
||||
auto CPU::reset() -> void {
|
||||
|
@ -255,7 +263,7 @@ auto CPU::reset() -> void {
|
|||
status.irq_pending = false;
|
||||
status.irq_hold = false;
|
||||
|
||||
status.reset_pending = true;
|
||||
status.reset_pending = !status.power_pending;
|
||||
status.interrupt_pending = true;
|
||||
|
||||
status.dma_active = false;
|
||||
|
|
|
@ -123,6 +123,7 @@ privileged:
|
|||
bool irq_pending;
|
||||
bool irq_hold;
|
||||
|
||||
bool power_pending;
|
||||
bool reset_pending;
|
||||
|
||||
//DMA
|
||||
|
|
|
@ -35,6 +35,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s.integer(status.irq_pending);
|
||||
s.integer(status.irq_hold);
|
||||
|
||||
s.integer(status.power_pending);
|
||||
s.integer(status.reset_pending);
|
||||
|
||||
s.integer(status.dma_active);
|
||||
|
|
Loading…
Reference in New Issue