Atari Jaguar Homebrew Project – Update (Weeks 5 – 6)

This time around I have been looking into how specifically input is detected by the Atari Jaguar. Before discussing HOW input is handled by the Jaguar, it seems proper to explore some of the devices used for input. Below is an image of the standard Atari Jaguar controller:

The standard controller features 3 face buttons, a “start” button, a “select” button, a 4 way directional pad, and a 12 button number pad. This makes for 21 buttons in total that must be considered.

The Jaguar featured 2 controller ports on the front of the unit, allowing for 2 controllers to be connected out of the box. A 4 player multi-tap was also released to allow for up to 4 players per console controller port, thus allowing for a maximum of 8 controllers.

Other controllers were also planned, but never released commercially (with the exception of a modified version of the original controller that added 3 additional face buttons that were remaps of existing number pad buttons). Because of this, the extent of my study is to write code to successfully poll an original controller for its current input and interpret the data I get back. Now that the expected types of input devices have been discussed, lets look at how input is requested in software.

Input handling for the Jaguar can be separated into two steps:
1. Request the current input from 1 subsection of 1 specific controller on each console controller port (meaning two total subsections are requested).
2. Read the state of the requested input from mapped memory.

First tackling the first step, requests for input state are written to the JOYSTICK register, which is a 16-bit wide register starting at memory address $F14000. Most individual bits on this register serve some sort of purpose for the input polling process:
Bits 0 – 3: An address of sorts indicating what “row” and what controller on port 1 we wish to get input for.
Bits 4 – 7: An address of sorts indicating what “row” and what controller on port 2 we wish to get input for.
Bit 8: Enables/Disables audio output system wide. High = Audio enabled. Low = Audio disabled.
Bits 9 – 14: Unused
Bit 15: Signals that input should now be polled. High = enable polling.

When looking at how input is queried for, one can make some observations. Firstly, the system allows a programmer to poll 2 controllers simultaneously. This is an interesting side detail for me personally, but I can easily imagine how (especially in multi-player games) one could optimize input handling with such a feature. As for the concept of a “row”, the Atari Jaguar breaks up each controller into 4 rows. Each row corresponds to a group of 6 buttons. What buttons correspond to what row is consistent from controller to controller regardless of port. This means that in order to get the entire state of a single controller, 4 separate polls must be made. Below is a graphic detailing this segmentation of buttons by row:

As for addressing a specific row for a specific controller, the Jaguar documentation offers this table as a reference for a 2-player configuration:

Standard 2-Player Controller Addressing Matrix
Outbits Bits 3 – 0 Bits 7 – 4 Port Row
0111 X 1 3
1011 X 1 2
1101 X 1 1
1110 X 1 0
1110 X 2 3
1101 X 2 2
1011 X 2 1
0111 X 2 0

Please take note that ports 1 and 2’s addresses mirror each other when addressing the same row. I’m not real sure WHY this is the case, as the bits that are written to already denote the intended port, but it still makes a difference.

Anyhow, now armed with the knowledge of how to address a specific row/controller and where to write the request, all that is left to do is construct our intended message and write it! Lets say we wish to get the state of row 0 from both ports 1 and 2 while keeping the audio enabled (why control of the audio was placed here is also not immediately obvious to me), we can express this by ORing together several values that represent our intention:

1000 0000 0000 0000 = $8000 ;This will enable our request
0000 0001 0000 0000 = $0100 ;This will enable the audio on the system
0000 0000 0111 0000 = $0070 ;This will set up the system to read row 0 on port 2
0000 0000 0000 1110 = $000E ;This will set up the system to read row 0 on port 1
—————————————- ;OR the whole thing together..
1000 0001 0111 1110 = $817E ;and we get the value to write to JOYSTICK

Now that we’ve written our value to JOYSTICK, we need to get the input data back. How we do this is by reading not one, but TWO registers. Our old pal JOYSTICK will store some of our data, while another register called JOYBUTS (address $F14002) is another 16-bit wide register. As far as receiving data goes, this table indicates what bits represent what buttons based on a specific row being requested. Bits referred to with a B followed by a number refers the the JOYBUTS register at the bit matching the number. Bits referred to with a J followed by a number refers the the JOYSTICK register at the bit matching the number.

