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:
- LPT port
- USB or PS/2 port (using scraped mouse)
- 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 |
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 onLight = False Do While (tmr > Timer) And (Not onLight) onLight = ((myIn(adr_bStatus) And 16) = 16) 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