bus/nes_ctrl: Split zapper sensor off as a reusable device. (#9417)

This commit is contained in:
0kmg 2022-03-17 08:29:56 -08:00 committed by GitHub
parent 665f605c65
commit acca7d5898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 352 additions and 249 deletions

View File

@ -3038,6 +3038,8 @@ if (BUSES["NES_CTRL"]~=null) then
MAME_DIR .. "src/devices/bus/nes_ctrl/partytap.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/powerpad.cpp",
MAME_DIR .. "src/devices/bus/nes_ctrl/powerpad.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/rob.cpp",
MAME_DIR .. "src/devices/bus/nes_ctrl/rob.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/snesadapter.cpp",
MAME_DIR .. "src/devices/bus/nes_ctrl/snesadapter.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/suborkey.cpp",
@ -3046,10 +3048,12 @@ if (BUSES["NES_CTRL"]~=null) then
MAME_DIR .. "src/devices/bus/nes_ctrl/turbofile.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/zapper.cpp",
MAME_DIR .. "src/devices/bus/nes_ctrl/zapper.h",
MAME_DIR .. "src/devices/bus/nes_ctrl/zapper_sensor.cpp",
MAME_DIR .. "src/devices/bus/nes_ctrl/zapper_sensor.h",
}
dependency {
{ MAME_DIR .. "src/devices/bus/nes_ctrl/zapper.cpp", GEN_DIR .. "emu/layout/nes_rob.lh" },
{ MAME_DIR .. "src/devices/bus/nes_ctrl/rob.cpp", GEN_DIR .. "emu/layout/nes_rob.lh" },
}
custombuildtask {

View File

@ -58,6 +58,7 @@
#include "pachinko.h"
#include "partytap.h"
#include "powerpad.h"
#include "rob.h"
#include "snesadapter.h"
#include "suborkey.h"
#include "turbofile.h"

View File

@ -0,0 +1,140 @@
// license:BSD-3-Clause
// copyright-holders:hap
/**********************************************************************
Nintendo HVC-012 Family Computer Robot / Nintendo NES-012 R.O.B.
TODO:
- does nes_rob have motor sensors? (eg. limit switches, or optical
sensor to determine position)
- can't really play anything with nes_rob, because of interaction
with physical objects (gyromite especially, since it has a gadget
to make the robot press joypad buttons)
**********************************************************************/
#include "emu.h"
#include "rob.h"
#include "nes_rob.lh"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(NES_ROB, nes_rob_device, "nes_rob", "Nintendo R.O.B. / Family Computer Robot")
static INPUT_PORTS_START( nes_rob )
PORT_START("EYE_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_START("EYE_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, 239)
INPUT_PORTS_END
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
ioport_constructor nes_rob_device::device_input_ports() const
{
return INPUT_PORTS_NAME( nes_rob );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// constructor
//-------------------------------------------------
nes_rob_device::nes_rob_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, NES_ROB, tag, owner, clock)
, device_nes_control_port_interface(mconfig, *this)
, m_maincpu(*this, "maincpu")
, m_sensor(*this, "sensor")
, m_eye_x(*this, "EYE_X")
, m_eye_y(*this, "EYE_Y")
, m_motor_out(*this, "rob_motor.%u", 0U)
, m_led_out(*this, "rob_led")
{
}
//-------------------------------------------------
// device_start
//-------------------------------------------------
void nes_rob_device::device_start()
{
// resolve handlers
m_motor_out.resolve();
m_led_out.resolve();
}
//-------------------------------------------------
// R.O.B. specific handlers
//-------------------------------------------------
u8 nes_rob_device::input_r()
{
// R00: lightsensor
return !m_sensor->detect_light(m_eye_x->read(), m_eye_y->read());
}
void nes_rob_device::output_w(offs_t offset, u8 data)
{
switch (offset & 3)
{
case 0:
// R03: led
m_led_out = BIT(data, 3);
break;
case 1:
// R10-R13: motors: down, up, close, open
for (int i = 0; i < 4; i++)
m_motor_out[i] = BIT(data, i);
break;
case 2:
// R20,R21: motors: right, left
for (int i = 0; i < 2; i++)
m_motor_out[i + 4] = BIT(data, i);
break;
default:
break;
}
}
void nes_rob_device::device_add_mconfig(machine_config &config)
{
SM590(config, m_maincpu, 455_kHz_XTAL);
m_maincpu->read_r<0>().set(FUNC(nes_rob_device::input_r));
m_maincpu->write_r<0>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<1>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<2>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<3>().set(FUNC(nes_rob_device::output_w));
NES_ZAPPER_SENSOR(config, m_sensor, 0);
if (m_port != nullptr)
m_sensor->set_screen_tag(m_port->m_screen);
// must use -numscreens 2 to see the output status
config.set_default_layout(layout_nes_rob);
}
ROM_START( nes_rob )
ROM_REGION( 0x200, "maincpu", 0 )
ROM_LOAD( "rfc-cpu10.ic1", 0x000, 0x200, CRC(f9c96b9c) SHA1(a87e2f0f5e454c093d1352ac368aa9e82e9f6790) )
ROM_END
const tiny_rom_entry *nes_rob_device::device_rom_region() const
{
return ROM_NAME(nes_rob);
}

View File

@ -0,0 +1,55 @@
// license:BSD-3-Clause
// copyright-holders:hap
/**********************************************************************
Nintendo HVC-012 Family Computer Robot / Nintendo NES-012 R.O.B.
**********************************************************************/
#ifndef MAME_BUS_NES_CTRL_ROB
#define MAME_BUS_NES_CTRL_ROB
#pragma once
#include "ctrl.h"
#include "cpu/sm510/sm590.h"
#include "zapper_sensor.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> nes_rob_device
class nes_rob_device : public device_t,
public device_nes_control_port_interface
{
public:
// construction/destruction
nes_rob_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
virtual ioport_constructor device_input_ports() const override;
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual const tiny_rom_entry *device_rom_region() const override;
private:
required_device<sm590_device> m_maincpu;
required_device<nes_zapper_sensor_device> m_sensor;
required_ioport m_eye_x;
required_ioport m_eye_y;
output_finder<6> m_motor_out;
output_finder<> m_led_out;
u8 input_r();
void output_w(offs_t offset, u8 data);
};
// device type definition
DECLARE_DEVICE_TYPE(NES_ROB, nes_rob_device)
#endif // MAME_BUS_NES_CTRL_ROB

View File

@ -4,33 +4,18 @@
Nintendo Family Computer & Entertainment System Zapper Lightgun
Nintendo Family Computer Bandai Hyper Shot Lightgun
Nintendo R.O.B.
TODO:
- nes_rob is in here because it needs the zapper light sensor,
in reality it's not connected to the control port at all, but how
would it be interfaced with the NES driver otherwise?
- does nes_rob have motor sensors? (eg. limit switches, or optical
sensor to determine position)
- can't really play anything with nes_rob, because of interaction
with physical objects (gyromite especially, since it has a gadget
to make the robot press joypad buttons)
**********************************************************************/
#include "emu.h"
#include "screen.h"
#include "zapper.h"
#include "nes_rob.lh"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(NES_ZAPPER, nes_zapper_device, "nes_zapper", "Nintendo Zapper Lightgun")
DEFINE_DEVICE_TYPE(NES_BANDAIHS, nes_bandaihs_device, "nes_bandaihs", "Bandai Hyper Shot Lightgun")
DEFINE_DEVICE_TYPE(NES_ROB, nes_rob_device, "nes_rob", "Nintendo R.O.B.")
static INPUT_PORTS_START( nes_zapper )
@ -58,15 +43,6 @@ static INPUT_PORTS_START( nes_bandaihs )
INPUT_PORTS_END
static INPUT_PORTS_START( nes_rob )
PORT_INCLUDE( nes_zapper )
// it has the x/y for aiming the 'eyes', but there is no lightgun trigger
PORT_MODIFY("ZAPPER_T")
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
@ -81,9 +57,15 @@ ioport_constructor nes_bandaihs_device::device_input_ports() const
return INPUT_PORTS_NAME( nes_bandaihs );
}
ioport_constructor nes_rob_device::device_input_ports() const
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void nes_zapper_device::device_add_mconfig(machine_config &config)
{
return INPUT_PORTS_NAME( nes_rob );
NES_ZAPPER_SENSOR(config, m_sensor, 0);
if (m_port != nullptr)
m_sensor->set_screen_tag(m_port->m_screen);
}
@ -98,6 +80,7 @@ ioport_constructor nes_rob_device::device_input_ports() const
nes_zapper_device::nes_zapper_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, type, tag, owner, clock)
, device_nes_control_port_interface(mconfig, *this)
, m_sensor(*this, "sensor")
, m_lightx(*this, "ZAPPER_X")
, m_lighty(*this, "ZAPPER_Y")
, m_trigger(*this, "ZAPPER_T")
@ -116,14 +99,6 @@ nes_bandaihs_device::nes_bandaihs_device(const machine_config &mconfig, const ch
{
}
nes_rob_device::nes_rob_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: nes_zapper_device(mconfig, NES_ROB, tag, owner, clock)
, m_maincpu(*this, "maincpu")
, m_motor_out(*this, "rob_motor.%u", 0U)
, m_led_out(*this, "rob_led")
{
}
//-------------------------------------------------
// device_start
@ -140,15 +115,6 @@ void nes_bandaihs_device::device_start()
save_item(NAME(m_strobe));
}
void nes_rob_device::device_start()
{
nes_zapper_device::device_start();
// resolve handlers
m_motor_out.resolve();
m_led_out.resolve();
}
//-------------------------------------------------
// read
@ -157,44 +123,8 @@ void nes_rob_device::device_start()
u8 nes_zapper_device::read_bit34()
{
u8 ret = m_trigger->read();
int x = m_lightx->read();
int y = m_lighty->read();
// radius of circle picked up by the gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 22;
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())
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_port->m_screen->update_now();
int sum = 0;
int scanned = 0;
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
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))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
// light not detected if average brightness is below threshold (default bit 3 is 0: light detected)
if (sum < bright * scanned)
if (!m_sensor->detect_light(m_lightx->read(), m_lighty->read()))
ret |= 0x08;
return ret;
@ -236,63 +166,3 @@ void nes_bandaihs_device::write(u8 data)
if (write_strobe(data))
m_latch = m_joypad->read();
}
//-------------------------------------------------
// R.O.B. specific handlers
//-------------------------------------------------
u8 nes_rob_device::input_r()
{
// R00: lightsensor
return (read_bit34() & 8) ? 1 : 0;
}
void nes_rob_device::output_w(offs_t offset, u8 data)
{
switch (offset & 3)
{
case 0:
// R03: led
m_led_out = BIT(data, 3);
break;
case 1:
// R10-R13: motors: down, up, close, open
for (int i = 0; i < 4; i++)
m_motor_out[i] = BIT(data, i);
break;
case 2:
// R20,R21: motors: right, left
for (int i = 0; i < 2; i++)
m_motor_out[i + 4] = BIT(data, i);
break;
default:
break;
}
}
void nes_rob_device::device_add_mconfig(machine_config &config)
{
SM590(config, m_maincpu, 455_kHz_XTAL);
m_maincpu->read_r<0>().set(FUNC(nes_rob_device::input_r));
m_maincpu->write_r<0>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<1>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<2>().set(FUNC(nes_rob_device::output_w));
m_maincpu->write_r<3>().set(FUNC(nes_rob_device::output_w));
// must use -numscreens 2 to see the output status
config.set_default_layout(layout_nes_rob);
}
ROM_START( nes_rob )
ROM_REGION( 0x200, "maincpu", 0 )
ROM_LOAD( "rfc-cpu10.ic1", 0x000, 0x200, CRC(f9c96b9c) SHA1(a87e2f0f5e454c093d1352ac368aa9e82e9f6790) )
ROM_END
const tiny_rom_entry *nes_rob_device::device_rom_region() const
{
return ROM_NAME(nes_rob);
}