Bit Row 0 Row 1 Row 2 Row 3
B0 Port1 – Pause Port1 – C1 Port1 – C2 Port1 – C3
B1 Port1 – A Port1 – B Port1 – C Port1 – Option
B2 Port2 – Pause Port2 – C1 Port2 – C2 Port2 – C3
B3 Port2 – A Port2 – B Port2 – C Port2 – Option
J8 Port1 – Up Port1 – * Port1 – 0 Port1 – #
J9 Port1 – Down Port1 – 7 Port1 – 8 Port1 – 9
J10 Port1 – Left Port1 – 4 Port1 – 5 Port1 – 6
J11 Port1 – Right Port1 – 1 Port1 – 2 Port1 – 3
J12 Port2 – Up Port2 – * Port2 – 0 Port2 – #
J13 Port2 – Down Port2 – 7 Port2 – 8 Port2 – 9
J14 Port2 – Left Port2 – 4 Port2 – 5 Port2 – 6
J15 Port2 – Right Port2 – 1 Port2 – 2 Port2 – 3

If a bit is of the value 0, then the button is pressed. A stored value of 1 indicates that the button is not being pressed.

Some of the more astute readers out there may have noticed references made to a C1, C2, and C3. These bits, instead of referencing buttons on the controller, are used to identify the type of controller being used (well, C2 and C3 are). Despite reading and re-reading the section of the documentation dealing with input handling, I could not find a use for C1. C2 and C3 may be compared to the chart below to identify the controller currently connected to the port in question:

C2 C3 Controller Identification
0 0 Reserved
0 1 Bank Switching Controller (Analog Joystick, VR Helmet, Racing Wheel)
1 0 “Tempest” Rotary Controller
1 1 “Standard” Jaguar Controller or No Controller

This covers some of the bits contained within JOYSTICK and JOYBUTS, but what do the other bits do? Well, I’m glad I asked, because here’s the answer!
Bits 0 – 3: See B0 – B3 in the chart above.
Bit 4: Identifier of video hardware. 1 = NTSC, 2 = PAL.
Bits 5 – 7: Reserved
Bits 8 – 15: Ignored

Bit 0: Cartridge EEPROM output data. (Not sure what this means, other than apparently output from the EEPROM. No further detail was given 🙁 )
Bits 1 – 7: Ignored
Bits 8 – 15: See J8 – J15 in the chart above.

So there we have handling simple input on the Atari Jaguar. As the controller identification table above indicates, the Jaguar was designed to handle more complicated controllers known as “Bank Switching” controllers. Since I am kinda cramming to get all my project info up on this site before GDC 2011, I will be omitting handling those controllers and the 4-Player Team Tap for now. Once my other projects are online, I will revisit this post and add that information. Its very interesting, but not terribly relevant to my goals, as I own no “Bank Switching” controllers. I will end this post today with a quick code snippet I wrote to confirm my understanding of input handling. The following is written in 68k asm and lacks any of the bootstrap code needed to initialize the system.

;The following demo is set up such that the program will loop polling for input.
;If UP is pressed, Data Register 0 (d0) will have the hex value 0xDEADBEEF
;written to it and the program will enter an infinite loop.
;If DOWN is pressed, Data Register 4 (d4) will have the hex value 0xDEADBEEF
;written to it and the program will enter an infinite loop.
.globl ___sam
.extern _DrawString
.include “”

    ;Get input from row 0 of port 1 (I write f for port 2 because I don’t care about port 2)
    lea JOYSTICK,a0
    move.w #$81fe, (a0)
    ;Check if UP is being pressed on controller 1.
    move.l #0, d1 ;Make sure our storage register is clear
    move.w (a0), d1 ;Copy the contents of JOYSTICK to d1
    move.l d1, d5 ;Copy the data of d1 into d5 so d1’s data remains undisturbed during the comparison
    and.w #256, d5 ;Checking if J8 is 0
    cmp #0, d5
    ;If so, write $DEADBEEF to d0 and go to infiLoop
    beq UpLoop
    ;Check if DOWN is being pressed on controller 1.
    move.l d1, d5 ;Since d5 no longer stores the value of JOYSTICK, re-copy it over
    and.w #512, d5 ;Checking against J9
    cmp #0, d5
    ;If so, write $DEADBEEF to d4 and go to infiLoop
    ;else, branch to __sam
    beq DownLoop
    bra ___sam

    move.l #$DEADBEEF, d0
    bra UpLoop

    move.l #$DEADBEEF, d4
    bra DownLoop

Cheers and have a good one Internet,
Sam Bushman

Leave a Comment