Detecting screen (X,Y) location for the NES Zapper

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
WhoaMan
Posts: 168
Joined: Sat Oct 02, 2004 12:07 pm

Detecting screen (X,Y) location for the NES Zapper

Post by WhoaMan » Mon Jun 03, 2013 2:41 pm

I have been thinking about this off and on for a few years now and finally got around to testing it out, so far it seems to be working so I decided to see what others think about this method. What I am trying to do, is detect where on the screen (divided in to 16x16 pixel blocks for a grid of 16x15) the NES Zapper is aimed when the trigger is pulled. The major drawback about doing this how many frames it takes to do the detection. This method uses the second name table's attribute bytes to quickly display the test patterns.

For my testing, I had my palette setup as follows:
$0f, $0f, $0f, $0f
$0f, $30, $30, $30
$0f, $30, $30, $30
$0f, $30, $30, $30
$0f, $30, $30, $30
$0f, $30, $30, $30
$0f, $30, $30, $30
$0f, $30, $30, $30

I set the first name table ($2000) to a blank tile showing the background color and the second name table ($2800) to a blank tile showing the second color.


Main program loop:
check for trigger and update trigger flag (set if trigger pulled, unset otherwise)
ORA the Zapper's hit bit against previous stored value

NMI:
check trigger flag, if set jump to zapper handler
otherwise, run normal NMI stuff

Zapper Handler:
-- Phase 0, set Scroll to display second name table. disable background and sprites. set zapper X and Y variables to 0
-- Phase 1, check for phase 0 hit (to check for cheating). Write Phase 1 pattern shown below to second attribute table
-- Phase 2, check for phase 1 hit. if bit set, ora $08 against Zapper Y and save. Write Phase 2 pattern shown below to second attribute table
-- Phase 3, check for phase 2 hit. if bit set, ora $04 against Zapper Y and save. Write Phase 3 pattern shown below to second attribute table
-- Phase 4, check for phase 3 hit. if bit set, ora $02 against Zapper Y and save. Write Phase 4 pattern shown below to second attribute table
-- Phase 5, check for phase 4 hit. if bit set, ora $01 against Zapper Y and save. Write Phase 5 pattern shown below to second attribute table
-- Phase 6, check for phase 5 hit. if bit set, ora $08 against Zapper X and save. Write Phase 6 pattern shown below to second attribute table
-- Phase 7, check for phase 6 hit. if bit set, ora $04 against Zapper X and save. Write Phase 7 pattern shown below to second attribute table
-- Phase 8, check for phase 7 hit. if bit set, ora $02 against Zapper X and save. Write Phase 8 pattern shown below to second attribute table
-- Phase 9, check for phase 8 hit. if bit set, ora $01 against Zapper X and save. reset scroll to show first name table. clear phase counter and trigger flag


Setup and checking for cheating (Phases 0 and 1)
Image


Detecting Y Location (Phases 1 thru 5)
Image
---
Image
---
Image
---
Image


Detecting X Location (Phases 5 thru 9)
Image
---
Image
---
Image
---
Image

Anyway, I am curious to see what others think about this, if anyone has ideas for improvement, or if anyone things i should abandon this idea :)

I hope to clean up and re-write my test code tonight, after doing so I will post it here.
i dont have much to say

User avatar
Quietust
Posts: 1492
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Detecting screen (X,Y) location for the NES Zapper

Post by Quietust » Mon Jun 03, 2013 2:58 pm

Zap Ruder effectively does this already, though it makes use of cycle timing instead of binary searches.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

tepples
Posts: 21750
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Detecting screen (X,Y) location for the NES Zapper

Post by tepples » Mon Jun 03, 2013 3:29 pm

Zap Ruder does the Y assuming 113.667 cycles per line. I could never get it doing X reliably; it works on emulators, but on NES + PowerPak, the onset of light detection is delayed by up to about 80 pixels (15 microseconds) from where the Zapper is pointed. I imagine Operation Wolf does the same thing with its white Y detection frame before it shows a black screen with a white "cloud" that scans across for eight frames. (Video of Operation Wolf)

If you want to use vertical columns to detect X, a 4-frame search for the X position would need to use Frank Gray's reflected binary code, not plain binary code as you demonstrate, because the Zapper samples an area rather than a single pixel and might return false results if one frame is black on the left and white on the right and another frame is white on the left and black on the right in the same position. This happens all across the screen in binary code, but any given position will have only one transition in reflected binary. A reflected binary sequence would look like this:

Code: Select all