View File

@ -4,7 +4,6 @@
Nintendo Family Computer & Entertainment System Zapper Lightgun
Nintendo Family Computer Bandai Hyper Shot Lightgun
Nintendo R.O.B.
**********************************************************************/
@ -13,9 +12,9 @@
#pragma once
#include "ctrl.h"
#include "cpu/sm510/sm590.h"
#include "zapper_sensor.h"
//**************************************************************************
// TYPE DEFINITIONS
@ -38,11 +37,13 @@ protected:
// device-level overrides
virtual void device_start() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual u8 read_bit34() override;
virtual u8 read_exp(offs_t offset) override;
private:
required_device<nes_zapper_sensor_device> m_sensor;
required_ioport m_lightx;
required_ioport m_lighty;
required_ioport m_trigger;
@ -72,37 +73,8 @@ private:
};
// ======================> nes_rob_device
class nes_rob_device : public nes_zapper_device
{
public:
// construction/destruction
nes_rob_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
virtual ioport_constructor device_input_ports() const override;
protected:
// device-level overrides
virtual void device_start() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual const tiny_rom_entry *device_rom_region() const override;
virtual u8 read_exp(offs_t offset) override { return 0; }
private:
required_device<sm590_device> m_maincpu;
output_finder<6> m_motor_out;
output_finder<> m_led_out;
u8 input_r();
void output_w(offs_t offset, u8 data);
};
// device type definition
DECLARE_DEVICE_TYPE(NES_ZAPPER, nes_zapper_device)
DECLARE_DEVICE_TYPE(NES_BANDAIHS, nes_bandaihs_device)
DECLARE_DEVICE_TYPE(NES_ROB, nes_rob_device)
#endif // MAME_BUS_NES_CTRL_ZAPPER

