diff --git a/bsnes.cfg b/bsnes.cfg index 2e683772..8bb84202 100644 --- a/bsnes.cfg +++ b/bsnes.cfg @@ -1,57 +1,87 @@ -#[bsnes v0.009 configuration file] +# Applies contrast adjust filter to video output when enabled +# Works by lowering the brightness of darker colors, +# while leaving brighter colors alone; thus reducing saturation +# (default = true) +snes.video_color_curve = true -#[apu enable] -apu.enabled = true +# Selects color adjustment filter for video output +# 0 = Normal (no filter, rgb555) +# 1 = Grayscale mode (l5) +# 2 = VGA mode (rgb332) +# 3 = Genesis mode (rgb333) +# (default = 0) +snes.video_color_adjust_mode = 0 -#[video mode] -# 0: 256x224w -# 1: 512x448w -# 2: 960x720w -# 3: 640x480f -# 4: 1024x768f +# Mutes SNES audio output when enabled +# (default = true) +snes.mute = true + +# Video mode +# 0 = 256x224w +# 1 = 512x448w +# 2 = 960x720w +# 3 = 640x480f +# 4 = 1024x768f +# (default = 1) video.mode = 1 -#[video memory type] -# true: video ram (VRAM) -# false: system ram (SRAM) -# -# VRAM results in the image being stretched in hardware, -# which is generally much faster, and automatically adds -# bilinear filtering (if the card supports it). -# -# However, some video cards end up taking a major speed -# loss when this option is enabled. It is also the only -# way to guarantee that the output image will not be -# filtered. +# Use Video RAM instead of System RAM +# (default = true) video.use_vram = true -#[color curve] -# gives a more NTSC TV-style feel to the color palette -# by darkening the image contrast. -video.color_curve = enabled - -#[show fps] -# true: show fps in titlebar -# false: do not show fps in titlebar -gui.show_fps = true - -#[wait for vertical retrace] +# Wait for vertical retrace when updating screen +# (default = false) video.vblank = false -#[joypad 1 configuration] -# Key numbers are standard windows VK_* keys. -# Unfortunately, I don't have a table of common -# key mappings to list here... use GUI joypad -# configuration utility to edit these. +# Show framerate in window title +# (default = true) +gui.show_fps = false + +# Joypad1 up +# (default = 0x26) input.joypad1.up = 0x26 + +# Joypad1 down +# (default = 0x28) input.joypad1.down = 0x28 + +# Joypad1 left +# (default = 0x25) input.joypad1.left = 0x25 + +# Joypad1 right +# (default = 0x27) input.joypad1.right = 0x27 + +# Joypad1 A +# (default = 0x58) input.joypad1.a = 0x58 + +# Joypad1 B +# (default = 0x5a) input.joypad1.b = 0x5a + +# Joypad1 X +# (default = 0x53) input.joypad1.x = 0x53 + +# Joypad1 Y +# (default = 0x41) input.joypad1.y = 0x41 + +# Joypad1 L +# (default = 0x44) input.joypad1.l = 0x44 + +# Joypad1 R +# (default = 0x43) input.joypad1.r = 0x43 + +# Joypad1 select +# (default = 0x10) input.joypad1.select = 0x10 -input.joypad1.start = 0x0d + +# Joypad1 start +# (default = 0xd) +input.joypad1.start = 0xd + diff --git a/bsnes.exe b/bsnes.exe index 6081d321..2a1747ec 100644 Binary files a/bsnes.exe and b/bsnes.exe differ diff --git a/license.txt b/license.txt index ae85df60..23d55685 100644 --- a/license.txt +++ b/license.txt @@ -17,5 +17,5 @@ emulators. The Simple DirectMedia Layer library source code is available from: http://www.libsdl.org/ -This library is distributed under the terms of the GNU LGPL license: +This library is distributed under the terms of the GNU LGPL: http://www.gnu.org/copyleft/lesser.html diff --git a/src/apu/apu.cpp b/src/apu/apu.cpp index 3700272a..27735f3d 100644 --- a/src/apu/apu.cpp +++ b/src/apu/apu.cpp @@ -1,2 +1,3 @@ #include "../base.h" +#include "iplrom.h" #include "dapu.cpp" diff --git a/src/apu/apu.h b/src/apu/apu.h index 2b37273e..8645248f 100644 --- a/src/apu/apu.h +++ b/src/apu/apu.h @@ -1,10 +1,9 @@ -#define _APU_IPLROM -#include "iplrom.h" #include "apuregs.h" class APU { public: APURegs regs; +static const uint8 iplrom[64]; enum { FLAG_N = 0x80, FLAG_V = 0x40, FLAG_P = 0x20, FLAG_B = 0x10, @@ -18,6 +17,7 @@ APURegs regs; virtual uint8 port_read (uint8 port) = 0; virtual void port_write(uint8 port, uint8 value) = 0; + virtual uint8 *get_spcram_handle() = 0; virtual void run() = 0; virtual uint32 cycles_executed() = 0; virtual void power() = 0; diff --git a/src/apu/bapu/bapu.cpp b/src/apu/bapu/bapu.cpp index 452fbc2d..7395bce2 100644 --- a/src/apu/bapu/bapu.cpp +++ b/src/apu/bapu/bapu.cpp @@ -24,7 +24,7 @@ uint8 r; break; case 0xf3: //DSPDATA //0x80-0xff is a read-only mirror of 0x00-0x7f - r = dsp_regs[status.dsp_addr & 0x7f]; + r = dsp->read(status.dsp_addr & 0x7f); break; case 0xf4: //CPUIO0 case 0xf5: //CPUIO1 @@ -111,7 +111,7 @@ void bAPU::spcram_write(uint16 addr, uint8 value) { case 0xf3: //DSPDATA //0x80-0xff is a read-only mirror of 0x00-0x7f if(status.dsp_addr < 0x80) { - dsp_regs[status.dsp_addr & 0x7f] = value; + dsp->write(status.dsp_addr & 0x7f, value); } break; case 0xf4: //CPUIO0 @@ -209,6 +209,14 @@ void bAPU::stack_write(uint8 value) { regs.sp--; } +uint8 *bAPU::get_spcram_handle() { + if(!spcram) { + alert("bAPU::get_spcram_handle() -- spcram uninitialized"); + } + + return spcram; +} + void bAPU::run() { exec_cycle(); } @@ -247,15 +255,12 @@ void bAPU::reset() { t0.stage3_ticks = 0; t1.stage3_ticks = 0; t2.stage3_ticks = 0; - - memset(dsp_regs, 0, 128); } bAPU::bAPU() { init_op_table(); spcram = (uint8*)malloc(65536); - memcpy(iplrom, spc700_iplrom, 64); t0.cycle_frequency = 128; //1.024mhz / 8khz = 128 t1.cycle_frequency = 128; //1.024mhz / 8khz = 128 diff --git a/src/apu/bapu/bapu.h b/src/apu/bapu/bapu.h index b09d4338..065f9972 100644 --- a/src/apu/bapu/bapu.h +++ b/src/apu/bapu/bapu.h @@ -29,12 +29,13 @@ struct { bAPUTimer t0, t1, t2; -uint8 *spcram, iplrom[64], dsp_regs[128]; +uint8 *spcram; inline uint8 spcram_read (uint16 addr); inline void spcram_write(uint16 addr, uint8 value); inline uint8 port_read (uint8 port); inline void port_write(uint8 port, uint8 value); + inline uint8 *get_spcram_handle(); inline void run(); inline uint32 cycles_executed(); inline void power(); diff --git a/src/apu/bapu/bapugen.exe b/src/apu/bapu/bapugen.exe deleted file mode 100644 index 4f0e81b9..00000000 Binary files a/src/apu/bapu/bapugen.exe and /dev/null differ diff --git a/src/apu/bapuskip/bapuskip.h b/src/apu/bapuskip/bapuskip.h index 9d907c23..63b1da50 100644 --- a/src/apu/bapuskip/bapuskip.h +++ b/src/apu/bapuskip/bapuskip.h @@ -1,4 +1,7 @@ class bAPUSkip : public APU { +private: +uint8 spcram[65536]; + public: struct _apu_port { @@ -26,6 +29,7 @@ enum { uint8 port_read (uint8 port); void port_write (uint8 port, uint8 value); + uint8 *get_spcram_handle() { return spcram; } void run(); uint32 cycles_executed() { return 12 * 24; } void power(); diff --git a/src/apu/iplrom.h b/src/apu/iplrom.h index 84cb6441..9fcf4d38 100644 --- a/src/apu/iplrom.h +++ b/src/apu/iplrom.h @@ -3,12 +3,8 @@ //allow writing to the IPLROM, all writes are //instead mapped to the extended SPC700 RAM region, //accessible when $f1 bit 7 is clear. -//If you use this buffer directly, make sure not -//to write to it, as this will break other APU -//implementations that attempt to use this buffer. -#ifdef _APU_IPLROM -const uint8 spc700_iplrom[64] = { +const uint8 APU::iplrom[64] = { /*ffc0*/ 0xcd, 0xef, //mov x,#$ef /*ffc2*/ 0xbd, //mov sp,x /*ffc3*/ 0xe8, 0x00, //mov a,#$00 @@ -43,6 +39,3 @@ const uint8 spc700_iplrom[64] = { /*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) /*fffe*/ 0xc0, 0xff //---reset vector location ($ffc0) }; -#else -extern const uint8 spc700_iplrom[64]; -#endif diff --git a/src/base.h b/src/base.h index bbb683fb..783fa03f 100644 --- a/src/base.h +++ b/src/base.h @@ -1,5 +1,8 @@ #include #include "lib/libbase.h" +#include "lib/libvector.h" +#include "lib/libstring.h" +#include "lib/libconfig.h" #if defined(_WIN32) #define _WIN32_ @@ -11,12 +14,6 @@ #error "unknown architecture" #endif -//structs -typedef struct { -uint8 *data; -uint32 size; -}lfile; - //platform-specific global functions void *memalloc(uint32 size, char *name = 0, ...); void memfree(void *mem, char *name = 0, ...); diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index b26d0e3f..c26694bb 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,6 +1,15 @@ #include "../../base.h" #include "sdd1emu.cpp" +void SDD1::init() { +} + +void SDD1::enable() { + for(int i=0x4800;i<=0x4807;i++) { + mem_bus->set_mmio_mapper(i, mmio); + } +} + void SDD1::power() { reset(); } diff --git a/src/chip/sdd1/sdd1.h b/src/chip/sdd1/sdd1.h index d8cba35b..6bb4af8f 100644 --- a/src/chip/sdd1/sdd1.h +++ b/src/chip/sdd1/sdd1.h @@ -21,6 +21,7 @@ struct { bool dma_active; }sdd1; void init(); + void enable(); void power(); void reset(); uint32 offset(uint32 addr); diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index 3601ecd9..590037ab 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -74,6 +74,14 @@ tm *t; srtc.data[12] = t->tm_wday; } +void SRTC::init() { +} + +void SRTC::enable() { + mem_bus->set_mmio_mapper(0x2800, mmio); + mem_bus->set_mmio_mapper(0x2801, mmio); +} + void SRTC::power() { memset(&srtc, 0, sizeof(srtc)); reset(); diff --git a/src/chip/srtc/srtc.h b/src/chip/srtc/srtc.h index 8f0ef48b..532a1f1e 100644 --- a/src/chip/srtc/srtc.h +++ b/src/chip/srtc/srtc.h @@ -41,11 +41,13 @@ Index Description Range 12 Day of week 0-6 (0=Sunday, ...) ******************************/ struct { - int8 index; - uint8 mode; - uint8 data[MAX_SRTC_INDEX + 1]; -}srtc; + int8 index; + uint8 mode; + uint8 data[MAX_SRTC_INDEX + 1]; +} srtc; void set_time(); + void init(); + void enable(); void power(); void reset(); void write(uint8 data); diff --git a/src/config/config.cpp b/src/config/config.cpp new file mode 100644 index 00000000..a3d82316 --- /dev/null +++ b/src/config/config.cpp @@ -0,0 +1,28 @@ +Config config_file; + +namespace config { + +SNES::VideoColorAdjust SNES::video_color_curve(&config_file, "snes.video_color_curve", + "Applies contrast adjust filter to video output when enabled\n" + "Works by lowering the brightness of darker colors,\n" + "while leaving brighter colors alone; thus reducing saturation", + true, Setting::TRUE_FALSE); + +SNES::VideoColorAdjust SNES::video_color_adjust_mode(&config_file, "snes.video_color_adjust_mode", + "Selects color adjustment filter for video output\n" + " 0 = Normal (no filter, rgb555)\n" + " 1 = Grayscale mode (l5)\n" + " 2 = VGA mode (rgb332)\n" + " 3 = Genesis mode (rgb333)", + 0, Setting::DEC); + +void SNES::VideoColorAdjust::set(uint32 _data) { + data = _data; + ::snes->update_color_lookup_table(); +} + +Setting SNES::mute(&config_file, "snes.mute", + "Mutes SNES audio output when enabled", + true, Setting::TRUE_FALSE); + +}; diff --git a/src/config/config.h b/src/config/config.h new file mode 100644 index 00000000..9933d6c8 --- /dev/null +++ b/src/config/config.h @@ -0,0 +1,15 @@ +extern Config config_file; + +namespace config { + +extern struct SNES { + static class VideoColorAdjust : public Setting { + public: + void set(uint32 _data); + SettingOperators(VideoColorAdjust); + } video_color_curve, video_color_adjust_mode; + + static Setting mute; +} snes; + +}; diff --git a/src/cpu/bcpu/bcpu.cpp b/src/cpu/bcpu/bcpu.cpp index 33ea3b4c..295b1f2e 100644 --- a/src/cpu/bcpu/bcpu.cpp +++ b/src/cpu/bcpu/bcpu.cpp @@ -13,152 +13,26 @@ #include "bcpu_timing.cpp" -uint8 bCPU::pio_status() { - return status.pio; -} +#include "bcpu_int.cpp" -/*********** - *** IRQ *** - *********** -cycles: - [1] pbr,pc ; io/opcode - [2] pbr,pc ; io - [3] 0,s ; pbr - [4] 0,s-1 ; pch - [5] 0,s-2 ; pcl - [6] 0,s-3 ; p - [7] 0,va ; aavl - [8] 0,va+1 ; aavh -*/ -void bCPU::irq(uint16 addr) { - if(status.cpu_state == CPUSTATE_WAI) { - status.cpu_state = CPUSTATE_RUN; - regs.pc.w++; - } - -//GTE documentation is incorrect, first cycle -//is a memory read fetch from PBR:PC - add_cycles(mem_bus->speed(regs.pc.d)); - add_cycles(6); - stack_write(regs.pc.b); - stack_write(regs.pc.h); - stack_write(regs.pc.l); - stack_write(regs.p); - rd.l = op_read(OPMODE_ADDR, addr); - rd.h = op_read(OPMODE_ADDR, addr + 1); - - regs.pc.b = 0x00; - regs.pc.w = rd.w; - regs.p.i = 1; - regs.p.d = 0; - -//let debugger know the new IRQ opcode address - snes->notify(SNES::CPU_EXEC_OPCODE_END); -} - -//vcounter range verified on real hardware, -//HDMA runs on the very first scanline of vblank -bool bCPU::hdma_test() { - if(status.hdma_triggered == false) { - if(vcounter() <= (overscan()?239:224) && hcounter() >= 278) { - status.hdma_triggered = true; - return true; - } - } - return false; -} - -//NMI range: V==225/240,H>=12 ; V>225/240 -bool bCPU::nmi_test() { - if(status.nmi_exec == true)return false; - -//[status.cycle_count index] -// 6->12 -// 8->14 -int hc = status.cycle_count + 6; - if(vcounter() == ((overscan()?239:224) + 1) && hcycles() < hc) { - //dprintf("* miss at %3d,%4d,%3x : %d x=%0.4x", vcounter(), hcycles(), hcounter(), status.cycle_count, regs.x.w); - } - - if( - (vcounter() == ((overscan()?239:224) + 1) && hcycles() >= hc) || - (vcounter() > ((overscan()?239:224) + 1)) - ) { - //dprintf("* %3d,%4d,%3x : %d x=%0.4x", vcounter(), hcycles(), hcounter(), status.cycle_count, regs.x.w); - status.nmi_exec = true; - return status.nmi_enabled; - } - - return false; -} - -bool bCPU::irq_test() { -int vpos, hpos; - if(regs.p.i)return false; //no interrupt can occur with I flag set - if(status.irq_read == true)return false; //same as above - if(status.virq_enabled == false && status.hirq_enabled == false)return false; - -//if irq_exec is true, then an IRQ occurred already. -//IRQs will continue to fire until $4211 is read from, or -//$4200 is written to, where irq_exec is set back to false. - if(status.irq_exec == true) { - return true; - } - -//calculate V/H positions required for IRQ to trigger - vpos = status.virq_pos; - hpos = (status.hirq_enabled) ? status.hirq_pos : 0; - -//positions that can never be latched -//region_scanlines() = 262/NTSC, 312/PAL -//PAL results are unverified on hardware - if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)return false; - if(vpos == (region_scanlines() - 1) && hpos == 339 && interlace() == false)return false; - if(vpos == region_scanlines() && interlace() == false)return false; - if(vpos == region_scanlines() && hpos == 339)return false; - if(vpos > region_scanlines())return false; - if(hpos > 339)return false; - - if(hpos == 0) { - hpos = status.cycle_count + 14; - } else { - hpos <<= 2; - hpos += status.cycle_count + 18; - //it should be OK to use the current line cycles/frame lines, - //as the IRQ will only trigger on the correct scanline anyway... - if(hpos >= time.line_cycles) { - hpos -= time.line_cycles; - vpos++; - if(vpos >= time.frame_lines) { - vpos = 0; - } - } - } - - if(status.virq_enabled == true && vcounter() != vpos)return false; - - if(hcycles() >= hpos) { - status.irq_exec = true; - return true; - } - - return false; -} +uint8 bCPU::pio_status() { return status.pio; } void bCPU::run() { switch(status.cpu_state) { case CPUSTATE_DMA: dma_run(); break; - case CPUSTATE_RUN: case CPUSTATE_WAI: + case CPUSTATE_RUN: if(status.cycle_pos == 0) { //interrupts only trigger on opcode edges - if(nmi_test() == true) { + if(time.nmi_pending == true) { + time.nmi_pending = false; irq(0xffea); break; } - if(irq_test() == true) { + if(time.irq_pending == true) { + time.irq_pending = false; irq(0xffee); break; } @@ -184,19 +58,14 @@ void bCPU::scanline() { //connected status bit. status.joypad1_read_pos = 16; } - - if(status.virq_enabled == false) { - status.irq_read = false; - } } void bCPU::frame() { hdma_initialize(); - status.nmi_read = false; - status.nmi_exec = false; - - status.irq_read = false; + time.nmi_read = 1; + time.nmi_line = 1; + time.nmi_transition = 0; } void bCPU::power() { @@ -234,12 +103,6 @@ void bCPU::reset() { status.hdma_triggered = false; - status.nmi_read = false; - status.nmi_exec = false; - - status.irq_read = false; - status.irq_exec = false; - apu_port[0] = 0x00; apu_port[1] = 0x00; apu_port[2] = 0x00; @@ -258,43 +121,52 @@ void bCPU::port_write(uint8 port, uint8 value) { void bCPU::cpu_c2() { if(regs.d.l != 0x00) { - status.cycle_count = 6; - add_cycles(6); + cpu_io(); } } void bCPU::cpu_c4(uint16 x, uint16 y) { if(!regs.p.x && (x & 0xff00) != (y & 0xff00)) { - status.cycle_count = 6; - add_cycles(6); + cpu_io(); } } void bCPU::cpu_c6(uint16 addr) { if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) { - status.cycle_count = 6; - add_cycles(6); + cpu_io(); } } +/* The next 3 functions control bus timing for the CPU. + * cpu_io is an I/O cycle, and always 6 clock cycles long. + * mem_read / mem_write indicate memory access bus cycle, + * they are either 6, 8, or 12 bus cycles long, depending + * both on location and the $420d.1 FastROM enable bit. + */ + void bCPU::cpu_io() { + if(status.is_last_cycle)last_cycle_exec(); + status.cycle_count = 6; add_cycles(6); } uint8 bCPU::mem_read(uint32 addr) { + if(status.is_last_cycle)last_cycle_exec(); + status.cycle_count = mem_bus->speed(addr); - add_cycles(2); + add_cycles(status.cycle_count - 4); regs.mdr = mem_bus->read(addr); - add_cycles(status.cycle_count - 2); + add_cycles(4); return regs.mdr; } void bCPU::mem_write(uint32 addr, uint8 value) { + if(status.is_last_cycle)last_cycle_exec(); + status.cycle_count = mem_bus->speed(addr); - add_cycles(6); + add_cycles(status.cycle_count); mem_bus->write(addr, value); - add_cycles(status.cycle_count - 6); } uint32 bCPU::op_addr(uint8 mode, uint32 addr) { @@ -306,12 +178,11 @@ uint32 bCPU::op_addr(uint8 mode, uint32 addr) { addr &= 0xffffff; break; case OPMODE_DBR: - addr &= 0xffffff; - addr = (regs.db << 16) + addr; + addr = ((regs.db << 16) + addr) & 0xffffff; break; case OPMODE_PBR: addr &= 0xffff; - addr = (regs.pc.b << 16) | addr; + addr = (regs.pc.b << 16) + addr; break; case OPMODE_DP: addr &= 0xffff; diff --git a/src/cpu/bcpu/bcpu.h b/src/cpu/bcpu/bcpu.h index 121b5fca..d593b288 100644 --- a/src/cpu/bcpu/bcpu.h +++ b/src/cpu/bcpu/bcpu.h @@ -10,8 +10,7 @@ bCPU *cpu; class bCPU : public CPU { private: -typedef void (bCPU::*op)(); -op optbl[256]; +void (bCPU::*optbl[256])(); enum { NTSC = 0, PAL = 1 }; uint8 region; @@ -53,20 +52,13 @@ struct { uint8 opcode; uint32 cycles_executed; +//set by last_cycle(), cleared by last_cycle_exec() + bool is_last_cycle; + uint8 dma_state; uint32 dma_cycle_count; bool hdma_triggered; -//used by $4210 read bit 7 - bool nmi_read; -//used by NMI test, set when NMI executed this frame - bool nmi_exec; - -//IRQ is level-sensitive, so $4211 must be read to -//prevent multiple interrupts from occurring - bool irq_read; -//this is used to return $4211 bit 7 - bool irq_exec; //$4207-$420a uint16 virq_trigger, hirq_trigger; @@ -98,7 +90,7 @@ struct { //$4214-$4216 uint16 r4214; uint16 r4216; -}status; +} status; struct { uint32 read_index; //set to 0 at beginning of DMA/HDMA @@ -136,12 +128,13 @@ struct { bool hdma_repeat; uint16 hdma_iaddr; bool hdma_active; -}channel[8]; +} channel[8]; inline bool hdma_test(); + + inline void irq(uint16 addr); inline bool nmi_test(); inline bool irq_test(); - inline void irq(uint16 addr); inline uint8 pio_status(); inline void run(); @@ -225,6 +218,8 @@ struct { enum { CYCLE_OPREAD = 0, CYCLE_READ, CYCLE_WRITE, CYCLE_IO }; inline void exec_cycle(); + inline void last_cycle(); + inline void last_cycle_exec(); inline void cycle_edge(); inline bool in_opcode(); diff --git a/src/cpu/bcpu/bcpu_dma.cpp b/src/cpu/bcpu/bcpu_dma.cpp index 7388358e..61a6b12f 100644 --- a/src/cpu/bcpu/bcpu_dma.cpp +++ b/src/cpu/bcpu/bcpu_dma.cpp @@ -113,11 +113,11 @@ uint16 index; void bCPU::hdma_run() { int l, xferlen; uint8 x, active_channels = 0; -static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; +static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; for(int i=0;i<8;i++) { if(channel[i].hdma_active == false)continue; -// add_cycles(8); + add_cycles(8); active_channels++; if(channel[i].hdma_line_counter == 0) { @@ -140,7 +140,7 @@ static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; if(channel[i].hdma_indirect == true) { channel[i].hdma_iaddr = mem_bus->read(hdma_addr(i)); channel[i].hdma_iaddr |= mem_bus->read(hdma_addr(i)) << 8; -// add_cycles(16); + add_cycles(16); } } @@ -157,12 +157,12 @@ static hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; } hdma_write(i, l, x); -// add_cycles(8); + add_cycles(8); } } if(active_channels != 0) { -// add_cycles(18); + add_cycles(18); } } diff --git a/src/cpu/bcpu/bcpu_exec.cpp b/src/cpu/bcpu/bcpu_exec.cpp index 6e861392..0cef3fae 100644 --- a/src/cpu/bcpu/bcpu_exec.cpp +++ b/src/cpu/bcpu/bcpu_exec.cpp @@ -1,4 +1,15 @@ -inline void bCPU::cycle_edge() { +void bCPU::last_cycle() { + status.is_last_cycle = true; +} + +void bCPU::last_cycle_exec() { + status.is_last_cycle = false; + + time.nmi_pending = nmi_test(); + time.irq_pending = irq_test(); +} + +void bCPU::cycle_edge() { int c, n, z; if(status.dma_state != DMASTATE_STOP) { switch(status.dma_state) { @@ -36,15 +47,17 @@ int c, n, z; } void bCPU::exec_cycle() { +//on first cycle? if(status.cycle_pos == 0) { snes->notify(SNES::CPU_EXEC_OPCODE_BEGIN); status.opcode = op_read(); status.cycle_pos = 1; - } else { - (this->*optbl[status.opcode])(); - if(status.cycle_pos == 0) { - snes->notify(SNES::CPU_EXEC_OPCODE_END); - } + return; + } + + (this->*optbl[status.opcode])(); + if(status.cycle_pos == 0) { + snes->notify(SNES::CPU_EXEC_OPCODE_END); } } diff --git a/src/cpu/bcpu/bcpu_int.cpp b/src/cpu/bcpu/bcpu_int.cpp new file mode 100644 index 00000000..5a4eb1a8 --- /dev/null +++ b/src/cpu/bcpu/bcpu_int.cpp @@ -0,0 +1,70 @@ +/* +[IRQ cycles] + [1] pbr,pc ; opcode + [2] pbr,pc ; io + [3] 0,s ; pbr + [4] 0,s-1 ; pch + [5] 0,s-2 ; pcl + [6] 0,s-3 ; p + [7] 0,va ; aavl + [8] 0,va+1 ; aavh +*/ +void bCPU::irq(uint16 addr) { +//WDC documentation is incorrect, first cycle +//is a memory read fetch from PBR:PC + add_cycles(mem_bus->speed(regs.pc.d)); + add_cycles(6); + stack_write(regs.pc.b); + stack_write(regs.pc.h); + stack_write(regs.pc.l); + stack_write(regs.p); + rd.l = op_read(OPMODE_ADDR, addr); + rd.h = op_read(OPMODE_ADDR, addr + 1); + + regs.pc.b = 0x00; + regs.pc.w = rd.w; + regs.p.i = 1; + regs.p.d = 0; + +//let debugger know the new IRQ opcode address + snes->notify(SNES::CPU_EXEC_OPCODE_END); +} + +bool bCPU::nmi_test() { + if(time.nmi_transition == 0)return false; + time.nmi_transition = 0; + + if(status.cpu_state == CPUSTATE_WAI) { + status.cpu_state = CPUSTATE_RUN; + } + + return true; +} + +bool bCPU::irq_test() { + if(time.irq_transition == 1)goto _true; + + if(time.irq_read == 0) { + if(time.irq_line == 1 && (irq_trigger_pos_match(0) || irq_trigger_pos_match(2))) { + return false; + } + goto _true; + } + + if(time.irq_line == 0) { + time.irq_line = 1; + goto _true; + } + + return false; + +_true: + time.irq_transition = 0; + + if(status.cpu_state == CPUSTATE_WAI) { + status.cpu_state = CPUSTATE_RUN; + } + + if(regs.p.i)return false; + return true; +} diff --git a/src/cpu/bcpu/bcpu_mmio.cpp b/src/cpu/bcpu/bcpu_mmio.cpp index e36680b2..fa23536a 100644 --- a/src/cpu/bcpu/bcpu_mmio.cpp +++ b/src/cpu/bcpu/bcpu_mmio.cpp @@ -42,18 +42,17 @@ uint8 r; } //JOYSER0 -/* - The joypad contains a small bit shifter that has 16 bits. - Reading from 4016 reads one bit from this buffer, then moves - the buffer left one, and adds a '1' to the rightmost bit. - Writing a one to $4016 will fill the buffer with the current - joypad button states, and lock the bit shifter at position - zero. All reads will be the first buffer state, or 'B'. - A zero must be written back to $4016 to unlock the buffer, - so that reads will increment the bit shifting position. -*/ //7-2 = MDR //1-0 = Joypad serial data +/* The joypad contains a small bit shifter that has 16 bits. + * Reading from 4016 reads one bit from this buffer, then moves + * the buffer left one, and adds a '1' to the rightmost bit. + * Writing a one to $4016 will fill the buffer with the current + * joypad button states, and lock the bit shifter at position + * zero. All reads will be the first buffer state, or 'B'. + * A zero must be written back to $4016 to unlock the buffer, + * so that reads will increment the bit shifting position. + */ uint8 bCPU::mmio_r4016() { uint8 r; r = regs.mdr & 0xfc; @@ -96,44 +95,19 @@ uint8 r; } //RDNMI -/* $4210 bit 7 (NMI triggered bit) is set at: - * V=225/240,HC>=2, - * V>225 - * The bit is only set once per NMI trigger, so - * subsequent reads return this bit as being clear. - * There is but one exception: if the $4210 read - * occurs at *exactly* V=225/240,HC==2, then $4210 - * bit 7 will be set, and the next read will also - * have this bit set. - */ //7 = NMI acknowledge //6-4 = MDR //3-0 = CPU (5a22) version uint8 bCPU::mmio_r4210() { -uint8 r; -uint16 v, h, hc, vs; - r = regs.mdr & 0x70; +uint8 r; + r = regs.mdr & 0x70; + r |= uint8(!time.nmi_read) << 7; - v = vcounter(); - h = hcounter(); - hc = hcycles(); - vs = (overscan()?239:224); - - if(status.nmi_read == false) { - if( - (v == (vs + 1) && hc >= 2) || - (v > (vs + 1)) - ) { - r |= 0x80; - - //test for special case where NMI read not raised - if(v != (vs + 1) || hc != 2) { - status.nmi_read = true; - } - } + if(!nmi_trigger_pos_match(0) && !nmi_trigger_pos_match(2)) { + time.nmi_read = 1; } - r |= 0x02; //cpu version number + r |= (cpu_version & 0x0f); return r; } @@ -142,10 +116,15 @@ uint16 v, h, hc, vs; //6-0 = MDR uint8 bCPU::mmio_r4211() { uint8 r; - r = regs.mdr & 0x7f; - if(status.irq_exec == true)r |= 0x80; - status.irq_exec = false; - status.irq_read = true; + r = regs.mdr & 0x7f; + r |= uint8(!time.irq_read) << 7; + + if(!irq_trigger_pos_match(0) && !irq_trigger_pos_match(2)) { + time.irq_read = 1; + time.irq_line = 1; + time.irq_transition = 0; + } + return r; } @@ -155,23 +134,20 @@ uint8 r; //5-1 = MDR //0 = joypad ready uint8 bCPU::mmio_r4212() { -uint8 r; -uint16 v, h, hc, vs; +uint8 r; r = regs.mdr & 0x3e; - v = vcounter(); - h = hcounter(); - hc = hcycles(); - vs = (overscan()?239:224); +uint16 vs = overscan() ? 240 : 225; //auto joypad polling - if(v >= (vs + 1) && v <= (vs + 3))r |= 0x01; + if(time.v >= vs && time.v <= (vs + 2))r |= 0x01; //hblank - if(hc <= 2 || hc >= 1096)r |= 0x40; + if(time.hc <= 2 || time.hc >= 1096)r |= 0x40; //vblank - if(v >= (vs + 1))r |= 0x80; + if(time.v >= vs)r |= 0x80; + return r; } @@ -291,9 +267,7 @@ uint8 bCPU::mmio_r43xb(uint8 i) { } uint8 bCPUMMIO::read(uint32 addr) { -uint8 i; -//cpu->sync(); - +uint i; //APU if(addr >= 0x2140 && addr <= 0x217f) { return apu->port_read(addr & 3); @@ -337,6 +311,8 @@ uint8 i; case 0x4218:return cpu->mmio_r4218(); //JOY1L case 0x4219:return cpu->mmio_r4219(); //JOY1H case 0x421a:case 0x421b:case 0x421c:case 0x421d:case 0x421e:case 0x421f:return 0x00; +case 0x4000:dprintf("* 4000 read at %3d,%4d <%d>", cpu->time.v, cpu->time.hc, cpu->status.cycle_count);break; +case 0x4200:dprintf("* 4200 read at %3d,%4d", cpu->time.v, cpu->time.hc);break; } return cpu->regs.mdr; @@ -383,14 +359,20 @@ void bCPU::mmio_w4200(uint8 value) { status.hirq_enabled = !!(value & 0x10); status.auto_joypad_poll = !!(value & 0x01); - if(status.nmi_enabled == false) { - status.nmi_read = false; + if(time.nmi_read == 0) { + if(time.nmi_line == 1 && !status.nmi_enabled == 0) { + time.nmi_transition = 1; + } + time.nmi_line = !status.nmi_enabled; } - status.irq_exec = false; - if(status.virq_enabled == true || status.hirq_enabled == true) { - status.irq_read = false; + if(status.virq_enabled == false && status.hirq_enabled == false) { + time.irq_line = 1; + time.irq_read = 1; + time.irq_transition = 0; } + + update_interrupts(); } //WRIO @@ -424,32 +406,32 @@ void bCPU::mmio_w4205(uint8 value) { //WRDIVB void bCPU::mmio_w4206(uint8 value) { status.div_b = value; - status.r4214 = (status.div_b)?status.div_a / status.div_b : 0xffff; - status.r4216 = (status.div_b)?status.div_a % status.div_b : status.div_a; + status.r4214 = (status.div_b) ? status.div_a / status.div_b : 0xffff; + status.r4216 = (status.div_b) ? status.div_a % status.div_b : status.div_a; } //HTIMEL void bCPU::mmio_w4207(uint8 value) { status.hirq_pos = (status.hirq_pos & 0xff00) | value; - status.irq_read = false; + update_interrupts(); } //HTIMEH void bCPU::mmio_w4208(uint8 value) { status.hirq_pos = (status.hirq_pos & 0x00ff) | (value << 8); - status.irq_read = false; + update_interrupts(); } //VTIMEL void bCPU::mmio_w4209(uint8 value) { status.virq_pos = (status.virq_pos & 0xff00) | value; - status.irq_read = false; + update_interrupts(); } //VTIMEH void bCPU::mmio_w420a(uint8 value) { status.virq_pos = (status.virq_pos & 0x00ff) | (value << 8); - status.irq_read = false; + update_interrupts(); } //DMAEN @@ -479,7 +461,7 @@ void bCPU::mmio_w420c(uint8 value) { //MEMSEL void bCPU::mmio_w420d(uint8 value) { - mem_bus->fastROM = value & 1; + mem_bus->set_speed(value & 1); } //DMAPx @@ -549,8 +531,6 @@ void bCPU::mmio_w43xb(uint8 value, uint8 i) { void bCPUMMIO::write(uint32 addr, uint8 value) { uint8 i; -//cpu->sync(); - //APU if(addr >= 0x2140 && addr <= 0x217f) { cpu->port_write(addr & 3, value); @@ -600,6 +580,7 @@ uint8 i; case 0x420b:cpu->mmio_w420b(value);return; //DMAEN case 0x420c:cpu->mmio_w420c(value);return; //HDMAEN case 0x420d:cpu->mmio_w420d(value);return; //MEMSEL +case 0x4000:dprintf("* 4000 write at %3d,%4d", cpu->time.v, cpu->time.hc);break; } } diff --git a/src/cpu/bcpu/bcpu_op.h b/src/cpu/bcpu/bcpu_op.h index 63b88b2d..1b3f44f6 100644 --- a/src/cpu/bcpu/bcpu_op.h +++ b/src/cpu/bcpu/bcpu_op.h @@ -186,7 +186,6 @@ void op_sta_idpy(); void op_sta_ildpy(); void op_sta_sr(); void op_sta_isry(); -void op_bra(); void op_bcc(); void op_bcs(); void op_bne(); @@ -195,6 +194,7 @@ void op_bpl(); void op_bmi(); void op_bvc(); void op_bvs(); +void op_bra(); void op_brl(); void op_jmp_addr(); void op_jmp_long(); diff --git a/src/cpu/bcpu/bcpu_op_misc.cpp b/src/cpu/bcpu/bcpu_op_misc.cpp index 9b6fe3af..ff08ae50 100644 --- a/src/cpu/bcpu/bcpu_op_misc.cpp +++ b/src/cpu/bcpu/bcpu_op_misc.cpp @@ -1,6 +1,7 @@ void bCPU::op_nop() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -10,6 +11,7 @@ void bCPU::op_nop() { void bCPU::op_wdm() { switch(status.cycle_pos++) { case 1: + last_cycle(); op_read(); status.cycle_pos = 0; break; @@ -22,6 +24,7 @@ void bCPU::op_xba() { cpu_io(); break; case 2: + last_cycle(); cpu_io(); regs.a.l ^= regs.a.h; regs.a.h ^= regs.a.l; @@ -54,6 +57,7 @@ void bCPU::op_mvn() { else { regs.x.w++; regs.y.w++; } break; case 6: + last_cycle(); cpu_io(); if(regs.a.w--)regs.pc.w -= 3; status.cycle_pos = 0; @@ -82,6 +86,7 @@ void bCPU::op_mvp() { else { regs.x.w--; regs.y.w--; } break; case 6: + last_cycle(); cpu_io(); if(regs.a.w--)regs.pc.w -= 3; status.cycle_pos = 0; @@ -111,6 +116,7 @@ void bCPU::op_brk() { rd.l = op_read(OPMODE_LONG, (regs.e)?0xfffe:0xffe6); break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, (regs.e)?0xffff:0xffe7); regs.pc.b = 0x00; regs.pc.w = rd.w; @@ -143,6 +149,7 @@ void bCPU::op_cop() { rd.l = op_read(OPMODE_LONG, (regs.e)?0xfff4:0xffe4); break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, (regs.e)?0xfff5:0xffe5); regs.pc.b = 0x00; regs.pc.w = rd.w; @@ -160,6 +167,7 @@ void bCPU::op_stp() { status.cpu_state = CPUSTATE_STP; break; case 2: + last_cycle(); cpu_io(); regs.pc.w--; status.cycle_pos = 0; @@ -174,8 +182,11 @@ void bCPU::op_wai() { status.cpu_state = CPUSTATE_WAI; break; case 2: + last_cycle(); cpu_io(); - regs.pc.w--; + if(status.cpu_state == CPUSTATE_WAI) { + regs.pc.w--; + } status.cycle_pos = 0; break; } @@ -184,6 +195,7 @@ void bCPU::op_wai() { void bCPU::op_xce() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); bool c = regs.p.c; regs.p.c = regs.e; @@ -202,6 +214,7 @@ void bCPU::op_xce() { void bCPU::op_clc() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.c = 0; status.cycle_pos = 0; @@ -212,6 +225,7 @@ void bCPU::op_clc() { void bCPU::op_cld() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.d = 0; status.cycle_pos = 0; @@ -222,6 +236,7 @@ void bCPU::op_cld() { void bCPU::op_cli() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.i = 0; status.cycle_pos = 0; @@ -232,6 +247,7 @@ void bCPU::op_cli() { void bCPU::op_clv() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.v = 0; status.cycle_pos = 0; @@ -242,6 +258,7 @@ void bCPU::op_clv() { void bCPU::op_sec() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.c = 1; status.cycle_pos = 0; @@ -252,6 +269,7 @@ void bCPU::op_sec() { void bCPU::op_sed() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.d = 1; status.cycle_pos = 0; @@ -262,6 +280,7 @@ void bCPU::op_sed() { void bCPU::op_sei() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.p.i = 1; status.cycle_pos = 0; @@ -275,6 +294,7 @@ void bCPU::op_rep() { rd.l = op_read(); break; case 2: + last_cycle(); cpu_io(); regs.p &=~ rd.l; if(regs.e)regs.p |= 0x30; @@ -293,6 +313,7 @@ void bCPU::op_sep() { rd.l = op_read(); break; case 2: + last_cycle(); cpu_io(); regs.p |= rd.l; if(regs.e)regs.p |= 0x30; @@ -308,6 +329,7 @@ void bCPU::op_sep() { void bCPU::op_tax() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.x.l = regs.a.l; @@ -326,6 +348,7 @@ void bCPU::op_tax() { void bCPU::op_tay() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.y.l = regs.a.l; @@ -344,6 +367,7 @@ void bCPU::op_tay() { void bCPU::op_txa() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.a.l = regs.x.l; @@ -362,6 +386,7 @@ void bCPU::op_txa() { void bCPU::op_txy() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.y.l = regs.x.l; @@ -380,6 +405,7 @@ void bCPU::op_txy() { void bCPU::op_tya() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.a.l = regs.y.l; @@ -398,6 +424,7 @@ void bCPU::op_tya() { void bCPU::op_tyx() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.x.l = regs.y.l; @@ -416,6 +443,7 @@ void bCPU::op_tyx() { void bCPU::op_tcd() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.d.w = regs.a.w; regs.p.n = !!(regs.d.w & 0x8000); @@ -428,6 +456,7 @@ void bCPU::op_tcd() { void bCPU::op_tcs() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.s.w = regs.a.w; if(regs.e)regs.s.h = 0x01; @@ -439,6 +468,7 @@ void bCPU::op_tcs() { void bCPU::op_tdc() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.a.w = regs.d.w; regs.p.n = !!(regs.a.w & 0x8000); @@ -451,6 +481,7 @@ void bCPU::op_tdc() { void bCPU::op_tsc() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); regs.a.w = regs.s.w; if(regs.e) { @@ -468,6 +499,7 @@ void bCPU::op_tsc() { void bCPU::op_tsx() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.x.l = regs.s.l; @@ -486,6 +518,7 @@ void bCPU::op_tsx() { void bCPU::op_txs() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.e) { regs.s.l = regs.x.l; @@ -511,6 +544,7 @@ void bCPU::op_pha() { stack_write(regs.a.h); break; case 3: + last_cycle(); stack_write(regs.a.l); status.cycle_pos = 0; break; @@ -527,6 +561,7 @@ void bCPU::op_phx() { stack_write(regs.x.h); break; case 3: + last_cycle(); stack_write(regs.x.l); status.cycle_pos = 0; break; @@ -543,6 +578,7 @@ void bCPU::op_phy() { stack_write(regs.y.h); break; case 3: + last_cycle(); stack_write(regs.y.l); status.cycle_pos = 0; break; @@ -559,6 +595,7 @@ void bCPU::op_phd() { stack_write(regs. d.h); break; case 3: + last_cycle(); stack_write(regs. d.l); status.cycle_pos = 0; break; @@ -571,6 +608,7 @@ void bCPU::op_phb() { cpu_io(); break; case 2: + last_cycle(); stack_write(regs.db); status.cycle_pos = 0; break; @@ -583,6 +621,7 @@ void bCPU::op_phk() { cpu_io(); break; case 2: + last_cycle(); stack_write(regs.pc.b); status.cycle_pos = 0; break; @@ -595,6 +634,7 @@ void bCPU::op_php() { cpu_io(); break; case 2: + last_cycle(); stack_write(regs.p); status.cycle_pos = 0; break; @@ -610,6 +650,7 @@ void bCPU::op_pla() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); regs.a.l = stack_read(); if(regs.p.m) { regs.p.n = !!(regs.a.l & 0x80); @@ -618,6 +659,7 @@ void bCPU::op_pla() { } break; case 4: + last_cycle(); regs.a.h = stack_read(); regs.p.n = !!(regs.a.w & 0x8000); regs.p.z = (regs.a.w == 0); @@ -635,6 +677,7 @@ void bCPU::op_plx() { cpu_io(); break; case 3: + if(regs.p.x)last_cycle(); regs.x.l = stack_read(); if(regs.p.x) { regs.p.n = !!(regs.x.l & 0x80); @@ -643,6 +686,7 @@ void bCPU::op_plx() { } break; case 4: + last_cycle(); regs.x.h = stack_read(); regs.p.n = !!(regs.x.w & 0x8000); regs.p.z = (regs.x.w == 0); @@ -660,6 +704,7 @@ void bCPU::op_ply() { cpu_io(); break; case 3: + if(regs.p.x)last_cycle(); regs.y.l = stack_read(); if(regs.p.x) { regs.p.n = !!(regs.y.l & 0x80); @@ -668,6 +713,7 @@ void bCPU::op_ply() { } break; case 4: + last_cycle(); regs.y.h = stack_read(); regs.p.n = !!(regs.y.w & 0x8000); regs.p.z = (regs.y.w == 0); @@ -685,6 +731,7 @@ void bCPU::op_pld() { cpu_io(); break; case 3: + if(0)last_cycle(); regs. d.l = stack_read(); if(0) { regs.p.n = !!(regs. d.l & 0x80); @@ -693,6 +740,7 @@ void bCPU::op_pld() { } break; case 4: + last_cycle(); regs. d.h = stack_read(); regs.p.n = !!(regs. d.w & 0x8000); regs.p.z = (regs. d.w == 0); @@ -710,6 +758,7 @@ void bCPU::op_plb() { cpu_io(); break; case 3: + last_cycle(); regs.db = stack_read(); regs.p.n = !!(regs.db & 0x80); regs.p.z = (regs.db == 0); @@ -727,6 +776,7 @@ void bCPU::op_plp() { cpu_io(); break; case 3: + last_cycle(); regs.p = stack_read(); if(regs.e)regs.p |= 0x30; if(regs.p.x) { @@ -750,6 +800,7 @@ void bCPU::op_pea() { stack_write(aa.h); break; case 4: + last_cycle(); stack_write(aa.l); status.cycle_pos = 0; break; @@ -774,6 +825,7 @@ void bCPU::op_pei() { stack_write(aa.h); break; case 6: + last_cycle(); stack_write(aa.l); status.cycle_pos = 0; break; @@ -796,6 +848,7 @@ void bCPU::op_per() { stack_write(rd.h); break; case 5: + last_cycle(); stack_write(rd.l); status.cycle_pos = 0; break; diff --git a/src/cpu/bcpu/bcpu_op_pc.cpp b/src/cpu/bcpu/bcpu_op_pc.cpp index 94a23447..b02d9dc8 100644 --- a/src/cpu/bcpu/bcpu_op_pc.cpp +++ b/src/cpu/bcpu/bcpu_op_pc.cpp @@ -1,27 +1,7 @@ -void bCPU::op_bra() { - switch(status.cycle_pos++) { - case 1: - rd.l = op_read(); - if(1) { - aa.w = regs.pc.d + (int8)rd.l; - regs.pc.w = aa.w; - } else { - status.cycle_pos = 0; - } - break; - case 2: - cpu_c6(aa.w); - break; - case 3: - cpu_io(); - status.cycle_pos = 0; - break; - } -} - void bCPU::op_bcc() { switch(status.cycle_pos++) { case 1: + if(!!regs.p.c)last_cycle(); rd.l = op_read(); if(!regs.p.c) { aa.w = regs.pc.d + (int8)rd.l; @@ -34,6 +14,7 @@ void bCPU::op_bcc() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -43,6 +24,7 @@ void bCPU::op_bcc() { void bCPU::op_bcs() { switch(status.cycle_pos++) { case 1: + if(!regs.p.c)last_cycle(); rd.l = op_read(); if(regs.p.c) { aa.w = regs.pc.d + (int8)rd.l; @@ -55,6 +37,7 @@ void bCPU::op_bcs() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -64,6 +47,7 @@ void bCPU::op_bcs() { void bCPU::op_bne() { switch(status.cycle_pos++) { case 1: + if(!!regs.p.z)last_cycle(); rd.l = op_read(); if(!regs.p.z) { aa.w = regs.pc.d + (int8)rd.l; @@ -76,6 +60,7 @@ void bCPU::op_bne() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -85,6 +70,7 @@ void bCPU::op_bne() { void bCPU::op_beq() { switch(status.cycle_pos++) { case 1: + if(!regs.p.z)last_cycle(); rd.l = op_read(); if(regs.p.z) { aa.w = regs.pc.d + (int8)rd.l; @@ -97,6 +83,7 @@ void bCPU::op_beq() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -106,6 +93,7 @@ void bCPU::op_beq() { void bCPU::op_bpl() { switch(status.cycle_pos++) { case 1: + if(!!regs.p.n)last_cycle(); rd.l = op_read(); if(!regs.p.n) { aa.w = regs.pc.d + (int8)rd.l; @@ -118,6 +106,7 @@ void bCPU::op_bpl() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -127,6 +116,7 @@ void bCPU::op_bpl() { void bCPU::op_bmi() { switch(status.cycle_pos++) { case 1: + if(!regs.p.n)last_cycle(); rd.l = op_read(); if(regs.p.n) { aa.w = regs.pc.d + (int8)rd.l; @@ -139,6 +129,7 @@ void bCPU::op_bmi() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -148,6 +139,7 @@ void bCPU::op_bmi() { void bCPU::op_bvc() { switch(status.cycle_pos++) { case 1: + if(!!regs.p.v)last_cycle(); rd.l = op_read(); if(!regs.p.v) { aa.w = regs.pc.d + (int8)rd.l; @@ -160,6 +152,7 @@ void bCPU::op_bvc() { cpu_c6(aa.w); break; case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -169,6 +162,7 @@ void bCPU::op_bvc() { void bCPU::op_bvs() { switch(status.cycle_pos++) { case 1: + if(!regs.p.v)last_cycle(); rd.l = op_read(); if(regs.p.v) { aa.w = regs.pc.d + (int8)rd.l; @@ -181,6 +175,25 @@ void bCPU::op_bvs() { cpu_c6(aa.w); break; case 3: + last_cycle(); + cpu_io(); + status.cycle_pos = 0; + break; + } +} + +void bCPU::op_bra() { + switch(status.cycle_pos++) { + case 1: + rd.l = op_read(); + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; + break; + case 2: + cpu_c6(aa.w); + break; + case 3: + last_cycle(); cpu_io(); status.cycle_pos = 0; break; @@ -196,6 +209,7 @@ void bCPU::op_brl() { rd.h = op_read(); break; case 3: + last_cycle(); cpu_io(); regs.pc.w = regs.pc.d + (int16)rd.w; status.cycle_pos = 0; @@ -209,6 +223,7 @@ void bCPU::op_jmp_addr() { rd.l = op_read(); break; case 2: + last_cycle(); rd.h = op_read(); regs.pc.w = rd.w; status.cycle_pos = 0; @@ -225,6 +240,7 @@ void bCPU::op_jmp_long() { rd.h = op_read(); break; case 3: + last_cycle(); rd.b = op_read(); regs.pc.d = rd.d & 0xffffff; status.cycle_pos = 0; @@ -244,6 +260,7 @@ void bCPU::op_jmp_iaddr() { rd.l = op_read(OPMODE_ADDR, aa.w); break; case 4: + last_cycle(); rd.h = op_read(OPMODE_ADDR, aa.w + 1); regs.pc.w = rd.w; status.cycle_pos = 0; @@ -266,6 +283,7 @@ void bCPU::op_jmp_iaddrx() { rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w); break; case 5: + last_cycle(); rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); regs.pc.w = rd.w; status.cycle_pos = 0; @@ -288,6 +306,7 @@ void bCPU::op_jmp_iladdr() { rd.h = op_read(OPMODE_ADDR, aa.w + 1); break; case 5: + last_cycle(); rd.b = op_read(OPMODE_ADDR, aa.w + 2); regs.pc.d = rd.d & 0xffffff; status.cycle_pos = 0; @@ -311,6 +330,7 @@ void bCPU::op_jsr_addr() { stack_write(regs.pc.h); break; case 5: + last_cycle(); stack_write(regs.pc.l); regs.pc.w = aa.w; status.cycle_pos = 0; @@ -340,6 +360,7 @@ void bCPU::op_jsr_long() { stack_write(regs.pc.h); break; case 7: + last_cycle(); stack_write(regs.pc.l); regs.pc.d = aa.d & 0xffffff; status.cycle_pos = 0; @@ -368,6 +389,7 @@ void bCPU::op_jsr_iaddrx() { rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w); break; case 7: + last_cycle(); rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); regs.pc.w = rd.w; status.cycle_pos = 0; @@ -402,6 +424,7 @@ void bCPU::op_rti() { } break; case 6: + last_cycle(); rd.b = stack_read(); regs.pc.d = rd.d & 0xffffff; status.cycle_pos = 0; @@ -424,6 +447,7 @@ void bCPU::op_rts() { rd.h = stack_read(); break; case 5: + last_cycle(); cpu_io(); regs.pc.w = rd.w; regs.pc.w++; @@ -447,6 +471,7 @@ void bCPU::op_rtl() { rd.h = stack_read(); break; case 5: + last_cycle(); rd.b = stack_read(); regs.pc.d = rd.d & 0xffffff; regs.pc.w++; diff --git a/src/cpu/bcpu/bcpu_op_read.cpp b/src/cpu/bcpu/bcpu_op_read.cpp index 8274a2dd..d905520c 100644 --- a/src/cpu/bcpu/bcpu_op_read.cpp +++ b/src/cpu/bcpu/bcpu_op_read.cpp @@ -1,10 +1,12 @@ void bCPU::op_adc_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_adc_w(); status.cycle_pos = 0; @@ -15,10 +17,12 @@ void bCPU::op_adc_const() { void bCPU::op_and_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_and_w(); status.cycle_pos = 0; @@ -29,10 +33,12 @@ void bCPU::op_and_const() { void bCPU::op_cmp_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_cmp_w(); status.cycle_pos = 0; @@ -43,10 +49,12 @@ void bCPU::op_cmp_const() { void bCPU::op_cpx_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.x)last_cycle(); rd.l = op_read(); if(regs.p.x) { op_cpx_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_cpx_w(); status.cycle_pos = 0; @@ -57,10 +65,12 @@ void bCPU::op_cpx_const() { void bCPU::op_cpy_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.x)last_cycle(); rd.l = op_read(); if(regs.p.x) { op_cpy_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_cpy_w(); status.cycle_pos = 0; @@ -71,10 +81,12 @@ void bCPU::op_cpy_const() { void bCPU::op_eor_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_eor_w(); status.cycle_pos = 0; @@ -85,10 +97,12 @@ void bCPU::op_eor_const() { void bCPU::op_lda_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_lda_w(); status.cycle_pos = 0; @@ -99,10 +113,12 @@ void bCPU::op_lda_const() { void bCPU::op_ldx_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.x)last_cycle(); rd.l = op_read(); if(regs.p.x) { op_ldx_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_ldx_w(); status.cycle_pos = 0; @@ -113,10 +129,12 @@ void bCPU::op_ldx_const() { void bCPU::op_ldy_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.x)last_cycle(); rd.l = op_read(); if(regs.p.x) { op_ldy_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_ldy_w(); status.cycle_pos = 0; @@ -127,10 +145,12 @@ void bCPU::op_ldy_const() { void bCPU::op_ora_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_ora_w(); status.cycle_pos = 0; @@ -141,10 +161,12 @@ void bCPU::op_ora_const() { void bCPU::op_sbc_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 2: + last_cycle(); rd.h = op_read(); op_sbc_w(); status.cycle_pos = 0; @@ -161,10 +183,12 @@ void bCPU::op_adc_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -181,10 +205,12 @@ void bCPU::op_and_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_and_w(); status.cycle_pos = 0; @@ -201,10 +227,12 @@ void bCPU::op_bit_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_bit_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_bit_w(); status.cycle_pos = 0; @@ -221,10 +249,12 @@ void bCPU::op_cmp_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -241,10 +271,12 @@ void bCPU::op_cpx_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.x) { op_cpx_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_cpx_w(); status.cycle_pos = 0; @@ -261,10 +293,12 @@ void bCPU::op_cpy_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.x) { op_cpy_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_cpy_w(); status.cycle_pos = 0; @@ -281,10 +315,12 @@ void bCPU::op_eor_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -301,10 +337,12 @@ void bCPU::op_lda_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -321,10 +359,12 @@ void bCPU::op_ldx_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.x) { op_ldx_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_ldx_w(); status.cycle_pos = 0; @@ -341,10 +381,12 @@ void bCPU::op_ldy_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.x) { op_ldy_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_ldy_w(); status.cycle_pos = 0; @@ -361,10 +403,12 @@ void bCPU::op_ora_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -381,10 +425,12 @@ void bCPU::op_sbc_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -404,10 +450,12 @@ void bCPU::op_adc_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -427,10 +475,12 @@ void bCPU::op_and_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_and_w(); status.cycle_pos = 0; @@ -450,10 +500,12 @@ void bCPU::op_bit_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_bit_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_bit_w(); status.cycle_pos = 0; @@ -473,10 +525,12 @@ void bCPU::op_cmp_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -496,10 +550,12 @@ void bCPU::op_eor_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -519,10 +575,12 @@ void bCPU::op_lda_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -542,10 +600,12 @@ void bCPU::op_ldy_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.x) { op_ldy_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_ldy_w(); status.cycle_pos = 0; @@ -565,10 +625,12 @@ void bCPU::op_ora_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -588,10 +650,12 @@ void bCPU::op_sbc_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -611,10 +675,12 @@ void bCPU::op_adc_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -634,10 +700,12 @@ void bCPU::op_and_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_and_w(); status.cycle_pos = 0; @@ -657,10 +725,12 @@ void bCPU::op_cmp_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -680,10 +750,12 @@ void bCPU::op_eor_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -703,10 +775,12 @@ void bCPU::op_lda_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -726,10 +800,12 @@ void bCPU::op_ldx_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.x) { op_ldx_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_ldx_w(); status.cycle_pos = 0; @@ -749,10 +825,12 @@ void bCPU::op_ora_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -772,10 +850,12 @@ void bCPU::op_sbc_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -795,10 +875,12 @@ void bCPU::op_adc_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_adc_w(); status.cycle_pos = 0; @@ -818,10 +900,12 @@ void bCPU::op_and_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_and_w(); status.cycle_pos = 0; @@ -841,10 +925,12 @@ void bCPU::op_cmp_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_cmp_w(); status.cycle_pos = 0; @@ -864,10 +950,12 @@ void bCPU::op_eor_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_eor_w(); status.cycle_pos = 0; @@ -887,10 +975,12 @@ void bCPU::op_lda_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_lda_w(); status.cycle_pos = 0; @@ -910,10 +1000,12 @@ void bCPU::op_ora_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_ora_w(); status.cycle_pos = 0; @@ -933,10 +1025,12 @@ void bCPU::op_sbc_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_sbc_w(); status.cycle_pos = 0; @@ -956,10 +1050,12 @@ void bCPU::op_adc_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -979,10 +1075,12 @@ void bCPU::op_and_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_and_w(); status.cycle_pos = 0; @@ -1002,10 +1100,12 @@ void bCPU::op_cmp_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -1025,10 +1125,12 @@ void bCPU::op_eor_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -1048,10 +1150,12 @@ void bCPU::op_lda_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -1071,10 +1175,12 @@ void bCPU::op_ora_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -1094,10 +1200,12 @@ void bCPU::op_sbc_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -1114,10 +1222,12 @@ void bCPU::op_adc_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_adc_w(); status.cycle_pos = 0; @@ -1134,10 +1244,12 @@ void bCPU::op_and_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_and_w(); status.cycle_pos = 0; @@ -1154,10 +1266,12 @@ void bCPU::op_bit_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_bit_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_bit_w(); status.cycle_pos = 0; @@ -1174,10 +1288,12 @@ void bCPU::op_cmp_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_cmp_w(); status.cycle_pos = 0; @@ -1194,10 +1310,12 @@ void bCPU::op_cpx_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.x) { op_cpx_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_cpx_w(); status.cycle_pos = 0; @@ -1214,10 +1332,12 @@ void bCPU::op_cpy_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.x) { op_cpy_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_cpy_w(); status.cycle_pos = 0; @@ -1234,10 +1354,12 @@ void bCPU::op_eor_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_eor_w(); status.cycle_pos = 0; @@ -1254,10 +1376,12 @@ void bCPU::op_lda_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_lda_w(); status.cycle_pos = 0; @@ -1274,10 +1398,12 @@ void bCPU::op_ldx_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.x) { op_ldx_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_ldx_w(); status.cycle_pos = 0; @@ -1294,10 +1420,12 @@ void bCPU::op_ldy_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.x) { op_ldy_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_ldy_w(); status.cycle_pos = 0; @@ -1314,10 +1442,12 @@ void bCPU::op_ora_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_ora_w(); status.cycle_pos = 0; @@ -1334,10 +1464,12 @@ void bCPU::op_sbc_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + 1); op_sbc_w(); status.cycle_pos = 0; @@ -1357,10 +1489,12 @@ void bCPU::op_adc_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -1380,10 +1514,12 @@ void bCPU::op_and_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_and_w(); status.cycle_pos = 0; @@ -1403,10 +1539,12 @@ void bCPU::op_bit_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_bit_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_bit_w(); status.cycle_pos = 0; @@ -1426,10 +1564,12 @@ void bCPU::op_cmp_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -1449,10 +1589,12 @@ void bCPU::op_eor_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -1472,10 +1614,12 @@ void bCPU::op_lda_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -1495,10 +1639,12 @@ void bCPU::op_ldy_dpx() { cpu_io(); break; case 4: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.x) { op_ldy_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_ldy_w(); status.cycle_pos = 0; @@ -1518,10 +1664,12 @@ void bCPU::op_ora_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -1541,10 +1689,12 @@ void bCPU::op_sbc_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.x.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -1564,10 +1714,12 @@ void bCPU::op_ldx_dpy() { cpu_io(); break; case 4: + if(regs.p.x)last_cycle(); rd.l = op_read(OPMODE_DP, dp + regs.y.w); if(regs.p.x) { op_ldx_b(); status.cycle_pos = 0; } break; case 5: + last_cycle(); rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1); op_ldx_w(); status.cycle_pos = 0; @@ -1590,10 +1742,12 @@ void bCPU::op_adc_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -1616,10 +1770,12 @@ void bCPU::op_and_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_and_w(); status.cycle_pos = 0; @@ -1642,10 +1798,12 @@ void bCPU::op_cmp_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -1668,10 +1826,12 @@ void bCPU::op_eor_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -1694,10 +1854,12 @@ void bCPU::op_lda_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -1720,10 +1882,12 @@ void bCPU::op_ora_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -1746,10 +1910,12 @@ void bCPU::op_sbc_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 6: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -1775,10 +1941,12 @@ void bCPU::op_adc_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -1804,10 +1972,12 @@ void bCPU::op_and_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_and_w(); status.cycle_pos = 0; @@ -1833,10 +2003,12 @@ void bCPU::op_cmp_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -1862,10 +2034,12 @@ void bCPU::op_eor_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -1891,10 +2065,12 @@ void bCPU::op_lda_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -1920,10 +2096,12 @@ void bCPU::op_ora_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -1949,10 +2127,12 @@ void bCPU::op_sbc_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -1978,10 +2158,12 @@ void bCPU::op_adc_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -2007,10 +2189,12 @@ void bCPU::op_and_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_and_w(); status.cycle_pos = 0; @@ -2036,10 +2220,12 @@ void bCPU::op_cmp_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -2065,10 +2251,12 @@ void bCPU::op_eor_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -2094,10 +2282,12 @@ void bCPU::op_lda_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -2123,10 +2313,12 @@ void bCPU::op_ora_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -2152,10 +2344,12 @@ void bCPU::op_sbc_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -2181,10 +2375,12 @@ void bCPU::op_adc_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_adc_w(); status.cycle_pos = 0; @@ -2210,10 +2406,12 @@ void bCPU::op_and_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_and_w(); status.cycle_pos = 0; @@ -2239,10 +2437,12 @@ void bCPU::op_cmp_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_cmp_w(); status.cycle_pos = 0; @@ -2268,10 +2468,12 @@ void bCPU::op_eor_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_eor_w(); status.cycle_pos = 0; @@ -2297,10 +2499,12 @@ void bCPU::op_lda_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_lda_w(); status.cycle_pos = 0; @@ -2326,10 +2530,12 @@ void bCPU::op_ora_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_ora_w(); status.cycle_pos = 0; @@ -2355,10 +2561,12 @@ void bCPU::op_sbc_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + 1); op_sbc_w(); status.cycle_pos = 0; @@ -2384,10 +2592,12 @@ void bCPU::op_adc_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -2413,10 +2623,12 @@ void bCPU::op_and_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_and_w(); status.cycle_pos = 0; @@ -2442,10 +2654,12 @@ void bCPU::op_cmp_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -2471,10 +2685,12 @@ void bCPU::op_eor_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -2500,10 +2716,12 @@ void bCPU::op_lda_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -2529,10 +2747,12 @@ void bCPU::op_ora_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -2558,10 +2778,12 @@ void bCPU::op_sbc_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -2578,10 +2800,12 @@ void bCPU::op_adc_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_adc_w(); status.cycle_pos = 0; @@ -2598,10 +2822,12 @@ void bCPU::op_and_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_and_w(); status.cycle_pos = 0; @@ -2618,10 +2844,12 @@ void bCPU::op_cmp_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_cmp_w(); status.cycle_pos = 0; @@ -2638,10 +2866,12 @@ void bCPU::op_eor_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_eor_w(); status.cycle_pos = 0; @@ -2658,10 +2888,12 @@ void bCPU::op_lda_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_lda_w(); status.cycle_pos = 0; @@ -2678,10 +2910,12 @@ void bCPU::op_ora_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_ora_w(); status.cycle_pos = 0; @@ -2698,10 +2932,12 @@ void bCPU::op_sbc_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_SP, sp); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 4: + last_cycle(); rd.h = op_read(OPMODE_SP, sp + 1); op_sbc_w(); status.cycle_pos = 0; @@ -2727,10 +2963,12 @@ void bCPU::op_adc_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_adc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_adc_w(); status.cycle_pos = 0; @@ -2756,10 +2994,12 @@ void bCPU::op_and_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_and_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_and_w(); status.cycle_pos = 0; @@ -2785,10 +3025,12 @@ void bCPU::op_cmp_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_cmp_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_cmp_w(); status.cycle_pos = 0; @@ -2814,10 +3056,12 @@ void bCPU::op_eor_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_eor_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_eor_w(); status.cycle_pos = 0; @@ -2843,10 +3087,12 @@ void bCPU::op_lda_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_lda_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_lda_w(); status.cycle_pos = 0; @@ -2872,10 +3118,12 @@ void bCPU::op_ora_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_ora_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_ora_w(); status.cycle_pos = 0; @@ -2901,10 +3149,12 @@ void bCPU::op_sbc_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_sbc_b(); status.cycle_pos = 0; } break; case 7: + last_cycle(); rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_sbc_w(); status.cycle_pos = 0; @@ -2915,6 +3165,7 @@ void bCPU::op_sbc_isry() { void bCPU::op_bit_const() { switch(status.cycle_pos++) { case 1: + if(regs.p.m)last_cycle(); rd.l = op_read(); if(regs.p.m) { regs.p.z = ((rd.l & regs.a.l) == 0); @@ -2922,6 +3173,7 @@ void bCPU::op_bit_const() { } break; case 2: + last_cycle(); rd.h = op_read(); regs.p.z = ((rd.w & regs.a.w) == 0); status.cycle_pos = 0; diff --git a/src/cpu/bcpu/bcpu_op_rmw.cpp b/src/cpu/bcpu/bcpu_op_rmw.cpp index 8d716c91..41f0939c 100644 --- a/src/cpu/bcpu/bcpu_op_rmw.cpp +++ b/src/cpu/bcpu/bcpu_op_rmw.cpp @@ -1,6 +1,7 @@ void bCPU::op_inc() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.a.l++; @@ -19,6 +20,7 @@ void bCPU::op_inc() { void bCPU::op_inx() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.x.l++; @@ -37,6 +39,7 @@ void bCPU::op_inx() { void bCPU::op_iny() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.y.l++; @@ -55,6 +58,7 @@ void bCPU::op_iny() { void bCPU::op_dec() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.a.l--; @@ -73,6 +77,7 @@ void bCPU::op_dec() { void bCPU::op_dex() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.x.l--; @@ -91,6 +96,7 @@ void bCPU::op_dex() { void bCPU::op_dey() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.x) { regs.y.l--; @@ -109,6 +115,7 @@ void bCPU::op_dey() { void bCPU::op_asl() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.p.c = !!(regs.a.l & 0x80); @@ -129,6 +136,7 @@ void bCPU::op_asl() { void bCPU::op_lsr() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); if(regs.p.m) { regs.p.c = regs.a.l & 1; @@ -149,6 +157,7 @@ void bCPU::op_lsr() { void bCPU::op_rol() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); uint16 c = regs.p.c; if(regs.p.m) { @@ -172,6 +181,7 @@ void bCPU::op_rol() { void bCPU::op_ror() { switch(status.cycle_pos++) { case 1: + last_cycle(); cpu_io(); uint16 c; if(regs.p.m) { @@ -218,6 +228,7 @@ void bCPU::op_inc_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -248,6 +259,7 @@ void bCPU::op_dec_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -278,6 +290,7 @@ void bCPU::op_asl_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -308,6 +321,7 @@ void bCPU::op_lsr_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -338,6 +352,7 @@ void bCPU::op_rol_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -368,6 +383,7 @@ void bCPU::op_ror_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -398,6 +414,7 @@ void bCPU::op_trb_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -428,6 +445,7 @@ void bCPU::op_tsb_addr() { op_write(OPMODE_DBR, aa.w + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w, rd.l); status.cycle_pos = 0; break; @@ -461,6 +479,7 @@ void bCPU::op_inc_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -494,6 +513,7 @@ void bCPU::op_dec_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -527,6 +547,7 @@ void bCPU::op_asl_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -560,6 +581,7 @@ void bCPU::op_lsr_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -593,6 +615,7 @@ void bCPU::op_rol_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -626,6 +649,7 @@ void bCPU::op_ror_addrx() { op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -656,6 +680,7 @@ void bCPU::op_inc_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -686,6 +711,7 @@ void bCPU::op_dec_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -716,6 +742,7 @@ void bCPU::op_asl_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -746,6 +773,7 @@ void bCPU::op_lsr_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -776,6 +804,7 @@ void bCPU::op_rol_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -806,6 +835,7 @@ void bCPU::op_ror_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -836,6 +866,7 @@ void bCPU::op_trb_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -866,6 +897,7 @@ void bCPU::op_tsb_dp() { op_write(OPMODE_DP, dp + 1, rd.h); break; case 7: + last_cycle(); op_write(OPMODE_DP, dp, rd.l); status.cycle_pos = 0; break; @@ -899,6 +931,7 @@ void bCPU::op_inc_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -932,6 +965,7 @@ void bCPU::op_dec_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -965,6 +999,7 @@ void bCPU::op_asl_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -998,6 +1033,7 @@ void bCPU::op_lsr_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -1031,6 +1067,7 @@ void bCPU::op_rol_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; @@ -1064,6 +1101,7 @@ void bCPU::op_ror_dpx() { op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); break; case 8: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, rd.l); status.cycle_pos = 0; break; diff --git a/src/cpu/bcpu/bcpu_op_write.cpp b/src/cpu/bcpu/bcpu_op_write.cpp index 6bff82ad..42d218a6 100644 --- a/src/cpu/bcpu/bcpu_op_write.cpp +++ b/src/cpu/bcpu/bcpu_op_write.cpp @@ -7,10 +7,12 @@ void bCPU::op_sta_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w, regs.a.w); if(regs.p.m)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, regs.a.w >> 8); status.cycle_pos = 0; break; @@ -26,10 +28,12 @@ void bCPU::op_stx_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); op_write(OPMODE_DBR, aa.w, regs.x.w); if(regs.p.x)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, regs.x.w >> 8); status.cycle_pos = 0; break; @@ -45,10 +49,12 @@ void bCPU::op_sty_addr() { aa.h = op_read(); break; case 3: + if(regs.p.x)last_cycle(); op_write(OPMODE_DBR, aa.w, regs.y.w); if(regs.p.x)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, regs.y.w >> 8); status.cycle_pos = 0; break; @@ -64,10 +70,12 @@ void bCPU::op_stz_addr() { aa.h = op_read(); break; case 3: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w, 0x0000); if(regs.p.m)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, 0x0000 >> 8); status.cycle_pos = 0; break; @@ -86,10 +94,12 @@ void bCPU::op_sta_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, regs.a.w); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w + 1, regs.a.w >> 8); status.cycle_pos = 0; break; @@ -108,10 +118,12 @@ void bCPU::op_stz_addrx() { cpu_c4(aa.w, aa.w + regs.x.w); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w, 0x0000); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.x.w + 1, 0x0000 >> 8); status.cycle_pos = 0; break; @@ -130,10 +142,12 @@ void bCPU::op_sta_addry() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -152,10 +166,12 @@ void bCPU::op_sta_long() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_LONG, aa.d, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_LONG, aa.d + 1, regs.a.h); status.cycle_pos = 0; break; @@ -174,10 +190,12 @@ void bCPU::op_sta_longx() { aa.b = op_read(); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -193,10 +211,12 @@ void bCPU::op_sta_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); op_write(OPMODE_DP, dp, regs.a.w); if(regs.p.m)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DP, dp + 1, regs.a.w >> 8); status.cycle_pos = 0; break; @@ -212,10 +232,12 @@ void bCPU::op_stx_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); op_write(OPMODE_DP, dp, regs.x.w); if(regs.p.x)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DP, dp + 1, regs.x.w >> 8); status.cycle_pos = 0; break; @@ -231,10 +253,12 @@ void bCPU::op_sty_dp() { cpu_c2(); break; case 3: + if(regs.p.x)last_cycle(); op_write(OPMODE_DP, dp, regs.y.w); if(regs.p.x)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DP, dp + 1, regs.y.w >> 8); status.cycle_pos = 0; break; @@ -250,10 +274,12 @@ void bCPU::op_stz_dp() { cpu_c2(); break; case 3: + if(regs.p.m)last_cycle(); op_write(OPMODE_DP, dp, 0x0000); if(regs.p.m)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_DP, dp + 1, 0x0000 >> 8); status.cycle_pos = 0; break; @@ -272,10 +298,12 @@ void bCPU::op_sta_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, regs.a.w); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w + 1, regs.a.w >> 8); status.cycle_pos = 0; break; @@ -294,10 +322,12 @@ void bCPU::op_sty_dpx() { cpu_io(); break; case 4: + if(regs.p.x)last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, regs.y.w); if(regs.p.x)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w + 1, regs.y.w >> 8); status.cycle_pos = 0; break; @@ -316,10 +346,12 @@ void bCPU::op_stz_dpx() { cpu_io(); break; case 4: + if(regs.p.m)last_cycle(); op_write(OPMODE_DP, dp + regs.x.w, 0x0000); if(regs.p.m)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DP, dp + regs.x.w + 1, 0x0000 >> 8); status.cycle_pos = 0; break; @@ -338,10 +370,12 @@ void bCPU::op_stx_dpy() { cpu_io(); break; case 4: + if(regs.p.x)last_cycle(); op_write(OPMODE_DP, dp + regs.y.w, regs.x.l); if(regs.p.x)status.cycle_pos = 0; break; case 5: + last_cycle(); op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h); status.cycle_pos = 0; break; @@ -363,10 +397,12 @@ void bCPU::op_sta_idp() { aa.h = op_read(OPMODE_DP, dp + 1); break; case 5: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 6: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -391,10 +427,12 @@ void bCPU::op_sta_ildp() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); op_write(OPMODE_LONG, aa.d, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 7: + last_cycle(); op_write(OPMODE_LONG, aa.d + 1, regs.a.h); status.cycle_pos = 0; break; @@ -419,10 +457,12 @@ void bCPU::op_sta_idpx() { aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); break; case 6: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -447,10 +487,12 @@ void bCPU::op_sta_idpy() { cpu_c4(aa.w, aa.w + regs.y.w); break; case 6: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -475,10 +517,12 @@ void bCPU::op_sta_ildpy() { aa.b = op_read(OPMODE_DP, dp + 2); break; case 6: + if(regs.p.m)last_cycle(); op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 7: + last_cycle(); op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h); status.cycle_pos = 0; break; @@ -494,10 +538,12 @@ void bCPU::op_sta_sr() { cpu_io(); break; case 3: + if(regs.p.m)last_cycle(); op_write(OPMODE_SP, sp, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 4: + last_cycle(); op_write(OPMODE_SP, sp + 1, regs.a.h); status.cycle_pos = 0; break; @@ -522,10 +568,12 @@ void bCPU::op_sta_isry() { cpu_io(); break; case 6: + if(regs.p.m)last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)status.cycle_pos = 0; break; case 7: + last_cycle(); op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); status.cycle_pos = 0; break; diff --git a/src/cpu/bcpu/bcpu_optable.cpp b/src/cpu/bcpu/bcpu_optable.cpp index 253f75b9..4041724c 100644 --- a/src/cpu/bcpu/bcpu_optable.cpp +++ b/src/cpu/bcpu/bcpu_optable.cpp @@ -186,7 +186,6 @@ optbl[0x91] = &bCPU::op_sta_idpy; optbl[0x97] = &bCPU::op_sta_ildpy; optbl[0x83] = &bCPU::op_sta_sr; optbl[0x93] = &bCPU::op_sta_isry; -optbl[0x80] = &bCPU::op_bra; optbl[0x90] = &bCPU::op_bcc; optbl[0xb0] = &bCPU::op_bcs; optbl[0xd0] = &bCPU::op_bne; @@ -195,6 +194,7 @@ optbl[0x10] = &bCPU::op_bpl; optbl[0x30] = &bCPU::op_bmi; optbl[0x50] = &bCPU::op_bvc; optbl[0x70] = &bCPU::op_bvs; +optbl[0x80] = &bCPU::op_bra; optbl[0x82] = &bCPU::op_brl; optbl[0x4c] = &bCPU::op_jmp_addr; optbl[0x5c] = &bCPU::op_jmp_long; diff --git a/src/cpu/bcpu/bcpu_timing.cpp b/src/cpu/bcpu/bcpu_timing.cpp index d5e10792..e25535c3 100644 --- a/src/cpu/bcpu/bcpu_timing.cpp +++ b/src/cpu/bcpu/bcpu_timing.cpp @@ -25,19 +25,158 @@ * 4 cycles short? */ -uint16 bCPU::vcounter() { return time.v; } -uint16 bCPU::hcounter() { return get_hcounter(); } -uint16 bCPU::hcycles() { return time.hc; } +uint16 bCPU::vcounter() { return time.v; } +uint16 bCPU::hcounter() { return get_hcounter(); } +uint16 bCPU::hcycles() { return time.hc; } + bool bCPU::interlace() { return time.interlace; } bool bCPU::interlace_field() { return time.interlace_field; } bool bCPU::overscan() { return time.overscan; } uint16 bCPU::region_scanlines() { return time.region_scanlines; } -void bCPU::set_interlace(bool r) { time.interlace = r; } -void bCPU::set_overscan(bool r) { time.overscan = r; } +void bCPU::set_interlace(bool r) { time.interlace = r; update_interrupts(); } +void bCPU::set_overscan (bool r) { time.overscan = r; update_interrupts(); } uint8 bCPU::dma_counter() { return (time.dma_counter + time.hc) & 6; } +bool bCPU::nmi_trigger_pos_match(uint32 offset) { +uint16 v = overscan() ? 240 : 225; +uint16 hc = 2 + offset; + return (time.v == v && time.hc == hc); +} + +bool bCPU::irq_trigger_pos_match(uint32 offset) { +uint16 v = status.virq_pos; +uint16 hc = (status.hirq_enabled) ? status.hirq_pos : 0; + +//positions that can never be latched +//region_scanlines() = 262/NTSC, 312/PAL +//PAL results are unverified on hardware + if(v == 240 && hc == 339 && interlace() == false && interlace_field() == 1)return false; + if(v == (region_scanlines() - 1) && hc == 339 && interlace() == false)return false; + if(v == region_scanlines() && interlace() == false)return false; + if(v == region_scanlines() && hc == 339)return false; + if(v > region_scanlines())return false; + if(hc > 339)return false; + + hc = (hc != 0) ? ((hc << 2) + 14) : 10; + hc += offset; + if(hc >= time.line_cycles) { + hc -= time.line_cycles; + if(++v >= time.frame_lines) { + v = 0; + } + } + + if((status.virq_enabled == true && time.v == v) || status.virq_enabled == false) { + return (time.hc == hc); + } + + return false; +} + +void bCPU::update_nmi() { + if(time.v == (overscan() ? 240 : 225)) { + time.nmi_read_trigger_pos = 2; + time.nmi_line_trigger_pos = 6; + } else { + time.nmi_read_trigger_pos = -64; + time.nmi_line_trigger_pos = -64; + } +} + +void bCPU::update_irq() { +int vpos = status.virq_pos; +int hpos = (status.hirq_enabled) ? status.hirq_pos : 0; + +//positions that can never be latched +//region_scanlines() = 262/NTSC, 312/PAL +//PAL results are unverified on hardware + if(vpos == 240 && hpos == 339 && interlace() == false && interlace_field() == 1)goto _nolatch; + if(vpos == (region_scanlines() - 1) && hpos == 339 && interlace() == false)goto _nolatch; + if(vpos == region_scanlines() && interlace() == false)goto _nolatch; + if(vpos == region_scanlines() && hpos == 339)goto _nolatch; + if(vpos > region_scanlines())goto _nolatch; + if(hpos > 339)goto _nolatch; + + hpos = (hpos != 0) ? ((hpos << 2) + 14) : 10; + if(hpos >= time.line_cycles) { + hpos -= time.line_cycles; + if(++vpos >= time.frame_lines) { + vpos = 0; + } + } + + if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) { + time.irq_read_trigger_pos = hpos; + } else { + time.irq_read_trigger_pos = -64; + } + + hpos += 4; + if(hpos >= time.line_cycles) { + hpos -= time.line_cycles; + if(++vpos >= time.frame_lines) { + vpos = 0; + } + } + + if((status.virq_enabled == true && time.v == vpos) || status.virq_enabled == false) { + time.irq_line_trigger_pos = hpos; + } else { + time.irq_line_trigger_pos = -64; + } + + return; + +_nolatch: + time.irq_read_trigger_pos = -64; + time.irq_line_trigger_pos = -64; +} + +void bCPU::update_interrupts() { + update_nmi(); + update_irq(); +} + +void bCPU::poll_interrupts(int cycles) { +int16 hc, hc_end; + if(time.hc == 0) { + hc = -1; + hc_end = cycles; + } else { + hc = time.hc; + hc_end = time.hc + cycles; + } + + if(hc < time.nmi_read_trigger_pos && time.nmi_read_trigger_pos <= hc_end) { + //nmi_read can go low even with NMI interrupts disabled in $4200.d7 + time.nmi_read = 0; + } + + if(hc < time.nmi_line_trigger_pos && time.nmi_line_trigger_pos <= hc_end) { + if(status.nmi_enabled == true) { + if(time.nmi_line == 1) { + time.nmi_transition = 1; + } + time.nmi_line = 0; + } + } + + if(hc < time.irq_read_trigger_pos && time.irq_read_trigger_pos <= hc_end) { + if(status.virq_enabled == true || status.hirq_enabled == true) { + time.irq_read = 0; + } + } + + if(hc < time.irq_line_trigger_pos && time.irq_line_trigger_pos <= hc_end) { + if(status.virq_enabled == true || status.hirq_enabled == true) { + time.irq_line = 0; + time.irq_transition = 1; + } + } +} + //all scanlines are 1364 cycles long, except scanline 240 //on non-interlace odd-frames, which is 1360 cycles long. //[NTSC] @@ -68,7 +207,6 @@ void bCPU::inc_vcounter() { } } - time.hc = 0; time.dma_counter = time.line_cycles & 6; if(time.v == 240 && time.interlace == false && time.interlace_field == 1) { time.line_cycles = 1360; @@ -76,6 +214,8 @@ void bCPU::inc_vcounter() { time.line_cycles = 1364; } time.dram_refreshed = false; + + update_interrupts(); } //all dots are 4 cycles long, except dots 323 and 327. dots 323 and 327 @@ -93,16 +233,6 @@ uint16 bCPU::get_hcounter() { return (time.hc - ((time.hc > 1292) << 1) - ((time.hc > 1310) << 1)) >> 2; } -void bCPU::dram_refresh() { - time.dram_refreshed = true; - add_cycles(40); - if(time.v != 240 || time.interlace != false || time.interlace_field != 1) { - //alternate between 534 and 538 every scanline except 240ni1 - //in reality, this is probably based on frame cycle length... - time.dram_refresh_pos ^= 12; - } -} - uint32 bCPU::cycles_executed() { uint32 r = status.cycles_executed; status.cycles_executed = 0; @@ -112,28 +242,73 @@ uint32 r = status.cycles_executed; void bCPU::add_cycles(int cycles) { status.cycles_executed += cycles; - cycles >>= 1; - while(cycles--) { - time.hc += 2; + poll_interrupts(cycles); - if(time.hc >= time.line_cycles) { - inc_vcounter(); - if(time.v == 0) { - frame(); - ppu->frame(); - } - scanline(); - ppu->scanline(); + if(time.hc + cycles >= time.line_cycles) { + cycles = (time.hc + cycles) - time.line_cycles; + time.hc = 0; + + inc_vcounter(); + poll_interrupts(cycles); + + if(time.v == 0) { + frame(); + ppu->frame(); + snes->frame(); } - if(time.dram_refreshed == false && time.hc >= time.dram_refresh_pos) { - dram_refresh(); - } + scanline(); + ppu->scanline(); +// ppu->render_scanline(); + snes->scanline(); + time.line_rendered = false; + } - if(hdma_test() == true) { - hdma_run(); + if(time.line_rendered == false) { + //rendering should start at H=22, but due to inaccurate + //timing, and due to using a scanline-based renderer, use + //a higher value to allow more games to run properly... + //H=48 fixes off-by-one HDMA effects with FF6's battles + if(time.hc + cycles >= (48 * 4)) { + cycles = (time.hc + cycles) - (48 * 4); + time.hc = (48 * 4); + time.line_rendered = true; + ppu->render_scanline(); } } + + if(time.dram_refreshed == false) { + if(time.hc + cycles >= time.dram_refresh_pos) { + time.dram_refreshed = true; + status.cycles_executed += 40; + cycles = (time.hc + cycles) - time.dram_refresh_pos; + time.hc = time.dram_refresh_pos + 40; + + if(cpu_version == 2) { + if(time.v != 240 || time.interlace != false || time.interlace_field != 1) { + if(time.dram_refresh_pos == 534) { + time.dram_refresh_pos = 538; + } else { + time.dram_refresh_pos = 534; + } + } + } + } + } + + if(status.hdma_triggered == false) { + //vcounter range verified on hardware + if(time.v <= (overscan() ? 239 : 224)) { + if(time.hc + cycles >= 1112) { //278 * 4 = 1112 + cycles = (time.hc + cycles) - 1112; + time.hc = 1112; + status.hdma_triggered = true; + hdma_run(); + } + } + } + + time.hc += cycles; } void bCPU::time_reset() { @@ -150,10 +325,20 @@ void bCPU::time_reset() { time.line_cycles = 1364; time.dram_refreshed = false; - time.dram_refresh_pos = 538; + time.dram_refresh_pos = (cpu_version == 2) ? 538 : 530; time.dma_counter = 0; + time.nmi_pending = false; + time.irq_pending = false; + time.nmi_line = time.nmi_read = 1; + time.irq_line = time.irq_read = 1; + + time.nmi_transition = 0; + time.irq_transition = 0; + + update_interrupts(); + switch(region) { case NTSC: time.region_scanlines = 262; diff --git a/src/cpu/bcpu/bcpu_timing.h b/src/cpu/bcpu/bcpu_timing.h index c6fd4dd6..42a15158 100644 --- a/src/cpu/bcpu/bcpu_timing.h +++ b/src/cpu/bcpu/bcpu_timing.h @@ -4,13 +4,42 @@ struct { bool interlace, interlace_field, overscan; uint16 line_cycles, frame_lines; + bool line_rendered; + bool dram_refreshed; uint16 dram_refresh_pos; uint8 dma_counter; uint16 region_scanlines; -}time; + +//nmi_pending, irq_pending are used by last_cycle() +//nmi_line = /NMI, nmi_read = $4210.7 +//irq_line = /IRQ, irq_read = $4211.7 + bool nmi_pending, nmi_line, nmi_read; + bool irq_pending, irq_line, irq_read; + +//NMI is edge-sensitive, meaning it triggers when /NMI +//transitions from high to low. This value is set to 1 +//when that happens, and cleared after the nmi_test() +//routine acknowledges it and invokes the NMI interrupt + bool nmi_transition; + +//IRQ is level-sensitive, so it does not need to keep +//track of transitions from high to low. IRQs will +//continue to fire as long as /IRQ stays low. +//However, if a write to $4200 forces IRQs high at the +//exact same clock cycle that /IRQ goes low, the /IRQ +//will still occur. Hence the need for this variable. + bool irq_transition; + +//position is relative to time.hc, set at start of each scanline +//-64 means no trigger point on this scanline +//$4210/$4211 status bits get set before /NMI and /IRQ go low, +//hence the need for two variables for each. + int32 nmi_read_trigger_pos, nmi_line_trigger_pos; + int32 irq_read_trigger_pos, irq_line_trigger_pos; +} time; inline uint16 vcounter(); inline uint16 hcounter(); @@ -20,6 +49,14 @@ inline bool interlace_field(); inline bool overscan(); inline uint16 region_scanlines(); +inline bool nmi_trigger_pos_match(uint32 offset); +inline bool irq_trigger_pos_match(uint32 offset); + +inline void update_nmi(); +inline void update_irq(); +inline void update_interrupts(); +inline void poll_interrupts(int cycles); + inline void set_interlace(bool r); inline void set_overscan (bool r); @@ -27,6 +64,5 @@ inline uint8 dma_counter(); inline void inc_vcounter(); inline uint16 get_hcounter(); -inline void dram_refresh(); inline void add_cycles(int cycles); inline void time_reset(); diff --git a/src/cpu/bcpu/bcpugen.cpp b/src/cpu/bcpu/bcpugen.cpp index 9f49452f..d5406c9a 100644 --- a/src/cpu/bcpu/bcpugen.cpp +++ b/src/cpu/bcpu/bcpugen.cpp @@ -53,7 +53,6 @@ char t[4096]; void update_line(int i, int n) { char t[4096]; - sprintf(t, "goto l%d;", n + 2); replace(line[i], "end;", "status.cycle_pos = 0;"); replace(line[i], "skip;", "status.cycle_pos++;"); } @@ -88,7 +87,11 @@ char t[4096]; i++; } - if(!strcmp(line[i], "}"))strcat(output_op, " status.cycle_pos = 0;\r\n"); + + if(!strcmp(line[i], "}")) { + strcat(output_op, " status.cycle_pos = 0;\r\n"); + } + strcat(output_op, " break;\r\n"); } strcat(output_op, " }\r\n}"); diff --git a/src/cpu/bcpu/bcpugen.exe b/src/cpu/bcpu/bcpugen.exe deleted file mode 100644 index db34eb92..00000000 Binary files a/src/cpu/bcpu/bcpugen.exe and /dev/null differ diff --git a/src/cpu/bcpu/op_misc.b b/src/cpu/bcpu/op_misc.b index d35fac92..847c832e 100644 --- a/src/cpu/bcpu/op_misc.b +++ b/src/cpu/bcpu/op_misc.b @@ -1,14 +1,17 @@ nop(0xea) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); } wdm(0x42) { -1:op_read(); +1:last_cycle(); + op_read(); } xba(0xeb) { 1:cpu_io(); -2:cpu_io(); +2:last_cycle(); + cpu_io(); regs.a.l ^= regs.a.h; regs.a.h ^= regs.a.l; regs.a.l ^= regs.a.h; @@ -26,7 +29,8 @@ mvp(0x44, --) { 5:cpu_io(); if(regs.p.x) { regs.x.l$1; regs.y.l$1; } else { regs.x.w$1; regs.y.w$1; } -6:cpu_io(); +6:last_cycle(); + cpu_io(); if(regs.a.w--)regs.pc.w -= 3; } @@ -39,7 +43,8 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) { 4:stack_write(regs.pc.l); 5:stack_write(regs.p); 6:rd.l = op_read(OPMODE_LONG, (regs.e)?$1:$3); -7:rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4); +7:last_cycle(); + rd.h = op_read(OPMODE_LONG, (regs.e)?$2:$4); regs.pc.b = 0x00; regs.pc.w = rd.w; regs.p.i = 1; @@ -49,19 +54,24 @@ cop(0x02, 0xfff4, 0xfff5, 0xffe4, 0xffe5) { stp(0xdb) { 1:cpu_io(); status.cpu_state = CPUSTATE_STP; -2:cpu_io(); +2:last_cycle(); + cpu_io(); regs.pc.w--; } wai(0xcb) { 1:cpu_io(); status.cpu_state = CPUSTATE_WAI; -2:cpu_io(); - regs.pc.w--; +2:last_cycle(); + cpu_io(); + if(status.cpu_state == CPUSTATE_WAI) { + regs.pc.w--; + } } xce(0xfb) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); bool c = regs.p.c; regs.p.c = regs.e; regs.e = c; @@ -80,14 +90,16 @@ clv(0xb8, regs.p.v = 0), sec(0x38, regs.p.c = 1), sed(0xf8, regs.p.d = 1), sei(0x78, regs.p.i = 1) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); $1; } rep(0xc2, &=~), sep(0xe2, |=) { 1:rd.l = op_read(); -2:cpu_io(); +2:last_cycle(); + cpu_io(); regs.p $1 rd.l; if(regs.e)regs.p |= 0x30; if(regs.p.x) { @@ -102,7 +114,8 @@ txa(0x8a, regs.p.m, a, x), txy(0x9b, regs.p.x, y, x), tya(0x98, regs.p.m, a, y), tyx(0xbb, regs.p.x, x, y) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if($1) { regs.$2.l = regs.$3.l; regs.p.n = !!(regs.$2.l & 0x80); @@ -115,27 +128,31 @@ tyx(0xbb, regs.p.x, x, y) { } tcd(0x5b) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); regs.d.w = regs.a.w; regs.p.n = !!(regs.d.w & 0x8000); regs.p.z = (regs.d.w == 0); } tcs(0x1b) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); regs.s.w = regs.a.w; if(regs.e)regs.s.h = 0x01; } tdc(0x7b) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); regs.a.w = regs.d.w; regs.p.n = !!(regs.a.w & 0x8000); regs.p.z = (regs.a.w == 0); } tsc(0x3b) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); regs.a.w = regs.s.w; if(regs.e) { regs.p.n = !!(regs.a.l & 0x80); @@ -147,7 +164,8 @@ tsc(0x3b) { } tsx(0xba) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if(regs.p.x) { regs.x.l = regs.s.l; regs.p.n = !!(regs.x.l & 0x80); @@ -160,7 +178,8 @@ tsx(0xba) { } txs(0x9a) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if(regs.e) { regs.s.l = regs.x.l; regs.p.n = !!(regs.s.l & 0x80); @@ -179,14 +198,16 @@ phd(0x0b, 0, d) { 1:cpu_io(); if($1)skip; 2:stack_write(regs.$2.h); -3:stack_write(regs.$2.l); +3:last_cycle(); + stack_write(regs.$2.l); } phb(0x8b, regs.db), phk(0x4b, regs.pc.b), php(0x08, regs.p) { 1:cpu_io(); -2:stack_write($1); +2:last_cycle(); + stack_write($1); } pla(0x68, regs.p.m, a), @@ -195,13 +216,15 @@ ply(0x7a, regs.p.x, y), pld(0x2b, 0, d) { 1:cpu_io(); 2:cpu_io(); -3:regs.$2.l = stack_read(); +3:if($1)last_cycle(); + regs.$2.l = stack_read(); if($1) { regs.p.n = !!(regs.$2.l & 0x80); regs.p.z = (regs.$2.l == 0); end; } -4:regs.$2.h = stack_read(); +4:last_cycle(); + regs.$2.h = stack_read(); regs.p.n = !!(regs.$2.w & 0x8000); regs.p.z = (regs.$2.w == 0); } @@ -209,7 +232,8 @@ pld(0x2b, 0, d) { plb(0xab) { 1:cpu_io(); 2:cpu_io(); -3:regs.db = stack_read(); +3:last_cycle(); + regs.db = stack_read(); regs.p.n = !!(regs.db & 0x80); regs.p.z = (regs.db == 0); } @@ -217,7 +241,8 @@ plb(0xab) { plp(0x28) { 1:cpu_io(); 2:cpu_io(); -3:regs.p = stack_read(); +3:last_cycle(); + regs.p = stack_read(); if(regs.e)regs.p |= 0x30; if(regs.p.x) { regs.x.h = 0x00; @@ -229,7 +254,8 @@ pea(0xf4) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:stack_write(aa.h); -4:stack_write(aa.l); +4:last_cycle(); + stack_write(aa.l); } pei(0xd4) { @@ -238,7 +264,8 @@ pei(0xd4) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:stack_write(aa.h); -6:stack_write(aa.l); +6:last_cycle(); + stack_write(aa.l); } per(0x62) { @@ -247,5 +274,6 @@ per(0x62) { 3:cpu_io(); rd.w = regs.pc.d + (int16)aa.w; 4:stack_write(rd.h); -5:stack_write(rd.l); +5:last_cycle(); + stack_write(rd.l); } diff --git a/src/cpu/bcpu/op_pc.b b/src/cpu/bcpu/op_pc.b index c24ff5b5..e202f222 100644 --- a/src/cpu/bcpu/op_pc.b +++ b/src/cpu/bcpu/op_pc.b @@ -1,4 +1,3 @@ -bra(0x80, 1), bcc(0x90, !regs.p.c), bcs(0xb0, regs.p.c), bne(0xd0, !regs.p.z), @@ -7,7 +6,8 @@ bpl(0x10, !regs.p.n), bmi(0x30, regs.p.n), bvc(0x50, !regs.p.v), bvs(0x70, regs.p.v) { -1:rd.l = op_read(); +1:if(!$1)last_cycle(); + rd.l = op_read(); if($1) { aa.w = regs.pc.d + (int8)rd.l; regs.pc.w = aa.w; @@ -15,26 +15,39 @@ bvs(0x70, regs.p.v) { end; } 2:cpu_c6(aa.w); -3:cpu_io(); +3:last_cycle(); + cpu_io(); +} + +bra(0x80) { +1:rd.l = op_read(); + aa.w = regs.pc.d + (int8)rd.l; + regs.pc.w = aa.w; +2:cpu_c6(aa.w); +3:last_cycle(); + cpu_io(); } brl(0x82) { 1:rd.l = op_read(); 2:rd.h = op_read(); -3:cpu_io(); +3:last_cycle(); + cpu_io(); regs.pc.w = regs.pc.d + (int16)rd.w; } jmp_addr(0x4c) { 1:rd.l = op_read(); -2:rd.h = op_read(); +2:last_cycle(); + rd.h = op_read(); regs.pc.w = rd.w; } jmp_long(0x5c) { 1:rd.l = op_read(); 2:rd.h = op_read(); -3:rd.b = op_read(); +3:last_cycle(); + rd.b = op_read(); regs.pc.d = rd.d & 0xffffff; } @@ -42,7 +55,8 @@ jmp_iaddr(0x6c) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:rd.l = op_read(OPMODE_ADDR, aa.w); -4:rd.h = op_read(OPMODE_ADDR, aa.w + 1); +4:last_cycle(); + rd.h = op_read(OPMODE_ADDR, aa.w + 1); regs.pc.w = rd.w; } @@ -51,7 +65,8 @@ jmp_iaddrx(0x7c) { 2:aa.h = op_read(); 3:cpu_io(); 4:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w); -5:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); regs.pc.w = rd.w; } @@ -60,7 +75,8 @@ jmp_iladdr(0xdc) { 2:aa.h = op_read(); 3:rd.l = op_read(OPMODE_ADDR, aa.w); 4:rd.h = op_read(OPMODE_ADDR, aa.w + 1); -5:rd.b = op_read(OPMODE_ADDR, aa.w + 2); +5:last_cycle(); + rd.b = op_read(OPMODE_ADDR, aa.w + 2); regs.pc.d = rd.d & 0xffffff; } @@ -70,7 +86,8 @@ jsr_addr(0x20) { 3:cpu_io(); 4:regs.pc.w--; stack_write(regs.pc.h); -5:stack_write(regs.pc.l); +5:last_cycle(); + stack_write(regs.pc.l); regs.pc.w = aa.w; } @@ -82,7 +99,8 @@ jsr_long(0x22) { 5:aa.b = op_read(); 6:regs.pc.w--; stack_write(regs.pc.h); -7:stack_write(regs.pc.l); +7:last_cycle(); + stack_write(regs.pc.l); regs.pc.d = aa.d & 0xffffff; } @@ -93,7 +111,8 @@ jsr_iaddrx(0xfc) { 4:aa.h = op_read(); 5:cpu_io(); 6:rd.l = op_read(OPMODE_PBR, aa.w + regs.x.w); -7:rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_PBR, aa.w + regs.x.w + 1); regs.pc.w = rd.w; } @@ -112,7 +131,8 @@ rti(0x40) { regs.pc.w = rd.w; end; } -6:rd.b = stack_read(); +6:last_cycle(); + rd.b = stack_read(); regs.pc.d = rd.d & 0xffffff; } @@ -121,7 +141,8 @@ rts(0x60) { 2:cpu_io(); 3:rd.l = stack_read(); 4:rd.h = stack_read(); -5:cpu_io(); +5:last_cycle(); + cpu_io(); regs.pc.w = rd.w; regs.pc.w++; } @@ -131,7 +152,8 @@ rtl(0x6b) { 2:cpu_io(); 3:rd.l = stack_read(); 4:rd.h = stack_read(); -5:rd.b = stack_read(); +5:last_cycle(); + rd.b = stack_read(); regs.pc.d = rd.d & 0xffffff; regs.pc.w++; } diff --git a/src/cpu/bcpu/op_read.b b/src/cpu/bcpu/op_read.b index fb384d71..6cb88746 100644 --- a/src/cpu/bcpu/op_read.b +++ b/src/cpu/bcpu/op_read.b @@ -9,9 +9,11 @@ ldx_const(0xa2, ldx, regs.p.x), ldy_const(0xa0, ldy, regs.p.x), ora_const(0x09, ora, regs.p.m), sbc_const(0xe9, sbc, regs.p.m) { -1:rd.l = op_read(); +1:if($2)last_cycle(); + rd.l = op_read(); if($2) { op_$1_b(); end; } -2:rd.h = op_read(); +2:last_cycle(); + rd.h = op_read(); op_$1_w(); } @@ -29,9 +31,11 @@ ora_addr(0x0d, ora, regs.p.m), sbc_addr(0xed, sbc, regs.p.m) { 1:aa.l = op_read(); 2:aa.h = op_read(); -3:rd.l = op_read(OPMODE_DBR, aa.w); +3:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w); if($2) { op_$1_b(); end; } -4:rd.h = op_read(OPMODE_DBR, aa.w + 1); +4:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + 1); op_$1_w(); } @@ -47,9 +51,11 @@ sbc_addrx(0xfd, sbc, regs.p.m) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:cpu_c4(aa.w, aa.w + regs.x.w); -4:rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w + regs.x.w); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + regs.x.w + 1); op_$1_w(); } @@ -64,9 +70,11 @@ sbc_addry(0xf9, sbc, regs.p.m) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:cpu_c4(aa.w, aa.w + regs.y.w); -4:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_$1_w(); } @@ -80,9 +88,11 @@ sbc_long(0xef, sbc, regs.p.m) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:aa.b = op_read(); -4:rd.l = op_read(OPMODE_LONG, aa.d); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_LONG, aa.d); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_LONG, aa.d + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_LONG, aa.d + 1); op_$1_w(); } @@ -96,9 +106,11 @@ sbc_longx(0xff, sbc, regs.p.m) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:aa.b = op_read(); -4:rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_LONG, aa.d + regs.x.w); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_LONG, aa.d + regs.x.w + 1); op_$1_w(); } @@ -116,9 +128,11 @@ ora_dp(0x05, ora, regs.p.m), sbc_dp(0xe5, sbc, regs.p.m) { 1:dp = op_read(); 2:cpu_c2(); -3:rd.l = op_read(OPMODE_DP, dp); +3:if($2)last_cycle(); + rd.l = op_read(OPMODE_DP, dp); if($2) { op_$1_b(); end; } -4:rd.h = op_read(OPMODE_DP, dp + 1); +4:last_cycle(); + rd.h = op_read(OPMODE_DP, dp + 1); op_$1_w(); } @@ -134,9 +148,11 @@ sbc_dpx(0xf5, sbc, regs.p.m) { 1:dp = op_read(); 2:cpu_c2(); 3:cpu_io(); -4:rd.l = op_read(OPMODE_DP, dp + regs.x.w); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_DP, dp + regs.x.w); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_DP, dp + regs.x.w + 1); op_$1_w(); } @@ -144,9 +160,11 @@ ldx_dpy(0xb6, ldx, regs.p.x) { 1:dp = op_read(); 2:cpu_c2(); 3:cpu_io(); -4:rd.l = op_read(OPMODE_DP, dp + regs.y.w); +4:if($2)last_cycle(); + rd.l = op_read(OPMODE_DP, dp + regs.y.w); if($2) { op_$1_b(); end; } -5:rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1); +5:last_cycle(); + rd.h = op_read(OPMODE_DP, dp + regs.y.w + 1); op_$1_w(); } @@ -161,9 +179,11 @@ sbc_idp(0xf2, sbc, regs.p.m) { 2:cpu_c2(); 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); -5:rd.l = op_read(OPMODE_DBR, aa.w); +5:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w); if($2) { op_$1_b(); end; } -6:rd.h = op_read(OPMODE_DBR, aa.w + 1); +6:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + 1); op_$1_w(); } @@ -179,9 +199,11 @@ sbc_idpx(0xe1, sbc, regs.p.m) { 3:cpu_io(); 4:aa.l = op_read(OPMODE_DP, dp + regs.x.w); 5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); -6:rd.l = op_read(OPMODE_DBR, aa.w); +6:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w); if($2) { op_$1_b(); end; } -7:rd.h = op_read(OPMODE_DBR, aa.w + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + 1); op_$1_w(); } @@ -197,9 +219,11 @@ sbc_idpy(0xf1, sbc, regs.p.m) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:cpu_c4(aa.w, aa.w + regs.y.w); -6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); +6:if($2)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if($2) { op_$1_b(); end; } -7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_$1_w(); } @@ -215,9 +239,11 @@ sbc_ildp(0xe7, sbc, regs.p.m) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:aa.b = op_read(OPMODE_DP, dp + 2); -6:rd.l = op_read(OPMODE_LONG, aa.d); +6:if($2)last_cycle(); + rd.l = op_read(OPMODE_LONG, aa.d); if($2) { op_$1_b(); end; } -7:rd.h = op_read(OPMODE_LONG, aa.d + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_LONG, aa.d + 1); op_$1_w(); } @@ -233,9 +259,11 @@ sbc_ildpy(0xf7, sbc, regs.p.m) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:aa.b = op_read(OPMODE_DP, dp + 2); -6:rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); +6:if($2)last_cycle(); + rd.l = op_read(OPMODE_LONG, aa.d + regs.y.w); if($2) { op_$1_b(); end; } -7:rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_LONG, aa.d + regs.y.w + 1); op_$1_w(); } @@ -248,9 +276,11 @@ ora_sr(0x03, ora, regs.p.m), sbc_sr(0xe3, sbc, regs.p.m) { 1:sp = op_read(); 2:cpu_io(); -3:rd.l = op_read(OPMODE_SP, sp); +3:if($2)last_cycle(); + rd.l = op_read(OPMODE_SP, sp); if($2) { op_$1_b(); end; } -4:rd.h = op_read(OPMODE_SP, sp + 1); +4:last_cycle(); + rd.h = op_read(OPMODE_SP, sp + 1); op_$1_w(); } @@ -266,18 +296,22 @@ sbc_isry(0xf3, sbc) { 3:aa.l = op_read(OPMODE_SP, sp); 4:aa.h = op_read(OPMODE_SP, sp + 1); 5:cpu_io(); -6:rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); +6:if(regs.p.m)last_cycle(); + rd.l = op_read(OPMODE_DBR, aa.w + regs.y.w); if(regs.p.m) { op_$1_b(); end; } -7:rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); +7:last_cycle(); + rd.h = op_read(OPMODE_DBR, aa.w + regs.y.w + 1); op_$1_w(); } bit_const(0x89) { -1:rd.l = op_read(); +1:if(regs.p.m)last_cycle(); + rd.l = op_read(); if(regs.p.m) { regs.p.z = ((rd.l & regs.a.l) == 0); end; } -2:rd.h = op_read(); +2:last_cycle(); + rd.h = op_read(); regs.p.z = ((rd.w & regs.a.w) == 0); } diff --git a/src/cpu/bcpu/op_rmw.b b/src/cpu/bcpu/op_rmw.b index 2fff41b2..47385f06 100644 --- a/src/cpu/bcpu/op_rmw.b +++ b/src/cpu/bcpu/op_rmw.b @@ -1,7 +1,8 @@ inc(0x1a, regs.p.m, a), inx(0xe8, regs.p.x, x), iny(0xc8, regs.p.x, y) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if($1) { regs.$2.l++; regs.p.n = !!(regs.$2.l & 0x80); @@ -16,7 +17,8 @@ iny(0xc8, regs.p.x, y) { dec(0x3a, regs.p.m, a), dex(0xca, regs.p.x, x), dey(0x88, regs.p.x, y) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if($1) { regs.$2.l--; regs.p.n = !!(regs.$2.l & 0x80); @@ -29,7 +31,8 @@ dey(0x88, regs.p.x, y) { } asl(0x0a) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if(regs.p.m) { regs.p.c = !!(regs.a.l & 0x80); regs.a.l <<= 1; @@ -44,7 +47,8 @@ asl(0x0a) { } lsr(0x4a) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); if(regs.p.m) { regs.p.c = regs.a.l & 1; regs.a.l >>= 1; @@ -59,7 +63,8 @@ lsr(0x4a) { } rol(0x2a) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); uint16 c = regs.p.c; if(regs.p.m) { regs.p.c = !!(regs.a.l & 0x80); @@ -77,7 +82,8 @@ rol(0x2a) { } ror(0x6a) { -1:cpu_io(); +1:last_cycle(); + cpu_io(); uint16 c; if(regs.p.m) { c = (regs.p.c)?0x80:0; @@ -113,7 +119,8 @@ tsb_addr(0x0c, tsb) { if(regs.p.m) { op_$1_b(); skip; } else op_$1_w(); 6:op_write(OPMODE_DBR, aa.w + 1, rd.h); -7:op_write(OPMODE_DBR, aa.w, rd.l); +7:last_cycle(); + op_write(OPMODE_DBR, aa.w, rd.l); } inc_addrx(0xfe, inc), @@ -132,7 +139,8 @@ ror_addrx(0x7e, ror) { if(regs.p.m) { op_$1_b(); skip; } else op_$1_w(); 7:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, rd.h); -8:op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); +8:last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.x.w, rd.l); } inc_dp(0xe6, inc), @@ -152,7 +160,8 @@ tsb_dp(0x04, tsb) { if(regs.p.m) { op_$1_b(); skip; } else op_$1_w(); 6:op_write(OPMODE_DP, dp + 1, rd.h); -7:op_write(OPMODE_DP, dp, rd.l); +7:last_cycle(); + op_write(OPMODE_DP, dp, rd.l); } inc_dpx(0xf6, inc), @@ -171,5 +180,6 @@ ror_dpx(0x76, ror) { if(regs.p.m) { op_$1_b(); skip; } else op_$1_w(); 7:op_write(OPMODE_DP, dp + regs.x.w + 1, rd.h); -8:op_write(OPMODE_DP, dp + regs.x.w, rd.l); +8:last_cycle(); + op_write(OPMODE_DP, dp + regs.x.w, rd.l); } diff --git a/src/cpu/bcpu/op_write.b b/src/cpu/bcpu/op_write.b index e1ba5c14..527e1505 100644 --- a/src/cpu/bcpu/op_write.b +++ b/src/cpu/bcpu/op_write.b @@ -4,9 +4,11 @@ sty_addr(0x8c, regs.p.x, regs.y.w), stz_addr(0x9c, regs.p.m, 0x0000) { 1:aa.l = op_read(); 2:aa.h = op_read(); -3:op_write(OPMODE_DBR, aa.w, $2); +3:if($1)last_cycle(); + op_write(OPMODE_DBR, aa.w, $2); if($1)end; -4:op_write(OPMODE_DBR, aa.w + 1, $2 >> 8); +4:last_cycle(); + op_write(OPMODE_DBR, aa.w + 1, $2 >> 8); } sta_addrx(0x9d, regs.p.m, regs.a.w), @@ -14,36 +16,44 @@ stz_addrx(0x9e, regs.p.m, 0x0000) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:cpu_c4(aa.w, aa.w + regs.x.w); -4:op_write(OPMODE_DBR, aa.w + regs.x.w, $2); +4:if($1)last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.x.w, $2); if($1)end; -5:op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8); +5:last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.x.w + 1, $2 >> 8); } sta_addry(0x99) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:cpu_c4(aa.w, aa.w + regs.y.w); -4:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); +4:if(regs.p.m)last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)end; -5:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); +5:last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); } sta_long(0x8f) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:aa.b = op_read(); -4:op_write(OPMODE_LONG, aa.d, regs.a.l); +4:if(regs.p.m)last_cycle(); + op_write(OPMODE_LONG, aa.d, regs.a.l); if(regs.p.m)end; -5:op_write(OPMODE_LONG, aa.d + 1, regs.a.h); +5:last_cycle(); + op_write(OPMODE_LONG, aa.d + 1, regs.a.h); } sta_longx(0x9f) { 1:aa.l = op_read(); 2:aa.h = op_read(); 3:aa.b = op_read(); -4:op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l); +4:if(regs.p.m)last_cycle(); + op_write(OPMODE_LONG, aa.d + regs.x.w, regs.a.l); if(regs.p.m)end; -5:op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h); +5:last_cycle(); + op_write(OPMODE_LONG, aa.d + regs.x.w + 1, regs.a.h); } sta_dp(0x85, regs.p.m, regs.a.w), @@ -52,9 +62,11 @@ sty_dp(0x84, regs.p.x, regs.y.w), stz_dp(0x64, regs.p.m, 0x0000) { 1:dp = op_read(); 2:cpu_c2(); -3:op_write(OPMODE_DP, dp, $2); +3:if($1)last_cycle(); + op_write(OPMODE_DP, dp, $2); if($1)end; -4:op_write(OPMODE_DP, dp + 1, $2 >> 8); +4:last_cycle(); + op_write(OPMODE_DP, dp + 1, $2 >> 8); } sta_dpx(0x95, regs.p.m, regs.a.w), @@ -63,18 +75,22 @@ stz_dpx(0x74, regs.p.m, 0x0000) { 1:dp = op_read(); 2:cpu_c2(); 3:cpu_io(); -4:op_write(OPMODE_DP, dp + regs.x.w, $2); +4:if($1)last_cycle(); + op_write(OPMODE_DP, dp + regs.x.w, $2); if($1)end; -5:op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8); +5:last_cycle(); + op_write(OPMODE_DP, dp + regs.x.w + 1, $2 >> 8); } stx_dpy(0x96) { 1:dp = op_read(); 2:cpu_c2(); 3:cpu_io(); -4:op_write(OPMODE_DP, dp + regs.y.w, regs.x.l); +4:if(regs.p.x)last_cycle(); + op_write(OPMODE_DP, dp + regs.y.w, regs.x.l); if(regs.p.x)end; -5:op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h); +5:last_cycle(); + op_write(OPMODE_DP, dp + regs.y.w + 1, regs.x.h); } sta_idp(0x92) { @@ -82,9 +98,11 @@ sta_idp(0x92) { 2:cpu_c2(); 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); -5:op_write(OPMODE_DBR, aa.w, regs.a.l); +5:if(regs.p.m)last_cycle(); + op_write(OPMODE_DBR, aa.w, regs.a.l); if(regs.p.m)end; -6:op_write(OPMODE_DBR, aa.w + 1, regs.a.h); +6:last_cycle(); + op_write(OPMODE_DBR, aa.w + 1, regs.a.h); } sta_ildp(0x87) { @@ -93,9 +111,11 @@ sta_ildp(0x87) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:aa.b = op_read(OPMODE_DP, dp + 2); -6:op_write(OPMODE_LONG, aa.d, regs.a.l); +6:if(regs.p.m)last_cycle(); + op_write(OPMODE_LONG, aa.d, regs.a.l); if(regs.p.m)end; -7:op_write(OPMODE_LONG, aa.d + 1, regs.a.h); +7:last_cycle(); + op_write(OPMODE_LONG, aa.d + 1, regs.a.h); } sta_idpx(0x81) { @@ -104,9 +124,11 @@ sta_idpx(0x81) { 3:cpu_io(); 4:aa.l = op_read(OPMODE_DP, dp + regs.x.w); 5:aa.h = op_read(OPMODE_DP, dp + regs.x.w + 1); -6:op_write(OPMODE_DBR, aa.w, regs.a.l); +6:if(regs.p.m)last_cycle(); + op_write(OPMODE_DBR, aa.w, regs.a.l); if(regs.p.m)end; -7:op_write(OPMODE_DBR, aa.w + 1, regs.a.h); +7:last_cycle(); + op_write(OPMODE_DBR, aa.w + 1, regs.a.h); } sta_idpy(0x91) { @@ -115,9 +137,11 @@ sta_idpy(0x91) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:cpu_c4(aa.w, aa.w + regs.y.w); -6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); +6:if(regs.p.m)last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)end; -7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); +7:last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); } sta_ildpy(0x97) { @@ -126,17 +150,21 @@ sta_ildpy(0x97) { 3:aa.l = op_read(OPMODE_DP, dp); 4:aa.h = op_read(OPMODE_DP, dp + 1); 5:aa.b = op_read(OPMODE_DP, dp + 2); -6:op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l); +6:if(regs.p.m)last_cycle(); + op_write(OPMODE_LONG, aa.d + regs.y.w, regs.a.l); if(regs.p.m)end; -7:op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h); +7:last_cycle(); + op_write(OPMODE_LONG, aa.d + regs.y.w + 1, regs.a.h); } sta_sr(0x83) { 1:sp = op_read(); 2:cpu_io(); -3:op_write(OPMODE_SP, sp, regs.a.l); +3:if(regs.p.m)last_cycle(); + op_write(OPMODE_SP, sp, regs.a.l); if(regs.p.m)end; -4:op_write(OPMODE_SP, sp + 1, regs.a.h); +4:last_cycle(); + op_write(OPMODE_SP, sp + 1, regs.a.h); } sta_isry(0x93) { @@ -145,7 +173,9 @@ sta_isry(0x93) { 3:aa.l = op_read(OPMODE_SP, sp); 4:aa.h = op_read(OPMODE_SP, sp + 1); 5:cpu_io(); -6:op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); +6:if(regs.p.m)last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w, regs.a.l); if(regs.p.m)end; -7:op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); +7:last_cycle(); + op_write(OPMODE_DBR, aa.w + regs.y.w + 1, regs.a.h); } diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 6c43bc68..2bec94aa 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -2,5 +2,6 @@ #include "dcpu.cpp" CPU::CPU() { + cpu_version = 2; mmio = &mmio_unmapped; } diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 9d1a01f5..f249338c 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -2,6 +2,12 @@ class CPU { public: +//CPU version number +//* 1 and 2 are known +//* reported by $4210 +//* affects DRAM refresh behavior +uint8 cpu_version; + //timing virtual uint16 vcounter() = 0; virtual uint16 hcounter() = 0; @@ -9,7 +15,6 @@ public: virtual bool interlace() = 0; virtual bool interlace_field() = 0; virtual bool overscan() = 0; - virtual void set_interlace(bool r) = 0; virtual void set_overscan (bool r) = 0; diff --git a/src/dsp/bdsp/bdsp.cpp b/src/dsp/bdsp/bdsp.cpp new file mode 100644 index 00000000..abd1e944 --- /dev/null +++ b/src/dsp/bdsp/bdsp.cpp @@ -0,0 +1,583 @@ +#include "../../base.h" +#include "bdsp_tables.cpp" + +uint8 bDSP::read_8 (uint16 addr) { return (spcram[addr]); } +uint16 bDSP::read_16 (uint16 addr) { return (spcram[(addr + 1) & 0xffff] << 8) + (spcram[addr]); } +void bDSP::write_8 (uint16 addr, uint8 data) { spcram[addr] = data; } +void bDSP::write_16(uint16 addr, uint16 data) { spcram[addr++] = data; spcram[addr] = data >> 8; } + +uint8 bDSP::read(uint8 addr) { +int i, v, n; + addr &= 127; + v = addr >> 4; + n = addr & 15; + + switch(addr) { + case 0x00:case 0x10:case 0x20:case 0x30: + case 0x40:case 0x50:case 0x60:case 0x70: + return voice[v].VOLL; + case 0x01:case 0x11:case 0x21:case 0x31: + case 0x41:case 0x51:case 0x61:case 0x71: + return voice[v].VOLR; + case 0x02:case 0x12:case 0x22:case 0x32: + case 0x42:case 0x52:case 0x62:case 0x72: + return voice[v].PITCH >> 8; + case 0x03:case 0x13:case 0x23:case 0x33: + case 0x43:case 0x53:case 0x63:case 0x73: + return voice[v].PITCH; + case 0x04:case 0x14:case 0x24:case 0x34: + case 0x44:case 0x54:case 0x64:case 0x74: + return voice[v].SRCN; + case 0x05:case 0x15:case 0x25:case 0x35: + case 0x45:case 0x55:case 0x65:case 0x75: + return voice[v].ADSR1; + case 0x06:case 0x16:case 0x26:case 0x36: + case 0x46:case 0x56:case 0x66:case 0x76: + return voice[v].ADSR2; + case 0x07:case 0x17:case 0x27:case 0x37: + case 0x47:case 0x57:case 0x67:case 0x77: + return voice[v].GAIN; + case 0x08:case 0x18:case 0x28:case 0x38: + case 0x48:case 0x58:case 0x68:case 0x78: + return voice[v].ENVX; + case 0x09:case 0x19:case 0x29:case 0x39: + case 0x49:case 0x59:case 0x69:case 0x79: + return voice[v].OUTX; + + case 0x0f:case 0x1f:case 0x2f:case 0x3f: + case 0x4f:case 0x5f:case 0x6f:case 0x7f: + return status.FIR[v]; + + case 0x0c:return status.MVOLL; + case 0x1c:return status.MVOLR; + case 0x2c:return status.EVOLL; + case 0x3c:return status.EVOLR; + case 0x4c:return status.KON; + case 0x5c:return status.KOFF; + case 0x6c:return status.FLG; + case 0x7c:return status.ENDX; + + case 0x0d:return status.EFB; + case 0x2d:return status.PMON; + case 0x3d:return status.NON; + case 0x4d:return status.EON; + case 0x5d:return status.DIR; + case 0x6d:return status.ESA; + case 0x7d:return status.EDL; + } + + return dspram[addr]; +} + +void bDSP::write(uint8 addr, uint8 data) { +int i, v, n; +//0x80-0xff is a read-only mirror of 0x00-0x7f + if(addr & 0x80)return; + + v = addr >> 4; + n = addr & 15; + + switch(addr) { + case 0x00:case 0x10:case 0x20:case 0x30: + case 0x40:case 0x50:case 0x60:case 0x70: + voice[v].VOLL = data; + break; + case 0x01:case 0x11:case 0x21:case 0x31: + case 0x41:case 0x51:case 0x61:case 0x71: + voice[v].VOLR = data; + break; + case 0x02:case 0x12:case 0x22:case 0x32: + case 0x42:case 0x52:case 0x62:case 0x72: + voice[v].PITCH &= 0xff00; + voice[v].PITCH |= data; + break; + case 0x03:case 0x13:case 0x23:case 0x33: + case 0x43:case 0x53:case 0x63:case 0x73: + voice[v].PITCH &= 0x00ff; + voice[v].PITCH |= data << 8; + break; + case 0x04:case 0x14:case 0x24:case 0x34: + case 0x44:case 0x54:case 0x64:case 0x74: + //voice[v].SRCN = data; + //below is anomie's code, but TRAC says writing SRCN doesn't affect anything until a + //BRR-with-end block is encountered, where it loads the loop address from the new SRCN + if(voice[v].SRCN != data) { + voice[v].SRCN = data; + voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + ((voice[v].brr_looped)?2:0)); + voice[v].brr_index = 0; + } + break; + case 0x05:case 0x15:case 0x25:case 0x35: + case 0x45:case 0x55:case 0x65:case 0x75: + voice[v].ADSR1 = data; + voice[v].AdjustEnvelope(); + break; + case 0x06:case 0x16:case 0x26:case 0x36: + case 0x46:case 0x56:case 0x66:case 0x76: + voice[v].ADSR2 = data; + //sustain_level = 0-7, 7 is a special case handled by ATTACK envx mode + voice[v].env_sustain = (voice[v].ADSR_sus_level() + 1) << 8; + voice[v].AdjustEnvelope(); + break; + case 0x07:case 0x17:case 0x27:case 0x37: + case 0x47:case 0x57:case 0x67:case 0x77: + voice[v].GAIN = data; + voice[v].AdjustEnvelope(); + break; + case 0x08:case 0x18:case 0x28:case 0x38: + case 0x48:case 0x58:case 0x68:case 0x78: + voice[v].ENVX = data; + break; + case 0x09:case 0x19:case 0x29:case 0x39: + case 0x49:case 0x59:case 0x69:case 0x79: + voice[v].OUTX = data; + break; + + case 0x0f:case 0x1f:case 0x2f:case 0x3f: + case 0x4f:case 0x5f:case 0x6f:case 0x7f: + status.FIR[v] = data; + break; + + case 0x0c:status.MVOLL = data;break; + case 0x1c:status.MVOLR = data;break; + case 0x2c:status.EVOLL = data;break; + case 0x3c:status.EVOLR = data;break; + + case 0x4c: + status.KON = data; + status.kon = data; + status.key_flag = true; + break; + + case 0x5c: + status.KOFF = data; + status.key_flag = true; + break; + + case 0x6c: + status.FLG = data; + status.key_flag = true; + status.noise_rate = RateTable[data & 0x1f]; + break; + + case 0x7c: + //read-only register, writes clear all bits of ENDX + status.ENDX = 0; + break; + + case 0x0d:status.EFB = data;break; + case 0x2d:status.PMON = data;break; + case 0x3d:status.NON = data;break; + case 0x4d:status.EON = data;break; + case 0x5d:status.DIR = data;break; + case 0x6d:status.ESA = data;break; + + case 0x7d: + status.EDL = data; + status.echo_size = (data & 0x0f) << 11; + break; + } + + dspram[addr] = data; +} + +void bDSP::power() { +int v; + spcram = apu->get_spcram_handle(); + memset(dspram, 0x00, 128); + + for(v=0;v<8;v++) { + voice[v].VOLL = 0; + voice[v].VOLR = 0; + voice[v].PITCH = 0; + voice[v].SRCN = 0; + voice[v].ADSR1 = 0; + voice[v].ADSR2 = 0; + voice[v].GAIN = 0; + + status.FIR[v] = 0; + } + + status.MVOLL = status.MVOLR = 0; + status.EVOLL = status.EVOLR = 0; + status.ENDX = 0; + status.EFB = 0; + status.PMON = 0; + status.NON = 0; + status.EON = 0; + status.DIR = 0; + status.ESA = 0; + status.EDL = 0; + + status.echo_size = 0; + status.echo_target = 0; + + reset(); +} + +void bDSP::reset() { +int v; + status.KON = 0x00; + status.KOFF = 0x00; + status.FLG |= 0xe0; + + status.kon = 0x00; + status.key_flag = false; + + status.noise_ctr = 0; + status.noise_rate = 0; + status.noise_sample = 0x4000; + + status.echo_index = 0; + status.fir_buffer_index = 0; + + for(v=0;v<8;v++) { + voice[v].ENVX = 0; + voice[v].OUTX = 0; + + voice[v].pitch_ctr = 0; + + voice[v].brr_index = 0; + voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].brr_data_index = 0; + + voice[v].envx = 0; + voice[v].env_ctr = 0; + voice[v].env_rate = 0; + voice[v].env_state = SILENCE; + voice[v].env_mode = DIRECT; + + status.fir_buffer[0][v] = 0; + status.fir_buffer[1][v] = 0; + } + + dsp_counter = 0; +} + +int32 bDSP::clamp(int32 bits, int32 x) { +int32 b = 1 << (bits - 1); + if(x > (b - 1)) { + return (b - 1); + } else if(x < -b) { + return -b; + } else { + return x; + } +} + +int32 bDSP::clip(int32 bits, int32 x) { +int32 b = 1 << (bits - 1); + if(x & b) { + return x | ~(b - 1); + } else { + return x & (b - 1); + } +} + +uint32 bDSP::run() { +int v, d; +uint8 pmon; +int32 sample; +int32 msamplel, msampler; +int32 esamplel, esampler; +int32 fir_samplel, fir_sampler; + pmon = status.PMON & ~status.NON & ~1; + + if(!(dsp_counter++ & 1) && status.key_flag) { + for(v=0;v<8;v++) { + if(status.soft_reset()) { + if(voice[v].env_state != SILENCE) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } else if(status.KOFF & (1 << v)) { + if(voice[v].env_state != SILENCE && voice[v].env_state != RELEASE) { + voice[v].env_state = RELEASE; + voice[v].AdjustEnvelope(); + } + } else if(status.kon & (1 << v)) { + voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2)); + voice[v].brr_index = -9; + voice[v].brr_looped = false; + voice[v].brr_data[0] = 0; + voice[v].brr_data[1] = 0; + voice[v].brr_data[2] = 0; + voice[v].brr_data[3] = 0; + voice[v].envx = 0; + voice[v].env_state = ATTACK; + voice[v].AdjustEnvelope(); + } + } + status.ENDX &= ~status.kon; + status.kon = 0; + status.key_flag = false; + } + +//update noise + status.noise_ctr += status.noise_rate; + if(status.noise_ctr >= 0x7800) { + status.noise_ctr -= 0x7800; + status.noise_sample = (status.noise_sample >> 1) | + (((status.noise_sample << 14) ^ (status.noise_sample << 13)) & 0x4000); + } + + msamplel = msampler = 0; + esamplel = esampler = 0; + + for(v=0;v<8;v++) { + if(voice[v].brr_index < -1) { + voice[v].brr_index++; + voice[v].OUTX = voice[v].outx = 0; + voice[v].ENVX = 0; + continue; + } + + if(voice[v].brr_index >= 0) { + if(pmon & (1 << v)) { + voice[v].pitch_ctr += (voice[v].pitch_rate() * (voice[v - 1].outx + 0x8000)) >> 15; + } else { + voice[v].pitch_ctr += voice[v].pitch_rate(); + } + } else { + voice[v].pitch_ctr = 0x3000; + voice[v].brr_index = 0; + } + + //decode BRR samples + while(voice[v].pitch_ctr >= 0) { + voice[v].pitch_ctr -= 0x1000; + + voice[v].brr_data_index++; + voice[v].brr_data_index &= 3; + + if(voice[v].brr_index == 0) { + voice[v].brr_header = read_8(voice[v].brr_ptr); + + if(voice[v].brr_header_flags() & BRR_END) { + status.ENDX |= (1 << v); + } + + if(voice[v].brr_header_flags() == BRR_END) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + } + +#define S(x) voice[v].brr_data[(voice[v].brr_data_index + (x)) & 3] + if(voice[v].env_state != SILENCE) { + sample = read_8(voice[v].brr_ptr + 1 + (voice[v].brr_index >> 1)); + if(voice[v].brr_index & 1) { + sample = clip(4, sample); + } else { + sample = clip(4, sample >> 4); + } + + if(voice[v].brr_header_shift() <= 12) { + sample = (sample << voice[v].brr_header_shift() >> 1); + } else { + sample &= ~0x7ff; + } + + switch(voice[v].brr_header_filter()) { + case 0: //direct + break; + case 1: //15/16 + sample += S(-1) + ((-S(-1)) >> 4); + break; + case 2: //61/32 - 15/16 + sample += (S(-1) << 1) + ((-((S(-1) << 1) + S(-1))) >> 5) + - S(-2) + (S(-2) >> 4); + break; + case 3: //115/64 - 13/16 + sample += (S(-1) << 1) + ((-(S(-1) + (S(-1) << 2) + (S(-1) << 3))) >> 6) + - S(-2) + (((S(-2) << 1) + S(-2)) >> 4); + break; + } + + S(0) = sample = clip(15, clamp(16, sample)); + } else { + S(0) = sample = 0; + } + + if(++voice[v].brr_index > 15) { + voice[v].brr_index = 0; + if(voice[v].brr_header_flags() & BRR_END) { + voice[v].brr_ptr = read_16((status.DIR << 8) + (voice[v].SRCN << 2) + 2); + voice[v].brr_looped = true; + } else { + voice[v].brr_ptr += 9; + } + } + } + + //volume envelope adjust + voice[v].env_ctr += voice[v].env_rate; + + if(voice[v].env_ctr >= 0x7800) { + voice[v].env_ctr -= 0x7800; + switch(voice[v].env_mode) { + case DIRECT: + voice[v].env_rate = 0; + break; + case LINEAR_DEC: + voice[v].envx -= 32; + if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case LINEAR_INC: + voice[v].envx += 32; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + if(voice[v].ADSR_enabled() && voice[v].env_state == ATTACK) { + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + } + break; + case EXP_DEC: + //multiply by 255/256ths + voice[v].envx -= ((voice[v].envx - 1) >> 8) + 1; + if(voice[v].ADSR_enabled() && voice[v].env_state == DECAY && voice[v].envx <= voice[v].env_sustain) { + voice[v].env_state = SUSTAIN; + voice[v].AdjustEnvelope(); + } else if(voice[v].envx <= 0) { + voice[v].envx = 0; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + break; + case BENT_INC: + if(voice[v].envx < 0x600) { + voice[v].envx += 32; + } else { + voice[v].envx += 8; + + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + voice[v].env_rate = 0; + voice[v].env_mode = DIRECT; + } + } + break; + case FAST_ATTACK: + voice[v].envx += 0x400; + if(voice[v].envx >= 0x7ff) { + voice[v].envx = 0x7ff; + + //attack raises to max. envx, if sustain is also set to max. envx, skip decay phase + voice[v].env_state = ((voice[v].env_sustain == 0x800) ? SUSTAIN : DECAY); + voice[v].AdjustEnvelope(); + } + break; + case RELEASE_DEC: + voice[v].envx -= 8; + if(voice[v].envx <= 0) { + voice[v].env_state = SILENCE; + voice[v].AdjustEnvelope(); + } + break; + } + } + + voice[v].ENVX = voice[v].envx >> 4; + + //gaussian interpolation / noise + if(status.NON & (1 << v)) { + sample = status.noise_sample; + } else { + d = voice[v].pitch_ctr >> 4; //-256 <= sample <= -1 + sample = ((GaussTable[ -1-d] * S(-3)) >> 11); + sample += ((GaussTable[255-d] * S(-2)) >> 11); + sample += ((GaussTable[512+d] * S(-1)) >> 11); + sample = clip (15, sample); + sample += ((GaussTable[256+d] * S( 0)) >> 11); + sample = clamp(15, sample); + } +#undef S + + //envelope / volume adjust + sample = (sample * voice[v].envx) >> 11; + voice[v].outx = sample << 1; + voice[v].OUTX = sample >> 7; + + if(!status.mute()) { + msamplel += ((sample * voice[v].VOLL) >> 7) << 1; + msampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + + if((status.EON & (1 << v)) && status.echo_write()) { + esamplel += ((sample * voice[v].VOLL) >> 7) << 1; + esampler += ((sample * voice[v].VOLR) >> 7) << 1; + } + } + +//echo (FIR) adjust +#define F(c,x) status.fir_buffer[c][(status.fir_buffer_index+(x)) & 7] + status.fir_buffer_index++; + F(0,0) = read_16((status.ESA << 8) + status.echo_index); + F(1,0) = read_16((status.ESA << 8) + status.echo_index + 2); + + fir_samplel = (F(0,-0) * status.FIR[7] + + F(0,-1) * status.FIR[6] + + F(0,-2) * status.FIR[5] + + F(0,-3) * status.FIR[4] + + F(0,-4) * status.FIR[3] + + F(0,-5) * status.FIR[2] + + F(0,-6) * status.FIR[1] + + F(0,-7) * status.FIR[0]); + + fir_sampler = (F(1,-0) * status.FIR[7] + + F(1,-1) * status.FIR[6] + + F(1,-2) * status.FIR[5] + + F(1,-3) * status.FIR[4] + + F(1,-4) * status.FIR[3] + + F(1,-5) * status.FIR[2] + + F(1,-6) * status.FIR[1] + + F(1,-7) * status.FIR[0]); +#undef F + +//update echo buffer + if(status.echo_write()) { + esamplel += (fir_samplel * status.EFB) >> 14; + esampler += (fir_sampler * status.EFB) >> 14; + + esamplel = clamp(16, esamplel); + esampler = clamp(16, esampler); + + write_16((status.ESA << 8) + status.echo_index, esamplel); + write_16((status.ESA << 8) + status.echo_index + 2, esampler); + } + + status.echo_index += 4; + if(status.echo_index >= status.echo_target) { + status.echo_index = 0; + status.echo_target = status.echo_size; + } + +//main output adjust + if(!status.mute()) { + msamplel = (msamplel * status.MVOLL) >> 7; + msampler = (msampler * status.MVOLR) >> 7; + + msamplel += (fir_samplel * status.EVOLL) >> 14; + msampler += (fir_sampler * status.EVOLR) >> 14; + + msamplel = clamp(16, msamplel); + msampler = clamp(16, msampler); + } + + return (uint32)(((uint16)msamplel) | ((uint16)msampler << 16)); +} + +bDSP::bDSP() {} +bDSP::~bDSP() {} diff --git a/src/dsp/bdsp/bdsp.h b/src/dsp/bdsp/bdsp.h new file mode 100644 index 00000000..40eb6849 --- /dev/null +++ b/src/dsp/bdsp/bdsp.h @@ -0,0 +1,176 @@ +class bDSP : public DSP { +private: +uint8 dspram[128]; +uint8 *spcram; + +uint32 dsp_counter; + +enum { + BRR_END = 1, + BRR_LOOP = 2 +}; + +uint8 read_8 (uint16 addr); +uint16 read_16 (uint16 addr); +void write_8 (uint16 addr, uint8 data); +void write_16(uint16 addr, uint16 data); + +public: +static const uint16 RateTable[32]; +static const int16 GaussTable[512]; + +enum EnvelopeStates { + ATTACK, + DECAY, + SUSTAIN, + RELEASE, + SILENCE +}; + +enum EnvelopeModes { + DIRECT, + LINEAR_DEC, + EXP_DEC, + LINEAR_INC, + BENT_INC, + + FAST_ATTACK, + RELEASE_DEC +}; + +private: +struct Status { +//$0c,$1c + int8 MVOLL, MVOLR; +//$2c,$3c + int8 EVOLL, EVOLR; +//$4c,$5c + uint8 KON, KOFF; +//$6c + uint8 FLG; +//$7c + uint8 ENDX; +//$0d + int8 EFB; +//$2d,$3d,$4d + uint8 PMON, NON, EON; +//$5d + uint8 DIR; +//$6d,$7d + uint8 ESA, EDL; + +//$xf + int8 FIR[8]; + +//internal variables + uint8 kon; + bool key_flag; + + int16 noise_ctr, noise_rate; + uint16 noise_sample; + + uint16 echo_index, echo_size, echo_target; + int16 fir_buffer[2][8]; + uint8 fir_buffer_index; + +//functions + bool soft_reset() { return !!(FLG & 0x80); } + bool mute() { return !!(FLG & 0x40); } + bool echo_write() { return !(FLG & 0x20); } +} status; + +struct Voice { +//$x0-$x1 + int8 VOLL, VOLR; +//$x2-$x3 + int16 PITCH; +//$x4 + uint8 SRCN; +//$x5-$x7 + uint8 ADSR1, ADSR2, GAIN; +//$x8-$x9 + uint8 ENVX, OUTX; + +//internal variables + int16 pitch_ctr; + + int8 brr_index; + uint16 brr_ptr; + uint8 brr_header; + bool brr_looped; + + int16 brr_data[4]; + uint8 brr_data_index; + + int16 envx; + uint16 env_ctr, env_rate, env_sustain; + enum EnvelopeStates env_state; + enum EnvelopeModes env_mode; + + int16 outx; + +//functions + int16 pitch_rate() { return PITCH & 0x3fff; } + + uint8 brr_header_shift() { return brr_header >> 4; } + uint8 brr_header_filter() { return (brr_header >> 2) & 3; } + uint8 brr_header_flags() { return brr_header & 3; } + + bool ADSR_enabled() { return !!(ADSR1 & 0x80); } + uint8 ADSR_decay() { return (ADSR1 >> 4) & 7; } + uint8 ADSR_attack() { return ADSR1 & 15; } + uint8 ADSR_sus_level() { return ADSR2 >> 5; } + uint8 ADSR_sus_rate() { return ADSR2 & 31; } + + void AdjustEnvelope() { + if(env_state == SILENCE) { + env_mode = DIRECT; + env_rate = 0; + envx = 0; + } else if(env_state == RELEASE) { + env_mode = RELEASE_DEC; + env_rate = 0x7800; + } else if(ADSR_enabled()) { + switch(env_state) { + case ATTACK: + env_rate = RateTable[(ADSR_attack() << 1) + 1]; + env_mode = (env_rate == 0x7800) ? FAST_ATTACK : LINEAR_INC; + break; + case DECAY: + env_rate = RateTable[(ADSR_decay() << 1) + 0x10]; + env_mode = EXP_DEC; + break; + case SUSTAIN: + env_rate = RateTable[ADSR_sus_rate()]; + env_mode = (env_rate == 0) ? DIRECT : EXP_DEC; + break; + } + } else if(GAIN & 0x80) { + switch(GAIN & 0x60) { + case 0x00:env_mode = LINEAR_DEC; break; + case 0x20:env_mode = EXP_DEC; break; + case 0x40:env_mode = LINEAR_INC; break; + case 0x60:env_mode = BENT_INC; break; + } + env_rate = RateTable[GAIN & 0x1f]; + } else { + env_mode = DIRECT; + env_rate = 0; + envx = (GAIN & 0x7f) << 4; + } + } +} voice[8]; + + int32 clamp(int32 bits, int32 x); + int32 clip (int32 bits, int32 x); +public: + uint8 read (uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + uint32 run(); + + bDSP(); + ~bDSP(); +}; diff --git a/src/dsp/bdsp/bdsp_tables.cpp b/src/dsp/bdsp/bdsp_tables.cpp new file mode 100644 index 00000000..27e12904 --- /dev/null +++ b/src/dsp/bdsp/bdsp_tables.cpp @@ -0,0 +1,73 @@ +const uint16 bDSP::RateTable[32] = { + 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, + 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, + 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, + 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 +}; + +const int16 bDSP::GaussTable[512] = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, + 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, + 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, + 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, + 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, + 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, + 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, + 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, + 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, + 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, + 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, + 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, + 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, + 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, + 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, + 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, + 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, + 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, + 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, + 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, + 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, + 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, + 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, + 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, + 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, + 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, + 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, + 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, + 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, + 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, + 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, + 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, + 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, + 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, + 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, + 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, + 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, + 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, + 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, + 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, + 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, + 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, + 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, + 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, + 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, + 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, + 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, + 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, + 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, + 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, + 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, + 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, + 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, + 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, + 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, + 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, + 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, + 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, + 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, + 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 +}; diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h new file mode 100644 index 00000000..af495adb --- /dev/null +++ b/src/dsp/dsp.h @@ -0,0 +1,9 @@ +class DSP { +public: + virtual uint8 read (uint8 addr) = 0; + virtual void write(uint8 addr, uint8 data) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + virtual uint32 run() = 0; +}; diff --git a/src/interface.h b/src/interface.h index ca3281a7..829a5f6f 100644 --- a/src/interface.h +++ b/src/interface.h @@ -1,3 +1,6 @@ +#define BSNES_VERSION "0.012" +#define BSNES_TITLE "bsnes v" BSNES_VERSION + #include "reader/reader.h" #include "memory/memory.h" @@ -14,6 +17,10 @@ extern CPU *cpu; #include "apu/bapuskip/bapuskip.h" extern APU *apu; +#include "dsp/dsp.h" +#include "dsp/bdsp/bdsp.h" +extern DSP *dsp; + #include "ppu/ppu.h" #include "ppu/bppu/bppu.h" extern PPU *ppu; @@ -26,10 +33,14 @@ extern SNES *snes; extern SRTC *srtc; extern SDD1 *sdd1; +#include "config/config.h" + #ifdef INTERFACE_MAIN +#include "config/config.cpp" MemBus *mem_bus; CPU *cpu; APU *apu; + DSP *dsp; PPU *ppu; SNES *snes; diff --git a/src/lib/libbase.h b/src/lib/libbase.h index 55648c03..b203d073 100644 --- a/src/lib/libbase.h +++ b/src/lib/libbase.h @@ -18,6 +18,8 @@ #define FALSE 0 #endif +#define zerofree(__n) if(__n) { free(__n); __n = 0; } + typedef unsigned int uint; typedef unsigned char byte; diff --git a/src/lib/libconfig.cpp b/src/lib/libconfig.cpp index f4d43221..949e7c47 100644 --- a/src/lib/libconfig.cpp +++ b/src/lib/libconfig.cpp @@ -1,273 +1,161 @@ #include "libbase.h" #include "libconfig.h" -config_item::config_item() { - strcpy(name, ""); - strcpy(strdef, ""); - is_string = false; - source = 0; - strsource = 0; - def = 0; - type = 0; +void Setting::toggle() { + data ^= 1; + set(data); } -void config::add(uint32 *variable, char *name, uint32 def, uint32 type) { -int n; - if(item_count >= 4096)return; - n = item_count; - - item[n] = new config_item(); - strcpy(item[n]->name, name); - item[n]->is_string = false; - item[n]->def = def; - item[n]->source = variable; - *item[n]->source = item[n]->def; - item[n]->type = type; - - item_count++; +uint Setting::get() { + return data; } -void config::add(string *variable, char *name, char *def, uint32 type) { -int n; - if(item_count >= 4096)return; - n = item_count; +void Setting::set(uint _data) { +printf("%s %d\n", name, _data); + data = _data; - item[n] = new config_item(); - strcpy(item[n]->name, name); - item[n]->is_string = true; - strcpy(item[n]->strdef, def); - item[n]->strsource = variable; - strcpy(*item[n]->strsource, item[n]->strdef); - item[n]->type = type; - - item_count++; -} - -uint32 config::find(char *name) { - for(int i=0;iname, name)) { - return i; - } + if(type != DEC && type != HEX) { + //data is a boolean type + data &= 1; } - return null; } -void config::load(char *fn) { +Setting::Setting(Config *_parent, char *_name, char *_desc, uint _data, uint _type) { +int s; + if(_parent) { + _parent->add(this); + } + + s = strlen(_name); + name = (char*)malloc(s + 1); + strcpy(name, _name); + + if(_desc) { + s = strlen(_desc); + desc = (char*)malloc(s + 1); + strcpy(desc, _desc); + } else { + desc = (char*)malloc(1); + *desc = 0; + } + + data = _data; + def = _data; + type = _type; +} + +void Config::add(Setting *setting) { + list[list_count++] = setting; +} + +uint Config::string_to_uint(uint type, char *input) { + if(!strcmp(input, "true") || + !strcmp(input, "enabled") || + !strcmp(input, "on") || + !strcmp(input, "yes") + ) { + return (uint)true; + } + + if(!strcmp(input, "false") || + !strcmp(input, "disabled") || + !strcmp(input, "off") || + !strcmp(input, "no") + ) { + return (uint)false; + } + + if(!strbegin(input, "0x")) { + return strhex(input + 2); + } + + return strdec(input); +} + +char *Config::uint_to_string(uint type, uint input) { +static char output[512]; + switch(type) { + case Setting::TRUE_FALSE: + sprintf(output, "%s", (input & 1)?"true":"false"); + break; + case Setting::ENABLED_DISABLED: + sprintf(output, "%s", (input & 1)?"enabled":"disabled"); + break; + case Setting::ON_OFF: + sprintf(output, "%s", (input & 1)?"on":"off"); + break; + case Setting::YES_NO: + sprintf(output, "%s", (input & 1)?"yes":"no"); + break; + case Setting::DEC: + sprintf(output, "%d", input); + break; + case Setting::HEX: + sprintf(output, "0x%x", input); + break; + } + + return output; +} + +bool Config::load(char *fn) { FILE *fp; -char *buffer; -int i, fsize; -uint32 l; fp = fopen(fn, "rb"); -//file doesn't exist yet, do nothing - if(!fp)return; + if(!fp)return false; +//load the config file into memory fseek(fp, 0, SEEK_END); - fsize = ftell(fp); +int fsize = ftell(fp); fseek(fp, 0, SEEK_SET); - -//blank file, do nothing - if(fsize == 0) { - fclose(fp); - return; - } - - buffer = (char*)malloc(fsize + 1); +char *buffer = (char*)malloc(fsize + 1); fread(buffer, 1, fsize, fp); fclose(fp); - buffer[fsize] = 0; - + *(buffer + fsize) = 0; strcpy(data, buffer); free(buffer); - replace(data, "\r\n", "\n"); - qreplace(data, "\t", " "); +//split the file into lines + replace(data, "\r\n", "\n"); + qreplace(data, "\t", ""); + qreplace(data, " ", ""); split(line, "\n", data); - for(i=0;isource = 1; - } else if(!strcmp(part[1], "false") || !strcmp(part[1], "no") || - !strcmp(part[1], "off") || !strcmp(part[1], "disabled")) { - *item[l]->source = 0; - } else if(item[l]->type == HEX) { - *item[l]->source = strhex(strptr(part[1]) + 2); //skip 0x prefix - } else { /* fall back on DEC */ - *item[l]->source = strdec(part[1]); - } + split(part, "=", line[i]); + for(int l=0;lname, part[0])) { + list[l]->set(string_to_uint(list[l]->type, strptr(part[1]))); } } } -} -void config::load(substring &fn) { load(strptr(fn)); } -//create a text string from config item[i] to be output via config->save() -void config::set_newline(int i) { -char t[16]; - if(item[i]->is_string == true) { - strcpy(newline, item[i]->name); - strcat(newline, " = \""); - strcat(newline, *item[i]->strsource); - strcat(newline, "\""); - } else { - strcpy(newline, item[i]->name); - strcat(newline, " = "); - switch(item[i]->type) { - case TRUEFALSE: - strcat(newline, (*item[i]->source)?"true":"false"); - break; - case YESNO: - strcat(newline, (*item[i]->source)?"yes":"no"); - break; - case ONOFF: - strcat(newline, (*item[i]->source)?"on":"off"); - break; - case ENABLED: - strcat(newline, (*item[i]->source)?"enabled":"disabled"); - break; - case HEX: - sprintf(t, "0x%0.2x", *item[i]->source); - strcat(newline, t); - break; - case DEC: - default: - sprintf(t, "%d", *item[i]->source); - strcat(newline, t); - break; - } - } + return true; } +bool Config::load(substring &fn) { return load(strptr(fn)); } -void config::save(char *fn) { +bool Config::save(char *fn) { FILE *fp; -int i, fsize; -uint32 l; -char *buffer; -uint8 set[4096]; -bool blank = false; - fp = fopen(fn, "rb"); - if(!fp) { - blank = true; - } else { - fseek(fp, 0, SEEK_END); - fsize = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if(fsize == 0) { - blank = true; - } else { - buffer = (char*)malloc(fsize + 1); - fread(buffer, 1, fsize, fp); - buffer[fsize] = 0; - - strcpy(data, buffer); - free(buffer); - } - fclose(fp); - } - fp = fopen(fn, "wb"); -//no write access? - if(!fp)return; + if(!fp)return false; -//list of config items. if the item is set in the -//existing config file, then don't test it to see -//if it needs to be written again later on - memset(set, 0, item_count); - - if(blank == false) { + for(int i=0;idesc); replace(data, "\r\n", "\n"); - qreplace(data, "\t", " "); - - split(line, "\n", data); - split(oldline, "\n", data); - - for(i=0;itype, list[i]->def)); + fprintf(fp, "%s = %s\r\n\r\n", list[i]->name, uint_to_string(list[i]->type, list[i]->data)); } -int lines_written; - for(i=lines_written=0;i t.set(0); +#define SettingOperators(__name) \ + inline __name &operator=(const bool _data) { set((uint)_data); return *this; } \ + inline __name &operator=(const uint _data) { set(_data); return *this; } \ + inline __name &operator=(const uint8 _data) { set(_data); return *this; } \ + inline __name &operator=(const uint16 _data) { set(_data); return *this; } \ + inline __name &operator=(const uint32 _data) { set(_data); return *this; } \ + inline __name &operator=(const int _data) { set(_data); return *this; } \ + inline __name &operator=(const int8 _data) { set(_data); return *this; } \ + inline __name &operator=(const int16 _data) { set(_data); return *this; } \ + inline __name &operator=(const int32 _data) { set(_data); return *this; } \ + void toggle() { data ^= 1; set(data); } \ + __name(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = Setting::DEC) : \ + Setting(_parent, _name, _desc, _data, _type) {} + +class Setting { + friend class Config; + +protected: +uint data, type, def; -class config { -private: -uint32 item_count; -config_item *item[4096]; -string data, line, oldline, newline, part; public: enum { - TRUEFALSE = 0, - YESNO = 1, - ONOFF = 2, - ENABLED = 3, - DEC = 4, - HEX = 5, - STR = 6 + TRUE_FALSE, + ENABLED_DISABLED, + ON_OFF, + YES_NO, + BOOL, + DEC, + HEX }; - void add(uint32 *variable, char *name, uint32 def, uint32 type = DEC); - void add(string *variable, char *name, char *def, uint32 type = STR); - uint32 find(char *name); - void load(char *fn); - void load(substring &fn); - void save(char *fn); - void save(substring &fn); - void set_newline(int i); +char *name, *desc; + virtual void toggle(); + virtual uint get(); + virtual void set(uint _data); - config(); - ~config(); + Setting(Config *_parent, char *_name, char *_desc = 0, uint _data = 0, uint _type = DEC); + + inline operator bool() { return (bool)get(); } + inline operator uint() { return get(); } + inline operator uint8() { return get(); } + inline operator uint16() { return get(); } + inline operator uint32() { return get(); } + inline operator int() { return get(); } + inline operator int8() { return get(); } + inline operator int16() { return get(); } + inline operator int32() { return get(); } + + inline Setting &operator=(const bool _data) { set((uint)_data); return *this; } + inline Setting &operator=(const uint _data) { set(_data); return *this; } + inline Setting &operator=(const uint8 _data) { set(_data); return *this; } + inline Setting &operator=(const uint16 _data) { set(_data); return *this; } + inline Setting &operator=(const uint32 _data) { set(_data); return *this; } + inline Setting &operator=(const int _data) { set(_data); return *this; } + inline Setting &operator=(const int8 _data) { set(_data); return *this; } + inline Setting &operator=(const int16 _data) { set(_data); return *this; } + inline Setting &operator=(const int32 _data) { set(_data); return *this; } +}; + +class Config { +protected: +vector list; +uint list_count; + +string data, line, part, subpart; + +uint string_to_uint(uint type, char *input); +char *uint_to_string(uint type, uint input); + +public: + void add(Setting *setting); + bool load(char *fn); + bool load(substring &fn); + bool save(char *fn); + bool save(substring &fn); + Config(); }; #endif diff --git a/src/memory/bmemory/bcart_exhirom.h b/src/memory/bmemory/bcart_exhirom.h index 63b48660..d6b9b34f 100644 --- a/src/memory/bmemory/bcart_exhirom.h +++ b/src/memory/bmemory/bcart_exhirom.h @@ -6,7 +6,7 @@ public: uint8 *rom, *sram; uint32 rom_size, sram_size; uint8 read (uint32 addr); - void write(uint32 addr, byte value); + void write(uint32 addr, uint8 value); void write_protect(bool r); void set_cartinfo(CartInfo *ci); diff --git a/src/memory/bmemory/bcart_exlorom.h b/src/memory/bmemory/bcart_exlorom.h index a3996a05..c51018d4 100644 --- a/src/memory/bmemory/bcart_exlorom.h +++ b/src/memory/bmemory/bcart_exlorom.h @@ -6,7 +6,7 @@ public: uint8 *rom, *sram; uint32 rom_size, sram_size; uint8 read (uint32 addr); - void write(uint32 addr, byte value); + void write(uint32 addr, uint8 value); void write_protect(bool r); void set_cartinfo(CartInfo *ci); diff --git a/src/memory/bmemory/bcart_hirom.h b/src/memory/bmemory/bcart_hirom.h index f8eba666..02631437 100644 --- a/src/memory/bmemory/bcart_hirom.h +++ b/src/memory/bmemory/bcart_hirom.h @@ -7,7 +7,7 @@ public: uint8 *rom, *sram; uint32 rom_size, sram_size; uint8 read (uint32 addr); - void write(uint32 addr, byte value); + void write(uint32 addr, uint8 value); void write_protect(bool r); void set_cartinfo(CartInfo *ci); diff --git a/src/memory/bmemory/bcart_lorom.h b/src/memory/bmemory/bcart_lorom.h index 8fdd058c..b456bee3 100644 --- a/src/memory/bmemory/bcart_lorom.h +++ b/src/memory/bmemory/bcart_lorom.h @@ -7,7 +7,7 @@ public: uint8 *rom, *sram; uint32 rom_size, sram_size; uint8 read (uint32 addr); - void write(uint32 addr, byte value); + void write(uint32 addr, uint8 value); void write_protect(bool r); void set_cartinfo(CartInfo *ci); diff --git a/src/memory/bmemory/bmemory.cpp b/src/memory/bmemory/bmemory.cpp index 08a7ebf4..010459a3 100644 --- a/src/memory/bmemory/bmemory.cpp +++ b/src/memory/bmemory/bmemory.cpp @@ -171,33 +171,39 @@ void bMemBus::get_cartinfo(CartInfo *ci) { ***************************************/ uint8 bMemBus::read(uint32 addr) { -uint32 b, w, r; - addr &= 0xffffff; - b = (addr >> 16); - w = (addr & 0xffff); +static uint32 r; + switch(addr & 0xc00000) { + case 0x400000: + if((addr & 0xfe0000) == 0x7e0000) { + r = wram[addr & 0x01ffff]; + break; + } + //fallthrough + case 0xc00000: + r = cart->read(addr); + break; - if(b <= 0x3f) { - if(w <= 0x1fff) { - r = wram[w]; - } else if(w <= 0x5fff) { - r = mmio[w - 0x2000]->read(w); - } else { +//case 0x000000: +//case 0x800000: + default: + switch(addr & 0x00e000) { + case 0x0000: + r = wram[addr & 0x1fff]; + break; + case 0x2000: + case 0x4000: + r = mmio[(addr - 0x2000) & 0x3fff]->read(addr & 0x7fff); + break; +// case 0x6000: +// case 0x8000: +// case 0xa000: +// case 0xc000: +// case 0xe000: + default: r = cart->read(addr); + break; } - } else if(b <= 0x7d) { - r = cart->read(addr); - } else if(b <= 0x7f) { - r = wram[addr & 0x01ffff]; - } else if(b <= 0xbf) { - if(w <= 0x1fff) { - r = wram[w]; - } else if(w <= 0x5fff) { - r = mmio[w - 0x2000]->read(w); - } else { - r = cart->read(addr); - } - } else { - r = cart->read(addr); + break; } snes->notify(SNES::MEM_READ, addr, r); @@ -205,33 +211,38 @@ uint32 b, w, r; } void bMemBus::write(uint32 addr, uint8 value) { -uint32 b, w; - addr &= 0xffffff; - b = (addr >> 16); - w = (addr & 0xffff); + switch(addr & 0xc00000) { + case 0x400000: + if((addr & 0xfe0000) == 0x7e0000) { + wram[addr & 0x01ffff] = value; + break; + } + //fallthrough + case 0xc00000: + cart->write(addr, value); + break; - if(b <= 0x3f) { - if(w <= 0x1fff) { - wram[w] = value; - } else if(w <= 0x5fff) { - mmio[w - 0x2000]->write(w, value); - } else { +//case 0x000000: +//case 0x800000: + default: + switch(addr & 0x00e000) { + case 0x0000: + wram[addr & 0x1fff] = value; + break; + case 0x2000: + case 0x4000: + mmio[(addr - 0x2000) & 0x3fff]->write(addr & 0x7fff, value); + break; +// case 0x6000: +// case 0x8000: +// case 0xa000: +// case 0xc000: +// case 0xe000: + default: cart->write(addr, value); + break; } - } else if(b <= 0x7d) { - cart->write(addr, value); - } else if(b <= 0x7f) { - wram[addr & 0x01ffff] = value; - } else if(b <= 0xbf) { - if(w <= 0x1fff) { - wram[w] = value; - } else if(w <= 0x5fff) { - mmio[w - 0x2000]->write(w, value); - } else { - cart->write(addr, value); - } - } else { - cart->write(addr, value); + break; } snes->notify(SNES::MEM_WRITE, addr, value); @@ -243,7 +254,7 @@ void bMemBus::power() { } void bMemBus::reset() { - fastROM = false; + set_speed(false); } bMemBus::bMemBus() { @@ -253,5 +264,5 @@ bMemBus::bMemBus() { } bMemBus::~bMemBus() { - if(wram)free(wram); + zerofree(wram); } diff --git a/src/memory/bmemory/bmemory.h b/src/memory/bmemory/bmemory.h index 5c633d9e..992ac956 100644 --- a/src/memory/bmemory/bmemory.h +++ b/src/memory/bmemory/bmemory.h @@ -18,13 +18,13 @@ bool rom_loaded; enum { LOROM = 0x20, HIROM = 0x21, EXLOROM = 0x22, EXHIROM = 0x25 }; uint8 read (uint32 addr); - void write(uint32 addr, byte value); + void write(uint32 addr, uint8 value); - bool load_cart(Reader *rf); - bool load_sram(Reader *rf); - bool save_sram(Writer *wf); - void unload_cart(); - void get_cartinfo(CartInfo *ci); + bool load_cart(Reader *rf); + bool load_sram(Reader *rf); + bool save_sram(Writer *wf); + void unload_cart(); + void get_cartinfo(CartInfo *ci); void power(); void reset(); diff --git a/src/memory/memory.cpp b/src/memory/memory.cpp index 4308c031..ed894abd 100644 --- a/src/memory/memory.cpp +++ b/src/memory/memory.cpp @@ -1,6 +1,6 @@ #include "../base.h" -uint16 Memory::read_word(uint32 addr, uint8 wrap) { +uint16 Memory::read_word(uint32 addr, uint wrap) { uint16 r; switch(wrap) { case WRAP_NONE: @@ -19,24 +19,24 @@ uint16 r; return r; } -void Memory::write_word(uint32 addr, uint16 value, uint8 wrap) { +void Memory::write_word(uint32 addr, uint16 data, uint wrap) { switch(wrap) { case WRAP_NONE: - write(addr, value); - write(addr + 1, value >> 8); + write(addr, data); + write(addr + 1, data >> 8); return; case WRAP_BANK: - write(addr, value); - write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8); + write(addr, data); + write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); return; case WRAP_PAGE: - write(addr, value); - write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8); + write(addr, data); + write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); return; } } -uint32 Memory::read_long(uint32 addr, uint8 wrap) { +uint32 Memory::read_long(uint32 addr, uint wrap) { uint32 r; switch(wrap) { case WRAP_NONE: @@ -58,22 +58,22 @@ uint32 r; return r; } -void Memory::write_long(uint32 addr, uint32 value, uint8 wrap) { +void Memory::write_long(uint32 addr, uint32 data, uint wrap) { switch(wrap) { case WRAP_NONE: - write(addr, value); - write(addr + 1, value >> 8); - write(addr + 2, value >> 16); + write(addr, data); + write(addr + 1, data >> 8); + write(addr + 2, data >> 16); return; case WRAP_BANK: - write(addr, value); - write((addr & 0xff0000) | ((addr + 1) & 0xffff), value >> 8); - write((addr & 0xff0000) | ((addr + 2) & 0xffff), value >> 16); + write(addr, data); + write((addr & 0xff0000) | ((addr + 1) & 0xffff), data >> 8); + write((addr & 0xff0000) | ((addr + 2) & 0xffff), data >> 16); return; case WRAP_PAGE: - write(addr, value); - write((addr & 0xffff00) | ((addr + 1) & 0xff), value >> 8); - write((addr & 0xffff00) | ((addr + 2) & 0xff), value >> 16); + write(addr, data); + write((addr & 0xffff00) | ((addr + 1) & 0xff), data >> 8); + write((addr & 0xffff00) | ((addr + 2) & 0xff), data >> 16); return; } } @@ -82,16 +82,30 @@ MMIO mmio_unmapped; uint8 MMIO::read (uint32 addr) { return cpu->regs.mdr; } void MMIO::write(uint32 addr, uint8 value) {} -uint8 MemBus::speed(uint32 addr) { +uint8 MemBus::calc_speed(uint32 addr, bool fast) { if((addr & 0xc00000) == 0x400000)return 8; - if((addr & 0x808000) == 0x808000)return fastROM?6:8; - if((addr & 0xc00000) == 0xc00000)return fastROM?6:8; + if((addr & 0x808000) == 0x808000)return fast?6:8; + if((addr & 0xc00000) == 0xc00000)return fast?6:8; if((addr & 0xe000) == 0x2000)return 6; if((addr & 0xfe00) == 0x4000)return 12; if((addr & 0xe000) == 0x4000)return 6; return 8; } +uint8 MemBus::speed(uint32 addr) { + return speed_table[addr >> 9]; +} + +void MemBus::set_speed(bool fast) { + fastROM = fast; + + if(fastROM) { + speed_table = (uint8*)speed_table_fastrom; + } else { + speed_table = (uint8*)speed_table_slowrom; + } +} + void MemBus::flush_mmio_mappers() { for(int i=0;i<0x4000;i++) { mmio[i] = &mmio_unmapped; @@ -99,7 +113,7 @@ void MemBus::flush_mmio_mappers() { } bool MemBus::set_mmio_mapper(uint16 addr, MMIO *mapper) { -/* out of range? */ +//out of range? if(addr < 0x2000 || addr >= 0x6000)return false; mmio[(addr - 0x2000) & 0x3fff] = mapper; @@ -110,6 +124,12 @@ MemBus::MemBus() { int i; fastROM = false; flush_mmio_mappers(); + + for(i=0;i<32768;i++) { + speed_table_slowrom[i] = calc_speed(i << 9, false); + speed_table_fastrom[i] = calc_speed(i << 9, true); + } + speed_table = (uint8*)speed_table_slowrom; } MemBus::~MemBus() {} diff --git a/src/memory/memory.h b/src/memory/memory.h index 9c7a10b5..01424c13 100644 --- a/src/memory/memory.h +++ b/src/memory/memory.h @@ -1,18 +1,18 @@ class Memory { public: enum { WRAP_NONE = 0, WRAP_BANK = 1, WRAP_PAGE = 2 }; - virtual uint8 read (uint32 addr) = 0; - virtual void write(uint32 addr, uint8 value) = 0; - virtual uint16 read_word (uint32 addr, uint8 wrap = WRAP_NONE); - virtual void write_word(uint32 addr, uint16 value, uint8 wrap = WRAP_NONE); - virtual uint32 read_long (uint32 addr, uint8 wrap = WRAP_NONE); - virtual void write_long(uint32 addr, uint32 value, uint8 wrap = WRAP_NONE); + virtual uint8 read (uint32 addr) = 0; + virtual void write (uint32 addr, uint8 data) = 0; + virtual uint16 read_word (uint32 addr, uint wrap = WRAP_NONE); + virtual void write_word(uint32 addr, uint16 data, uint wrap = WRAP_NONE); + virtual uint32 read_long (uint32 addr, uint wrap = WRAP_NONE); + virtual void write_long(uint32 addr, uint32 data, uint wrap = WRAP_NONE); }; typedef struct { -uint8 *rom, *sram; -uint32 rom_size, sram_size; -}CartInfo; + uint8 *rom, *sram; + uint32 rom_size, sram_size; +} CartInfo; class Cart : public Memory { public: @@ -31,15 +31,26 @@ public: Cart *cart; MMIO *mmio[0x4000]; bool fastROM; - virtual void flush_mmio_mappers(); - virtual bool set_mmio_mapper(uint16 addr, MMIO *mapper); - virtual uint8 speed(uint32 addr); + void flush_mmio_mappers(); + bool set_mmio_mapper(uint16 addr, MMIO *mapper); - virtual bool load_cart(Reader *rf) = 0; - virtual bool load_sram(Reader *rf) = 0; - virtual bool save_sram(Writer *wf) = 0; - virtual void unload_cart() = 0; - virtual void get_cartinfo(CartInfo *ci) = 0; +private: +//0x1000000 / 512 = 32768 +//512 = 0x200, smallest block of a different-speed memory range +//ex. $4000-$41ff = 512 +uint8 *speed_table, + speed_table_slowrom[32768], + speed_table_fastrom[32768]; + inline uint8 calc_speed(uint32 addr, bool fast); +public: + uint8 speed(uint32 addr); + void set_speed(bool fast); + + virtual bool load_cart(Reader *rf) = 0; + virtual bool load_sram(Reader *rf) = 0; + virtual bool save_sram(Writer *wf) = 0; + virtual void unload_cart() = 0; + virtual void get_cartinfo(CartInfo *ci) = 0; virtual void power() = 0; virtual void reset() = 0; diff --git a/src/ppu/bppu/bppu.cpp b/src/ppu/bppu/bppu.cpp index 43ee573d..cc276a06 100644 --- a/src/ppu/bppu/bppu.cpp +++ b/src/ppu/bppu/bppu.cpp @@ -6,7 +6,7 @@ void bPPU::run() {} void bPPU::scanline() { _y = cpu->vcounter(); - _screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6)?512:256; + _screen_width = (regs.bg_mode == 5 || regs.bg_mode == 6) ? 512 : 256; _interlace = cpu->interlace(); _interlace_field = cpu->interlace_field(); @@ -25,27 +25,23 @@ void bPPU::scanline() { } } - if(_y == (cpu->overscan()?239:224) && regs.display_disabled == false) { + if(_y == (cpu->overscan() ? 239 : 224) && regs.display_disabled == false) { //OAM address reset regs.oam_addr = ((regs.oam_addrh << 8) | regs.oam_addrl) << 1; } +} +void bPPU::render_scanline() { //only allow frameskip setting to ignore actual rendering; not RTO, etc. if(settings.frameskip_pos != 0)return; - if(_y > 0 && _y < (cpu->overscan()?239:224)) { - if(regs.bg_mode == 5 || regs.bg_mode == 6) { - output->hires = true; - output->line[_y].hires = true; - } - if(_interlace == true) { - output->interlace = true; - output->line[_y].interlace = true; - } + if(_y > 0 && _y < (cpu->overscan() ? 239 : 224)) { render_line(); } } +bool bPPU::render_frame() { return (settings.frameskip_pos == 0); } + void bPPU::frame() { if(settings.frameskip_changed == true) { settings.frameskip_changed = false; @@ -58,12 +54,6 @@ void bPPU::frame() { if(settings.frameskip_pos != 0)return; snes->notify(SNES::RENDER_FRAME); - output->hires = false; - output->interlace = false; - for(int i=0;i<239;i++) { - output->line[i].hires = false; - output->line[i].interlace = false; - } } void bPPU::set_frameskip(int fs) { @@ -82,7 +72,6 @@ void bPPU::power() { } void bPPU::reset() { - memset(output->buffer, 0, 512 * 478 * 2); frame(); memset(sprite_list, 0, sizeof(sprite_list)); @@ -335,9 +324,9 @@ bPPU::bPPU() { mmio = new bPPUMMIO(this); - vram = (uint8*)memalloc(65536, "bPPU::vram"); - oam = (uint8*)memalloc( 544, "bPPU::oam"); - cgram = (uint8*)memalloc( 512, "bPPU::cgram"); + vram = (uint8*)malloc(65536); + oam = (uint8*)malloc( 544); + cgram = (uint8*)malloc( 512); memset(vram, 0, 65536); memset(oam, 0, 544); memset(cgram, 0, 512); @@ -346,7 +335,7 @@ bPPU::bPPU() { int i, l; uint8 r, g, b; -float m; +double m; uint16 *ptr; for(l=0;l<16;l++) { mosaic_table[l] = (uint16*)malloc(4096 * 2); @@ -358,7 +347,7 @@ uint16 *ptr; light_table = (uint16*)malloc(16 * 32768 * 2); ptr = (uint16*)light_table; for(l=0;l<16;l++) { - m = (float)l / 15.0; + m = (double)l / 15.0; for(i=0;i<32768;i++) { r = (i ) & 31; g = (i >> 5) & 31; @@ -366,9 +355,9 @@ uint16 *ptr; if(l == 0) { r = g = b = 0; } else if(l == 15); else { - r = (uint8)((float)r * m); - g = (uint8)((float)g * m); - b = (uint8)((float)b * m); + r = (uint8)((double)r * m); + g = (uint8)((double)g * m); + b = (uint8)((double)b * m); } *ptr++ = (r) | (g << 5) | (b << 10); } @@ -378,30 +367,15 @@ uint16 *ptr; bPPU::~bPPU() { delete(mmio); - if(vram) { - free(vram); - vram = 0; - } - if(oam) { - free(oam); - oam = 0; - } - if(cgram) { - free(cgram); - cgram = 0; - } + zerofree(vram); + zerofree(oam); + zerofree(cgram); for(int i=0;i<16;i++) { - if(mosaic_table[i]) { - free(mosaic_table[i]); - mosaic_table[i] = 0; - } + zerofree(mosaic_table[i]); } - if(light_table) { - memfree(light_table); - light_table = 0; - } + zerofree(light_table); } bPPUMMIO::bPPUMMIO(bPPU *_ppu) { diff --git a/src/ppu/bppu/bppu.h b/src/ppu/bppu/bppu.h index 36c7740c..c403df54 100644 --- a/src/ppu/bppu/bppu.h +++ b/src/ppu/bppu/bppu.h @@ -25,12 +25,12 @@ struct sprite_item { bool vflip, hflip; uint8 palette; uint8 priority; -}sprite_list[128]; +} sprite_list[128]; struct { int32 frameskip, frameskip_pos; bool frameskip_changed; -}settings; +} settings; struct { //open bus support @@ -119,6 +119,7 @@ struct { //$2130 uint8 color_mask, colorsub_mask; bool addsub_mode; + bool direct_color; //$2131 bool color_mode, color_halve; @@ -145,7 +146,7 @@ struct { //$213e bool time_over, range_over; uint16 oam_itemcount, oam_tilecount; -}regs; +} regs; uint8 vram_read (uint16 addr); void vram_write (uint16 addr, uint8 value); uint8 oam_read (uint16 addr); @@ -225,7 +226,7 @@ struct { void latch_counters(); -/* PPU render functions */ +//PPU render functions #include "bppu_render.h" @@ -234,13 +235,17 @@ uint16 *mosaic_table[16]; void render_line(); void update_oam_status(); -/* Required functions */ +//required functions void run(); void scanline(); + void render_scanline(); void frame(); void power(); void reset(); void set_frameskip(int fs); + bool render_frame(); + + bool scanline_is_hires() { return (regs.bg_mode == 5 || regs.bg_mode == 6); } bPPU(); ~bPPU(); diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index c0dfdfd0..916e465a 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -393,6 +393,7 @@ void bPPU::mmio_w2130(uint8 value) { regs.color_mask = (value >> 6) & 3; regs.colorsub_mask = (value >> 4) & 3; regs.addsub_mode = !!(value & 0x02); + regs.direct_color = !!(value & 0x01); } //CGADDSUB @@ -538,9 +539,9 @@ uint8 bPPU::mmio_r213d() { //STAT77 uint8 bPPU::mmio_r213e() { uint8 r = 0x00; - r |= (regs.time_over) ?0x80:0x00; - r |= (regs.range_over)?0x40:0x00; - r |= 0x01; //PPU1 version number + r |= (regs.time_over) ? 0x80 : 0x00; + r |= (regs.range_over) ? 0x40 : 0x00; + r |= (ppu1_version & 0x0f); regs.ppu1_mdr = r; return regs.ppu1_mdr; } @@ -560,7 +561,7 @@ uint8 r = 0x00; } r |= (regs.ppu2_mdr & 0x20); r |= (region << 4); //0 = NTSC, 1 = PAL - r |= 0x03; //PPU2 version number + r |= (ppu2_version & 0x0f); regs.ppu2_mdr = r; return regs.ppu2_mdr; } diff --git a/src/ppu/bppu/bppu_render.cpp b/src/ppu/bppu/bppu_render.cpp index 52f25a6c..131d8d59 100644 --- a/src/ppu/bppu/bppu_render.cpp +++ b/src/ppu/bppu/bppu_render.cpp @@ -104,32 +104,30 @@ Mode7: -> 1, 2, 3, 4, 5 OAM0, BG1n, OAM1, OAM2, OAM3 -*** -This appears to be incorrect, possibly should be... +Mode 7 EXTBG: -> + 1, 2, 3, 4, 5, 6, 7 BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3 -*** -Mode 7 (extbg): -> - 1, 2, 3, 4, 5, 6 - BG2B, OAM0, OAM1, BG2A, OAM2, OAM3 */ inline void bPPU::render_line_mode7() { if(regs.mode7_extbg == false) { - render_line_mode7(2, 0, 0); //bg2 priorities are ignored + render_line_mode7(BG1, 2, 2); render_line_oam(1, 3, 4, 5); } else { - render_line_mode7(0, 1, 4); //bg1 priority is ignored - render_line_oam(2, 3, 5, 6); + render_line_mode7(BG1, 3, 3); + render_line_mode7(BG2, 1, 5); + render_line_oam(2, 4, 6, 7); } } void bPPU::render_line() { if(regs.display_disabled == true) { - memset(output->buffer + (_y << 10), 0, 2048); + memset(snes->get_ppu_output_handle(), 0, 1024); return; } clear_pixel_cache(); build_color_window_tables(); + switch(regs.bg_mode) { case 0:render_line_mode0();break; case 1:render_line_mode1();break; @@ -140,5 +138,6 @@ void bPPU::render_line() { case 6:render_line_mode6();break; case 7:render_line_mode7();break; } + render_line_output(); } diff --git a/src/ppu/bppu/bppu_render.h b/src/ppu/bppu/bppu_render.h index 89ebdb10..cc6f4229 100644 --- a/src/ppu/bppu/bppu_render.h +++ b/src/ppu/bppu/bppu_render.h @@ -18,15 +18,17 @@ enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 }; enum { PC_BG1 = 0x80, PC_BG2 = 0x81, PC_BG3 = 0x82, PC_BG4 = 0x83, PC_OAM = 0x84, PC_BACK = 0x00 }; struct _pixel { -//palette # index for main/subscreen pixels -//0 = transparent / use palette color # 0 -uint8 src_main, src_sub; +//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0 +//needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work +uint16 src_main, src_sub; //indicates source of palette # for main/subscreen (BG1-4, OAM, or back) -uint8 bg_main, bg_sub; +uint8 bg_main, bg_sub; +//true when bg_main == OAM && palette index >= 192, disables color add/sub effects +uint8 color_exempt; //priority level of src_n. to set src_n, //the priority of the pixel must be >pri_n -uint8 pri_main, pri_sub; -}pixel_cache[512]; +uint8 pri_main, pri_sub; +} pixel_cache[512]; uint8 *bg_tiledata[3]; uint8 *bg_tiledata_state[3]; @@ -55,7 +57,7 @@ uint8 oam_itemlist[32]; struct oam_tileitem { uint16 x, y, pri, pal, tile; bool hflip; -}oam_tilelist[34]; +} oam_tilelist[34]; enum { OAM_PRI_NONE = 4 }; uint8 oam_line_pal[512], oam_line_pri[512]; @@ -66,15 +68,16 @@ void render_oam_tile(int tile_num); void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); //bppu_render_mode7.cpp -void render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri); +void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos); //bppu_render_addsub.cpp -inline uint16 addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg); -inline uint16 addsub_pixel(int cdest_index, int cdest_bg); +inline uint16 addsub_pixels(uint32 cdest, uint32 csrc); +inline uint16 addsub_pixel (uint32 cdest); //bppu_render_line.cpp enum { BLENDTYPE_BACK = 0, BLENDTYPE_MAIN = 1, BLENDTYPE_SUB = 2, BLENDTYPE_COMBINE = 3 }; -inline uint16 get_palette(int index); +inline uint16 get_palette(uint8 index); +inline uint16 get_direct_color(uint8 p, uint8 t); inline uint16 get_pixel(int x); inline void render_line_output(); diff --git a/src/ppu/bppu/bppu_render_addsub.cpp b/src/ppu/bppu/bppu_render_addsub.cpp index fa727483..43bea198 100644 --- a/src/ppu/bppu/bppu_render_addsub.cpp +++ b/src/ppu/bppu/bppu_render_addsub.cpp @@ -1,15 +1,6 @@ -inline uint16 bPPU::addsub_pixels(int cdest_index, int cdest_bg, int csrc_index, int csrc_bg) { +inline uint16 bPPU::addsub_pixels(uint32 cdest, uint32 csrc) { int r, g, b; -uint32 cdest = get_palette(cdest_index); -uint32 csrc = get_palette(csrc_index); uint16 res; -//oam palettes 0-3 are not affected by color add/sub - if(cdest_bg == OAM) { - if(cdest_index < 192) { - return cdest; - } - } - switch(regs.color_mode) { case 0: //COLORMODE_ADD: if(regs.color_halve == true) { @@ -53,18 +44,10 @@ uint16 res; return 0x0000; //prevent annoying warning message } -inline uint16 bPPU::addsub_pixel(int cdest_index, int cdest_bg) { +inline uint16 bPPU::addsub_pixel(uint32 cdest) { int r, g, b; -uint32 cdest = get_palette(cdest_index); uint32 csrc = (regs.color_r) | (regs.color_g << 5) | (regs.color_b << 10); uint16 res; -//only oam palettes 4-7 are affected by color add/sub - if(cdest_bg == OAM) { - if(cdest_index < 192) { - return cdest; - } - } - switch(regs.color_mode) { case 0: //COLORMODE_ADD: if(regs.color_halve == true && regs.addsub_mode == 0) { diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index cad885ae..7379d277 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -3,7 +3,7 @@ void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri return; } -int x; +int x; int _scaddr = regs.bg_scaddr[bg]; int _tdaddr = regs.bg_tdaddr[bg]; bool _bg_enabled = regs.bg_enabled[bg]; @@ -25,17 +25,7 @@ uint16 opt_valid_bit; //offset-per-tile valid flag bit //entry point. This allows all 256 palette colors //to be used, instead of just the first 32. //entry = bg * 32, where 32 is from 8 * 4 -uint8 bgpal_index; - if(regs.bg_mode == 0) { - switch(bg) { - case BG1:bgpal_index = 0;break; - case BG2:bgpal_index = 32;break; - case BG3:bgpal_index = 64;break; - case BG4:bgpal_index = 96;break; - } - } else { - bgpal_index = 0; - } +uint8 bgpal_index = (regs.bg_mode == 0) ? (bg << 5) : 0; uint8 pal_size, tiledata_size; switch(color_depth) { @@ -114,36 +104,43 @@ int mosaic_x, mosaic_y; } else { mtable = (uint16*)mosaic_table[0]; } + mosaic_x = mtable[bg_x]; mosaic_y = mtable[bg_y]; uint8 tile_x; uint16 t, base_xpos, base_pos, pos; uint16 tile_num; -int mirror_x, mirror_y; -uint8 pal_index; +int mirror_x, mirror_y; +uint8 pal_index, pal_num; uint8 *tile_ptr; int xpos, ypos; uint16 map_index, hoffset, voffset, col; + build_window_tables(bg); uint8 *wt_main = main_windowtable[bg]; uint8 *wt_sub = sub_windowtable[bg]; - build_window_tables(bg); - for(screen_x=0;screen_x<_screen_width;screen_x++) { - //offset-per-tile mode. horizontal OPT is buggy, so it is disabled - //vertical OPT seems to be working OK... + + screen_x = 0; + do { //for(screen_x=0;screen_x<_screen_width;screen_x++) { if(regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6) { if(regs.bg_mode == 6) { + //hires adjust tile_x = (mtable[screen_x + (hscroll & 15)] >> 4); } else { tile_x = (mtable[screen_x + (hscroll & 7)] >> 3); } + hoffset = hscroll; voffset = vscroll; + + //tile 0 is unaffected by OPT mode... if(tile_x != 0) { - tile_x = (tile_x - 1) & 31; + //multiply by two to index into 16-bit table entries + tile_x = ((tile_x - 1) & 31) << 1; + if(regs.bg_mode == 4) { - pos = regs.bg_scaddr[BG3] + (tile_x << 1); + pos = regs.bg_scaddr[BG3] + tile_x; t = *((uint16*)vram + (pos >> 1)); if(t & opt_valid_bit) { if(!(t & 0x8000)) { @@ -153,18 +150,19 @@ uint8 *wt_sub = sub_windowtable[bg]; } } } else { - pos = regs.bg_scaddr[BG3] + (tile_x << 1); + pos = regs.bg_scaddr[BG3] + tile_x; t = *((uint16*)vram + (pos >> 1)); if(t & opt_valid_bit) { hoffset = ((t & 0x1ff8) | (hscroll & 7)) & screen_width_mask; } - pos = regs.bg_scaddr[BG3] + 64 + (tile_x << 1); + pos = regs.bg_scaddr[BG3] + 64 + tile_x; t = *((uint16*)vram + (pos >> 1)); if(t & opt_valid_bit) { voffset = (t & 0x1fff) & screen_height_mask; } } } + mosaic_x = mtable[(screen_x + hoffset) & screen_width_mask ]; mosaic_y = mtable[(screen_y + voffset) & screen_height_mask]; } @@ -189,30 +187,36 @@ uint8 *wt_sub = sub_windowtable[bg]; base_pos = (((mosaic_y >> tile_height) & 31) << 5) + ((mosaic_x >> tile_width) & 31); pos = _scaddr + map_index + (base_pos << 1); t = *((uint16*)vram + (pos >> 1)); - mirror_y = (t & 0x8000)?1:0; - mirror_x = (t & 0x4000)?1:0; + mirror_y = !!(t & 0x8000); + mirror_x = !!(t & 0x4000); int _pri; _pri = (t & 0x2000) ? pri1_pos : pri0_pos; tile_num = t & 0x03ff; + + //16x16 horizontal tile mirroring if(tile_width == 4) { if(((mosaic_x & 15) >= 8 && !mirror_x) || ((mosaic_x & 15) < 8 && mirror_x))tile_num++; tile_num &= 0x03ff; } + + //16x16 vertical tile mirroring if(tile_height == 4) { if(((mosaic_y & 15) >= 8 && !mirror_y) || ((mosaic_y & 15) < 8 && mirror_y))tile_num += 16; tile_num &= 0x03ff; } + tile_num += (_tdaddr >> tiledata_size); if(bg_td_state[tile_num] == 1) { render_bg_tile(color_depth, tile_num); } - pal_index = ((t >> 10) & 7) * pal_size + bgpal_index; + pal_num = ((t >> 10) & 7); + pal_index = pal_num * pal_size + bgpal_index; if(mirror_y) { ypos = (7 - (mosaic_y & 7)); } else { ypos = ( (mosaic_y & 7)); } @@ -220,23 +224,30 @@ int _pri; //loop while we are rendering from the same tile, as there's no need to do all of the above work //unless we have rendered all of the visible tile, taking mosaic into account. tile_ptr = (uint8*)bg_td + (tile_num << 6) + (ypos << 3); - while(1) { + do { if(mirror_x) { xpos = (7 - (mosaic_x & 7)); } else { xpos = ( (mosaic_x & 7)); } col = *(tile_ptr + xpos); if(col && main_colorwindowtable[screen_x]) { + if(regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4)) { + col = get_direct_color(pal_num, col); + } else { + col = get_palette(col + pal_index); + } + if(_bg_enabled == true && !wt_main[screen_x]) { if(pixel_cache[screen_x].pri_main < _pri) { pixel_cache[screen_x].pri_main = _pri; pixel_cache[screen_x].bg_main = 0x80 | bg; - pixel_cache[screen_x].src_main = col + pal_index; + pixel_cache[screen_x].src_main = col; + pixel_cache[screen_x].color_exempt = false; } } if(_bgsub_enabled == true && !wt_sub[screen_x]) { if(pixel_cache[screen_x].pri_sub < _pri) { pixel_cache[screen_x].pri_sub = _pri; pixel_cache[screen_x].bg_sub = 0x80 | bg; - pixel_cache[screen_x].src_sub = col + pal_index; + pixel_cache[screen_x].src_sub = col; } } } @@ -246,8 +257,7 @@ int _pri; mosaic_x = mtable[bg_x]; if(base_xpos != ((mosaic_x >> 3) & 31))break; - screen_x++; - if(screen_x >= _screen_width)break; - } - } + if(++screen_x >= _screen_width)break; + } while(1); + } while(++screen_x < _screen_width); } diff --git a/src/ppu/bppu/bppu_render_line.cpp b/src/ppu/bppu/bppu_render_line.cpp index ee83802d..7b657c07 100644 --- a/src/ppu/bppu/bppu_render_line.cpp +++ b/src/ppu/bppu/bppu_render_line.cpp @@ -1,36 +1,45 @@ -inline uint16 bPPU::get_palette(int index) { +inline uint16 bPPU::get_palette(uint8 index) { return *((uint16*)cgram + index); } +inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) { +//p = 00000bgr +//t = BBGGGRRR +//r = 0BBb00GGGg0RRRr0 + return ((t & 7) << 2) | ((p & 1) << 1) | + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); +} + inline uint16 bPPU::get_pixel(int x) { _pixel *p = &pixel_cache[x]; -uint16 _r; +uint16 _r, src_back = get_palette(0); if(p->bg_main && p->bg_sub) { - if(regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { + if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { if(regs.addsub_mode) { - _r = addsub_pixels(p->src_main, p->bg_main & 0x7f, p->src_sub, p->bg_sub & 0x7f); + _r = addsub_pixels(p->src_main, p->src_sub); } else { - _r = addsub_pixel(p->src_main, p->bg_main & 0x7f); + _r = addsub_pixel(p->src_main); } } else { - _r = get_palette(p->src_main); + _r = p->src_main; } } else if(p->bg_main) { - if(regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { - _r = addsub_pixel(p->src_main, p->bg_main & 0x7f); + if(p->color_exempt == false && regs.bg_color_enabled[p->bg_main & 0x7f] && sub_colorwindowtable[x]) { + _r = addsub_pixel(p->src_main); } else { - _r = get_palette(p->src_main); + _r = p->src_main; } } else if(p->bg_sub) { if(regs.bg_color_enabled[BACK]) { if(sub_colorwindowtable[x]) { if(regs.addsub_mode) { - _r = addsub_pixels(0, BACK, p->src_sub, p->bg_sub & 0x7f); + _r = addsub_pixels(src_back, p->src_sub); } else { - _r = addsub_pixel(0, BACK); + _r = addsub_pixel(src_back); } } else { - _r = get_palette(0); + _r = src_back; } } else { _r = 0x0000; @@ -38,9 +47,9 @@ uint16 _r; } else { if(main_colorwindowtable[x]) { if(regs.bg_color_enabled[BACK] && sub_colorwindowtable[x]) { - _r = addsub_pixel(0, BACK); + _r = addsub_pixel(src_back); } else { - _r = get_palette(0); + _r = src_back; } } else { _r = 0x0000; @@ -53,10 +62,7 @@ inline void bPPU::render_line_output() { int x; uint16 _r; uint16 *ptr; - ptr = (uint16*)output->buffer + (_y << 10); - if(_interlace == true) { - ptr += _interlace_field << 9; - } + ptr = (uint16*)snes->get_ppu_output_handle(); uint16 *ltable; ltable = (uint16*)light_table + (regs.display_brightness << 15); @@ -64,8 +70,7 @@ uint16 *ltable; if(_screen_width == 256) { for(x=0;x<256;x++) { _r = get_pixel(x); - *ptr = *(ltable + _r); - ptr += 2; + *ptr++ = *(ltable + _r); } } else { for(x=0;x<512;x++) { diff --git a/src/ppu/bppu/bppu_render_mode7.cpp b/src/ppu/bppu/bppu_render_mode7.cpp index 8ed29b5b..1671f6c5 100644 --- a/src/ppu/bppu/bppu_render_mode7.cpp +++ b/src/ppu/bppu/bppu_render_mode7.cpp @@ -1,62 +1,72 @@ -#define CLIP_10BIT_SIGNED(x) \ - ((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3) +/* + bsnes mode7 renderer -#define CAST_WORDTOINT(x) \ - (int32)(((x & 0x8000) ? (x | 0xffff0000) : (x & 0x00007fff))) + base algorithm written by anomie + bsnes implementation written by byuu + + supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic + does not support pseudo-hires + interlace support is automatic via main rendering routine +*/ + +//13-bit sign extend +//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv +#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) ) +//#define CLIP(x) ( ((x) & 0x03ff) | (((x) & 0x2000) ? -0x0400 : 0) ) + +void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return; + +int32 x, y; +int32 a, b, c, d, cx, cy; +int32 hofs, vofs; -void bPPU::render_line_mode7(uint8 bg1_pri, uint8 bg2b_pri, uint8 bg2a_pri) { -int32 x; -int32 step_m7a, step_m7c, m7a, m7b, m7c, m7d; -int32 hoffset, voffset; -int32 centerx, centery; -int32 xx, yy; int32 px, py; int32 tx, ty, tile, palette; -uint8 layer_pos; - hoffset = ((int32)regs.m7_hofs << 19) >> 19; - voffset = ((int32)regs.m7_vofs << 19) >> 19; + a = int32(int16(regs.m7a)); + b = int32(int16(regs.m7b)); + c = int32(int16(regs.m7c)); + d = int32(int16(regs.m7d)); - centerx = ((int32)regs.m7x << 19) >> 19; - centery = ((int32)regs.m7y << 19) >> 19; + cx = (int32(regs.m7x) << 19) >> 19; + cy = (int32(regs.m7y) << 19) >> 19; + hofs = (int32(regs.m7_hofs) << 19) >> 19; + vofs = (int32(regs.m7_vofs + 1) << 19) >> 19; + +int _pri, _x; +bool _bg_enabled = regs.bg_enabled[bg]; +bool _bgsub_enabled = regs.bgsub_enabled[bg]; + + build_window_tables(bg); +uint8 *wt_main = main_windowtable[bg]; +uint8 *wt_sub = sub_windowtable[bg]; if(regs.mode7_vflip == true) { - yy = 255 - _y; + y = 255 - _y; } else { - yy = _y; - } - yy += CLIP_10BIT_SIGNED(voffset - centery); - - m7b = CAST_WORDTOINT(regs.m7b) * yy + (centerx << 8); - m7d = CAST_WORDTOINT(regs.m7d) * yy + (centery << 8); - - step_m7a = CAST_WORDTOINT(regs.m7a); - step_m7c = CAST_WORDTOINT(regs.m7c); - - xx = CLIP_10BIT_SIGNED(hoffset - centerx); - - m7a = CAST_WORDTOINT(regs.m7a) * xx; - m7c = CAST_WORDTOINT(regs.m7c) * xx; - -int _pri, _x, _bg; -bool _bg_enabled, _bgsub_enabled; - if(regs.mode7_extbg == false) { - _pri = bg1_pri; - _bg = BG1; - _bg_enabled = regs.bg_enabled[BG1]; - _bgsub_enabled = regs.bgsub_enabled[BG1]; - } else { - _bg = BG2; - _bg_enabled = regs.bg_enabled[BG2]; - _bgsub_enabled = regs.bgsub_enabled[BG2]; + y = _y; } -uint8 *wt_main = main_windowtable[_bg]; -uint8 *wt_sub = sub_windowtable[_bg]; - build_window_tables(_bg); +uint16 *mtable_x, *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]; + } 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]; + } +int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8); +int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8); for(x=0;x<256;x++) { - px = ((m7a + m7b) >> 8); - py = ((m7c + m7d) >> 8); + px = psx + (a * mtable_x[x]); + py = psy + (c * mtable_x[x]); + + //mask floating-point bits (low 8 bits) + px >>= 8; + py >>= 8; switch(regs.mode7_repeat) { case 0: //screen repitition outside of screen area @@ -68,20 +78,7 @@ uint8 *wt_sub = sub_windowtable[_bg]; tile = vram[(ty * 128 + tx) << 1]; palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; break; - case 2: //character 0 repetition outside of screen area - if(px < 0 || px > 1023 || py < 0 || py > 1023) { - tx = 0; - ty = 0; - } else { - px &= 1023; - py &= 1023; - tx = ((px >> 3) & 127); - ty = ((py >> 3) & 127); - } - tile = vram[(ty * 128 + tx) << 1]; - palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - break; - case 3: //palette color 0 outside of screen area + case 2: //palette color 0 outside of screen area if(px < 0 || px > 1023 || py < 0 || py > 1023) { palette = 0; } else { @@ -93,46 +90,61 @@ uint8 *wt_sub = sub_windowtable[_bg]; palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; } break; + case 3: //character 0 repetition outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + } + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; } - if(!palette)goto _end_setpixel; - - if(regs.mode7_extbg == false) { - //_pri set at top of function, as it is static - if(regs.mode7_hflip == true) { - _x = 255 - x; - } else { - _x = x; - } + if(bg == BG1) { + _pri = pri0_pos; } else { - _pri = (palette >> 7) ? bg2a_pri : bg2b_pri; + _pri = (palette >> 7) ? pri1_pos : pri0_pos; palette &= 0x7f; - if(regs.mode7_hflip == true) { - _x = 255 - x; - } else { - _x = x; - } + } + + if(!palette)continue; + + if(regs.mode7_hflip == true) { + _x = 255 - x; + } else { + _x = x; } if(main_colorwindowtable[_x]) { - if(_bg_enabled == true && !wt_main[_x]) { + uint32 col; + if(regs.direct_color == true && bg == BG1) { + //direct color mode does not apply to bg2, as it is only 128 colors... + col = get_direct_color(0, palette); + } else { + col = get_palette(palette); + } + + if(regs.bg_enabled[bg] == true && !wt_main[_x]) { if(pixel_cache[_x].pri_main < _pri) { pixel_cache[_x].pri_main = _pri; - pixel_cache[_x].bg_main = 0x80 | _bg; - pixel_cache[_x].src_main = palette; + pixel_cache[_x].bg_main = 0x80 | bg; + pixel_cache[_x].src_main = col; + pixel_cache[_x].color_exempt = false; } } - if(_bgsub_enabled == true && !wt_sub[_x]) { + if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { if(pixel_cache[_x].pri_sub < _pri) { pixel_cache[_x].pri_sub = _pri; - pixel_cache[_x].bg_sub = 0x80 | _bg; - pixel_cache[_x].src_sub = palette; + pixel_cache[_x].bg_sub = 0x80 | bg; + pixel_cache[_x].src_sub = col; } } } - -_end_setpixel: - m7a += step_m7a; - m7c += step_m7c; } } + +#undef CLIP diff --git a/src/ppu/bppu/bppu_render_oam.cpp b/src/ppu/bppu/bppu_render_oam.cpp index be44c52f..54d7f1a8 100644 --- a/src/ppu/bppu/bppu_render_oam.cpp +++ b/src/ppu/bppu/bppu_render_oam.cpp @@ -256,14 +256,15 @@ int _pri; if(pixel_cache[x].pri_main < _pri) { pixel_cache[x].pri_main = _pri; pixel_cache[x].bg_main = PC_OAM; - pixel_cache[x].src_main = oam_line_pal[x]; + pixel_cache[x].src_main = get_palette(oam_line_pal[x]); + pixel_cache[x].color_exempt = (oam_line_pal[x] < 192); } } if(_bgsub_enabled == true && !wt_sub[x]) { if(pixel_cache[x].pri_sub < _pri) { pixel_cache[x].pri_sub = _pri; pixel_cache[x].bg_sub = PC_OAM; - pixel_cache[x].src_sub = oam_line_pal[x]; + pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); } } } diff --git a/src/ppu/bppu/snes9x_render_mode7.cpp b/src/ppu/bppu/snes9x_render_mode7.cpp new file mode 100644 index 00000000..572a414f --- /dev/null +++ b/src/ppu/bppu/snes9x_render_mode7.cpp @@ -0,0 +1,148 @@ +#define CLIP_10BIT_SIGNED(x) \ + ((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3) + +#define CAST_WORDTOINT(x) \ + (int32)((((x) & 0x8000) ? ((x) | 0xffff0000) : ((x) & 0x00007fff))) + +void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) { +int32 x; +int32 step_m7a, step_m7c, m7a, m7b, m7c, m7d; +int32 hoffset, voffset; +int32 centerx, centery; +int32 xx, yy; +int32 px, py; +int32 tx, ty, tile, palette; +uint8 layer_pos; + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false)return; + +uint16 *mtable_x, *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]; + } 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]; + } + + hoffset = ((int32)regs.m7_hofs << 19) >> 19; + voffset = ((int32)regs.m7_vofs << 19) >> 19; + + centerx = ((int32)regs.m7x << 19) >> 19; + centery = ((int32)regs.m7y << 19) >> 19; + + if(regs.mode7_vflip == true) { + yy = 255 - _y; + } else { + yy = _y; + } + yy = mtable_y[yy]; + yy += CLIP_10BIT_SIGNED(voffset - centery); + + m7b = CAST_WORDTOINT(regs.m7b) * yy + (centerx << 8); + m7d = CAST_WORDTOINT(regs.m7d) * yy + (centery << 8); + + step_m7a = CAST_WORDTOINT(regs.m7a); + step_m7c = CAST_WORDTOINT(regs.m7c); + + xx = CLIP_10BIT_SIGNED(hoffset - centerx); + + m7a = CAST_WORDTOINT(regs.m7a) * xx; + m7c = CAST_WORDTOINT(regs.m7c) * xx; + +int _pri, _x; +bool _bg_enabled, _bgsub_enabled; + _bg_enabled = regs.bg_enabled[bg]; + _bgsub_enabled = regs.bgsub_enabled[bg]; + + build_window_tables(bg); +uint8 *wt_main = main_windowtable[bg]; +uint8 *wt_sub = sub_windowtable[bg]; + + for(x=0;x<256;x++) { + px = ((m7a + step_m7a * mtable_x[x] + m7b) >> 8); + py = ((m7c + step_m7c * mtable_x[x] + m7d) >> 8); + + switch(regs.mode7_repeat) { + case 0: //screen repitition outside of screen area + case 1: //same as case 0 + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + case 2: //palette color 0 outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + break; + case 3: //character 0 repetition outside of screen area + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + tx = 0; + ty = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + } + tile = vram[(ty * 128 + tx) << 1]; + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + } + + if(bg == BG1) { + _pri = pri0_pos; + } else { + _pri = (palette >> 7) ? pri1_pos : pri0_pos; + palette &= 0x7f; + } + + if(!palette)continue; + + if(regs.mode7_hflip == true) { + _x = 255 - x; + } else { + _x = x; + } + + if(main_colorwindowtable[_x]) { + uint32 col; + if(regs.direct_color == true && bg == BG1) { + //direct color mode does not apply to bg2 as it is only 128 colors... + col = get_direct_color(0, palette); + } else { + col = get_palette(palette); + } + + if(regs.bg_enabled[bg] == true && !wt_main[_x]) { + if(pixel_cache[_x].pri_main < _pri) { + pixel_cache[_x].pri_main = _pri; + pixel_cache[_x].bg_main = 0x80 | bg; + pixel_cache[_x].src_main = col; + pixel_cache[_x].color_exempt = false; + } + } + if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { + if(pixel_cache[_x].pri_sub < _pri) { + pixel_cache[_x].pri_sub = _pri; + pixel_cache[_x].bg_sub = 0x80 | bg; + pixel_cache[_x].src_sub = col; + } + } + } + } +} + +#undef CLIP_10BIT_SIGNED +#undef CAST_WORDTOINT diff --git a/src/ppu/ppu.cpp b/src/ppu/ppu.cpp index 3f7a90ce..224bd621 100644 --- a/src/ppu/ppu.cpp +++ b/src/ppu/ppu.cpp @@ -1,27 +1,17 @@ #include "../base.h" -PPUOutput::PPUOutput() { - buffer = (uint16*)memalloc(512 * 478 * 2, "PPUOutput::buffer"); - memset(buffer, 0, 512 * 478 * 2); - hires = false; - interlace = false; - for(int i=0;i<239;i++) { - line[i].hires = false; - line[i].interlace = false; - } -} - -PPUOutput::~PPUOutput() { - if(buffer)memfree(buffer, "PPUOutput::buffer"); +void PPU::get_scanline_info(scanline_info *info) { + info->hires = scanline_is_hires(); + info->interlace = cpu->interlace(); } void PPU::set_frameskip(int fs) {} +bool PPU::render_frame() { return true; } PPU::PPU() { + ppu1_version = 1; + ppu2_version = 3; mmio = &mmio_unmapped; - output = new PPUOutput(); } -PPU::~PPU() { - if(output)delete(output); -} +PPU::~PPU() {} diff --git a/src/ppu/ppu.h b/src/ppu/ppu.h index f4c20eca..f2997aea 100644 --- a/src/ppu/ppu.h +++ b/src/ppu/ppu.h @@ -1,19 +1,25 @@ -class PPUOutput { -public: -bool hires, interlace; -struct { -bool hires, interlace; -}line[239]; -uint16 *buffer; - PPUOutput(); - ~PPUOutput(); -}; - class PPU { public: -int _y; -PPUOutput *output; -MMIO *mmio; +//PPU1 version number +//* 1 is known +//* reported by $213e +uint8 ppu1_version; + +//PPU2 version number +//* 1 and 3 are known +//* reported by $213f +uint8 ppu2_version; + +int _y; +MMIO *mmio; + +struct scanline_info { + bool hires; + bool interlace; +}; + virtual bool scanline_is_hires() = 0; + virtual void get_scanline_info(scanline_info *info); + virtual uint8 vram_read (uint16 addr) = 0; virtual void vram_write (uint16 addr, uint8 value) = 0; virtual uint8 oam_read (uint16 addr) = 0; @@ -25,10 +31,12 @@ MMIO *mmio; virtual void run() = 0; virtual void scanline() = 0; + virtual void render_scanline() = 0; virtual void frame() = 0; virtual void power() = 0; virtual void reset() = 0; virtual void set_frameskip(int fs); + virtual bool render_frame(); PPU(); ~PPU(); diff --git a/src/sdl/Makefile b/src/sdl/Makefile index 7d81aeb3..0275a371 100644 --- a/src/sdl/Makefile +++ b/src/sdl/Makefile @@ -6,6 +6,7 @@ OBJS = sdlmain.o \ memory.o bmemory.o \ cpu.o bcpu.o \ apu.o bapu.o bapuskip.o \ + bdsp.o \ ppu.o bppu.o \ snes.o \ srtc.o sdd1.o @@ -60,6 +61,12 @@ bapu.o: ../apu/bapu/* bapuskip.o: ../apu/bapuskip/* $(CC) $(CFLAGS) -c ../apu/bapuskip/bapuskip.cpp +########### +### dsp ### +########### +bdsp.o: ../dsp/bdsp/* + $(CC) $(CFLAGS) -c ../dsp/bdsp/bdsp.cpp + ########### ### ppu ### ########### diff --git a/src/sdl/Makefile.win32 b/src/sdl/Makefile.win32 index 53c00b32..fb3b12fb 100644 --- a/src/sdl/Makefile.win32 +++ b/src/sdl/Makefile.win32 @@ -6,6 +6,7 @@ OBJS = sdlmain.obj \ memory.obj bmemory.obj \ cpu.obj bcpu.obj \ apu.obj bapu.obj bapuskip.obj \ + bdsp.obj \ ppu.obj bppu.obj \ snes.obj \ srtc.obj sdd1.obj @@ -61,6 +62,12 @@ bapu.obj: ../apu/bapu/* bapuskip.obj: ../apu/bapuskip/* $(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp +########### +### dsp ### +########### +bdsp.obj: ../dsp/bdsp/* + $(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp + ########### ### ppu ### ########### diff --git a/src/sdl/bsnes.cpp b/src/sdl/bsnes.cpp index 50336b7a..054b3243 100644 --- a/src/sdl/bsnes.cpp +++ b/src/sdl/bsnes.cpp @@ -1,13 +1,13 @@ void bSNES::set_status(uint32 new_status) { run_status = new_status; } uint32 bSNES::get_status() { return run_status; } -void bSNES::snes_run() { +void bSNES::run() { if(!rom_image->loaded())return; switch(run_status) { case RUN: while(update_frame == false) { - run(); + SNES::run(); } update_frame = false; render(); @@ -17,7 +17,8 @@ void bSNES::snes_run() { } } -void bSNES::render_frame() {} +void bSNES::video_run() { render(); } +void bSNES::sound_run() {} /*********************** *** Input functions *** @@ -28,18 +29,18 @@ void bSNES::render_frame() {} //to throw error messages about a bad free call to stdout... void bSNES::poll_input() { uint8 *keystate = SDL_GetKeyState(0); - joypad1.up = keystate[cfg.input.joypad1.up]; - joypad1.down = keystate[cfg.input.joypad1.down]; - joypad1.left = keystate[cfg.input.joypad1.left]; - joypad1.right = keystate[cfg.input.joypad1.right]; - joypad1.select = keystate[cfg.input.joypad1.select]; - joypad1.start = keystate[cfg.input.joypad1.start]; - joypad1.y = keystate[cfg.input.joypad1.y]; - joypad1.b = keystate[cfg.input.joypad1.b]; - joypad1.x = keystate[cfg.input.joypad1.x]; - joypad1.a = keystate[cfg.input.joypad1.a]; - joypad1.l = keystate[cfg.input.joypad1.l]; - joypad1.r = keystate[cfg.input.joypad1.r]; + joypad1.up = keystate[(int)config::input.joypad1.up]; + joypad1.down = keystate[(int)config::input.joypad1.down]; + joypad1.left = keystate[(int)config::input.joypad1.left]; + joypad1.right = keystate[(int)config::input.joypad1.right]; + joypad1.select = keystate[(int)config::input.joypad1.select]; + joypad1.start = keystate[(int)config::input.joypad1.start]; + joypad1.y = keystate[(int)config::input.joypad1.y]; + joypad1.b = keystate[(int)config::input.joypad1.b]; + joypad1.x = keystate[(int)config::input.joypad1.x]; + joypad1.a = keystate[(int)config::input.joypad1.a]; + joypad1.l = keystate[(int)config::input.joypad1.l]; + joypad1.r = keystate[(int)config::input.joypad1.r]; //It's impossible to hold both up+down, or left+right down //at the same time on a directional pad; and besides, allowing diff --git a/src/sdl/bsnes.h b/src/sdl/bsnes.h index 472df4e5..dfefc6e1 100644 --- a/src/sdl/bsnes.h +++ b/src/sdl/bsnes.h @@ -14,10 +14,12 @@ bJoypad joypad1, joypad2; public: enum { STOP = 0, RUN }; + void run(); + void video_run(); + void sound_run(); + void set_status(uint32 new_status); uint32 get_status(); - void snes_run(); - void render_frame(); //input functions void poll_input(); diff --git a/src/sdl/bsnes_sdl.cfg b/src/sdl/bsnes_sdl.cfg deleted file mode 100644 index 3e9cb767..00000000 --- a/src/sdl/bsnes_sdl.cfg +++ /dev/null @@ -1,18 +0,0 @@ -apu.enabled = true -video.fullscreen = false -video.display_width = 256 -video.display_height = 223 -video.output_width = 256 -video.output_height = 223 -input.joypad1.up = 0x111 -input.joypad1.down = 0x112 -input.joypad1.left = 0x114 -input.joypad1.right = 0x113 -input.joypad1.a = 0x78 -input.joypad1.b = 0x7a -input.joypad1.x = 0x73 -input.joypad1.y = 0x61 -input.joypad1.l = 0x64 -input.joypad1.r = 0x63 -input.joypad1.select = 0x12f -input.joypad1.start = 0x0d diff --git a/src/sdl/cc.bat b/src/sdl/cc.bat index 413eb640..cc1ab957 100644 --- a/src/sdl/cc.bat +++ b/src/sdl/cc.bat @@ -1,3 +1,2 @@ @nmake /NOLOGO /f Makefile.win32 -@move bsnes_sdl.exe ../../bsnes_sdl.exe>nul @pause \ No newline at end of file diff --git a/src/sdl/config.cpp b/src/sdl/config.cpp index 62177565..163d3646 100644 --- a/src/sdl/config.cpp +++ b/src/sdl/config.cpp @@ -1,47 +1,32 @@ -#define __config_add(name, def, type) add(&name, #name, def, type) +namespace config { -class Config : public config { -public: +struct Video { + static Setting fullscreen; + static Setting display_width, display_height; + static Setting output_width, output_height; +} video; +Setting Video::fullscreen(&config_file, "video.fullscreen", "Enable fullscreen mode at startup", false, Setting::TRUE_FALSE); +Setting Video::display_width (&config_file, "video.display_width", "Window / Fullscreen width", 320, Setting::DEC); +Setting Video::display_height(&config_file, "video.display_height", "Window / Fullscreen height", 240, Setting::DEC); +Setting Video::output_width (&config_file, "video.output_width", "SNES video output width", 256, Setting::DEC); +Setting Video::output_height (&config_file, "video.output_height", "SNES video output height", 223, Setting::DEC); -struct { - uint32 enabled; -}apu; +struct Input { + struct Joypad { + static Setting up, down, left, right, a, b, x, y, l, r, select, start; + } joypad1; +} input; +Setting Input::Joypad::up (&config_file, "input.joypad1.up", "Joypad1 up", SDLK_UP, Setting::HEX); +Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", SDLK_DOWN, Setting::HEX); +Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", SDLK_LEFT, Setting::HEX); +Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", SDLK_RIGHT, Setting::HEX); +Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", SDLK_x, Setting::HEX); +Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", SDLK_z, Setting::HEX); +Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", SDLK_s, Setting::HEX); +Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", SDLK_a, Setting::HEX); +Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", SDLK_d, Setting::HEX); +Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", SDLK_c, Setting::HEX); +Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", SDLK_RSHIFT, Setting::HEX); +Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", SDLK_RETURN, Setting::HEX); -struct { - uint32 fullscreen; - uint32 display_width, display_height; - uint32 output_width, output_height; -}video; - -struct { - struct { - uint32 up, down, left, right; - uint32 a, b, x, y, l, r; - uint32 select, start; - }joypad1; -}input; - - Config() { - __config_add(apu.enabled, true, TRUEFALSE); - - __config_add(video.fullscreen, false, TRUEFALSE); - __config_add(video.display_width, 256, DEC); - __config_add(video.display_height, 223, DEC); - __config_add(video.output_width, 256, DEC); - __config_add(video.output_height, 223, DEC); - - __config_add(input.joypad1.up, SDLK_UP, HEX); - __config_add(input.joypad1.down, SDLK_DOWN, HEX); - __config_add(input.joypad1.left, SDLK_LEFT, HEX); - __config_add(input.joypad1.right, SDLK_RIGHT, HEX); - __config_add(input.joypad1.a, SDLK_x, HEX); - __config_add(input.joypad1.b, SDLK_z, HEX); - __config_add(input.joypad1.x, SDLK_s, HEX); - __config_add(input.joypad1.y, SDLK_a, HEX); - __config_add(input.joypad1.l, SDLK_d, HEX); - __config_add(input.joypad1.r, SDLK_c, HEX); - __config_add(input.joypad1.select, SDLK_RSHIFT, HEX); - __config_add(input.joypad1.start, SDLK_RETURN, HEX); - } - -}cfg; +}; diff --git a/src/sdl/render.cpp b/src/sdl/render.cpp index 69b9ebec..38d4da84 100644 --- a/src/sdl/render.cpp +++ b/src/sdl/render.cpp @@ -1,87 +1,49 @@ -uint8 color_curve_table[32]; -uint32 color_lookup_table[65536]; - -void update_color_lookup_table() { -int i, r, g, b, c; - for(i=0,c=0;i<16;i++) { - color_curve_table[i] = c; - c = c + i + 1; - } - for(;i<31;i++) { - color_curve_table[i] = c; - c += 8; - } - color_curve_table[i] = 0xff; - -int color_depth = 16; - if(color_depth == 15) { - for(i=0;i<65536;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 3; - b = color_curve_table[b] >> 3; - color_lookup_table[i] = (r << 10) | (g << 5) | (b); - } - } else if(color_depth == 16) { - for(i=0;i<65536;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 2; - b = color_curve_table[b] >> 3; - color_lookup_table[i] = (r << 11) | (g << 5) | (b); - } - } else if(color_depth == 32) { - for(i=0;i<65536;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - r = color_curve_table[r]; - g = color_curve_table[g]; - b = color_curve_table[b]; - color_lookup_table[i] = (r << 16) | (g << 8) | (b); - } - } else { - alert("Error: Unsupported color depth [%d]", color_depth); - } -} - void render16() { -uint16 *src, *dest; +uint16 *dest, *src; uint32 pitch; int x, y; - pitch = (backbuffer->pitch >> 1) - 256; - src = (uint16*)ppu->output->buffer + (1 << 10); + +SNES::video_info vi; + snes->get_video_info(&vi); + + pitch = (backbuffer->pitch >> 1); dest = (uint16*)backbuffer->pixels; - for(y=0;y<223;y++) { - for(x=0;x<256;x++) { - *dest++ = color_lookup_table[*src]; - src += 2; + src = (uint16*)vi.data; + + if(vi.width == 256 && vi.height == 224) { + for(y=0;y<224;y++) { + memcpy(dest, src, 512); + dest += pitch; + src += 256; + } + } else if(vi.width == 512 && vi.height == 224) { + for(y=0;y<224;y++) { + memcpy(dest, src, 1024); + dest += pitch; + src += 512; + } + } else if(vi.width == 256 && vi.height == 448) { + for(y=0;y<448;y++) { + memcpy(dest, src, 512); + dest += pitch; + src += 256; + } + } else if(vi.width == 512 && vi.height == 448) { + for(y=0;y<448;y++) { + memcpy(dest, src, 1024); + dest += pitch; + src += 512; } - dest += pitch; - src += 512; } + + + screen_info.rs.x = 0; + screen_info.rs.y = (vi.height == 224) ? 1 : 2; + screen_info.rs.w = vi.width; + screen_info.rs.h = (vi.height == 224) ? 223 : 446; } -void render32() { -uint16 *src; -uint32 *dest, pitch; -int x, y; - pitch = (screen->pitch >> 2) - 256; - src = (uint16*)ppu->output->buffer + (1 << 10); - dest = (uint32*)screen->pixels; - for(y=0;y<223;y++) { - for(x=0;x<256;x++) { - *dest++ = color_lookup_table[*src]; - src += 2; - } - dest += pitch; - src += 512; - } -} +void render32() {} void render() { if(SDL_MUSTLOCK(screen)) { diff --git a/src/sdl/sdlmain.cpp b/src/sdl/sdlmain.cpp index e4f8f270..76d69354 100644 --- a/src/sdl/sdlmain.cpp +++ b/src/sdl/sdlmain.cpp @@ -1,14 +1,13 @@ #define INTERFACE_MAIN -#define BSNES_VERSION "0.011" -#define BSNES_TITLE "bsnes/SDL v" BSNES_VERSION #include "../base.h" #include "sdlmain.h" +#include "config.cpp" + #ifdef _WIN32_ HWND hwnd; #endif -#include "config.cpp" #include "bsnes.h" #include "rom.cpp" #include "render.cpp" @@ -47,24 +46,31 @@ va_list args; void init_snes() { mem_bus = new bMemBus(); cpu = new bCPU(); - if(cfg.apu.enabled) { - apu = new bAPU(); - } else { - apu = new bAPUSkip(); - } + apu = new bAPU(); + dsp = new bDSP(); ppu = new bPPU(); snes = new bSNES(); bsnes = static_cast(snes); snes->init(); + +//TODO: add sound support and remove this, +//this is used with linux/bsd and mkfifo to +//play audio in real-time while sound output +//isn't available. + snes->log_audio_enable("output.wav"); + + snes->set_playback_buffer_size(2000); } void term_snes() { - if(mem_bus) { delete(mem_bus); mem_bus = 0; } - if(cpu) { delete(cpu); cpu = 0; } - if(apu) { delete(apu); apu = 0; } - if(ppu) { delete(ppu); ppu = 0; } - if(snes) { delete(snes); snes = 0; } + snes->term(); + + if(mem_bus) { delete(static_cast(mem_bus)); mem_bus = 0; } + if(cpu) { delete(static_cast (cpu)); cpu = 0; } + if(apu) { delete(static_cast (apu)); apu = 0; } + if(ppu) { delete(static_cast (ppu)); ppu = 0; } + if(snes) { delete(static_cast (snes)); snes = 0; } } void center_window() { @@ -82,22 +88,17 @@ void set_window_info() { //SDL won't draw anything if you blit an image that's larger than //the display mode/window, even if you use the SoftStretch blit and //clip the source + dest rectangles properly... - if(cfg.video.display_width < cfg.video.output_width) { - cfg.video.display_width = cfg.video.output_width; + if((int)config::video.display_width < (int)config::video.output_width) { + config::video.display_width = config::video.output_width; } - if(cfg.video.display_height < cfg.video.output_height) { - cfg.video.display_height = cfg.video.output_height; + if((int)config::video.display_height < (int)config::video.output_height) { + config::video.display_height = config::video.output_height; } - screen_info.rd.x = (cfg.video.display_width - cfg.video.output_width ) >> 1; - screen_info.rd.y = (cfg.video.display_height - cfg.video.output_height) >> 1; - screen_info.rd.w = cfg.video.output_width; - screen_info.rd.h = cfg.video.output_height; - - screen_info.rs.x = 0; - screen_info.rs.y = 0; - screen_info.rs.w = 256; - screen_info.rs.h = 223; + screen_info.rd.x = ((int)config::video.display_width - (int)config::video.output_width ) >> 1; + screen_info.rd.y = ((int)config::video.display_height - (int)config::video.output_height) >> 1; + screen_info.rd.w = config::video.output_width; + screen_info.rd.h = config::video.output_height; } #ifdef _WIN32_ @@ -115,14 +116,13 @@ SDL_Event event; return 0; } - cfg.load("bsnes_sdl.cfg"); + config_file.load("bsnes_sdl.cfg"); rom_image = new ROMImage(); init_snes(); rom_image->select(argv[1]); rom_image->load(); - snes->power(); if(rom_image->loaded() == false) { alert("Failed to load image. Usage: bsnes_sdl "); @@ -132,13 +132,11 @@ SDL_Event event; SDL_Init(SDL_INIT_VIDEO); atexit(SDL_Quit); set_window_info(); - screen = SDL_SetVideoMode(cfg.video.display_width, cfg.video.display_height, 16, - SDL_SWSURFACE | ((cfg.video.fullscreen)?SDL_FULLSCREEN:0)); - if(!screen) { - alert("Failed to initialize SDL"); - goto _end; - } - backbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256, 223, 16, 0xf800, 0x07e0, 0x001f, 0x0000); + screen = SDL_SetVideoMode(config::video.display_width, config::video.display_height, 16, + SDL_SWSURFACE | ((config::video.fullscreen)?SDL_FULLSCREEN:0)); + if(!screen) { alert("Failed to initialize SDL"); goto _end; } + backbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 512, 448, 16, 0xf800, 0x07e0, 0x001f, 0x0000); + if(!backbuffer) { alert("Failed to initialize SDL"); goto _end; } SDL_WM_SetCaption(BSNES_TITLE, 0); #ifdef _WIN32_ @@ -146,18 +144,21 @@ SDL_Event event; #endif center_window(); - update_color_lookup_table(); + snes->power(); bsnes->set_status(bSNES::RUN); int cursor_status; while(1) { - bsnes->snes_run(); + bsnes->run(); while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_KEYUP: switch(event.key.keysym.sym) { case SDLK_ESCAPE: goto _end; + case SDLK_BACKSPACE: + snes->capture_screenshot(); + break; case SDLK_F10: //toggle cursor display cursor_status = (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)?SDL_DISABLE:SDL_ENABLE; SDL_ShowCursor(cursor_status); @@ -174,7 +175,7 @@ int cursor_status; } _end: - cfg.save("bsnes_sdl.cfg"); + config_file.save("bsnes_sdl.cfg"); term_snes(); return 0; diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index d86de697..1ed322fb 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -13,5 +13,5 @@ SDL_Surface *screen, *backbuffer; struct { -SDL_Rect rs, rd; -}screen_info; + SDL_Rect rs, rd; +} screen_info; diff --git a/src/sdl/sdlrun.bat b/src/sdl/sdlrun.bat index 2aaf95ea..d397c2f2 100644 --- a/src/sdl/sdlrun.bat +++ b/src/sdl/sdlrun.bat @@ -1 +1 @@ -c:\root\bsnes_g2\bsnes_sdl.exe c:\root\bsnes_testrom\zelda_us.smc \ No newline at end of file +bsnes_sdl c:\root\bsnes_testrom\zelda_us.smc \ No newline at end of file diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index d80cffe8..b9564c7e 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -1,24 +1,48 @@ #include "../base.h" +#include "snes_video.cpp" +#include "snes_audio.cpp" +#include "snes_input.cpp" + void SNES::run() { -uint32 cycles; +uint32 cycles, r; if(apusync.cycles < 0) { cpu->run(); apusync.cycles += apusync.apu_multbl[cpu->cycles_executed()]; } else { apu->run(); - apusync.cycles -= apusync.cpu_multbl[apu->cycles_executed()]; + cycles = apu->cycles_executed(); + apusync.dsp += cycles; + apusync.cycles -= apusync.cpu_multbl[cycles]; + + //1024000(SPC700) / 32000(DSP) = 32spc/dsp ticks + //24576000(Sound clock crystal) / 32000(DSP) = 768crystal/dsp ticks + while(apusync.dsp >= 768) { + apusync.dsp -= 768; + audio_update(dsp->run()); + } } } void SNES::init() { srtc = new SRTC(); sdd1 = new SDD1(); + + srtc->init(); + sdd1->init(); + + video_init(); + audio_init(); +} + +void SNES::term() { + audio_term(); } void SNES::power() { cpu->power(); apu->power(); + dsp->power(); ppu->power(); mem_bus->power(); @@ -30,16 +54,19 @@ int i; for(i=0x2100;i<=0x213f;i++)mem_bus->set_mmio_mapper(i, ppu->mmio); for(i=0x2140;i<=0x217f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); for(i=0x2180;i<=0x2183;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); -//S-RTC - mem_bus->set_mmio_mapper(0x2800, srtc->mmio); - mem_bus->set_mmio_mapper(0x2801, srtc->mmio); //input +mem_bus->set_mmio_mapper(0x4000, cpu->mmio); //test -- remove this mem_bus->set_mmio_mapper(0x4016, cpu->mmio); mem_bus->set_mmio_mapper(0x4017, cpu->mmio); for(i=0x4200;i<=0x421f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); for(i=0x4300;i<=0x437f;i++)mem_bus->set_mmio_mapper(i, cpu->mmio); -//S-DD1 - for(i=0x4800;i<=0x4807;i++)mem_bus->set_mmio_mapper(i, sdd1->mmio); + + srtc->enable(); + sdd1->enable(); + + memset(video.data, 0, 512 * 448 * sizeof(uint32)); + memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16)); + video_update(); } void SNES::reset() { @@ -47,13 +74,30 @@ void SNES::reset() { cpu->reset(); apu->reset(); + dsp->reset(); ppu->reset(); mem_bus->reset(); srtc->reset(); sdd1->reset(); + + memset(video.data, 0, 512 * 448 * sizeof(uint32)); + memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16)); + video_update(); } +void SNES::frame() { + video_update(); +} + +void SNES::scanline() { + video_scanline(); +} + +/**************** + *** PAL/NTSC *** + ****************/ + void SNES::set_region(uint8 new_region) { if(new_region == NTSC) { snes_region = NTSC; @@ -68,6 +112,10 @@ void SNES::set_region(uint8 new_region) { uint8 SNES::region() { return snes_region; } +/************** + *** Timing *** + **************/ + void SNES::update_timing() { apusync.cycles = 0; if(snes_region == NTSC) { @@ -87,6 +135,7 @@ int i; /*************************** *** Debugging functions *** ***************************/ + void SNES::notify(uint32 message, uint32 param1, uint32 param2) {} void SNES::debugger_enable() { @@ -102,8 +151,10 @@ bool SNES::debugger_enabled() { } SNES::SNES() { - is_debugger_enabled = true; + is_debugger_enabled = false; snes_region = NTSC; update_timing(); + + dsp_buffer.data = 0; } diff --git a/src/snes/snes.h b/src/snes/snes.h index a5e1699f..4524c304 100644 --- a/src/snes/snes.h +++ b/src/snes/snes.h @@ -6,10 +6,12 @@ uint8 snes_region; //APU synchronization struct { -int32 cpu_freq, apu_freq; -int32 cpu_multbl[1024], apu_multbl[1024]; -int32 cycles; -}apusync; + int32 cpu_freq, apu_freq; + int32 cpu_multbl[1024], apu_multbl[1024]; + int32 cycles; + + int32 dsp; +} apusync; void update_timing(); @@ -17,29 +19,22 @@ public: enum { NTSC = 0, PAL = 1 }; //system functions - void run(); - virtual void render_frame() = 0; - virtual void init(); - virtual void power(); - virtual void reset(); - virtual uint8 region(); - virtual void set_region(uint8 new_region); + virtual void run(); + virtual void init(); + virtual void term(); + virtual void power(); + virtual void reset(); -//input functions -enum { - DEV_JOYPAD1 = 0, - DEV_JOYPAD2 = 1 -}; -enum { - JOYPAD_B = 0, JOYPAD_Y = 1, - JOYPAD_SELECT = 2, JOYPAD_START = 3, - JOYPAD_UP = 4, JOYPAD_DOWN = 5, - JOYPAD_LEFT = 6, JOYPAD_RIGHT = 7, - JOYPAD_A = 8, JOYPAD_X = 9, - JOYPAD_L = 10, JOYPAD_R = 11 -}; - virtual void poll_input() = 0; - virtual bool get_input_status(uint8 device, uint8 button) = 0; + virtual void frame(); + virtual void scanline(); + +//PAL/NTSC + uint8 region(); + void set_region(uint8 new_region); + +#include "snes_video.h" +#include "snes_audio.h" +#include "snes_input.h" //debugging functions enum { @@ -53,10 +48,12 @@ enum { OAM_READ, OAM_WRITE, CGRAM_READ, CGRAM_WRITE, }; - virtual void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); virtual void debugger_enable(); virtual void debugger_disable(); virtual bool debugger_enabled(); +//message functions + virtual void notify(uint32 message, uint32 param1 = 0, uint32 param2 = 0); + SNES(); }; diff --git a/src/snes/snes_audio.cpp b/src/snes/snes_audio.cpp new file mode 100644 index 00000000..5f10e603 --- /dev/null +++ b/src/snes/snes_audio.cpp @@ -0,0 +1,145 @@ +void SNES::set_playback_buffer_size(uint32 buffer_size) { + if(dsp_buffer.data) { + free(dsp_buffer.data); + dsp_buffer.data = 0; + } + +//* 2 is for left/right channel data + dsp_buffer.data = (uint16*)malloc(buffer_size * sizeof(uint16) * 2); + memset(dsp_buffer.data, 0, buffer_size * sizeof(uint16) * 2); + + dsp_buffer.size = buffer_size; + dsp_buffer.pos = 0; +} + +uint32 SNES::get_playback_buffer_pos() { + return dsp_buffer.pos; +} + +uint16 *SNES::get_playback_buffer() { + return dsp_buffer.data; +} + +void SNES::audio_update(uint32 data) { + if(pcmfp) { + fputc(data, pcmfp); + fputc(data >> 8, pcmfp); + fputc(data >> 16, pcmfp); + fputc(data >> 24, pcmfp); + } + + if((bool)config::snes.mute == true)data = 0x0000; + + dsp_buffer.data[dsp_buffer.pos++] = (data) & 0xffff; + dsp_buffer.data[dsp_buffer.pos++] = (data >> 16) & 0xffff; + dsp_buffer.pos %= dsp_buffer.size; + + sound_run(); +} + +void SNES::log_audio_enable(const char *fn) { +char tfn[256]; +int i; + if(pcmfp)log_audio_disable(); + + if(!fn) { + for(i=0;i<=999;i++) { + sprintf(tfn, "audio%0.3d.wav", i); + pcmfp = fopen(tfn, "rb"); + if(!pcmfp)break; + fclose(pcmfp); + pcmfp = 0; + } + if(i >= 1000)return; + } else { + strcpy(tfn, fn); + } + + pcmfp = fopen(tfn, "wb"); + if(!pcmfp)return; + +//header + fwrite("RIFF", 1, 4, pcmfp); +//file size + fputc(0, pcmfp); + fputc(0, pcmfp); + fputc(0, pcmfp); + fputc(0, pcmfp); +//format + fwrite("WAVE", 1, 4, pcmfp); + fwrite("fmt ", 1, 4, pcmfp); +//fmt size + fputc(0x12, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); +//fmt type (PCM) + fputc(1, pcmfp); + fputc(0, pcmfp); +//channels + fputc(2, pcmfp); + fputc(0, pcmfp); +//sample rate (32000hz) + fputc(0x00, pcmfp); + fputc(0x7d, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); +//byte rate (32000 * 2 * (16 / 8) + fputc(0x00, pcmfp); + fputc(0xf4, pcmfp); + fputc(0x01, pcmfp); + fputc(0x00, pcmfp); +//block align (bytes per sample) (4) + fputc(4, pcmfp); + fputc(0, pcmfp); +//??? + fputc(0x10, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); + fwrite("fact", 1, 4, pcmfp); + fputc(0x04, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); + fputc(0x00, pcmfp); + fputc(0x0b, pcmfp); + fputc(0xf4, pcmfp); + fputc(0x01, pcmfp); + fputc(0x00, pcmfp); +//data + fwrite("data", 1, 4, pcmfp); +//data size + fputc(0, pcmfp); + fputc(0, pcmfp); + fputc(0, pcmfp); + fputc(0, pcmfp); +} + +void SNES::log_audio_disable() { + if(pcmfp) { + int fsize, t; + fseek(pcmfp, 0, SEEK_END); + fsize = ftell(pcmfp); + fseek(pcmfp, 4, SEEK_SET); + t = fsize - 2; + fputc(t, pcmfp); + fputc(t >> 8, pcmfp); + fputc(t >> 16, pcmfp); + fputc(t >> 24, pcmfp); + fseek(pcmfp, 0x36, SEEK_SET); + t = fsize - 0x3a; + fputc(t, pcmfp); + fputc(t >> 8, pcmfp); + fputc(t >> 16, pcmfp); + fputc(t >> 24, pcmfp); + fclose(pcmfp); + pcmfp = 0; + } +} + +void SNES::audio_init() { +} + +void SNES::audio_term() { + log_audio_disable(); +} diff --git a/src/snes/snes_audio.h b/src/snes/snes_audio.h new file mode 100644 index 00000000..4ba04c88 --- /dev/null +++ b/src/snes/snes_audio.h @@ -0,0 +1,22 @@ +struct { + uint16 *data; + uint32 size, pos; +} dsp_buffer; + +FILE *pcmfp; + +//buffer_size is in samples + void set_playback_buffer_size(uint32 buffer_size); + uint32 get_playback_buffer_pos(); + uint16 *get_playback_buffer(); + +//if a filename is not specified, one will be generated +//automatically ("audio%0.3d.wav") + void log_audio_enable(const char *fn = 0); + void log_audio_disable(); + + void audio_update(uint32 data); + void audio_init(); + void audio_term(); + + virtual void sound_run() = 0; diff --git a/src/snes/snes_input.cpp b/src/snes/snes_input.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/snes/snes_input.h b/src/snes/snes_input.h new file mode 100644 index 00000000..73fe859f --- /dev/null +++ b/src/snes/snes_input.h @@ -0,0 +1,21 @@ +enum { + DEV_JOYPAD1 = 0, + DEV_JOYPAD2 = 1 +}; + +enum { + JOYPAD_B = 0, JOYPAD_Y = 1, + JOYPAD_SELECT = 2, JOYPAD_START = 3, + JOYPAD_UP = 4, JOYPAD_DOWN = 5, + JOYPAD_LEFT = 6, JOYPAD_RIGHT = 7, + JOYPAD_A = 8, JOYPAD_X = 9, + JOYPAD_L = 10, JOYPAD_R = 11 +}; + +//The CPU calls poll_input() when the main interface should check the +//status of all joypad buttons and cache the results... + virtual void poll_input() = 0; + +//...and then the CPU calls get_input_status() whenever it needs one +//of the cached button values to be returned for emulation purposes. + virtual bool get_input_status(uint8 device, uint8 button) = 0; diff --git a/src/snes/snes_video.cpp b/src/snes/snes_video.cpp new file mode 100644 index 00000000..b273bf6a --- /dev/null +++ b/src/snes/snes_video.cpp @@ -0,0 +1,385 @@ +#include "snes_video_ex.cpp" + +const uint8 SNES::color_curve_table[32] = { + 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, + 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, + 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, + 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff +}; + +void SNES::update_color_lookup_table() { +int i, l, r, g, b; +double lr = 0.2126, lg = 0.7152, lb = 0.0722; //luminance + switch(video.depth) { + case 15: //rgb565 + for(i=0;i<32768;i++) { + r = (i ) & 31; + g = (i >> 5) & 31; + b = (i >> 10) & 31; + + if((bool)config::snes.video_color_curve == true) { + r = color_curve_table[r] >> 3; + g = color_curve_table[g] >> 3; + b = color_curve_table[b] >> 3; + } + + if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) { + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + + l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb); + if(l < 0)l = 0; + if(l > 255)l = 255; + r = g = b = l; + + r >>= 3; + g >>= 3; + b >>= 3; + } else if((int)config::snes.video_color_adjust_mode == VCA_VGA) { + //rgb555->rgb332 + r >>= 2; + g >>= 2; + b >>= 3; + + r = (r << 2) | (r >> 1); + g = (g << 2) | (g >> 1); + b = (b << 3) | (b << 1) | (b >> 1); + } else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) { + //rgb555->rgb333 + r >>= 2; + g >>= 2; + b >>= 2; + + r = (r << 2) | (r >> 1); + g = (g << 2) | (g >> 1); + b = (b << 2) | (b >> 1); + } + + color_lookup_table[i] = (r << 10) | (g << 5) | (b); + } + break; + case 16: //rgb565 + for(i=0;i<32768;i++) { + r = (i ) & 31; + g = (i >> 5) & 31; + b = (i >> 10) & 31; + + if((bool)config::snes.video_color_curve == true) { + r = color_curve_table[r] >> 3; + g = color_curve_table[g] >> 2; + b = color_curve_table[b] >> 3; + } else { + g = (g << 1) | (g >> 4); + } + + if((int)config::snes.video_color_adjust_mode == VCA_GRAYSCALE) { + r = (r << 3) | (r >> 2); + g = (g << 2) | (g >> 4); + b = (b << 3) | (b >> 2); + + l = int((double)r * lr) + ((double)g * lg) + ((double)b * lb); + if(l < 0)l = 0; + if(l > 255)l = 255; + r = g = b = l; + + r >>= 3; + g >>= 2; + b >>= 3; + } else if((int)config::snes.video_color_adjust_mode == VCA_VGA) { + //rgb565->rgb332 + r >>= 2; + g >>= 3; + b >>= 3; + + r = (r << 2) | (r >> 1); + g = (g << 3) | (g); + b = (b << 3) | (b << 1) | (b >> 1); + } else if((int)config::snes.video_color_adjust_mode == VCA_GENESIS) { + //rgb565->rgb333 + r >>= 2; + g >>= 3; + b >>= 2; + + r = (r << 2) | (r >> 1); + g = (g << 3) | (g); + b = (b << 2) | (b >> 1); + } + + color_lookup_table[i] = (r << 11) | (g << 5) | (b); + } + break; + default: + alert("Error: SNES::update_color_lookup_table() -- color depth %d not supported", video.depth); + break; + } +} + +void SNES::set_video_format(uint8 mode, uint8 depth) { +//only make changes at the start of a new frame + video.format_changed = true; + + video_changed.mode = mode; + video_changed.depth = depth; +} + +//internal function called at the start of the frame +//after SNES::set_video_format() is called +void SNES::update_video_format() { + video.mode = video_changed.mode; + video.depth = video_changed.depth; + + update_color_lookup_table(); + video.format_changed = false; +} + +void SNES::get_video_info(video_info *info) { + info->data = video.data; + info->mode = video.mode; + + switch(video.mode) { + case VM_256x224: + info->width = 256; + info->height = 224; + break; + case VM_512x224: + info->width = 512; + info->height = 224; + break; + case VM_256x448: + info->width = 256; + info->height = 448; + break; + case VM_512x448: + info->width = 512; + info->height = 448; + break; + case VM_VARIABLE: + if(video.frame_hires == false) { + info->width = 256; + } else { + info->width = 512; + } + + if(video.frame_interlace == false) { + info->height = 224; + } else { + info->height = 448; + } + + break; + } +} + +void SNES::video_update_256x224(uint16 *src) { +int x, y; +uint16 *dest; + dest = video.data; + + for(y=0;y<224;y++) { + if(video_frame[y].hires == false) { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*src++]; + } + src += 768; + } else { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*src]; + src += 2; + } + src += 512; + } + } +} + +void SNES::video_update_512x224(uint16 *src) { +int x, y; +uint16 *dest; + dest = video.data; + + for(y=0;y<224;y++) { + if(video_frame[y].hires == false) { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*src]; + *dest++ = color_lookup_table[*src++]; + } + src += 768; + } else { + for(x=0;x<512;x++) { + *dest++ = color_lookup_table[*src++]; + } + src += 512; + } + } +} + +void SNES::video_update_256x448(uint16 *src) { +int x, y; +uint16 *dest; +bool field = !cpu->interlace_field(); + dest = video.data; + + for(y=0;y<224;y++) { + if(video_frame[y].interlace == false) { + if(video_frame[y].hires == false) { + for(x=0;x<512;x++) { + *dest++ = color_lookup_table[*(src + (uint8)x)]; + } + src += 1024; + } else { + for(x=0;x<512;x++) { + *dest++ = color_lookup_table[*(src + ((x & 255) << 1))]; + } + src += 1024; + } + } else { + if(field) { + dest += 256; + src += 512; + } + + if(video_frame[y].hires == false) { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*src++]; + } + src += 256; + } else { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*src]; + src += 2; + } + } + + if(!field) { + dest += 256; + src += 512; + } + } + } +} + +void SNES::video_update_512x448(uint16 *src) { +int x, y; +uint16 *dest; +bool field = !cpu->interlace_field(); + dest = video.data; + + for(y=0;y<224;y++) { + if(video_frame[y].interlace == false) { + if(video_frame[y].hires == false) { + for(x=0;x<512;x++) { + *dest++ = color_lookup_table[*(src + (uint8)x)]; + *dest++ = color_lookup_table[*(src + (uint8)x)]; + } + src += 1024; + } else { + for(x=0;x<1024;x++) { + *dest++ = color_lookup_table[*(src + (x & 511))]; + } + src += 1024; + } + } else { + if(field) { + dest += 512; + src += 512; + } + + if(video_frame[y].hires == false) { + for(x=0;x<256;x++) { + *dest++ = color_lookup_table[*(src + x)]; + *dest++ = color_lookup_table[*(src + x)]; + } + src += 512; + } else { + for(x=0;x<512;x++) { + *dest++ = color_lookup_table[*src++]; + } + } + + if(!field) { + dest += 512; + src += 512; + } + } + } +} + +void SNES::video_update() { + if(!ppu->render_frame())return; + + if(video.format_changed == true) { + update_video_format(); + } + +uint16 *src = (uint16*)video.ppu_data + ((int(cpu->overscan()) << 3) * 1024); + switch(video.mode) { + case VM_256x224:video_update_256x224(src);break; + case VM_512x224:video_update_512x224(src);break; + case VM_256x448:video_update_256x448(src);break; + case VM_512x448:video_update_512x448(src);break; + case VM_VARIABLE: + switch(int(video.frame_hires) | (int(video.frame_interlace) << 1)) { + case 0:video_update_256x224(src);break; + case 1:video_update_512x224(src);break; + case 2:video_update_256x448(src);break; + case 3:video_update_512x448(src);break; + } + break; + } + +//SNES::capture_screenshot() was called by emulation interface + if(flag_output_screenshot == true) { + output_screenshot(); + flag_output_screenshot = false; + } + + video_run(); + + video.frame_hires = false; + video.frame_interlace = false; +} + +void SNES::video_scanline() { +int y = cpu->vcounter(); +int o = int(cpu->overscan()) << 3; + if(y <= (0 + o) || y >= (224 + o))return; + y -= o; + +PPU::scanline_info si; + ppu->get_scanline_info(&si); + + video_frame[y].hires = si.hires; + video_frame[y].interlace = si.interlace; + + video.frame_hires |= si.hires; + video.frame_interlace |= si.interlace; +} + +uint16 *SNES::get_ppu_output_handle() { + return (uint16*)(video.ppu_data + + (cpu->vcounter() * 1024) + + ((cpu->interlace() && cpu->interlace_field())?512:0)); +} + +void SNES::video_init() { +int i, c; + video.format_changed = false; + + video.data = (uint16*)malloc(512 * 448 * sizeof(uint32)); + video.ppu_data = (uint16*)malloc(512 * 480 * sizeof(uint16)); + memset(video.data, 0, 512 * 448 * sizeof(uint32)); + memset(video.ppu_data, 0, 512 * 480 * sizeof(uint16)); + + for(i=0;i<224;i++) { + video_frame[i].hires = false; + video_frame[i].interlace = false; + } + + video.frame_hires = false; + video.frame_interlace = false; + + set_video_format(VM_VARIABLE, 16); + update_video_format(); + + flag_output_screenshot = false; +} diff --git a/src/snes/snes_video.h b/src/snes/snes_video.h new file mode 100644 index 00000000..df89b25d --- /dev/null +++ b/src/snes/snes_video.h @@ -0,0 +1,72 @@ +enum { + VM_256x224, //video data will be scaled to 256x224 + VM_512x224, //.. + VM_256x448, //.. + VM_512x448, //.. + VM_VARIABLE //video data can be 256x224 - 512x448 +}; + +enum { + VMF_NORMAL = 0, + VMF_HIRES = 1, + VMF_INTERLACE = 2, + VMF_HINTERLACE = 3 +}; + +//video color adjustment +//via config::snes.video_color_adjust_mode +enum { + VCA_NORMAL = 0, + VCA_GRAYSCALE = 1, + VCA_VGA = 2, + VCA_GENESIS = 3 +}; + +static const uint8 color_curve_table[32]; +uint32 color_lookup_table[32768]; + +struct { + uint16 *data, *ppu_data; + uint8 mode; + uint8 depth; + + bool frame_hires, frame_interlace; + + bool format_changed; +} video, video_changed; + +struct { + bool hires, interlace; +} video_frame[224]; + +struct video_info { + uint16 *data; + uint8 mode; + uint32 width, height; +}; + +//public functions + void capture_screenshot(); + void update_color_lookup_table(); + + virtual void set_video_format(uint8 mode, uint8 depth); + virtual void get_video_info(video_info *info); + virtual void video_run() = 0; + +//private functions + uint16 *get_ppu_output_handle(); //used by PPU only +private: +//when a screenshot is requested, wait until the frame +//has finished rendering, so we can tell the image size +bool flag_output_screenshot; + uint16 to_rgb555(uint32 color); + void output_screenshot(); + void update_video_format(); + void video_update_256x224(uint16 *src); + void video_update_512x224(uint16 *src); + void video_update_256x448(uint16 *src); + void video_update_512x448(uint16 *src); + void video_update(); + void video_scanline(); + void video_init(); +public: diff --git a/src/snes/snes_video_ex.cpp b/src/snes/snes_video_ex.cpp new file mode 100644 index 00000000..7a899f72 --- /dev/null +++ b/src/snes/snes_video_ex.cpp @@ -0,0 +1,106 @@ +void SNES::capture_screenshot() { + flag_output_screenshot = true; +} + +//used to convert pixel data to write to rgb555 format +//bitmap image via SNES::output_screenshot() function +uint16 SNES::to_rgb555(uint32 color) { + if(video.depth == 15) { + //rgb555 + return color & 0x7fff; + } + + if(video.depth == 16) { + //rgb565->rgb555 + return ((color >> 1) & 0x7fe0) | (color & 0x001f); + } + + if(video.depth == 24 || video.depth == 32) { + //rgb888->rgb555 + return ((color >> 9) & 0x7c00) | ((color >> 6) & 0x03e0) | ((color >> 3) & 0x001f); + } + +//unsupported color depth + return color; +} + +void SNES::output_screenshot() { +FILE *fp; +char fn[256]; +int i; +//get a file name that doesn't exit... + for(i=0;i<=999;i++) { + sprintf(fn, "image%0.3d.bmp", i); + fp = fopen(fn, "rb"); + if(!fp)break; + fclose(fp); + fp = 0; + } + if(i >= 1000)return; + + fp = fopen(fn, "wb"); + if(!fp)return; + +video_info vi; + get_video_info(&vi); +int width, height; + width = vi.width; + height = (vi.height == 224) ? 223 : 446; +int32 fsize; + fsize = 0x36 + (width * height * sizeof(uint16)); + +//header + fputc('B', fp); + fputc('M', fp); +//file size + fputc(fsize, fp); + fputc(fsize >> 8, fp); + fputc(fsize >> 16, fp); + fputc(fsize >> 24, fp); +//??? + fputc(0, fp); + fputc(0, fp); + fputc(0, fp); + fputc(0, fp); +//start of data + fputc(0x36, fp); + fputc(0x00, fp); + fputc(0x00, fp); + fputc(0x00, fp); +//remaining header size + fputc(0x28, fp); + fputc(0x00, fp); + fputc(0x00, fp); + fputc(0x00, fp); +//width + fputc(width, fp); + fputc(width >> 8, fp); + fputc(width >> 16, fp); + fputc(width >> 24, fp); +//height + fputc(height, fp); + fputc(height >> 8, fp); + fputc(height >> 16, fp); + fputc(height >> 24, fp); +//format + fputc(0x01, fp); + fputc(0x00, fp); +//bpp + fputc(16, fp); + fputc( 0, fp); +//??? + for(i=0;i<24;i++) { fputc(0, fp); } + +int x, y; +uint16 c; + for(y=height;y>=1;y--) { + for(x=0;x> 8, fp); + } + } + + fclose(fp); + fp = 0; +} diff --git a/src/win/Makefile b/src/win/Makefile index eb4de871..c0dc7955 100644 --- a/src/win/Makefile +++ b/src/win/Makefile @@ -6,10 +6,11 @@ OBJS = winmain.obj \ memory.obj bmemory.obj \ cpu.obj bcpu.obj \ apu.obj bapu.obj bapuskip.obj \ + bdsp.obj \ ppu.obj bppu.obj \ snes.obj \ srtc.obj sdd1.obj -LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dxguid.lib +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib dsound.lib dxguid.lib all: $(OBJS) rc /r /fobsnes.res bsnes.rc @@ -21,7 +22,7 @@ clean: ###################### ### win32-specific ### ###################### -winmain.obj: *.cpp *.h +winmain.obj: *.cpp *.h ../config/* $(CC) $(CFLAGS) /c winmain.cpp ################# @@ -62,6 +63,12 @@ bapu.obj: ../apu/bapu/* bapuskip.obj: ../apu/bapuskip/* $(CC) $(CFLAGS) /c ../apu/bapuskip/bapuskip.cpp +########### +### dsp ### +########### +bdsp.obj: ../dsp/bdsp/* + $(CC) $(CFLAGS) /c ../dsp/bdsp/bdsp.cpp + ########### ### ppu ### ########### diff --git a/src/win/bsnes.cpp b/src/win/bsnes.cpp index ee5a828f..9bddbd74 100644 --- a/src/win/bsnes.cpp +++ b/src/win/bsnes.cpp @@ -43,31 +43,31 @@ uint32 bSNES::get_status() { return run_status; } -void bSNES::snes_run() { +void bSNES::run() { if(!rom_image->loaded())return; switch(run_status) { case RUN: while(update_frame == false) { - run(); + SNES::run(); } update_frame = false; return; case STOP: break; case RUNONCE: - run(); + SNES::run(); set_status(STOP); break; case RUNTOSIGNAL: - run(); + SNES::run(); if(w_bp->hit() == true) { set_status(STOP); disassemble_bp_op(); } break; case RUNTOFRAME: - run(); + SNES::run(); if(update_frame == true) { update_frame = false; set_status(STOP); @@ -79,7 +79,7 @@ void bSNES::snes_run() { } return; case RUNTOCPUSTEP: - run(); + SNES::run(); if(status.cpu_ran == true) { set_status(STOP); } else if(w_bp->hit() == true) { @@ -88,7 +88,7 @@ void bSNES::snes_run() { } break; case RUNTOCPUPROCEED: - run(); + SNES::run(); if(cpu->in_opcode() == false && status.cpu_stop_pos == cpu->regs.pc.d) { set_status(STOP); disassemble_cpu_op(); @@ -98,14 +98,14 @@ void bSNES::snes_run() { } break; case RUNTOCPUTRACE: - run(); + SNES::run(); if(status.cpu_trace_pos >= status.cpu_trace_stop) { set_status(STOP); disassemble_cpu_op(); } break; case RUNTOAPUSTEP: - run(); + SNES::run(); if(status.apu_ran == true || w_bp->hit() == true) { set_status(STOP); } @@ -113,28 +113,32 @@ void bSNES::snes_run() { } } -void bSNES::render_frame() { +void bSNES::video_run() { dd_renderer->update(); } +void bSNES::sound_run() { + ds_sound->run(); +} + /*********************** *** Input functions *** ***********************/ void bSNES::poll_input() { //only capture input when main window has focus if(GetForegroundWindow() == w_main->hwnd) { - joypad1.up = KeyDown(cfg.input.joypad1.up); - joypad1.down = KeyDown(cfg.input.joypad1.down); - joypad1.left = KeyDown(cfg.input.joypad1.left); - joypad1.right = KeyDown(cfg.input.joypad1.right); - joypad1.select = KeyDown(cfg.input.joypad1.select); - joypad1.start = KeyDown(cfg.input.joypad1.start); - joypad1.y = KeyDown(cfg.input.joypad1.y); - joypad1.b = KeyDown(cfg.input.joypad1.b); - joypad1.x = KeyDown(cfg.input.joypad1.x); - joypad1.a = KeyDown(cfg.input.joypad1.a); - joypad1.l = KeyDown(cfg.input.joypad1.l); - joypad1.r = KeyDown(cfg.input.joypad1.r); + joypad1.up = KeyDown(config::input.joypad1.up); + joypad1.down = KeyDown(config::input.joypad1.down); + joypad1.left = KeyDown(config::input.joypad1.left); + joypad1.right = KeyDown(config::input.joypad1.right); + joypad1.select = KeyDown(config::input.joypad1.select); + joypad1.start = KeyDown(config::input.joypad1.start); + joypad1.y = KeyDown(config::input.joypad1.y); + joypad1.b = KeyDown(config::input.joypad1.b); + joypad1.x = KeyDown(config::input.joypad1.x); + joypad1.a = KeyDown(config::input.joypad1.a); + joypad1.l = KeyDown(config::input.joypad1.l); + joypad1.r = KeyDown(config::input.joypad1.r); } else { joypad1.up = joypad1.down = joypad1.left = joypad1.right = joypad1.select = joypad1.start = @@ -272,7 +276,6 @@ void bSNES::notify(uint32 message, uint32 param1, uint32 param2) { switch(message) { case RENDER_FRAME: update_frame = true; - render_frame(); break; } diff --git a/src/win/bsnes.h b/src/win/bsnes.h index 049e0400..41165c76 100644 --- a/src/win/bsnes.h +++ b/src/win/bsnes.h @@ -35,10 +35,12 @@ enum { RUNTOAPUSTEP }; enum { DRAM = 0, SPCRAM = 1, VRAM = 2, OAM = 3, CGRAM = 4 }; + void run(); + void video_run(); + void sound_run(); + void set_status(uint32 new_status); uint32 get_status(); - void snes_run(); - void render_frame(); //input functions void poll_input(); diff --git a/src/win/bsnes.lnk b/src/win/bsnes.lnk index e83cb0a0..ce73821e 100644 Binary files a/src/win/bsnes.lnk and b/src/win/bsnes.lnk differ diff --git a/src/win/bsnes.res b/src/win/bsnes.res deleted file mode 100644 index 412d6082..00000000 Binary files a/src/win/bsnes.res and /dev/null differ diff --git a/src/win/config.cpp b/src/win/config.cpp index 9520d449..8f2427ca 100644 --- a/src/win/config.cpp +++ b/src/win/config.cpp @@ -1,52 +1,40 @@ -#define __config_add(name, def, type) add(&name, #name, def, type) +namespace config { -class Config : public config { -public: +struct Video { + static Setting mode, use_vram, vblank; +} video; +Setting Video::mode(&config_file, "video.mode", + "Video mode\n" + " 0 = 256x224w\n" + " 1 = 512x448w\n" + " 2 = 960x720w\n" + " 3 = 640x480f\n" + " 4 = 1024x768f", + 1, Setting::DEC); +Setting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", true, Setting::TRUE_FALSE); +Setting Video::vblank(&config_file, "video.vblank", "Wait for vertical retrace when updating screen", false, Setting::TRUE_FALSE); -struct { - uint32 enabled; -}apu; +struct GUI { + static Setting show_fps; +} gui; +Setting GUI::show_fps(&config_file, "gui.show_fps", "Show framerate in window title", true, Setting::TRUE_FALSE); -struct { - uint32 mode; - uint32 use_vram; - uint32 color_curve; - uint32 vblank; -}video; +struct Input { + struct Joypad { + static Setting up, down, left, right, a, b, x, y, l, r, select, start; + } joypad1; +} input; +Setting Input::Joypad::up (&config_file, "input.joypad1.up", "Joypad1 up", VK_UP, Setting::HEX); +Setting Input::Joypad::down (&config_file, "input.joypad1.down", "Joypad1 down", VK_DOWN, Setting::HEX); +Setting Input::Joypad::left (&config_file, "input.joypad1.left", "Joypad1 left", VK_LEFT, Setting::HEX); +Setting Input::Joypad::right (&config_file, "input.joypad1.right", "Joypad1 right", VK_RIGHT, Setting::HEX); +Setting Input::Joypad::a (&config_file, "input.joypad1.a", "Joypad1 A", 'X', Setting::HEX); +Setting Input::Joypad::b (&config_file, "input.joypad1.b", "Joypad1 B", 'Z', Setting::HEX); +Setting Input::Joypad::x (&config_file, "input.joypad1.x", "Joypad1 X", 'S', Setting::HEX); +Setting Input::Joypad::y (&config_file, "input.joypad1.y", "Joypad1 Y", 'A', Setting::HEX); +Setting Input::Joypad::l (&config_file, "input.joypad1.l", "Joypad1 L", 'D', Setting::HEX); +Setting Input::Joypad::r (&config_file, "input.joypad1.r", "Joypad1 R", 'C', Setting::HEX); +Setting Input::Joypad::select(&config_file, "input.joypad1.select", "Joypad1 select", VK_SHIFT, Setting::HEX); +Setting Input::Joypad::start (&config_file, "input.joypad1.start", "Joypad1 start", VK_RETURN, Setting::HEX); -struct { - struct { - uint32 up, down, left, right; - uint32 a, b, x, y, l, r; - uint32 select, start; - }joypad1; -}input; - -struct { - uint32 show_fps; -}gui; - - Config() { - __config_add(apu.enabled, true, TRUEFALSE); - - __config_add(video.mode, 1, DEC); - __config_add(video.use_vram, true, TRUEFALSE); - __config_add(video.color_curve, true, ENABLED); - __config_add(video.vblank, false, TRUEFALSE); - - __config_add(input.joypad1.up, VK_UP, HEX); - __config_add(input.joypad1.down, VK_DOWN, HEX); - __config_add(input.joypad1.left, VK_LEFT, HEX); - __config_add(input.joypad1.right, VK_RIGHT, HEX); - __config_add(input.joypad1.a, 'X', HEX); - __config_add(input.joypad1.b, 'Z', HEX); - __config_add(input.joypad1.x, 'S', HEX); - __config_add(input.joypad1.y, 'A', HEX); - __config_add(input.joypad1.l, 'D', HEX); - __config_add(input.joypad1.r, 'C', HEX); - __config_add(input.joypad1.select, VK_SHIFT, HEX); - __config_add(input.joypad1.start, VK_RETURN, HEX); - - __config_add(gui.show_fps, true, TRUEFALSE); - } -}cfg; +}; diff --git a/src/win/dd_renderer.cpp b/src/win/dd_renderer.cpp index 1c2484ec..0e6d88ec 100644 --- a/src/win/dd_renderer.cpp +++ b/src/win/dd_renderer.cpp @@ -4,68 +4,6 @@ DDRenderer::DDRenderer() { lpdds = 0; lpddsb = 0; lpddc = 0; - -int i, c; - for(i=0,c=0;i<16;i++) { - color_curve_table[i] = c; - c = c + i + 1; - } - for(;i<31;i++) { - color_curve_table[i] = c; - c += 8; - } - color_curve_table[i] = 0xff; -} - -void DDRenderer::update_color_lookup_table() { -int i, r, g, b; - lpddsb->GetSurfaceDesc(&ddsd); - color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount; - if(color_depth == 15) { - for(i=0;i<32768;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - if(cfg.video.color_curve) { - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 3; - b = color_curve_table[b] >> 3; - } - color_lookup_table[i] = (r << 10) | (g << 5) | (b); - } - } else if(color_depth == 16) { - for(i=0;i<32768;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - if(cfg.video.color_curve) { - r = color_curve_table[r] >> 3; - g = color_curve_table[g] >> 2; - b = color_curve_table[b] >> 3; - } else { - g = (g << 1) | (g >> 4); - } - color_lookup_table[i] = (r << 11) | (g << 5) | (b); - } - } else if(color_depth == 32) { - for(i=0;i<32768;i++) { - r = (i ) & 31; - g = (i >> 5) & 31; - b = (i >> 10) & 31; - if(cfg.video.color_curve) { - r = color_curve_table[r]; - g = color_curve_table[g]; - b = color_curve_table[b]; - } else { - r = (r << 3) | (r >> 2); - g = (g << 3) | (g >> 2); - b = (b << 3) | (b >> 2); - } - color_lookup_table[i] = (r << 16) | (g << 8) | (b); - } - } else { - alert("Error: Unsupported color depth [%d]", color_depth); - } } void DDRenderer::set_window(HWND hwnd_handle) { hwnd = hwnd_handle; } @@ -76,25 +14,24 @@ void DDRenderer::set_window(HWND hwnd_handle) { hwnd = hwnd_handle; } handles conversion from 16bpp->32bpp in hardware. This only works when both the source and dest buffers are in VRAM, though. - The SNES resolution is 256x224. The top scanline is never drawn, so - 256x223 is used. Hires mode doubles the screen width, and hires+interlace - double the screen height, making the resolution 512x446. + The SNES resolution is 256x224. Hires mode doubles the screen width, and + hires+interlace double the screen height, making the resolution 512x448. There is one more problem, however. On some video cards, when blitting - from video memory from 256x223->512x446, a bilinear filter is applied by + from video memory from 256x224->512x448, a bilinear filter is applied by the video card, and sometimes this filter tries to read past the source video memory to get interpolation data. This results in a line of garble on the right and bottom edges of the screen. Therefore, an additional 4 pixels in each direction is added to the backbuffer. - The backbuffer is thusly 512+4 * 476+4 + The backbuffer is thusly 512+4 * 480+4 */ void DDRenderer::create_backbuffer() { -int color_depth; +int depth; lpdds->GetSurfaceDesc(&ddsd); - color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount; - if(color_depth == 15 || color_depth == 16) { + depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + if(depth == 15 || depth == 16) { goto try_native_backbuffer; } else { - if(cfg.video.use_vram == false) { + if((int)config::video.use_vram == false) { goto try_native_backbuffer; } } @@ -103,14 +40,14 @@ int color_depth; ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; - if(cfg.video.use_vram) { + if(config::video.use_vram) { ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; } else { ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; } ddsd.dwWidth = 512 + 4; - ddsd.dwHeight = 476 + 4; + ddsd.dwHeight = 480 + 4; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; @@ -126,14 +63,14 @@ try_native_backbuffer: ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; - if(cfg.video.use_vram) { + if(config::video.use_vram) { ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; } else { ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; } ddsd.dwWidth = 512 + 4; - ddsd.dwHeight = 476 + 4; + ddsd.dwHeight = 480 + 4; lpdd->CreateSurface(&ddsd, &lpddsb, 0); @@ -147,6 +84,9 @@ DDBLTFX fx; fx.dwSize = sizeof(DDBLTFX); fx.dwFillColor = 0x00000000; lpddsb->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + + lpddsb->GetSurfaceDesc(&ddsd); + color_depth = ddsd.ddpfPixelFormat.dwRGBBitCount; } void DDRenderer::to_windowed() { @@ -166,7 +106,6 @@ void DDRenderer::to_windowed() { lpdds->SetClipper(lpddc); create_backbuffer(); - update_color_lookup_table(); redraw(); } @@ -195,14 +134,13 @@ void DDRenderer::to_fullscreen(int _width, int _height) { lpdds->SetClipper(lpddc); create_backbuffer(); - update_color_lookup_table(); redraw(); } void DDRenderer::set_source_window() { - SetRect(&lpddrc, 0, 0, - (ppu->output->hires == false)?256:512, - (ppu->output->interlace == false)?223:446); +//skip first scanline +int i = (vi.height == 224) ? 1 : 2; + SetRect(&lpddrc, 0, i, vi.width, vi.height); } void DDRenderer::redraw() { @@ -226,7 +164,7 @@ int rx, ry; break; } } - if(cfg.video.vblank) { + if(config::video.vblank) { lpdd->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0); } hr = lpdds->Blt(&rd, lpddsb, &lpddrc, DDBLT_WAIT, 0); @@ -235,7 +173,7 @@ int rx, ry; lpddsb->Restore(); } - if(cfg.gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return; + if((int)config::gui.show_fps == false || bsnes->get_status() == bSNES::STOP)return; uint32 fps; char s[256], t[256]; fps_timer->tick(); @@ -262,24 +200,22 @@ HRESULT hr; if(hr != DD_OK)return; set_source_window(); - if(ppu->output->hires == false) { - if(ppu->output->interlace == false) { - update16_256x224(); - } else { - update16_256x448(); - } - } else { - if(ppu->output->interlace == false) { - update16_512x224(); - } else { - update16_512x448(); - } + + if (vi.width == 256 && vi.height == 224) { + update16_256x224(); + } else if(vi.width == 512 && vi.height == 224) { + update16_512x224(); + } else if(vi.width == 256 && vi.height == 448) { + update16_256x448(); + } else if(vi.width == 512 && vi.height == 448) { + update16_512x448(); } lpddsb->Unlock(0); } -#include "dd_renderer32.cpp" +//#include "dd_renderer32.cpp" +/* void DDRenderer::update32() { HRESULT hr; hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); @@ -302,17 +238,21 @@ HRESULT hr; lpddsb->Unlock(0); } +*/ void DDRenderer::update() { + snes->get_video_info(&vi); + switch(color_depth) { case 15: case 16: update16(); break; case 32: - update32(); + //update32(); break; } + redraw(); } diff --git a/src/win/dd_renderer.h b/src/win/dd_renderer.h index 3a9c8f5d..b98127ec 100644 --- a/src/win/dd_renderer.h +++ b/src/win/dd_renderer.h @@ -1,4 +1,5 @@ #include + class DDRenderer { public: LPDIRECTDRAW lpdd; @@ -9,11 +10,10 @@ DDSURFACEDESC ddsd; DDSCAPS ddscaps; RECT lpddrc; HWND hwnd; -bool fullscreen; -int width, height; //used for fullscreen mode clipping only -uint8 color_depth; -uint8 color_curve_table[32]; -uint32 color_lookup_table[32768]; +bool fullscreen; +int width, height; //used for fullscreen mode clipping only +uint8 color_depth; +SNES::video_info vi; //initialized each frame at start of update() void set_window(HWND hwnd_handle); void create_backbuffer(); void to_windowed(); @@ -21,18 +21,17 @@ uint32 color_lookup_table[32768]; void set_source_window(); void redraw(); inline void update16_256x224(); - inline void update16_256x448(); inline void update16_512x224(); + inline void update16_256x448(); inline void update16_512x448(); void update16(); - inline void update32_256x224(); - inline void update32_256x448(); - inline void update32_512x224(); - inline void update32_512x448(); - void update32(); +//inline void update32_256x224(); +//inline void update32_512x224(); +//inline void update32_256x448(); +//inline void update32_512x448(); +//void update32(); void update(); void destroy(); - void update_color_lookup_table(); DDRenderer(); }; diff --git a/src/win/dd_renderer16.cpp b/src/win/dd_renderer16.cpp index 0f50abb5..6a4fef80 100644 --- a/src/win/dd_renderer16.cpp +++ b/src/win/dd_renderer16.cpp @@ -1,123 +1,79 @@ inline void DDRenderer::update16_256x224() { -uint16 *src; -uint16 *dest; +uint16 *src, *dest; uint32 pitch; int x, y; - src = (uint16*)ppu->output->buffer + (1 << 10); - dest = (uint16*)ddsd.lpSurface; - pitch = (ddsd.lPitch >> 1) - 256; + dest = (uint16*)ddsd.lpSurface; + src = (uint16*)vi.data; -int overscan_adjust = 0; - if(cpu->overscan() == true) { - src += 7 << 10; - overscan_adjust = 7; +#ifdef USE_X86_ASM + pitch = (ddsd.lPitch) - 512; + __asm { + mov edi,dest + mov esi,src + mov edx,224 + ly: + mov ecx,32 + lx: + movsd + movsd + movsd + movsd + loopnz lx + add edi,pitch + dec edx + jnz ly } +#else + pitch = (ddsd.lPitch >> 1); + for(y=0;y<224;y++) { + memcpy(dest, src, 512); + dest += pitch; + src += 256; + } +#endif +} - for(y=1+overscan_adjust;y<224+overscan_adjust;y++) { - x = 256; - while(x--) { - *dest++ = color_lookup_table[*src]; - src += 2; - } +inline void DDRenderer::update16_512x224() { +uint16 *src, *dest; +uint32 pitch; +int x, y; + pitch = (ddsd.lPitch >> 1); + dest = (uint16*)ddsd.lpSurface; + src = (uint16*)vi.data; + + for(y=0;y<224;y++) { + memcpy(dest, src, 1024); dest += pitch; src += 512; } } inline void DDRenderer::update16_256x448() { -uint16 *src; -uint16 *dest; +uint16 *src, *dest; uint32 pitch; int x, y; - src = (uint16*)ppu->output->buffer + (1 << 10); + pitch = (ddsd.lPitch >> 1); dest = (uint16*)ddsd.lpSurface; - pitch = (ddsd.lPitch >> 1) - 256; + src = (uint16*)vi.data; -int overscan_adjust = 0; - if(cpu->overscan() == true) { - src += 7 << 10; - overscan_adjust = 14; - } - - for(y=2+overscan_adjust;y<448+overscan_adjust;y++) { - x = 256; - while(x--) { - *dest++ = color_lookup_table[*src]; - src += 2; - } + for(y=0;y<448;y++) { + memcpy(dest, src, 512); dest += pitch; - if(ppu->output->line[y >> 1].interlace == false) { - src += (y & 1)?512:-512; - } - } -} - -inline void DDRenderer::update16_512x224() { -uint16 *src; -uint16 *dest; -uint32 pitch; -int x, y; - src = (uint16*)ppu->output->buffer + (1 << 10); - dest = (uint16*)ddsd.lpSurface; - pitch = (ddsd.lPitch >> 1) - 512; - -int overscan_adjust = 0; - if(cpu->overscan() == true) { - src += 7 << 10; - overscan_adjust = 7; - } - - for(y=1+overscan_adjust;y<224+overscan_adjust;y++) { - if(ppu->output->line[y].hires == true) { - x = 512; - while(x--) { - *dest++ = color_lookup_table[*src++]; - } - } else { - x = 256; - while(x--) { - *dest++ = color_lookup_table[*src]; - *dest++ = color_lookup_table[*src]; - src += 2; - } - } - dest += pitch; - src += 512; + src += 256; } } inline void DDRenderer::update16_512x448() { -uint16 *src; -uint16 *dest; +uint16 *src, *dest; uint32 pitch; int x, y; - src = (uint16*)ppu->output->buffer + (1 << 10); + pitch = (ddsd.lPitch >> 1); dest = (uint16*)ddsd.lpSurface; - pitch = (ddsd.lPitch >> 1) - 512; + src = (uint16*)vi.data; -int overscan_adjust = 0; - if(cpu->overscan() == true) { - src += 7 << 10; - overscan_adjust = 14; - } - - for(y=2+overscan_adjust;y<448+overscan_adjust;y++) { - if(ppu->output->line[y >> 1].hires == true) { - x = 512; - while(x--) { - *dest++ = color_lookup_table[*src++]; - } - } else { - x = 256; - while(x--) { - *dest++ = color_lookup_table[*src]; - *dest++ = color_lookup_table[*src]; - src += 2; - } - } + for(y=0;y<448;y++) { + memcpy(dest, src, 1024); dest += pitch; - if(ppu->output->line[y >> 1].interlace == false) { - src += (y & 1)?512:-512; - } + src += 512; } } diff --git a/src/win/ds_sound.cpp b/src/win/ds_sound.cpp new file mode 100644 index 00000000..ab6862e8 --- /dev/null +++ b/src/win/ds_sound.cpp @@ -0,0 +1,77 @@ +DSSound::DSSound() { + data.buffer = 0; + data.lpos = data.lsample = data.lbuffer = 0; +} + +void DSSound::run() { + if(snes->get_playback_buffer_pos() != 0)return; + +uint32 pos, status; + +//dsb_b[0]->SetFrequency(22050); + +//do { +// dsb_b[0]->GetStatus(&status); +//} while(status & DSBSTATUS_PLAYING); + + dsb_b[0]->Lock(0, DSP_BUFFER_SIZE * 4, &dslb, &dslbs, 0, 0, 0); + memcpy(dslb, snes->get_playback_buffer(), DSP_BUFFER_SIZE * 4); + dsb_b[0]->Unlock(dslb, dslbs, 0, 0); + + dsb_b[0]->SetCurrentPosition(0); + +//has the buffer stopped (possibly due to running too fast)? + dsb_b[0]->GetStatus(&status); + if(!(status & DSBSTATUS_PLAYING)) { + dsb_b[0]->Play(0, 0, 0); + } +} + +void DSSound::init() { + DirectSoundCreate(0, &ds, 0); + ds->SetCooperativeLevel(w_main->hwnd, DSSCL_PRIORITY); + + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = 0; + ds->CreateSoundBuffer(&dsbd, &dsb_p, 0); + + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 32000; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + dsb_p->SetFormat(&wfx); + + sample_size = (16 / 8) * 2; + buffer_size = DSP_BUFFER_SIZE * sample_size; + buffer_pos = 0; + + dsb_b = new LPDIRECTSOUNDBUFFER[1]; + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS; + dsbd.dwBufferBytes = buffer_size; + dsbd.guid3DAlgorithm = GUID_NULL; + dsbd.lpwfxFormat = &wfx; + ds->CreateSoundBuffer(&dsbd, &dsb_b[0], 0); + ds->CreateSoundBuffer(&dsbd, &dsb_b[1], 0); + dsb_b[0]->SetFrequency(32000); + dsb_b[1]->SetFrequency(32000); + + dsb_b[0]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0); + memset(dslb, 0, buffer_size); + dsb_b[0]->Unlock(dslb, dslbs, 0, 0); + + dsb_b[1]->Lock(0, buffer_size, &dslb, &dslbs, 0, 0, 0); + memset(dslb, 0, buffer_size); + dsb_b[1]->Unlock(dslb, dslbs, 0, 0); + + dsb_b[0]->Play(0, 0, 0); + + buffer_pos = 0; +} diff --git a/src/win/ds_sound.h b/src/win/ds_sound.h new file mode 100644 index 00000000..d580facb --- /dev/null +++ b/src/win/ds_sound.h @@ -0,0 +1,30 @@ +#include + +class DSSound { +public: +LPDIRECTSOUND ds; +LPDIRECTSOUNDBUFFER dsb_p, *dsb_b; +DSBUFFERDESC dsbd; +WAVEFORMATEX wfx; +uint32 sample_size; +uint32 buffer_size; +uint32 buffer_pos; + +void *dslb; +uint32 dslbs; + +public: + void run(); + void init(); + + DSSound(); +}; + +struct { + uint32 *buffer; + uint32 lpos; + uint32 lsample; + bool lbuffer; +} data; + +DSSound *ds_sound; diff --git a/src/win/ui.cpp b/src/win/ui.cpp index 5969c8d2..46ae9c34 100644 --- a/src/win/ui.cpp +++ b/src/win/ui.cpp @@ -2,6 +2,7 @@ HFONT hFont, hMonofont; HBRUSH hbr_backbrush; #include "dd_renderer.cpp" +#include "ds_sound.cpp" #include "uictl_editex.cpp" #include "ui_window.cpp" #include "ui_main.cpp" @@ -38,6 +39,7 @@ void CreateWindows() { void init_ui0() { dd_renderer = new DDRenderer(); + ds_sound = new DSSound(); w_main = new MainWindow(); w_console = new Console(); w_bp = new BreakpointEditor(); @@ -52,9 +54,10 @@ void init_ui1() { SetFocus(w_main->hwnd); dd_renderer->set_window(w_main->hwnd); dd_renderer->to_windowed(); + ds_sound->init(); w_main->show_menu(); - w_main->set_video_mode(cfg.video.mode); + w_main->set_video_mode(config::video.mode); w_main->set_frameskip(0); bsnes->debugger_deactivate(); diff --git a/src/win/ui.h b/src/win/ui.h index 8d207ca4..0ece67b8 100644 --- a/src/win/ui.h +++ b/src/win/ui.h @@ -29,14 +29,19 @@ enum { MENU_SETTINGS_VIDEOMODE_960x720w, MENU_SETTINGS_VIDEOMODE_640x480f, MENU_SETTINGS_VIDEOMODE_1024x768f, + MENU_SETTINGS_COLORADJUST_COLORCURVE, + MENU_SETTINGS_COLORADJUST_NORMAL, + MENU_SETTINGS_COLORADJUST_GRAYSCALE, + MENU_SETTINGS_COLORADJUST_VGA, + MENU_SETTINGS_COLORADJUST_GENESIS, MENU_SETTINGS_USEVRAM, MENU_SETTINGS_VBLANK, - MENU_SETTINGS_COLORCURVE, MENU_SETTINGS_SHOWFPS, - MENU_SETTINGS_APUENABLED, + MENU_SETTINGS_MUTE, MENU_SETTINGS_INPUTCFG_JOYPAD1, MENU_SETTINGS_DEBUGGER, MENU_MISC_SCREENSHOT, + MENU_MISC_LOGAUDIO, MENU_MISC_ABOUT }; diff --git a/src/win/ui_console.cpp b/src/win/ui_console.cpp index dcd1c059..52cf61fc 100644 --- a/src/win/ui_console.cpp +++ b/src/win/ui_console.cpp @@ -284,7 +284,7 @@ static uint8 linecol[4] = { 1, 2, 3 }; cpu->vcounter(), cpu->hcounter(), cpu->hcycles(), cpu->interlace(), cpu->interlace_field(), cpu->overscan()); strcat(s, t); - if(cfg.apu.enabled) { + if(1) { //config::apu.enabled sprintf(t, " -- CPU[$%0.2x,$%0.2x,$%0.2x,$%0.2x]<>APU[$%0.2x,$%0.2x,$%0.2x,$%0.2x]", cpu->port_read(0), cpu->port_read(1), cpu->port_read(2), cpu->port_read(3), apu->port_read(0), apu->port_read(1), apu->port_read(2), apu->port_read(3) diff --git a/src/win/ui_inputconfig.cpp b/src/win/ui_inputconfig.cpp index e5d5a732..258603b6 100644 --- a/src/win/ui_inputconfig.cpp +++ b/src/win/ui_inputconfig.cpp @@ -38,18 +38,18 @@ bool end_config = false; switch(config_type) { case JOYPAD1: switch(config_pos) { - case 0:cfg.input.joypad1.up = key;break; - case 1:cfg.input.joypad1.down = key;break; - case 2:cfg.input.joypad1.left = key;break; - case 3:cfg.input.joypad1.right = key;break; - case 4:cfg.input.joypad1.select = key;break; - case 5:cfg.input.joypad1.start = key;break; - case 6:cfg.input.joypad1.x = key;break; - case 7:cfg.input.joypad1.y = key;break; - case 8:cfg.input.joypad1.a = key;break; - case 9:cfg.input.joypad1.b = key;break; - case 10:cfg.input.joypad1.l = key;break; - case 11:cfg.input.joypad1.r = key;break; + case 0:config::input.joypad1.up = key;break; + case 1:config::input.joypad1.down = key;break; + case 2:config::input.joypad1.left = key;break; + case 3:config::input.joypad1.right = key;break; + case 4:config::input.joypad1.select = key;break; + case 5:config::input.joypad1.start = key;break; + case 6:config::input.joypad1.x = key;break; + case 7:config::input.joypad1.y = key;break; + case 8:config::input.joypad1.a = key;break; + case 9:config::input.joypad1.b = key;break; + case 10:config::input.joypad1.l = key;break; + case 11:config::input.joypad1.r = key;break; case 12:end_config = true;break; } config_pos++; diff --git a/src/win/ui_main.cpp b/src/win/ui_main.cpp index 855670bf..2b2c863b 100644 --- a/src/win/ui_main.cpp +++ b/src/win/ui_main.cpp @@ -95,7 +95,7 @@ void MainWindow::set_video_mode(uint8 mode) { break; } - cfg.video.mode = mode; + config::video.mode = mode; show(); } @@ -107,8 +107,8 @@ char t[4096]; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; - ofn.lpstrFilter = "SNES ROM Images (*.smc;*.swc;*.fig;*.ufo;*.gd3;*.078)\0" - "*.smc;*.swc;*.fig;*.ufo;*.gd3;*.078\0" + ofn.lpstrFilter = "SNES ROM Images (*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078)\0" + "*.smc;*.sfc;*.swc;*.fig;*.ufo;*.gd3;*.078\0" "All Files (*.*)\0" "*.*\0"; ofn.lpstrFile = t; @@ -133,6 +133,7 @@ void MainWindow::menu_unload() { long __stdcall wndproc_main(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { time_t timeout; +int i; switch(msg) { case WM_KEYDOWN: if(wparam == VK_ESCAPE) { @@ -145,7 +146,7 @@ time_t timeout; } break; case WM_COMMAND: -//below code fails becahse it is triggered after snes->poll_input()... +//below code fails because it is triggered after snes->poll_input()... //unsure how to fix this... // timeout = time(NULL); // while(difftime(time(NULL), timeout) < 5) { @@ -201,28 +202,39 @@ time_t timeout; case MENU_SETTINGS_VIDEOMODE_1024x768f: w_main->set_video_mode(VIDEOMODE_1024x768f); break; + case MENU_SETTINGS_COLORADJUST_COLORCURVE: + config::snes.video_color_curve.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, + (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); + break; + case MENU_SETTINGS_COLORADJUST_NORMAL: + case MENU_SETTINGS_COLORADJUST_GRAYSCALE: + case MENU_SETTINGS_COLORADJUST_VGA: + case MENU_SETTINGS_COLORADJUST_GENESIS: + i = LOWORD(wparam) - MENU_SETTINGS_COLORADJUST_NORMAL; + config::snes.video_color_adjust_mode = i; + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); + break; case MENU_SETTINGS_USEVRAM: - cfg.video.use_vram ^= 1; - w_main->set_video_mode(cfg.video.mode); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_USEVRAM, (cfg.video.use_vram)?MF_CHECKED:MF_UNCHECKED); + config::video.use_vram.toggle(); + w_main->set_video_mode(config::video.mode); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); break; case MENU_SETTINGS_VBLANK: - cfg.video.vblank ^= 1; - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_VBLANK, (cfg.video.vblank)?MF_CHECKED:MF_UNCHECKED); - break; - case MENU_SETTINGS_COLORCURVE: - cfg.video.color_curve ^= 1; - dd_renderer->update_color_lookup_table(); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_COLORCURVE, (cfg.video.color_curve)?MF_CHECKED:MF_UNCHECKED); + config::video.vblank.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); break; case MENU_SETTINGS_SHOWFPS: - cfg.gui.show_fps ^= 1; + config::gui.show_fps.toggle(); SetWindowText(w_main->hwnd, BSNES_TITLE); - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_SHOWFPS, (cfg.gui.show_fps)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); break; - case MENU_SETTINGS_APUENABLED: - cfg.apu.enabled ^= 1; - CheckMenuItem(w_main->hmenu, MENU_SETTINGS_APUENABLED, (cfg.apu.enabled)?MF_CHECKED:MF_UNCHECKED); + case MENU_SETTINGS_MUTE: + config::snes.mute.toggle(); + CheckMenuItem(w_main->hmenu, MENU_SETTINGS_MUTE, (config::snes.mute)?MF_CHECKED:MF_UNCHECKED); break; case MENU_SETTINGS_INPUTCFG_JOYPAD1: w_inputconfig->begin_config(InputConfig::JOYPAD1); @@ -236,6 +248,17 @@ time_t timeout; } break; case MENU_MISC_SCREENSHOT: + snes->capture_screenshot(); + break; + case MENU_MISC_LOGAUDIO: + i = CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, 0); + if(i == MF_UNCHECKED) { + CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_CHECKED); + snes->log_audio_enable(); + } else { + CheckMenuItem(w_main->hmenu, MENU_MISC_LOGAUDIO, MF_UNCHECKED); + snes->log_audio_disable(); + } break; case MENU_MISC_ABOUT: w_about->center(); @@ -256,6 +279,7 @@ time_t timeout; void MainWindow::create() { WNDCLASS wc; +int i; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); @@ -303,18 +327,26 @@ HMENU hsubmenu, hbranchmenu; AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Frameskip"); hbranchmenu = CreatePopupMenu(); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_256x224w, "256x224 Windowed [16:15]"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_512x448w, "512x448 Windowed [16:15]"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_960x720w, "960x720 Windowed [4:3]"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_640x480f, "640x480 Fullscreen [16:15]"); - AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_1024x768f, "1024x768 Fullscreen [4:3]"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_256x224w, "256x224 Windowed (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_512x448w, "512x448 Windowed (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_960x720w, "960x720 Windowed (4:3)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_640x480f, "640x480 Fullscreen (16:15)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_1024x768f, "1024x768 Fullscreen (4:3)"); AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Video Mode"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_USEVRAM, "Use &Video Memory Surface"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_VBLANK, "&Wait for Vertical Retrace"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_COLORCURVE, "Use &Color Curve"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_SHOWFPS, "&Show FPS"); - AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_APUENABLED, "&Enable APU"); + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_COLORCURVE, "Use &Color Curve"); + AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_NORMAL, "Normal (RGB555)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GRAYSCALE, "Grayscale Mode (L5)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_VGA, "VGA Mode (RGB332)"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_COLORADJUST_GENESIS, "Genesis Mode (RGB333)"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Color Adjust"); + + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_USEVRAM, "Use &Video Memory Surface"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_VBLANK, "&Wait For Vertical Retrace"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_SHOWFPS, "&Show FPS"); + AppendMenu(hsubmenu, MF_STRING, MENU_SETTINGS_MUTE, "&Mute Sound Output"); hbranchmenu = CreatePopupMenu(); AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_INPUTCFG_JOYPAD1, "Joypad 1"); @@ -325,14 +357,20 @@ HMENU hsubmenu, hbranchmenu; AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Settings"); hsubmenu = CreatePopupMenu(); - AppendMenu(hsubmenu, MF_STRING | MF_GRAYED, MENU_MISC_SCREENSHOT, "&Capture Screenshot"); + AppendMenu(hsubmenu, MF_STRING, MENU_MISC_SCREENSHOT, "&Capture Screenshot"); + AppendMenu(hsubmenu, MF_STRING, MENU_MISC_LOGAUDIO, "&Log Audio Data"); AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); AppendMenu(hsubmenu, MF_STRING, MENU_MISC_ABOUT, "&About..."); AppendMenu(hmenu, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Misc"); - CheckMenuItem(hmenu, MENU_SETTINGS_USEVRAM, (cfg.video.use_vram)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_VBLANK, (cfg.video.vblank)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_COLORCURVE, (cfg.video.color_curve)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_SHOWFPS, (cfg.gui.show_fps)?MF_CHECKED:MF_UNCHECKED); - CheckMenuItem(hmenu, MENU_SETTINGS_APUENABLED, (cfg.apu.enabled)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_USEVRAM, (config::video.use_vram)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_VBLANK, (config::video.vblank)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_COLORCURVE, (config::snes.video_color_curve)?MF_CHECKED:MF_UNCHECKED); + i = config::snes.video_color_adjust_mode; + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_NORMAL, (i == 0)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GRAYSCALE, (i == 1)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_VGA, (i == 2)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_COLORADJUST_GENESIS, (i == 3)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_SHOWFPS, (config::gui.show_fps)?MF_CHECKED:MF_UNCHECKED); + CheckMenuItem(hmenu, MENU_SETTINGS_MUTE, (config::snes.mute) ?MF_CHECKED:MF_UNCHECKED); } diff --git a/src/win/winmain.cpp b/src/win/winmain.cpp index 0d50ac7d..9ae050a4 100644 --- a/src/win/winmain.cpp +++ b/src/win/winmain.cpp @@ -1,14 +1,19 @@ #define INTERFACE_MAIN -#define BSNES_VERSION "0.011" -#define BSNES_TITLE "bsnes v" BSNES_VERSION + +//requires visual c++ +#define USE_X86_ASM + #include "winmain.h" #include "../base.h" #include "config.cpp" +#define DSP_BUFFER_SIZE 8000 + #include "bsnes.h" #include "ui.h" #include "dd_renderer.h" +#include "ds_sound.h" #include "timer.cpp" fpstimer *fps_timer; @@ -20,28 +25,47 @@ fpstimer *fps_timer; #include "ui.cpp" void init_snes() { -//clock = new bClock(); mem_bus = new bMemBus(); cpu = new bCPU(); - if(cfg.apu.enabled) { - apu = new bAPU(); - } else { - apu = new bAPUSkip(); - } +cpu->cpu_version = 1; + apu = new bAPU(); + dsp = new bDSP(); ppu = new bPPU(); snes = new bSNES(); bsnes = static_cast(snes); snes->init(); + snes->set_playback_buffer_size(DSP_BUFFER_SIZE); } void term_snes() { -//if(clock) { delete(clock); clock = 0; } - if(mem_bus) { delete(mem_bus); mem_bus = 0; } - if(cpu) { delete(cpu); cpu = 0; } - if(apu) { delete(apu); apu = 0; } - if(ppu) { delete(ppu); ppu = 0; } - if(snes) { delete(snes); snes = 0; } + snes->term(); + +//static casting is neccesary to call derived class deconstructor... + if(mem_bus) { + delete(static_cast(mem_bus)); + mem_bus = 0; + } + if(cpu) { + delete(static_cast(cpu)); + cpu = 0; + } + if(apu) { + delete(static_cast(apu)); + apu = 0; + } + if(dsp) { + delete(static_cast(dsp)); + dsp = 0; + } + if(ppu) { + delete(static_cast(ppu)); + ppu = 0; + } + if(snes) { + delete(static_cast(snes)); + snes = 0; + } } void get_config_fn(string &str) { @@ -56,7 +80,7 @@ int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL MSG msg; string cfg_fn; get_config_fn(cfg_fn); - cfg.load(cfg_fn); + config_file.load(cfg_fn); meminit(); fps_timer = new fpstimer(); fps_timer->start(); @@ -77,15 +101,16 @@ char **argv = __argv; } while(1) { - if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { - if(msg.message == WM_QUIT)break; + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(msg.message == WM_QUIT)goto _end; TranslateMessage(&msg); DispatchMessage(&msg); } - bsnes->snes_run(); + bsnes->run(); } - cfg.save(cfg_fn); +_end: + config_file.save(cfg_fn); delete(rom_image); term_snes(); memterm();