1                                 ...
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 ................%%%%%%%%%%%%%%%%
4 ........%%%%%%%%%%%%%%%%........
5 ....%%%%%%%%........%%%%%%%%....
6 ..%%%%....%%%%....%%%%....%%%%..
The six frames are used as follows:
  1. Player pulled the trigger during this frame. Check for cheating at the end of the following vblank; you can use the negative edge on sprite 0 to time this. If light is detected at the end of vblank, the player either is cheating or (because the photodiode bit returns 1 for no light) has unplugged the Zapper; ZapPing treats this as an unplugged Zapper and falls back to controller mode.
  2. Brighten the whole screen and use timed code to search linearly for the Y coordinate.
  3. Narrow to 128px
  4. Narrow to 64px
  5. Narrow to 32px
  6. Narrow to 16px
You wouldn't be able to get a continuous X position fix because of all the flicker.

Another tip: The NES Zapper supports both burst and continuous fire mode. Pull the trigger all the way for the normal burst mode (button active for about 5 frames); pull it only halfway for continuous fire. The "Axe" activity in Zap Ruder plays different timbres for burst and continuous fire.

Drag
Posts: 1285
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: Detecting screen (X,Y) location for the NES Zapper

Post by Drag » Mon Jun 03, 2013 4:01 pm

If you're already using timed code to check the Y position, why not sample the zapper twice per scanline during that check? (Once in the middle and once at the end) This would eliminate an entire frame of X-scanning.

You may be able to sample 4 times too, but I haven't run the numbers to see if the CPU's quick enough to do that.

Edit: This entirely depends on whether the zapper requires a direct shot with the electron beam, or if it can pick up the glow of the phosphors, and this is information I don't know. :P

Edit: Nevermind! I'm dumb. The reason you don't do this is because the light sensor doesn't react immediately when it detects something.

Edit: So why not show some kind of gun calibration screen before the game starts? Tell the user to aim at the center of the screen, and then show a sweeping bar from left to right, and use that to calibrate the delay between the screen and the zapper's reaction.

tepples
Posts: 21750
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Detecting screen (X,Y) location for the NES Zapper

Post by tepples » Mon Jun 03, 2013 5:19 pm

A Super Scope style calibration would help if the delay were constant, like audio or video lag on an HDTV. But fifteen seconds with my attempt to read X in Zap Ruder will show you that the delay changes randomly from frame to frame.

lidnariq
Posts: 8778
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Detecting screen (X,Y) location for the NES Zapper

Post by lidnariq » Mon Jun 03, 2013 7:47 pm

I wrote some very simple PIC code to shine a white LED at the zapper photodiode, and hooked up an oscilloscope to the demodulator's analog and digital (hysteretic) outputs.

There is a tremendous amount of noise in the signal, but it's a function of the brightness and duration. A very bright signal will produce a very reliable phase lock, even without repetition, but a dimmer signal allows phase noise. (Regardless of whether the input was repetitive or not, the digital output went true approximately 4÷(16kHz) later, and remained high even without reinforcement for ~10 scanlines). The 16kHz frequency filter is pretty lousy; I got reasonable results at 4 and 64kHz too. I'm pretty confident a zapper could be used with an LCD TV, but with reduced range, and the code would have to compensate for frame lag.

If people want oscilloscope traces, I can post some.

(I actually did this months ago but got distracted before I wrote it up)

User avatar
Memblers
Site Admin
Posts: 3770
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: Detecting screen (X,Y) location for the NES Zapper

Post by Memblers » Tue Jun 04, 2013 2:49 pm

A while back I thought of an (totally untested) hardware hack to read the Zapper in one frame. The Zapper control bits are present on the expansion port. So in theory you could put an MCU on an expansion board, with a timer that is reset by the /NMI signal (which is also on the exp port). Then it's just a matter of converting the timer count into X/Y values.

Though I suppose one could also use the controller port instead of the expansion port, and pass the Zapper through the MCU board. You'd have to strobe/poll the port at the same time every frame. So the connector would be easier to get, but at the cost of some jitter from the 6502's normal interrupt latency (assuming you just polled it in NMI like a normal controller).

lidnariq
Posts: 8778
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Detecting screen (X,Y) location for the NES Zapper

Post by lidnariq » Tue Jun 04, 2013 3:30 pm

The problem is that the repurposed IR demodulator inside the Zapper imposes a whole scanline of noise, so it's basically impossible to get an X coordinate out of it.

I guess I should actually go ahead and set up a simple input->output timing test.

Post Reply