This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Zapper Implementation

Zapper Implementation
by on (#49247)
Right now, I'm trying to figure out how to get the zapper to fire one shot at a time. As it stands, when I hold the right-click button down on the mouse, it repeats over and over and over again. I can't quite figure out what to do to make it single shot, such as in every other zapper game : P

Here is the code I have presently running in NMI:

   lda check_shot
   beq :+
      jsr object1_tile_switch
      lda reg2001_save
      ora #%00001000
      sta $2001
      sta reg2001_save
      lda #$00
      sta check_shot
   jmp @not_pulled

:   lda $4017
   and #test_trigger
   beq @not_pulled
      lda #$01
      sta check_shot
      lda reg2001_save
      and #%11110111
      sta $2001
      sta reg2001_save

The jsr object1_tile_switch is actually a badly named routine right now, as it's actually a palette switch for the testing (filling all white). I'm guessing this would be the way to do it, anyway.

But yeah, any ideas on how to make the zapper fire only once per frame when pulled? Thanks for any input; pun intended! ; )

by on (#49250)
I'd try treating it the same as a controller - save the zapper state from the previous frame, then branch if it was previously triggered. The zapper demo I made didn't even try to use the trigger.

by on (#49254)
Wouldn't this be the same as detecting only buttons that were just pressed on the joypad (as opposed to the ones that remain pressed since last time)? That is, invert the old state (EOR $FF) and AND with the current state.

That way, assuming the button/trigger was not pressed last frame, the old state is 0. The old state inverted becomes 1, AND'ed to the new state 1 (button/trigger is pressed) will result in 1, so the button/trigger has *just* been pressed. On the next frame, the old state is 1, and the new is also 1 (button/trigger remains pressed). 1 (old state) inverted is 0, AND'ed with 1 (new state) results in 0, so the button/trigger is either not pressed or remains pressed since last time, but you are not interested in either of those cases.

by on (#49296)
Didn't I hear something about how you have to read it every scanline? Or is that just when you are checking to see if there was a hit?

I'm assuming actually that's just during the reading phase that you have to check every scanline. But what I do in my game is I have three variables:


And this is how my controller code looks:


   lda ControlCurrent
   sta ControlPrevious

   ldx #1
   stx $4016
   stx $4016

   ldy #8
   lda $4016
   lsr a
   rol ControlCurrent
   bne -

   lda ControlCurrent
   and ControlPrevious
   eor ControlCurrent
   sta ControlTrigger

For ControlCurrent and ControlPrevious, it's obvious what those hold. Current holds the current button press bits for this frame. Previous holds the previous frame's button press status. And of course, each bit in the byte holds the status for the corresponding button:

Bit 7 - A button status
Bit 6 - B button status
Bit 5 - Select button Status (or is it start?)
Bit 4 - Start button Status (or is it select? I get these two confused all the time)
Bit 3 - Up button status
Bit 2 - Down button status
Bit 1 - Left button status
Bit 0 - Right button status

However, ControlTrigger holds which buttons have been NEWLY pressed. If you press A this frame, and it hasn't been pressed last frame, it will return a 1 in bit 7. However, if you have pressed A last frame, it will not return a 1 in bit 7. I call it "ControlTrigger" because it kind of reminds me of a trigger of a pistol being pulled or something where it only has effect the instant it's pulled. I didn't know what else to call it. It's really nice to have this byte because I can do things like:

lda ControlTrigger
and #BButton
bne Shoot
.... blah blah code for not shooting

.... blah blah code for shooting

I would absolutely stay away from reading hardware registers in game logic code as much as possible. It is kind of a bug waiting to happen in my opinion. So I would put all the information for button presses and trigger status into bytes in RAM.