diff options
Diffstat (limited to 'src/devices/bus/sms_ctrl/lphaser.cpp')
-rw-r--r-- | src/devices/bus/sms_ctrl/lphaser.cpp | 80 |
1 files changed, 33 insertions, 47 deletions
diff --git a/src/devices/bus/sms_ctrl/lphaser.cpp b/src/devices/bus/sms_ctrl/lphaser.cpp index d9fb0443ec8..7e64e7c7236 100644 --- a/src/devices/bus/sms_ctrl/lphaser.cpp +++ b/src/devices/bus/sms_ctrl/lphaser.cpp @@ -45,7 +45,9 @@ READ_LINE_MEMBER( sms_light_phaser_device::th_pin_r ) INPUT_CHANGED_MEMBER( sms_light_phaser_device::position_changed ) { if (newval != oldval) - sensor_check(); + { + sensor_check(0); + } } @@ -87,7 +89,9 @@ sms_light_phaser_device::sms_light_phaser_device(const machine_config &mconfig, device_sms_control_port_interface(mconfig, *this), m_lphaser_pins(*this, "CTRL_PORT"), m_lphaser_x(*this, "LPHASER_X"), - m_lphaser_y(*this, "LPHASER_Y"), m_sensor_last_state(0), m_lphaser_timer(nullptr) + m_lphaser_y(*this, "LPHASER_Y"), + m_sensor_last_state(0), + m_lphaser_timer(nullptr) { } @@ -99,7 +103,7 @@ sms_light_phaser_device::sms_light_phaser_device(const machine_config &mconfig, void sms_light_phaser_device::device_start() { save_item(NAME(m_sensor_last_state)); - m_lphaser_timer = timer_alloc(TIMER_LPHASER); + m_lphaser_timer = timer_alloc(FUNC(sms_light_phaser_device::sensor_check), this); } @@ -121,28 +125,28 @@ uint8_t sms_light_phaser_device::peripheral_r() /* Light Phaser (light gun) emulation notes: - - The sensor is activated based on color brightness of some individual - pixels being drawn by the beam, at circular area where the gun is aiming. + - The sensor is activated based on the brightness of individual pixels being + drawn by the beam, in circular area where the gun is aiming. - Currently, brightness is calculated based only on single pixels. - In general, after the trigger is pressed, games draw the next frame using - a light color pattern, to make sure sensor will be activated. If emulation - skips that frame, sensor may stay deactivated. Frameskip set to 0 (no skip) + a bright pattern, to make sure the sensor will be activated. If the emulation + skips that frame, the sensor may stay deactivated. Setting frameskip to 0 is recommended to avoid problems. - - When sensor switches from on (0) to off (1), a value is latched for the + - When the sensor switches from on (0) to off (1), a value is latched for the HCount register. - - When sensor switches from off to on, a flag is set. The emulation uses the - flag to signal that TH line is activated when the status of the input port - is read. After read, the flag is cleared, or else it is cleared later when - the Pause status is read (end of a frame). This is necessary because the + - When the sensor switches from off to on, a flag is set. The emulation uses + this flag to signal that the TH line is activated during status reads of the + input port. After reading, the flag is cleared, or it is cleared when the + Pause status is read at the end of a frame. This is necessary because the "Color & Switch Test" ROM only reads the TH state after VINT occurs. - - The gun test of "Color & Switch Test" is an example that requires checks - of sensor status independent of other events, like trigger press or TH bit + - The gun test in "Color & Switch Test" is an example that requires checks + of the sensor status independent of other events, like trigger press or TH bit reads. Another example is the title screen of "Hang-On & Safari Hunt", where - the game only reads HCount register in a loop, expecting a latch by the gun. - - The whole procedure is managed by a timer callback, that always reschedule - itself to run in some intervals when the beam is at the circular area. + the game only reads the HCount register in a loop, expecting a latch by the gun. + - The whole procedure is managed by a periodic timer callback, which is scheduled + to run in intervals when the beam is within the circular area. */ -int sms_light_phaser_device::bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y ) +int sms_light_phaser_device::bright_aim_area(int lgun_x, int lgun_y) { const int r_x_r = LGUN_RADIUS * LGUN_RADIUS; const rectangle &visarea = m_port->m_screen->visible_area(); @@ -151,8 +155,7 @@ int sms_light_phaser_device::bright_aim_area( emu_timer *timer, int lgun_x, int int beam_y = m_port->m_screen->vpos(); int beam_x_orig = beam_x; int beam_y_orig = beam_y; - int dy, result = 1; - double dx_radius; + int result = 1; bool new_check_point = false; aim_area.min_y = std::max(lgun_y - LGUN_RADIUS, visarea.min_y); @@ -166,23 +169,21 @@ int sms_light_phaser_device::bright_aim_area( emu_timer *timer, int lgun_x, int { beam_y = aim_area.min_y; } - dy = abs(beam_y - lgun_y); + int dy = abs(beam_y - lgun_y); /* Caculate distance in x of the radius, relative to beam's y distance. First try some shortcuts. */ - switch (dy) + double dx_radius = 0; + if (dy == 0) { - case LGUN_RADIUS: - dx_radius = 0; - break; - case 0: dx_radius = LGUN_RADIUS; - break; - default: + } + else if (dy != LGUN_RADIUS) + { /* step 1: r^2 = dx^2 + dy^2 */ /* step 2: dx^2 = r^2 - dy^2 */ /* step 3: dx = sqrt(r^2 - dy^2) */ - dx_radius = ceil((float) sqrt((float) (r_x_r - (dy * dy)))); + dx_radius = ceil(sqrt(double(r_x_r - (dy * dy)))); } aim_area.min_x = std::max(int32_t(lgun_x - dx_radius), visarea.min_x); @@ -252,7 +253,7 @@ int sms_light_phaser_device::bright_aim_area( emu_timer *timer, int lgun_x, int } } - timer->adjust(m_port->m_screen->time_until_pos(beam_y, beam_x)); + m_lphaser_timer->adjust(m_port->m_screen->time_until_pos(beam_y, beam_x)); return result; } @@ -273,30 +274,15 @@ uint16_t sms_light_phaser_device::screen_vpos_nonscaled(int scaled_vpos) } -void sms_light_phaser_device::sensor_check() +TIMER_CALLBACK_MEMBER(sms_light_phaser_device::sensor_check) { - int sensor_new_state; - const int x = screen_hpos_nonscaled(m_lphaser_x->read()); const int y = screen_vpos_nonscaled(m_lphaser_y->read()); - sensor_new_state = bright_aim_area(m_lphaser_timer, x, y); + int sensor_new_state = bright_aim_area(x, y); if (sensor_new_state != m_sensor_last_state) { m_port->th_pin_w(sensor_new_state); m_sensor_last_state = sensor_new_state; } } - - -void sms_light_phaser_device::device_timer(emu_timer &timer, device_timer_id id, int param) -{ - switch (id) - { - case TIMER_LPHASER: - sensor_check(); - break; - default: - throw emu_fatalerror("sms_light_phaser_device(%s): Unknown timer ID", tag()); - } -} |