Page 1 of 2
Zapper hit detection
Posted: Sat Mar 02, 2013 9:11 am
by Dafydd
In my game, I'm only going to have 1 target at any time, so I just want to know whether I found a hit or not.
If I've understood this correctly, it's not enough to just read from the zapper - it needs to be done while the frame is being drawn, because the light sensor turns off after a very short time compared to my eyes, which perceive the white area on the screen as being constantly white.
So how do I do this? One idea was to poll $2002 for vblank to end and then have a loop that polls $2002 for vblank to start and $4017 for a hit, or that polls just $4017 while waiting for nmi... how would you more experienced people do it?
Also, are there other light sources I could use, for debugging and such? What kind of light, specifically, does the zapper detect?
Re: Zapper hit detection
Posted: Sat Mar 02, 2013 11:10 am
by tepples
Dafydd wrote:One idea was to poll $2002 for vblank to end and then have a loop that polls $2002 for vblank to start and $4017 for a hit, or that polls just $4017 while waiting for nmi... how would you more experienced people do it?
What I did in
Zap Ruder was set up sprite 0 at the top of the screen, wait for the sprite 0 hit (0 then 1 on $2002 bit 6), and use the "zapkernels" (subroutines that I wrote using timed code to read $4017 once each scanline, whose source code is included with Zap Ruder) to wait up to 192 scanlines for light to be detected. If you just want a Y coordinate (as used in ZapPing), you can leave the whole screen bright and count scanlines.
Otherwise, darken the screen except for the targets and use the Y coordinate to narrow down which target was hit. If this Y coordinate is close to more than one target (not the case in 1 DUCK, but possible for 2 DUCKS or CLAY SHOOTING), turn each on individually. The Zap Ruder menu has two target groups: the boxes on the left and the boxes on the right. When the photodiode turns on, it darkens the groups in sequence over the course of the next two frames and moves the cursor to that group if darkening the group caused the photodiode to turn off.
In my experience, the Zapper's photodiode will turn on reliably when the color in the target area is $10 (light gray), $20-$2C (white and bright colors), or $30-$3C (white and pale colors). It's still unclear whether or not the IC in the Zapper includes a resonator at 15.7 kHz (SDTV horizontal scan rate) to distinguish TV light from other light sources.
Re: Zapper hit detection
Posted: Sat Mar 02, 2013 2:01 pm
by lidnariq
tepples wrote:It's still unclear whether or not the IC in the Zapper includes a resonator at 15.7 kHz (SDTV horizontal scan rate) to distinguish TV light from other light sources.
The IC used in the zapper is identical in functionality to a standard infrared remote demodulator IC; the selective frequency has been changed and the normally-IR photodiode has been replaced with a visible light one. I suppose I should sit down and build a testing rig to characterize the behavior.
Re: Zapper hit detection
Posted: Sun Mar 03, 2013 5:23 pm
by Dafydd
So... if I point the thing at the sun, it should register a hit, right? Why does a lightbulb, or anything that shines a white light, not do the trick? It doesn't react to just ANY visible light, does it?
(I should clarify I do know about the blank screens drawn before and after the frames that draw white hitboxes to prevent cheating)
Re: Zapper hit detection
Posted: Sun Mar 03, 2013 6:05 pm
by tepples
Light from a CRT SDTV turns on and off about 15,700 times a second. Light from a light bulb or the sun does not. The Zapper's demodulator IC detects the difference.
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 12:38 pm
by Dafydd
Ah, so it's not the frequency of the light, but the frequency at which it flashes. Got it.
Well, I made a screen where the upper quarter is in all white and the rest in all black, save for a message that is printed differently depending on whether you are aiming at white or not, and depending on whether the trigger is pulled. It does, however, not react to ANYTHING I do to it - pulling the trigger, aiming the zapper at the TV or elsewhere, into a pillow while pressing the trigger... nothing happens. It's just stuck displaying HIT. What am I doing wrong?
Code: Select all
wait_for_hit_and_vblank:
lda #$08
cmp $4017 ; set Z if A == $08, i.e. if $4017.3 is 1,
; i.e. light sense: not detected
bne wait_for_just_vblank ; if Z is not set: light detected!
lda #$10
cmp $4017 ; set Z if A == $10, i.e. if $4017.4 is 1,
; i.e. trigger is pulled
beq wait_for_just_vblank ; if Z is set: trigger pulled!
bit $2002
bpl wait_for_hit_and_vblank ; still no hit registered, wait some more
; we're now in vblank with no hit or trigger pull registered. Display MISS
lda $2002
lda #$22
sta $2006
lda #$2E
sta $2006
lda #$4D ; M
sta $2007
lda #$49 ; I
sta $2007
lda #$53 ; S
sta $2007
sta $2007
jmp set_scroll
; we're still not in vblank, so wait for it...
wait_for_just_vblank:
bit $2002
bpl wait_for_just_vblank
; we're now in vblank with either a hit or a pulled trigger. Display HIT
lda $2002
lda #$22
sta $2006
lda #$2E
sta $2006
lda #$48 ; H
sta $2007
lda #$49 ; I
sta $2007
lda #$54 ; T
sta $2007
set_scroll:
lda #$00
sta $2000
sta $2005
sta $2005
jmp wait_for_hit_and_vblank
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 12:50 pm
by 3gengames
You use bad programming, which is why. Here's the explaination:
Code: Select all
lda #$08
cmp $4017 ; set Z if A == $08, i.e. if $4017.3 is 1,
; i.e. light sense: not detected
bne wait_for_just_vblank ; if Z is not set: light detected!
lda #$10
cmp $4017 ; set Z if A == $10, i.e. if $4017.4 is 1,
; i.e. trigger is pulled
bne wait_for_just_vblank ; if Z is set: trigger is pulled!
$4017 return what it's supposed to OR'd with $40 because of open bus. AND other bits can be set, too. To get the bits you need the right way, use LDA+BIT or LDA+AND.
Code: Select all
lda #$08
bit $4017 ; set Z if A == $08, i.e. if $4017.3 is 1,
; i.e. light sense: not detected
bne wait_for_just_vblank ; if Z is not set: light detected!
lda #$10
bit $4017 ; set Z if A == $10, i.e. if $4017.4 is 1,
; i.e. trigger is pulled
bne wait_for_just_vblank ; if Z is set: trigger is pulled!
Code: Select all
lda $4017
and #$08 ; set Z if A == $08, i.e. if $4017.3 is 1,
; i.e. light sense: not detected
bne wait_for_just_vblank ; if Z is not set: light detected!
lda $4017
and #$10 ; set Z if A == $10, i.e. if $4017.4 is 1,
; i.e. trigger is pulled
bne wait_for_just_vblank ; if Z is set: trigger is pulled!
And on a side note, if you are not using pre-defined labels for the NES hardware, I'd say start doing that as looking at text is easier then looking at a bunch of numbers, especially when you do a ton of hardware interaction as remembering the registers is a waste of time to just having exactly what you're modifying in the name.
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 1:17 pm
by Dafydd
3gengames wrote:You use bad programming, which is why.
Fair enough...
Unfortunately, your code doesn't work either.
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 2:46 pm
by tepples
In case you missed it the first time:
Zap Ruder includes working code for Zapper polling that you are free to use in your own game.
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 3:02 pm
by Dafydd
I should have thanked you the first time

