Tuesday, December 7, 2010

“NES Zapper” interfaced to Your PC?

“NES Zapper” interfaced to Your PC?

Introduction


Few months ago I got an idea of interfacing NES, FAMICOM or FAR-EAST copy of the named console’s Zapper to my PC. I’ve realized that Zapper I had in my hands is not original Nintendo Zapper, but a non-licensed copy of the named one. Actually the whole console which my parents bought to me a long time ago is non-licensed copy (We were thinking it is original for almost 17 years now.). There is possibility that Zapper I used, and which I will describe here DOES NOT have the same pin out as every other Zapper out there as there were many non-licensed copies at the time.


Picture 1. Zapper

Although it had a futuristic look, and I always wandered how it works, I found out that it is actually very simple system. It only contains a simple contact trigger and few electronic parts on a PCB. PCB board found inside contains two transistors, four resistors, one capacitor and one photo-diode pointed toward the front end of Zapper’s pipe, looking thru optical lens.
There is no some space technology build in, as I thought 15 years ago…  It is actually, when you pull the trigger computer draw black screen with a white rectangle on a target position. If the Zapper is pointed at white rectangle, photo-diode will detect light, and electronic circuit sets a high level (1) on a “light” line, otherwise it will be low level (0), in next frame computer will draw a black frame to see whether player is cheating, if he now get a low level player have successfully hit the target, otherwise it will be considered that Zapper is pointed towards the constant light source and player had cheated. Process is repeated for every possible target on screen.



Picture 2. Zapper’s PCB with components

How to interface it?


First thing that came to my mind is “What is the easiest way to do such a thing?”

I’ve had a few ideas:
  1. LPT port
  2. USB or PS/2 port (using scraped mouse)
  3. USB port (using a microcontroller)


Each of ideas had its flaws and favors.

LPT port


Probably the simplest way to do it, is to use LPT (Parallel Printer) port set to SPP mode, favors are that it does not need any extra hardware except DB25 male connector, four diodes, and some soldering tools. Zapper utilizes 4 lines, +5V, GND, “Light”, “Trigger”, lines “light” and “shoot” are digital TTL level lines. Line “light” is set to logical 1 when the Zapper is pointed towards the light source otherwise is set to logical 0, and “shoot” line is set to logical 1 when the shoot trigger is pulled, otherwise is set to logical 0.


Pin
Name
Description
1
/STROBE
Strobe
2
D0
Data Bit 0
3
D1
Data Bit 1
4
D2
Data Bit 2
5
D3
Data Bit 3
6
D4
Data Bit 4
7
D5
Data Bit 5
8
D6
Data Bit 6
9
D7
Data Bit 7
10
/ACK
Acknowledge
11
BUSY
Busy
12
PE
Paper End
13
SEL
Select
14
/AUTOFD
Autofeed
15
/ERROR
Error
16
/INIT
Initialize
17
/SELIN
Select In
18
GND
Signal Ground
19
GND
Signal Ground
20
GND
Signal Ground
21
GND
Signal Ground
22
GND
Signal Ground
23
GND
Signal Ground
24
GND
Signal Ground
25
GND
Signal Ground
DB25 male connector
Table 1. LPT pin out (got it from Pinouts.ru)

Pin
Description
1
Ground
2
Data Clock
3
Data Latch
4
Serial Data
5
+5V
6
Gun Light
7
Gun Trigger


NES Controller jack

Table 2. NES Controler pin out (got it from diylive.net)

Since LPT port also work at TTL level, only problem I had is how to get +5V from the port. Solution is to use Data lines D0 to D3 (Bits 0 – 3 of Data register), set to logical 1, and soldered to a 4 diodes each line respectively, anode at connector side and cathodes soldered in one point at other.

The data output of the Parallel Port is normally TTL logic levels. Most Parallel Ports implemented in ASIC, can sink and source around 12mA.  However, there are other variations possible: Sink/Source 6mA, Source 12mA/Sink 20mA, Sink 16mA/Source 4mA, Sink/Source 12mA and others.