View File

@ -0,0 +1,72 @@
// license:BSD-3-Clause
// copyright-holders:kmg
/**********************************************************************
HLE of the common photodiode reading routine used in the NES,
Vs. System, and Playchoice-10 light guns, and R.O.B.
These all use a Sharp IR3T07, though the NES zapper is (always?)
seen with the IR3T07A revision.
**********************************************************************/
#include "emu.h"
#include "zapper_sensor.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(NES_ZAPPER_SENSOR, nes_zapper_sensor_device, "nes_zapper_sensor", "Nintendo Zapper Lightgun Photodiode")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// constructor
//-------------------------------------------------
nes_zapper_sensor_device::nes_zapper_sensor_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, NES_ZAPPER_SENSOR, tag, owner, clock)
, m_screen(*this, finder_base::DUMMY_TAG)
{
}
//-------------------------------------------------
// detect_light
//-------------------------------------------------
bool nes_zapper_sensor_device::detect_light(int x, int y)
{
int vpos = m_screen->vpos();
int hpos = m_screen->hpos();
// update the screen if necessary
if (!m_screen->vblank())
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_screen->update_now();
int sum = 0;
int scanned = 0;
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
rgb_t pix = 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))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
// light detected if average brightness is above threshold
return sum >= bright * scanned;
}