I had a look at it, and I don't really see what I'm doing differently from you (besides the obvious, what with the timed code and all). I have to say, I'm very impressed by the accuracy you've achieved. However, copy+pasting someone else's code wholesale kind of defeats the whole point of why I'm doing this in the first place, and this is way too advanced for me right now (it's also NTSC specific). I only have 1 target on the screen, so the code would be very simple, but I want to
understand what I'm doing.
... um. Ok, I changed
Code: Select all
lda $4017
and #$08
beq wait_for_just_vblank
for
Code: Select all
lda #$08
and $4017
beq wait_for_just_vblank
(as per zapkernel's example) and now it works. Also
Code: Select all
lda #$10
and $4017
bne wait_for_just_vblank
works for trigger detection.
So close, 3gengames!

I still don't know why this works and the other way didn't, but I'm going to find out.
Thanks again, tepples! Or, um, both of you, actually

Re: Zapper hit detection
Posted: Mon Mar 04, 2013 3:25 pm
by Dafydd
Umm... nope. Changed it back, still works. I think it was a misuse of beq and bne that did it. ...somehow. Here's some code that works, anyway. Maybe something similar should be on the wiki for noobs like me?
Code: Select all
lda #$08; 00001000
and $4017; ????1??? <- miss
; result: A=00001000, Z=0
beq hit_reported; results in no branch because Z=0
lda #$08; 00001000
and $4017; ????0??? <- hit
; result: A=00000000, Z=1
beq hit_reported; results in branch because Z=1
lda #$10; 00010000
and $4017; ???1???? <- trigger
; result: A=00001000, Z=0
bne trigger_pulled; results in branch because Z=0
lda #$10; 00010000
and $4017; ???0???? <- no trigger
; result: A=00000000, Z=1
bne trigger_pulled; results in no branch because Z=1
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 3:37 pm
by Quietust
tepples wrote:It's still unclear whether or not the IC in the Zapper includes a resonator at 15.7 kHz (SDTV horizontal scan rate) to distinguish TV light from other light sources.
It's my recollection that it's not the IC itself but a capacitor or resistor elsewhere in the circuit - I recall somebody replacing it in order to make their zapper sensitive to 31.5kHz and work properly with scan-doubled NTSC (in fact, I think it may have been kevtris back when he first made his FPGA console).
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 6:15 pm
by lidnariq
The RC pair off pin 3 (22Ω + 1µF) has a corner frequency of hsync rate÷2. This probably determines the bandwidth of the filter, i.e. the number of scanlines necessary to count.
Assuming that the mechanism of the IR3T07 is similar to the contemporary CX20106 or its equivalent GL3274, the 390KΩ resistor should be what determines the scanrate.
Amusing piece of trivia: The reason that the trigger is "true" for ≈6 vblanks is because that's approximately 10kΩ (the pullup inside the console) × 10µF (the capacitor inside the zapper) = 0.1s
Re: Zapper hit detection
Posted: Mon Mar 04, 2013 6:54 pm
by tepples
lidnariq wrote:Amusing piece of trivia: The reason that the trigger is "true" for ≈6 vblanks is because that's approximately 10kΩ (the pullup inside the console) × 10µF (the capacitor inside the zapper) = 0.1s
On an unmodified Zapper, I can hold the trigger halfway in and keep it true indefinitely. One activity in Zap Ruder relies on this to change the instrument's timbre.
Re: Zapper hit detection
Posted: Tue Mar 05, 2013 10:48 am
by Dafydd
Hmm,
This didn't work for me, anyway. Did they just make that up, or are there light bulbs that flash with the same frequency as CRT monitors?
But then again,