Scheme 1. DB25 Connector with diodes

For the “Light” and “Trigger” lines I’ve used Status lines PaperEnd and Select (Bits 4 and 5 of Status Register).

For the GND line, I’ve used Common Ground Line of LPT port, which is found on pin 18 of DB25 connector.

Procedure:

1.      Determine your Zapper’s pin-out, you should have 4 or 5 wires. In case there are four wires, you have +5V, GND, “Light”, “Trigger”; in case there are 5 wires there are +5V, GND, “Light”, “Trigger”, and “Trigger”-GND. Simplest way to do so is to volt-meter, and see the voltage between pins, if + wire is on +5V and Ground wire is on GND than it should show 5 Volts, if + wire is on “Light” and Ground wire is set on GND then it should show 5 Volts in the moment when Zapper is pointed towards the light source. For “Trigger” line it should be pretty much clear, there would be a wire soldered directly on a trigger. If you have 5 wires, short-circuit “Trigger”-GND line to GND line.
2.      Solder 5 diodes to each pin respectively for pins 2 to 6, anode to connector, cathode to each other and to +5V line.
3.      Solder GND line to pin 18 of connector.
4.      Solder “Light” line to pin 12.
5.      Solder “Trigger” line to pin 13.
6.      Try out your Zapper with examples found in this section.

Programming of LPT port (SPP mode) is not a hard task here; you’ll need access to only 3 I/O registers; depending on BIOS configuration they should be located at 0x378 for Data register, 0x379 for Status register and 0x380 for Control register. I will provide to you a dynamic library that allows you to access I/O registers from a programming language of your choice, and examples written in Visual Basic 6.0 and FreeBASIC (works under Windows and Linux).

' Stefan Kostic (c) 2009
' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' LightGun for PC

Option Explicit

Const myLPT1 = &H378
Const myLPT2 = &H278
Const myLPT3 = &H3BC

Public bData As Byte
Public bStatus As Byte
Public bControl As Byte

Public adr_bData As Long
Public adr_bStatus As Long
Public adr_bControl As Long

Public Declare Sub myOut Lib "s.dll" (ByVal myPort As Long, ByVal mydata As Byte)
Public Declare Function myIn Lib "s.dll" (ByVal myPort As Long) As Byte

Public Sub InitGun(ByVal PortNumber As Byte)
   
    Dim PortBaseAddress As Long
   
    Select Case PortNumber
    Case 1
        PortBaseAddress = myLPT1
    Case 2
        PortBaseAddress = myLPT2
    Case 3
        PortBaseAddress = myLPT3
    End Select
   
    adr_bData = PortBaseAddress
    adr_bStatus = PortBaseAddress + 1
    adr_bControl = PortBaseAddress + 2
   
    myOut adr_bData, &HFF
   
End Sub

Public Sub DeinitGun()
   
    myOut adr_bData, &H0

End Sub

Public Sub ReadPort()
   
    bData = myIn(adr_bData)
    bStatus = myIn(adr_bStatus)
    bControl = myIn(adr_bControl)
   
End Sub

Public Function onLight() As Boolean

    Dim tmr As Single

    tmr = Timer + 0.005
   
    onLight = False
   
    Do While (tmr > Timer) And (Not onLight)
       
        onLight = ((myIn(adr_bStatus) And 16) = 16)
       
    Loop
               
End Function

Public Function onTriger() As Boolean
   
    onTriger = ((myIn(adr_bStatus) And 32) = 32)
   
End Function

Code 1. VB Module

Take care that your that your BIOS configuration is set so the Parallel port you are using is set to use address that you have selected in program (by default 0x378, but can also be
0x278, 0x3BC).

General notices:


Flaws of using LPT port for interfacing are that newer desktop computers and notebooks do not have LPT port, and you’ll need Administrator privileges to access I/O register under Windows 2K/XP/Vista/7.

In future articles I will describe interfacing with USB and PS/2 ports.

Download 1: VB Example with library
Download 2: FreeBASIC example
Download 3: Input / Output register access library

Author: Stefan Kostic, 06.12.2010.