View File

@ -0,0 +1,53 @@
// license:BSD-3-Clause
// copyright-holders:kmg
/**********************************************************************
HLE of the common photodiode reading routine used in the NES,
Vs. System, and Playchoice-10 light guns, and R.O.B.
**********************************************************************/
#ifndef MAME_BUS_NES_CTRL_ZAPPER_SENSOR
#define MAME_BUS_NES_CTRL_ZAPPER_SENSOR
#pragma once
#include "screen.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> nes_zapper_sensor_device
class nes_zapper_sensor_device : public device_t
{
public:
// construction/destruction
nes_zapper_sensor_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
template <typename T> void set_screen_tag(T &&tag) { m_screen.set_tag(std::forward<T>(tag)); }
bool detect_light(int x, int y);
protected:
// device-level overrides
virtual void device_start() override { }
private:
// radius of circle picked up by the gun's photodiode
static constexpr int radius = 5;
// brightness threshold
static constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
static constexpr int sustain = 22;
required_device<screen_device> m_screen;
};
// device type definition
DECLARE_DEVICE_TYPE(NES_ZAPPER_SENSOR, nes_zapper_sensor_device)
#endif // MAME_BUS_NES_CTRL_ZAPPER_SENSOR

View File

@ -808,6 +808,8 @@ void playch10_state::playch10(machine_config &config)
m_ppu->int_callback().set_inputline(m_cartcpu, INPUT_LINE_NMI);
m_ppu->int_callback().append(FUNC(playch10_state::int_detect_w));
NES_ZAPPER_SENSOR(config, m_sensor, 0).set_screen_tag("bottom");
SPEAKER(config, "mono").front_center();
m_cartcpu->add_route(ALL_OUTPUTS, "mono", 0.50);

