From 0d8ac5b7608e87160071d41d2b98040f98f11132 Mon Sep 17 00:00:00 2001 From: 0kmg <9137159+0kmg@users.noreply.github.com> Date: Wed, 9 Feb 2022 09:18:19 -0900 Subject: nes.cpp, playch10.cpp, vsnes.cpp: Made zapper less bad. (#9284) - Limited light detection to scanlines recently drawn by CRT. - Lowered light detection threshold to better reflect how real zapper behaves; color doesn't need to be white. - Bugs fixed: * not detecting hits on certain white objects * misdetecting hits on objects far away from the pointer * not being able to track where zapper is pointed in demo/test software --- src/devices/bus/nes_ctrl/arkpaddle.cpp | 4 +- src/devices/bus/nes_ctrl/zapper.cpp | 51 +++++++++++++++-------- src/mame/drivers/playch10.cpp | 10 ++--- src/mame/drivers/vsnes.cpp | 8 ++-- src/mame/machine/playch10.cpp | 58 +++++++++++++++----------- src/mame/machine/vsnes.cpp | 75 ++++++++++++++-------------------- 6 files changed, 109 insertions(+), 97 deletions(-) diff --git a/src/devices/bus/nes_ctrl/arkpaddle.cpp b/src/devices/bus/nes_ctrl/arkpaddle.cpp index e322d10eeed..c529070bc12 100644 --- a/src/devices/bus/nes_ctrl/arkpaddle.cpp +++ b/src/devices/bus/nes_ctrl/arkpaddle.cpp @@ -33,9 +33,9 @@ DEFINE_DEVICE_TYPE(NES_ARKPADDLE_FC, nes_vausfc_device, "nes_vausfc", "FC Arkano static INPUT_PORTS_START( arkanoid_paddle ) PORT_START("PADDLE") - PORT_BIT( 0xff, 0x7f, IPT_PADDLE) PORT_SENSITIVITY(25) PORT_KEYDELTA(25) PORT_CENTERDELTA(0) PORT_MINMAX(0x4e,0xf2) // this minmax is from what Arkanoid 2 clamps values to, so an actual paddle's range may be greater + PORT_BIT( 0xff, 0x7f, IPT_PADDLE ) PORT_SENSITIVITY(25) PORT_KEYDELTA(25) PORT_CENTERDELTA(0) PORT_MINMAX(0x4e, 0xf2) // this minmax is from what Arkanoid 2 clamps values to, so an actual paddle's range may be greater PORT_START("BUTTON") - PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Paddle button") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Paddle button") INPUT_PORTS_END diff --git a/src/devices/bus/nes_ctrl/zapper.cpp b/src/devices/bus/nes_ctrl/zapper.cpp index e19c63fa2d2..4a1a2dd9408 100644 --- a/src/devices/bus/nes_ctrl/zapper.cpp +++ b/src/devices/bus/nes_ctrl/zapper.cpp @@ -21,11 +21,11 @@ DEFINE_DEVICE_TYPE(NES_BANDAIHS, nes_bandaihs_device, "nes_bandaihs", "Bandai Hy static INPUT_PORTS_START( nes_zapper ) PORT_START("ZAPPER_X") - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) PORT_MINMAX(0,255) + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) PORT_MINMAX(0, 255) PORT_START("ZAPPER_Y") - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_MINMAX(0,255) + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_MINMAX(0, 239) PORT_START("ZAPPER_T") - PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Lightgun Trigger") + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Lightgun Trigger") INPUT_PORTS_END @@ -116,25 +116,40 @@ u8 nes_zapper_device::read_bit34() int x = m_lightx->read(); int y = m_lighty->read(); + // effective range picked up by photodiode, i.e. gun position +- radius + constexpr int xrad = 0; + constexpr int yrad = 0; + // brightness threshold + constexpr int bright = 0x70; + // # of CRT scanlines that sustain brightness + constexpr int sustain = 20; + + int vpos = m_port->m_screen->vpos(); + int hpos = m_port->m_screen->hpos(); + // update the screen if necessary if (!m_port->m_screen->vblank()) - { - int vpos = m_port->m_screen->vpos(); - int hpos = m_port->m_screen->hpos(); - - if (vpos > y || (vpos == y && hpos >= x)) + if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad)) m_port->m_screen->update_now(); - } - - // get the pixel at the gun position - rgb_t pix = m_port->m_screen->pixel(x, y); - // check if the cursor is over a bright pixel - // FIXME: still a gross hack - if (pix.r() == 0xff && pix.b() == 0xff && pix.g() > 0x90) - ret &= ~0x08; // sprite hit - else - ret |= 0x08; // no sprite hit + // default to no light detected + ret |= 0x08; + + // check brightness of pixels nearby the gun position + for (int i = x - xrad; i <= x + xrad; i++) + for (int j = y - yrad; j <= y + yrad; j++) + { + rgb_t pix = m_port->m_screen->pixel(i, j); + + // only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki: + // "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray." + if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright) + { + ret &= ~0x08; // light detected + i = x + xrad; + break; + } + } return ret; } diff --git a/src/mame/drivers/playch10.cpp b/src/mame/drivers/playch10.cpp index 4184b48c54c..da5754a9bf4 100644 --- a/src/mame/drivers/playch10.cpp +++ b/src/mame/drivers/playch10.cpp @@ -711,15 +711,15 @@ static INPUT_PORTS_START( playch10 ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) INPUT_PORTS_END -/*Input Ports for gun games*/ +// Input Ports for gun games static INPUT_PORTS_START( playc10g ) PORT_INCLUDE(playch10) - PORT_START("GUNX") /* IN2 - FAKE - Gun X pos */ - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) + PORT_START("GUNX") // IN2 - FAKE - Gun X pos + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) PORT_MINMAX(0, 255) - PORT_START("GUNY") /* IN3 - FAKE - Gun Y pos */ - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) + PORT_START("GUNY") // IN3 - FAKE - Gun Y pos + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_MINMAX(0, 239) INPUT_PORTS_END diff --git a/src/mame/drivers/vsnes.cpp b/src/mame/drivers/vsnes.cpp index 66f8498a6ab..e5352b3c1a3 100644 --- a/src/mame/drivers/vsnes.cpp +++ b/src/mame/drivers/vsnes.cpp @@ -496,11 +496,11 @@ static INPUT_PORTS_START( vsnes_zapper ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_COIN2 ) PORT_IMPULSE(1) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN ) - PORT_START("GUNX") /* FAKE - Gun X pos */ - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) + PORT_START("GUNX") // FAKE - Gun X pos + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(70) PORT_KEYDELTA(30) PORT_MINMAX(0, 255) - PORT_START("GUNY") /* FAKE - Gun Y pos */ - PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) + PORT_START("GUNY") // FAKE - Gun Y pos + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(30) PORT_MINMAX(0, 239) INPUT_PORTS_END diff --git a/src/mame/machine/playch10.cpp b/src/mame/machine/playch10.cpp index 37708486976..ff3215ab4a5 100644 --- a/src/mame/machine/playch10.cpp +++ b/src/mame/machine/playch10.cpp @@ -212,50 +212,60 @@ uint8_t playch10_state::pc10_in0_r() uint8_t playch10_state::pc10_in1_r() { - int ret = (m_input_latch[1]) & 1; + int ret = m_input_latch[1] & 1; - /* shift */ + // shift m_input_latch[1] >>= 1; - /* do the gun thing */ + // do the gun thing if (m_pc10_gun_controller) { int trigger = ioport("P1")->read(); int x = ioport("GUNX")->read(); int y = ioport("GUNY")->read(); - /* no sprite hit (yet) */ - ret |= 0x08; + // effective range picked up by photodiode, i.e. gun position +- radius + constexpr int xrad = 0; + constexpr int yrad = 0; + // brightness threshold + constexpr int bright = 0x70; + // # of CRT scanlines that sustain brightness + constexpr int sustain = 20; + + int vpos = m_ppu->screen().vpos(); + int hpos = m_ppu->screen().hpos(); // update the screen if necessary if (!m_ppu->screen().vblank()) - { - int vpos = m_ppu->screen().vpos(); - int hpos = m_ppu->screen().hpos(); - - if (vpos > y || (vpos == y && hpos >= x)) + if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad)) m_ppu->screen().update_now(); - } - /* get the pixel at the gun position */ - rgb_t pix = m_ppu->screen().pixel(x, y); + // default to no light detected + ret |= 0x08; - /* look at the screen and see if the cursor is over a bright pixel */ - // FIXME: still a gross hack - if (pix.r() == 0xff && pix.b() == 0xff && pix.g() > 0x90) - { - ret &= ~0x08; /* sprite hit */ - } + // check brightness of pixels nearby the gun position + for (int i = x - xrad; i <= x + xrad; i++) + for (int j = y - yrad; j <= y + yrad; j++) + { + rgb_t pix = m_ppu->screen().pixel(i, j); - /* now, add the trigger if not masked */ + // only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki: + // "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray." + if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright) + { + ret &= ~0x08; // light detected + i = x + xrad; + break; + } + } + + // now, add the trigger if not masked if (!m_cntrl_mask) - { ret |= (trigger & 2) << 3; - } } - /* some games expect bit 6 to be set because the last entry on the data bus shows up */ - /* in the unused upper 3 bits, so typically a read from $4016 leaves 0x40 there. */ + // some games expect bit 6 to be set because the last entry on the data bus shows up + // in the unused upper 3 bits, so typically a read from $4016 leaves 0x40 there. ret |= 0x40; return ret; diff --git a/src/mame/machine/vsnes.cpp b/src/mame/machine/vsnes.cpp index 3a22c4795c3..855dc5d9b27 100644 --- a/src/mame/machine/vsnes.cpp +++ b/src/mame/machine/vsnes.cpp @@ -259,71 +259,58 @@ void vsnes_state::init_vsnormal() } /**********************************************************************************/ -/* Gun games: VROM Banking in controller 0 write */ +// Gun games: VROM Banking in controller 0 write void vsnes_state::gun_in0_w(uint8_t data) { if (m_do_vrom_bank) { - /* switch vrom */ + // switch vrom v_set_videorom_bank(0, 8, (data & 4) ? 8 : 0); } - /* here we do things a little different */ + // here we do things a little different if (m_input_strobe[0] & ~data & 1) { - /* load up the latches */ + // load up the latches m_input_latch[0] = ioport("IN0")->read(); + m_input_latch[1] = ioport("IN1")->read(); - /* do the gun thing */ + // do the gun thing int x = ioport("GUNX")->read(); - float y = ioport("GUNY")->read(); + int y = ioport("GUNY")->read(); - y = y * 0.9375f; // scale 256 (our gun input range is 0 - 255) to 240 (screen visible area / bitmap we're using is 0 - 239) + // effective range picked up by photodiode, i.e. gun position +- radius + constexpr int xrad = 0; + constexpr int yrad = 0; + // brightness threshold + constexpr int bright = 0x70; + // # of CRT scanlines that sustain brightness + constexpr int sustain = 20; - uint8_t realy = (int)y; + int vpos = m_ppu1->screen().vpos(); + int hpos = m_ppu1->screen().hpos(); // update the screen if necessary if (!m_ppu1->screen().vblank()) - { - int vpos = m_ppu1->screen().vpos(); - int hpos = m_ppu1->screen().hpos(); - - if (vpos > realy || (vpos == realy && hpos >= x)) + if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad)) m_ppu1->screen().update_now(); - } - - /* get the pixel at the gun position */ - rgb_t col = m_ppu1->screen().pixel(x, realy); - uint8_t bright = col.brightness(); - // todo, calculate how bright it is with pix.r * 0.3 + pix.g * 0.59 + pix.b * 0.11 ? - // the mame calc above is uint8_t brightness() const { return (r() * 222 + g() * 707 + b() * 71) / 1000; } (from lib/util/palette.h) -#if 0 - uint8_t r = col.r(); - uint8_t g = col.g(); - uint8_t b = col.b(); - printf("pix is %02x %02x %02x | %02x\n", r,g,b,bright); -#endif - if (bright == 0xff) - { - m_input_latch[0] |= 0x40; - } - - -#if 0 // this is junk code, only works for NES palette.. - /* get the color base from the ppu */ - uint32_t color_base = 0; - - /* look at the screen and see if the cursor is over a bright pixel */ - if ((pix == color_base + 0x20 ) || (pix == color_base + 0x30) || - (pix == color_base + 0x33 ) || (pix == color_base + 0x34)) - { - m_input_latch[0] |= 0x40; - } -#endif + // check brightness of pixels nearby the gun position + for (int i = x - xrad; i <= x + xrad; i++) + for (int j = y - yrad; j <= y + yrad; j++) + { + rgb_t pix = m_ppu1->screen().pixel(i, j); - m_input_latch[1] = ioport("IN1")->read(); + // only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki: + // "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray." + if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright) + { + m_input_latch[0] |= 0x40; + i = x + xrad; + break; + } + } } m_input_strobe[0] = data; -- cgit v1.2.3