diff --git a/license.txt b/license.txt index 5cdb0439..4b2a6214 100644 --- a/license.txt +++ b/license.txt @@ -53,11 +53,11 @@ information in the header. The lack of such a header indicates said file falls under the bsnes license. HQ2x Filter, author: MaxST, license: LGPL -JMA, author: NSRT Team, license: GPL * +JMA, author: NSRT Team, license: GPL (*) libco, author: byuu, license: public domain libui, author: byuu, license: public domain NTSC Filter, author: blargg, license: LGPL S-DD1, author: Andreas Naive, license: public domain zlib, license: zlib license -* bsnes has received an exemption from the copyright holder to use this work. +(*) bsnes has received an exemption from the copyright holder to use this work. diff --git a/readme.txt b/readme.txt index 8c4a6127..9dad17e9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ bsnes -Version 0.021 +Version 0.022 Author: byuu diff --git a/src/base.h b/src/base.h index 0ff5a37d..1e28caf6 100644 --- a/src/base.h +++ b/src/base.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.021" +#define BSNES_VERSION "0.022" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define MEMCORE bMemBus diff --git a/src/config/config.cpp b/src/config/config.cpp index 9ba34a87..85dd2619 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -89,6 +89,21 @@ IntegerSetting PPU::Hack::obj_cache(&config_file, "ppu.hack.obj_cache", "This is technically closer to the actual operation of the SNES,\n" "but can cause problems in some games if enabled", IntegerSetting::Boolean, false); +IntegerSetting PPU::Hack::oam_address_invalidation(&config_file, "ppu.hack.oam_address_invalidation", + "OAM access address changes during active display, as the S-PPU reads\n" + "data to render the display. Thusly, the address retrieved when accessing\n" + "OAM during active display is unpredictable. Unfortunately, the exact\n" + "algorithm for this is completely unknown at this time. It is more hardware\n" + "accurate to enable this setting, but one must *not* rely on the actual\n" + "address to match hardware under emulation.", + IntegerSetting::Boolean, true); +IntegerSetting PPU::Hack::cgram_address_invalidation(&config_file, "ppu.hack.cgram_address_invalidation", + "CGRAM access address changes during active display (excluding hblank), as\n" + "the S-PPU reads data to render the display. Thusly, as with OAM, the access\n" + "address is unpredictable. Again, enabling this setting is more hardware\n" + "accurate, but one must *not* rely on the actual address to match hardware\n" + "under emulation.", + IntegerSetting::Boolean, true); IntegerSetting PPU::opt_enable(0, "ppu.opt_enable", "Enable offset-per-tile effects", IntegerSetting::Boolean, true); IntegerSetting PPU::bg1_pri0_enable(0, "ppu.bg1_pri0_enable", "Enable BG1 Priority 0", IntegerSetting::Boolean, true); diff --git a/src/config/config.h b/src/config/config.h index 6cc53222..5840e3fa 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -30,6 +30,8 @@ extern struct PPU { struct Hack { static IntegerSetting render_scanline_position; static IntegerSetting obj_cache; + static IntegerSetting oam_address_invalidation; + static IntegerSetting cgram_address_invalidation; } hack; static IntegerSetting opt_enable; diff --git a/src/lib/libconfig.h b/src/lib/libconfig.h index 6c281836..a8789592 100644 --- a/src/lib/libconfig.h +++ b/src/lib/libconfig.h @@ -1,5 +1,5 @@ /* - libconfig : version 0.14 ~byuu (2007-06-10) + libconfig : version 0.14 ~byuu (2007-06-12) license: public domain */ @@ -103,7 +103,7 @@ string data; bool operator==(const char *x) { return !strcmp(data, x); } bool operator!=(const char *x) { return strcmp(data, x); } - StringSetting(Config *parent, const char *r_name, const char *r_desc, char *r_data) { + StringSetting(Config *parent, const char *r_name, const char *r_desc, const char *r_data) { type = Setting::String; name = strdup(r_name); desc = strdup(r_desc); diff --git a/src/ppu/bppu/bppu.h b/src/ppu/bppu/bppu.h index 85bf4dca..d497cf2a 100644 --- a/src/ppu/bppu/bppu.h +++ b/src/ppu/bppu/bppu.h @@ -148,8 +148,12 @@ struct { void cgram_write(uint16 addr, uint8 value); uint16 get_vram_address(); - uint8 vram_mmio_read (uint16 addr); - void vram_mmio_write(uint16 addr, uint8 data); + uint8 vram_mmio_read (uint16 addr); + void vram_mmio_write (uint16 addr, uint8 data); + uint8 oam_mmio_read (uint16 addr); + void oam_mmio_write (uint16 addr, uint8 data); + uint8 cgram_mmio_read (uint16 addr); + void cgram_mmio_write(uint16 addr, uint8 data); void mmio_w2100(uint8 value); //INIDISP void mmio_w2101(uint8 value); //OBSEL diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index 28f506ed..fbb1c80e 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -16,6 +16,11 @@ uint16 addr; return (addr << 1); } +//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will +//not be written anywhere at all. The below address ranges for where writes are invalid have +//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the +//write occurs during the very last clock cycle of vblank. + uint8 bPPU::vram_mmio_read(uint16 addr) { if(regs.display_disabled == true) { return vram_read(addr); @@ -46,20 +51,17 @@ uint16 ls = (r_cpu->region_scanlines() >> 1) - 1; void bPPU::vram_mmio_write(uint16 addr, uint8 data) { if(regs.display_disabled == true) { - vram_write(addr, data); - return; + return vram_write(addr, data); } uint16 v = r_cpu->vcounter(); uint16 hc = r_cpu->hclock(); if(v == 0) { if(hc <= 4) { - vram_write(addr, data); - return; + return vram_write(addr, data); } if(hc == 6) { - vram_write(addr, r_cpu->regs.mdr); - return; + return vram_write(addr, r_cpu->regs.mdr); } return; } @@ -72,13 +74,87 @@ uint16 hc = r_cpu->hclock(); if(hc <= 4) { return; } - vram_write(addr, data); - return; + return vram_write(addr, data); } vram_write(addr, data); } +//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered +//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for +//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be +//reverse engineered using a scanline renderer such as this, and at this time, there does not +//exist a more accurate SNES PPU emulator to work from. The only known game to actually access +//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218. +//It was decided by public consensus to map writes to this address to match Uniracers, primarily +//because it is the only game observed to do this, but also because mapping to this address does +//not contradict any of our findings, because we have no findings whatsoever on this behavior. +//Think of this what you will, I openly admit that this is a hack. But it is more accurate than +//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that +//accidentally accesses OAM during active display by virtue of not returning the expected data. +//You may disable this behavior by setting config::ppu.hack.oam_address_invalidation to false, +//or by changing the address from 0x0218 to 0x0000 below if it bothers you that greatly. + +uint8 bPPU::oam_mmio_read(uint16 addr) { + if(config::ppu.hack.oam_address_invalidation == false || regs.display_disabled == true) { + return oam_read(addr); + } + +uint16 v = r_cpu->vcounter(); + if(v < (!r_cpu->overscan() ? 225 : 240)) { + return oam_read(0x0218); + } + + return oam_read(addr); +} + +void bPPU::oam_mmio_write(uint16 addr, uint8 data) { + if(config::ppu.hack.oam_address_invalidation == false || regs.display_disabled == true) { + return oam_write(addr, data); + } + +uint16 v = r_cpu->vcounter(); + if(v < (!r_cpu->overscan() ? 225 : 240)) { + return oam_write(0x0218, data); + } + + oam_write(addr, data); +} + +//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the +//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know +//the exact algorithm used, but we have zero known examples of any commercial software that +//attempts to do this. Therefore, the addresses are mapped to 0x0000. There is nothing special +//about this address, it is simply more accurate to invalidate the 'expected' address than not. + +uint8 bPPU::cgram_mmio_read(uint16 addr) { + if(config::ppu.hack.cgram_address_invalidation == false || regs.display_disabled == true) { + return cgram_read(addr); + } + +uint16 v = r_cpu->vcounter(); +uint16 hc = r_cpu->hclock(); + if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { + return cgram_read(0x0000); + } + + return cgram_read(addr); +} + +void bPPU::cgram_mmio_write(uint16 addr, uint8 data) { + if(config::ppu.hack.cgram_address_invalidation == false || regs.display_disabled == true) { + return cgram_write(addr, data); + } + +uint16 v = r_cpu->vcounter(); +uint16 hc = r_cpu->hclock(); + if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { + return cgram_write(0x0000, data); + } + + cgram_write(addr, data); +} + //INIDISP void bPPU::mmio_w2100(uint8 value) { if(regs.display_disabled == true && r_cpu->vcounter() == (!r_cpu->overscan() ? 225 : 240)) { @@ -117,12 +193,12 @@ void bPPU::mmio_w2103(uint8 data) { //OAMDATA void bPPU::mmio_w2104(uint8 data) { if(regs.oam_addr & 0x0200) { - oam_write(regs.oam_addr, data); + oam_mmio_write(regs.oam_addr, data); } else if((regs.oam_addr & 1) == 0) { regs.oam_latchdata = data; } else { - oam_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); - oam_write((regs.oam_addr & ~1) + 1, data); + oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); + oam_mmio_write((regs.oam_addr & ~1) + 1, data); } regs.oam_addr++; @@ -353,8 +429,8 @@ void bPPU::mmio_w2122(uint8 value) { if(!(regs.cgram_addr & 1)) { regs.cgram_latchdata = value; } else { - cgram_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata); - cgram_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f); + cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata); + cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f); } regs.cgram_addr++; regs.cgram_addr &= 0x01ff; @@ -540,7 +616,7 @@ uint8 bPPU::mmio_r2137() { //OAMDATAREAD uint8 bPPU::mmio_r2138() { - regs.ppu1_mdr = oam_read(regs.oam_addr); + regs.ppu1_mdr = oam_mmio_read(regs.oam_addr); regs.oam_addr++; regs.oam_addr &= 0x03ff; @@ -581,10 +657,10 @@ uint16 addr = get_vram_address() + 1; //update bit 7 of the PPU2 MDR. uint8 bPPU::mmio_r213b() { if(!(regs.cgram_addr & 1)) { - regs.ppu2_mdr = cgram_read(regs.cgram_addr) & 0xff; + regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff; } else { regs.ppu2_mdr &= 0x80; - regs.ppu2_mdr |= cgram_read(regs.cgram_addr) & 0x7f; + regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f; } regs.cgram_addr++; regs.cgram_addr &= 0x01ff; diff --git a/src/ppu/bppu/bppu_render_bg.cpp b/src/ppu/bppu/bppu_render_bg.cpp index e9668985..b663b328 100644 --- a/src/ppu/bppu/bppu_render_bg.cpp +++ b/src/ppu/bppu/bppu_render_bg.cpp @@ -27,7 +27,9 @@ uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) { uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); if(y & 0x20)pos += bg_info[bg].scy; if(x & 0x20)pos += bg_info[bg].scx; - return read16(vram, regs.bg_scaddr[bg] + (pos << 1)); + +uint16 addr = regs.bg_scaddr[bg] + (pos << 1); + return (vram_read(addr + 0) << 0) | (vram_read(addr + 1) << 8); } #define setpixel_main(x) \ diff --git a/src/ui/audio/audio.cpp b/src/ui/audio/audio.cpp index 137ed08a..2201b46f 100644 --- a/src/ui/audio/audio.cpp +++ b/src/ui/audio/audio.cpp @@ -1,100 +1,4 @@ -/* - * interpolation routines are located in: /src/lib/libinterp.h - */ - -/* - -inline uint Audio::bind_range(uint min, uint max, uint index) { - return index < min ? min : index > max ? max : index; -} - -void Audio::resample_point( -uint32 *output, uint32 *input, uint output_samples, uint input_samples -) { -double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds -double sindex = 0.0; - for(uint x = 0; x <= output_samples; x++) { - uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)]; - uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)]; - double mu = sindex - uint(sindex); //calculate fractional portion of step - uint16 yl = interpolate_point(mu, int16(y0 >> 0), int16(y1 >> 0)); - uint16 yr = interpolate_point(mu, int16(y0 >> 16), int16(y1 >> 16)); - output[x] = (yl << 0) + (yr << 16); - sindex += scalar; - } -} - -void Audio::resample_linear( -uint32 *output, uint32 *input, uint output_samples, uint input_samples -) { -double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds -double sindex = 0.0; - for(uint x = 0; x <= output_samples; x++) { - uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)]; - uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)]; - double mu = sindex - uint(sindex); //calculate fractional portion of step - uint16 yl = interpolate_linear(mu, int16(y0 >> 0), int16(y1 >> 0)); - uint16 yr = interpolate_linear(mu, int16(y0 >> 16), int16(y1 >> 16)); - output[x] = (yl << 0) + (yr << 16); - sindex += scalar; - } -} - -void Audio::resample_cosine( -uint32 *output, uint32 *input, uint output_samples, uint input_samples -) { -double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds -double sindex = 0.0; - for(uint x = 0; x <= output_samples; x++) { - uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)]; - uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)]; - double mu = sindex - uint(sindex); //calculate fractional portion of step - uint16 yl = interpolate_cosine(mu, int16(y0 >> 0), int16(y1 >> 0)); - uint16 yr = interpolate_cosine(mu, int16(y0 >> 16), int16(y1 >> 16)); - output[x] = (yl << 0) + (yr << 16); - sindex += scalar; - } -} - -void Audio::resample_cubic( -uint32 *output, uint32 *input, uint output_samples, uint input_samples -) { -double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds -double sindex = 0.0; - for(uint x = 0; x <= output_samples; x++) { - uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) - 1)]; - uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 0)]; - uint32 y2 = input[bind_range(0, input_samples, uint32(sindex) + 1)]; - uint32 y3 = input[bind_range(0, input_samples, uint32(sindex) + 2)]; - double mu = sindex - uint(sindex); //calculate fractional portion of step - uint16 yl = sclamp<16>( interpolate_cubic(mu, int16(y0 >> 0), int16(y1 >> 0), int16(y2 >> 0), int16(y3 >> 0)) ); - uint16 yr = sclamp<16>( interpolate_cubic(mu, int16(y0 >> 16), int16(y1 >> 16), int16(y2 >> 16), int16(y3 >> 16)) ); - output[x] = (yl << 0) + (yr << 16); - sindex += scalar; - } -} - -void Audio::resample_hermite( -uint32 *output, uint32 *input, uint output_samples, uint input_samples -) { -double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds -double sindex = 0.0; - for(uint x = 0; x <= output_samples; x++) { - uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) - 1)]; - uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 0)]; - uint32 y2 = input[bind_range(0, input_samples, uint32(sindex) + 1)]; - uint32 y3 = input[bind_range(0, input_samples, uint32(sindex) + 2)]; - double mu = sindex - uint(sindex); //calculate fractional portion of step - uint16 yl = sclamp<16>( interpolate_hermite(mu, 0.0, 0.0, int16(y0 >> 0), int16(y1 >> 0), int16(y2 >> 0), int16(y3 >> 0)) ); - uint16 yr = sclamp<16>( interpolate_hermite(mu, 0.0, 0.0, int16(y0 >> 16), int16(y1 >> 16), int16(y2 >> 16), int16(y3 >> 16)) ); - output[x] = (yl << 0) + (yr << 16); - sindex += scalar; - } -} - -*/ - -// +#include "audio.h" void Audio::update_frequency() { uint freq = config::audio.frequency; diff --git a/src/ui/audio/audio.h b/src/ui/audio/audio.h index 9e38ab48..8086e6db 100644 --- a/src/ui/audio/audio.h +++ b/src/ui/audio/audio.h @@ -1,3 +1,6 @@ +#ifndef AUDIO_H +#define AUDIO_H + class Audio { public: uint frequency, latency; @@ -7,14 +10,7 @@ uint frequency, latency; virtual void init() {} virtual void term() {} -/* - uint bind_range(uint min, uint max, uint index); - void resample_point (uint32 *output, uint32 *input, uint output_samples, uint input_samples); - void resample_linear (uint32 *output, uint32 *input, uint output_samples, uint input_samples); - void resample_cosine (uint32 *output, uint32 *input, uint output_samples, uint input_samples); - void resample_cubic (uint32 *output, uint32 *input, uint output_samples, uint input_samples); - void resample_hermite(uint32 *output, uint32 *input, uint output_samples, uint input_samples); -*/ - Audio(); } *uiAudio; + +#endif diff --git a/src/ui/input/dinput.cpp b/src/ui/input/dinput.cpp index 7b118c55..0f9f946f 100644 --- a/src/ui/input/dinput.cpp +++ b/src/ui/input/dinput.cpp @@ -1,10 +1,14 @@ #include "dinput.h" +void InputDI::clear_input() { + memset(keystate, 0, sizeof keystate); +} + void InputDI::poll() { + clear_input(); + HRESULT hr; DIJOYSTATE2 js; - memset(keystate, 0, sizeof(keystate)); - if(di_key) { hr = di_key->GetDeviceState(256, keystate); if(FAILED(hr)) { diff --git a/src/ui/input/dinput.h b/src/ui/input/dinput.h index 245f8bce..8acf8cc7 100644 --- a/src/ui/input/dinput.h +++ b/src/ui/input/dinput.h @@ -15,6 +15,7 @@ LPDIRECTINPUT8 di; LPDIRECTINPUTDEVICE8 di_key, di_joy[DIRECTINPUT_JOYMAX]; uint32 di_joy_count; + void clear_input(); void poll(); void init(); void term(); diff --git a/src/ui/input/input.cpp b/src/ui/input/input.cpp index e69de29b..018d9c79 100644 --- a/src/ui/input/input.cpp +++ b/src/ui/input/input.cpp @@ -0,0 +1 @@ +#include "input.h" diff --git a/src/ui/input/input.h b/src/ui/input/input.h index d0b1b65b..6bc9801a 100644 --- a/src/ui/input/input.h +++ b/src/ui/input/input.h @@ -5,9 +5,6 @@ class Input { public: virtual bool key_down(uint16 key) { return false; } virtual bool key_up (uint16 key) { return !key_down(key); } - virtual void signal_key_down(uint16 key) {} - virtual void signal_key_up (uint16 key) {} - virtual void clear_input() {} virtual void poll() {} virtual void init() {} diff --git a/src/ui/input/inputui.cpp b/src/ui/input/inputui.cpp deleted file mode 100644 index 1a30f9b7..00000000 --- a/src/ui/input/inputui.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "inputui.h" diff --git a/src/ui/input/inputui.h b/src/ui/input/inputui.h deleted file mode 100644 index f452892e..00000000 --- a/src/ui/input/inputui.h +++ /dev/null @@ -1,28 +0,0 @@ -/***** - * InputUI - * - * Input wrapper to capture UI key presses. Useful for when OS does not - * support direct input polling methods (eg Xorg, etc) - *****/ - -#ifndef INPUTUI_H -#define INPUTUI_H - -class InputUI : public Input { -private: -bool keystate[65536]; - -public: - void init() { - Input::init(); - memset(keystate, 0, sizeof(keystate)); - } - - bool key_down(uint16 key) { return keystate[key]; } - void signal_key_down(uint16 key) { keystate[key] = true; } - void signal_key_up (uint16 key) { keystate[key] = false; } - - void clear_input() { memset(keystate, 0, sizeof(keystate)); } -}; - -#endif diff --git a/src/ui/input/xinput.cpp b/src/ui/input/xinput.cpp new file mode 100644 index 00000000..b420d705 --- /dev/null +++ b/src/ui/input/xinput.cpp @@ -0,0 +1,143 @@ +#include "xinput.h" + +bool InputX::key_down(uint16 key) { +#define map(i) (keymap[i >> 3] & (1 << (i & 7))) + switch(key) { + case keymap::esc: return map(0x09); + + case keymap::f1: return map(0x43); + case keymap::f2: return map(0x44); + case keymap::f3: return map(0x45); + case keymap::f4: return map(0x46); + case keymap::f5: return map(0x47); + case keymap::f6: return map(0x48); + case keymap::f7: return map(0x49); + case keymap::f8: return map(0x4a); + case keymap::f9: return map(0x4b); + case keymap::f10: return map(0x4c); + case keymap::f11: return map(0x5f); + case keymap::f12: return map(0x60); + + case keymap::print_screen: return map(0x6f); + case keymap::scroll_lock: return map(0x4e); + case keymap::pause: return map(0x6e); + + case keymap::grave: return map(0x31); + + case keymap::num_1: return map(0x0a); + case keymap::num_2: return map(0x0b); + case keymap::num_3: return map(0x0c); + case keymap::num_4: return map(0x0d); + case keymap::num_5: return map(0x0e); + case keymap::num_6: return map(0x0f); + case keymap::num_7: return map(0x10); + case keymap::num_8: return map(0x11); + case keymap::num_9: return map(0x12); + case keymap::num_0: return map(0x13); + + case keymap::minus: return map(0x14); + case keymap::equal: return map(0x15); + case keymap::backspace: return map(0x16); + + case keymap::ins: return map(0x6a); + case keymap::del: return map(0x6b); + case keymap::home: return map(0x61); + case keymap::end: return map(0x67); + case keymap::page_up: return map(0x63); + case keymap::page_down: return map(0x69); + + case keymap::a: return map(0x26); + case keymap::b: return map(0x38); + case keymap::c: return map(0x36); + case keymap::d: return map(0x28); + case keymap::e: return map(0x1a); + case keymap::f: return map(0x29); + case keymap::g: return map(0x2a); + case keymap::h: return map(0x2b); + case keymap::i: return map(0x1f); + case keymap::j: return map(0x2c); + case keymap::k: return map(0x2d); + case keymap::l: return map(0x2e); + case keymap::m: return map(0x3a); + case keymap::n: return map(0x39); + case keymap::o: return map(0x20); + case keymap::p: return map(0x21); + case keymap::q: return map(0x18); + case keymap::r: return map(0x1b); + case keymap::s: return map(0x27); + case keymap::t: return map(0x1c); + case keymap::u: return map(0x1e); + case keymap::v: return map(0x37); + case keymap::w: return map(0x19); + case keymap::x: return map(0x35); + case keymap::y: return map(0x1d); + case keymap::z: return map(0x34); + + case keymap::lbracket: return map(0x22); + case keymap::rbracket: return map(0x23); + case keymap::backslash: return map(0x33); + case keymap::semicolon: return map(0x2f); + case keymap::apostrophe: return map(0x30); + case keymap::comma: return map(0x3b); + case keymap::period: return map(0x3c); + case keymap::slash: return map(0x3d); + + case keymap::kp_1: return map(0x57); + case keymap::kp_2: return map(0x58); + case keymap::kp_3: return map(0x59); + case keymap::kp_4: return map(0x53); + case keymap::kp_5: return map(0x54); + case keymap::kp_6: return map(0x55); + case keymap::kp_7: return map(0x4f); + case keymap::kp_8: return map(0x50); + case keymap::kp_9: return map(0x51); + + case keymap::kp_plus: return map(0x56); + case keymap::kp_minus: return map(0x52); + case keymap::kp_mul: return map(0x3f); + case keymap::kp_div: return map(0x70); + case keymap::kp_enter: return map(0x6c); + + case keymap::num_lock: return map(0x4d); + case keymap::caps_lock: return map(0x42); + + case keymap::up: return map(0x62); + case keymap::down: return map(0x68); + case keymap::left: return map(0x64); + case keymap::right: return map(0x66); + + case keymap::tab: return map(0x17); + case keymap::enter: return map(0x24); + case keymap::space: return map(0x41); + + case keymap::lctrl: return map(0x25); + case keymap::rctrl: return map(0x6d); + case keymap::lalt: return map(0x40); + case keymap::ralt: return map(0x71); + case keymap::lshift: return map(0x32); + case keymap::rshift: return map(0x3e); + case keymap::lsuper: return map(0x73); + case keymap::rsuper: return map(0x74); + case keymap::menu: return map(0x75); + } +#undef map + + return false; +} + +void InputX::clear_input() { + memset(keymap, 0, sizeof keymap); +} + +void InputX::poll() { + XQueryKeymap(display, keymap); +} + +void InputX::init() { + Input::init(); + display = XOpenDisplay(0); +} + +void InputX::term() { + Input::term(); +} diff --git a/src/ui/input/xinput.h b/src/ui/input/xinput.h new file mode 100644 index 00000000..772a12ba --- /dev/null +++ b/src/ui/input/xinput.h @@ -0,0 +1,26 @@ +#ifndef XINPUT_H +#define XINPUT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class InputX : public Input { public: + bool key_down(uint16 key); + + void clear_input(); + void poll(); + void init(); + void term(); + +private: +Display *display; +char keymap[32]; +}; + +#endif diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 9002f25b..0e359867 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -39,8 +39,12 @@ void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) { //input +//allow_input() returns true only when main emulator window has focus +//TODO: draft a more elegant way to poll lui, etc platforms from here +bool allow_input(); //defined in lui/main.cpp + void SNESInterface::input_poll() { - uiInput->poll(); + allow_input() ? uiInput->poll() : uiInput->clear_input(); input_manager.poll(); } diff --git a/src/ui/lui/event.cpp b/src/ui/lui/event.cpp index 8fbbebc4..253cf691 100644 --- a/src/ui/lui/event.cpp +++ b/src/ui/lui/event.cpp @@ -17,9 +17,9 @@ uint multiplier = minmax<1, 5>(uint(config::video.multiplier)); height *= multiplier; if(config::video.aspect_correction == true) { if(config::video.region == 0) { - width = uint( double(width) * 8.0 / 7.0 ); //NTSC + width = uint( double(width) * 54.0 / 47.0 ); //NTSC } else { - width = uint( double(width) * 48.0 / 35.0 ); //PAL + width = uint( double(width) * 32.0 / 23.0 ); //PAL } } @@ -103,6 +103,7 @@ char fn[PATH_MAX]; cartridge.load(fn); cartridge.load_end(); snes.power(); + window_cheat_editor.refresh(); } void unload_rom() { @@ -112,6 +113,7 @@ void unload_rom() { uiAudio->clear_audio(); } window_main.set_text(BSNES_TITLE); + window_cheat_editor.refresh(); } void reset() { diff --git a/src/ui/lui/main.cpp b/src/ui/lui/main.cpp index 4ca2ad01..bc77f3ed 100644 --- a/src/ui/lui/main.cpp +++ b/src/ui/lui/main.cpp @@ -18,6 +18,15 @@ bool _term_ = false; #include "ui.cpp" #include "event.cpp" +bool allow_input() { +#if defined(PLATFORM_X) +//TODO: window_main.focused() does not work at all on X + return true; +#endif +//only allow input capture when main window is active + return window_main.focused() == true; +} + void alert(char *s, ...) { char str[4096]; va_list args; @@ -58,6 +67,10 @@ void run() { snes.runtoframe(); event::update_frame_counter(); } +#if defined(_MSC_VER) +//prevent bsnes from consuming 100% CPU resources when idle + else { Sleep(1); } +#endif } #if defined(PLATFORM_WIN) diff --git a/src/ui/lui/settings/ui_cheateditor.cpp b/src/ui/lui/settings/ui_cheateditor.cpp index 55c82576..7a4785a2 100644 --- a/src/ui/lui/settings/ui_cheateditor.cpp +++ b/src/ui/lui/settings/ui_cheateditor.cpp @@ -1,4 +1,62 @@ +void CheatEditorWindow::refresh() { + list.reset(); + + for(uint i = 0; i < cheat.count(); i++) { + bool enabled; + uint32 addr; + uint8 data; + char s_code[256], s_desc[256], s_result[1024]; + cheat.get(i, enabled, addr, data, s_code, s_desc); + sprintf(s_result, "%s|%s|%s", enabled ? "Enabled" : "Disabled", s_code, s_desc); + list.add_item(s_result); + } + + list.autosize_columns(); + +//enable controls only if cartridge is loaded +bool loaded = cartridge.loaded(); + add_code.enable(loaded); + toggle_code.enable(loaded); + delete_code.enable(loaded); +} + bool CheatEditorWindow::message(uint id, uintptr_t param) { +ui::Control *control = (ui::Control*)param; + + if(id == ui::Message::Clicked && control == &add_code) { + char s_code[256], s_desc[256]; + code.get_text(s_code, sizeof s_code); + desc.get_text(s_desc, sizeof s_desc); + cheat.add(false, s_code, s_desc); //param 0 = new codes disabled by default + refresh(); + return true; + } + + if((id == ui::Message::Clicked && control == &toggle_code) || + (id == ui::Message::DoubleClicked && control == &list)) { + int index = list.get_selection(); + if(index >= 0 && index < cheat.count()) { + cheat.enabled(index) ? cheat.disable(index) : cheat.enable(index); + bool enabled; + uint32 addr; + uint8 data; + char s_code[256], s_desc[256], s_result[1024]; + cheat.get(index, enabled, addr, data, s_code, s_desc); + sprintf(s_result, "%s|%s|%s", enabled ? "Enabled" : "Disabled", s_code, s_desc); + list.set_item(index, s_result); + } + return true; + } + + if(id == ui::Message::Clicked && control == &delete_code) { + int index = list.get_selection(); + if(index >= 0 && index < cheat.count()) { + cheat.remove(index); + refresh(); + } + return true; + } + return true; } @@ -7,9 +65,6 @@ void CheatEditorWindow::setup() { int x = 0, y = 0; list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Status|Code|Description"); - list.add_item("Enabled|0123-4567|Infinite Energy"); - list.add_item("Disabled|89ab-cdef|Infinite Lives"); - list.autosize_columns(); y += 290; add_code.create (*this, 0, x, y, 155, 30, "Add Code"); @@ -20,7 +75,5 @@ int x = 0, y = 0; code.create(*this, 0, x, y, 155, 30, ""); desc.create(*this, 0, x + 160, y, 315, 30, ""); - add_code.disable(); - toggle_code.disable(); - delete_code.disable(); + refresh(); } diff --git a/src/ui/lui/settings/ui_cheateditor.h b/src/ui/lui/settings/ui_cheateditor.h index 075ed825..b6d2f26e 100644 --- a/src/ui/lui/settings/ui_cheateditor.h +++ b/src/ui/lui/settings/ui_cheateditor.h @@ -1,10 +1,12 @@ -class CheatEditorWindow : public ui::Window { public: -ui::Listbox list; -ui::Button add_code; -ui::Button toggle_code; -ui::Button delete_code; -ui::Editbox code; -ui::Editbox desc; - bool message(uint id, uintptr_t param); - void setup(); -} window_cheat_editor; +class CheatEditorWindow : public ui::Window { public: +ui::Listbox list; +ui::Button add_code; +ui::Button toggle_code; +ui::Button delete_code; +ui::Editbox code; +ui::Editbox desc; + bool message(uint id, uintptr_t param); + void setup(); + + void refresh(); +} window_cheat_editor; diff --git a/src/ui/lui/settings/ui_inputconfig.cpp b/src/ui/lui/settings/ui_inputconfig.cpp index 1b66fd6e..a66b1f51 100644 --- a/src/ui/lui/settings/ui_inputconfig.cpp +++ b/src/ui/lui/settings/ui_inputconfig.cpp @@ -99,16 +99,6 @@ void InputConfigWindow::refresh_list() { bool InputConfigWindow::message(uint id, uintptr_t param) { ui::Control *control = (ui::Control*)param; - if(id == ui::Message::KeyDown) { - if(uiInput) { uiInput->signal_key_down(param); } - return true; - } - - if(id == ui::Message::KeyUp) { - if(uiInput) { uiInput->signal_key_up(param); } - return true; - } - if(id == ui::Message::Changed && control == &list) { int pos = list.get_selection(); setkey.enable(pos >= 0); @@ -180,16 +170,6 @@ bool InputCaptureWindow::message(uint id, uintptr_t param) { return false; } - if(id == ui::Message::KeyDown) { - if(uiInput) { uiInput->signal_key_down(param); } - return true; - } - - if(id == ui::Message::KeyUp) { - if(uiInput) { uiInput->signal_key_up(param); } - return true; - } - return true; } diff --git a/src/ui/lui/ui.cpp b/src/ui/lui/ui.cpp index 9b225ba7..2ab9cf1f 100644 --- a/src/ui/lui/ui.cpp +++ b/src/ui/lui/ui.cpp @@ -20,10 +20,9 @@ #include "../video/xv.cpp" #include "../video/gtk.cpp" #include "../audio/ao.cpp" + #include "../input/xinput.cpp" #endif -#include "../input/inputui.cpp" - void ui_init() { window_main.setup(); window_about.setup(); @@ -49,7 +48,6 @@ void ui_init() { (Audio*)new AudioDS(window_main.handle()); uiInput = config::system.input == "none" ? (Input*)new Input() : - config::system.input == "ui" ? (Input*)new InputUI() : (Input*)new InputDI(window_main.handle()); #elif defined(PLATFORM_X) uiVideo = @@ -61,7 +59,7 @@ void ui_init() { (Audio*)new AudioAO(config::system.audio_flags); uiInput = config::system.input == "none" ? (Input*)new Input() : - (Input*)new InputUI(); + (Input*)new InputX(); #endif uiVideo->init(); diff --git a/src/ui/lui/ui_main.cpp b/src/ui/lui/ui_main.cpp index 12e66ba4..38a63437 100644 --- a/src/ui/lui/ui_main.cpp +++ b/src/ui/lui/ui_main.cpp @@ -12,17 +12,11 @@ ui::Control *control = (ui::Control*)param; } if(id == ui::Message::KeyDown) { - if(uiInput) { uiInput->signal_key_down(param); } if(param == keymap::esc) { event::toggle_menu(); } if(param == keymap::f11) { event::toggle_fullscreen(); } return true; } - if(id == ui::Message::KeyUp) { - if(uiInput) { uiInput->signal_key_up(param); } - return true; - } - if(id == ui::Message::Clicked) { if(control == &menu_file_load) { event::load_rom(); diff --git a/src/ui/main.cpp b/src/ui/main.cpp index 0370fef4..58be59a9 100644 --- a/src/ui/main.cpp +++ b/src/ui/main.cpp @@ -11,10 +11,6 @@ void term_snes(); * hardware abstraction layer *****/ -#include "video/video.h" -#include "audio/audio.h" -#include "input/input.h" - #include "video/video.cpp" #include "audio/audio.cpp" #include "input/input.cpp" diff --git a/src/ui/video/video.cpp b/src/ui/video/video.cpp index e69de29b..09e03b06 100644 --- a/src/ui/video/video.cpp +++ b/src/ui/video/video.cpp @@ -0,0 +1 @@ +#include "video.h" diff --git a/src/ui/video/video.h b/src/ui/video/video.h index 8e54ac21..c2a525c1 100644 --- a/src/ui/video/video.h +++ b/src/ui/video/video.h @@ -1,3 +1,6 @@ +#ifndef VIDEO_H +#define VIDEO_H + class Video { public: enum Filter { @@ -18,3 +21,5 @@ enum Filter { Video() {} virtual ~Video() {} } *uiVideo; + +#endif