View File

@ -1770,6 +1770,8 @@ void vsnes_state::vsnes(machine_config &config)
m_ppu1->set_cpu_tag(m_maincpu);
m_ppu1->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
NES_ZAPPER_SENSOR(config, m_sensor, 0).set_screen_tag("screen1");
/* sound hardware */
SPEAKER(config, "mono").front_center();
maincpu.add_route(ALL_OUTPUTS, "mono", 0.50);

View File

@ -5,6 +5,7 @@
#pragma once
#include "bus/nes_ctrl/zapper_sensor.h"
#include "cpu/m6502/n2a03.h"
#include "machine/rp5h01.h"
#include "video/ppu2c0x.h"
@ -23,6 +24,7 @@ public:
, m_ram_8w(*this, "ram_8w")
, m_videoram(*this, "videoram")
, m_gfxdecode(*this, "gfxdecode")
, m_sensor(*this, "sensor")
, m_prg_banks(*this, "prg%u", 0U)
, m_prg_view(*this, "prg_view")
, m_vrom_region(*this, "gfx2")
@ -149,6 +151,7 @@ private:
required_shared_ptr<uint8_t> m_ram_8w;
required_shared_ptr<uint8_t> m_videoram;
required_device<gfxdecode_device> m_gfxdecode;
required_device<nes_zapper_sensor_device> m_sensor;
void init_prg_banking();
void prg32(int bank);

View File

@ -1,6 +1,7 @@
// license:BSD-3-Clause
// copyright-holders:Pierpaolo Prazzoli
#include "bus/nes_ctrl/zapper_sensor.h"
#include "machine/nvram.h"
#include "sound/sn76496.h"
#include "video/ppu2c0x.h"
@ -16,6 +17,7 @@ public:
, m_ppu2(*this, "ppu2")
, m_sn1(*this, "sn1")
, m_sn2(*this, "sn2")
, m_sensor(*this, "sensor")
, m_nvram(*this, "nvram")
, m_gfx1_rom(*this, "gfx1")
, m_chr_banks(*this, "chr%u", 0U)
@ -56,6 +58,7 @@ private:
optional_device<sn76489_device> m_sn1;
optional_device<sn76489_device> m_sn2;
optional_device<nes_zapper_sensor_device> m_sensor;
optional_device<nvram_device> m_nvram;
optional_memory_region m_gfx1_rom;

View File

@ -207,44 +207,8 @@ uint8_t playch10_state::pc10_in1_r()
if (m_pc10_gun_controller)
{
int trigger = ioport("P1")->read();
int x = ioport("GUNX")->read();
int y = ioport("GUNY")->read();
// radius of circle picked up by gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 22;
int vpos = m_ppu->screen().vpos();
int hpos = m_ppu->screen().hpos();
// update the screen if necessary
if (!m_ppu->screen().vblank())
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_ppu->screen().update_now();
int sum = 0;
int scanned = 0;
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
rgb_t pix = m_ppu->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))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
// light not detected if average brightness is below threshold (default bit 3 is 0: light detected)
if (sum < bright * scanned)
if (!m_sensor->detect_light(ioport("GUNX")->read(), ioport("GUNY")->read()))
ret |= 0x08;
// now, add the trigger if not masked

View File

@ -116,45 +116,7 @@ void vsnes_state::gun_in0_w(u8 data)
// load up the latch
m_input_latch[0] = ioport("IN0")->read();
// do the gun thing
int x = ioport("GUNX")->read();
int y = ioport("GUNY")->read();
// radius of circle picked up by the gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 22;
int vpos = m_ppu1->screen().vpos();
int hpos = m_ppu1->screen().hpos();
// update the screen if necessary
if (!m_ppu1->screen().vblank())
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_ppu1->screen().update_now();
int sum = 0;
int scanned = 0;
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
rgb_t pix = m_ppu1->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))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
// light detected if average brightness is above threshold
if (sum >= bright * scanned)
if (m_sensor->detect_light(ioport("GUNX")->read(), ioport("GUNY")->read()))
m_input_latch[0] |= 0x40;
}