mirror of
https://github.com/holub/mame
synced 2025-04-16 05:24:54 +03:00
bus/nes_ctrl: Split zapper sensor off as a reusable device. (#9417)
This commit is contained in:
parent
665f605c65
commit
acca7d5898
@ -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 {
|
||||
|
@ -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"
|
||||
|
140
src/devices/bus/nes_ctrl/rob.cpp
Normal file
140
src/devices/bus/nes_ctrl/rob.cpp
Normal 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);
|
||||
}
|
55
src/devices/bus/nes_ctrl/rob.h
Normal file
55
src/devices/bus/nes_ctrl/rob.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
72
src/devices/bus/nes_ctrl/zapper_sensor.cpp
Normal file
72
src/devices/bus/nes_ctrl/zapper_sensor.cpp
Normal 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;
|
||||
}
|
53
src/devices/bus/nes_ctrl/zapper_sensor.h
Normal file
53
src/devices/bus/nes_ctrl/zapper_sensor.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user