Parallel-Port SMS Control Pad

Published January 15, 2007
Advertisement
I've been wanting to attach an SMS control pad to my PC (and be able to use it to play games with) for a while, so put in an order from those excellent chaps at Rapid for the parts needed.

The joypad (as I've now learned from disassembly) is very primitive - 6 normally-open switches, each connected between a pin on the DE-9 connector and ground. The accepted layout adapter uses the 25-pin parallel port, connecting ground to pin 18, power to pin 1 (not that the control pad uses this pin) and 7 further connections from D0 to D6 for the buttons.


Master System Control Pad and a poorly-soldered DB-25 to DE-9 adapter.

I had been assured that the data lines on parallel ports (D0..D7) were pulled up, and so the layout seemed easy enough - D0..D6 will return highs normally, and when a button is pressed it is connected to ground.

Unfortunately, for whatever reason the data lines on the parallel port on my PC are not pulled up, at least not in any way that I can find to control. However, if you set the lines to be outputs (using bit 5 of the control register), set them all high, then flip them to inputs, they'll read as highs for a while until they float (slowly) back low again. I've used this to my advantage, and so have this:

/// Flags corresponding to which buttons are pressed.[Flags]public enum Buttons {    None = 0x00,    Up = 0x01,    Down = 0x02,    Left = 0x04,    Right = 0x08,    Button1 = 0x10,    Button2 = 0x20,    All = 0x3F,}   // Retrieve the status of the port.private Buttons GetRawStatus() {    // Set D0..D7 as outputs.    Output(this.BaseAddress + 2, 0x00);    // Set them high:    Output(this.BaseAddress + 0, 0xFF);    // Set D0..D7 as inputs.    Output(this.BaseAddress + 2, 0x20);    // Retrieve, invert and mask the data lines.    return (Buttons)(~(Input(this.BaseAddress + 0)) & (int)Buttons.All);}


This works very well, with one small problem: nothing is debounced, so pressing any button causes 10 or so press/release actions to be detected until the contacts settle. Therefore, the exposed method for retrieving the status is this:

/// Gets the status of the buttons from the connected SMS joypad./// The status of the buttons.public Buttons GetStatus() {    if (!this.Debounced) {        return GetRawStatus();    } else {        Buttons Last = GetRawStatus();        Buttons Current;        int MaximumIterations = 100;        while (((Current = GetRawStatus()) != Last) && (MaximumIterations-- > 0)) {            Last = Current;            Thread.Sleep(0);        }        return Current;    }}


For some strange reason, this doesn't quite work; after a while (or rebooting, or reading/writing the EPP registers) the port starts reading nothing but zeroes again. Running another piece of software that uses the parallel port fixes it.

One missing feature of the emulator was support for the SMS pause button. This button is attached to the Z80's non-maskable interrupt line, so pressing it results in the CPU pushing the program counter to the stack then jumping to $0066.

For most games the pause button just pauses the game, but for some others it will display a menu - such as in Psycho Fox, which lets you use the items you have collected to change animal or use a power-up.


Psycho Fox's in-game menu

One major long-standing bug in the emulator has been interrupt handling by the CPU. I think I've (finally!) got it, though it's still not entirely perfect. How I've set it up now is that a flag is set - IntPending or NmiPending, depending on whether the maskable or non-maskable interrupt pin has been modified - when the interrupt is requested, and cleared when it's been handled.



Japanese Master System BIOS

I have updated the memory emulation to better support BIOS ROMs. Initially, the "Majesco" Game Gear BIOS and some of the "Snail Maze" SMS BIOS worked (though the SMS BIOS would display "Software Error" on several games). I've tested a few of them and they seem to work pretty well.


Hang On and Safari Hunt

Whilst the Japanese BIOS has (in my opinion) the best final effect, it's the M404 prototype BIOS that has the best effect overall:

Previous Entry Sega BASIC
Next Entry Sega Tween
0 likes 5 comments

Comments

Monder
Why don't you try adding external pull-up resistors? Then everything should work fine.
January 15, 2007 07:18 AM
Ravuya
Ah, 8-bit graphics jiggery-pokery.

How many years I spent glued to the TV wondering how they did some of those SEGA logos on my Genesis. The SMS ones seem really cool, too; the SMS and GG weren't big where I lived so reading your journal is like going to a strange parallel realm of Sega.
January 15, 2007 07:48 AM
benryves
Quote:Original post by Monder
Why don't you try adding external pull-up resistors? Then everything should work fine.
Had I been designing the circuit myself I'd have done so - however, I'm using this layout so for maximum compatibility I'd rather have it work if people hadn't put the resistors in themselves.

The problem is that I seem to be losing the ability to drive pins high or low at all. I normally use the ability after a reboot, but can regain the ability after running a program that uses the port for its own reasons. I can then lose the ability to use the port once again by reading/writing to the EPP registers. (The port is in EPP mode). I don't have a reference, so am not sure what I need to be setting to get the port to behave itself.

Quote:Original post by Ravuya
...the SMS and GG weren't big where I lived so reading your journal is like going to a strange parallel realm of Sega.
I didn't own one myself originally, but I remember that people at school had Sega consoles - Nintendo didn't appear to be as popular.
January 15, 2007 08:43 AM
paulecoyote
awesome!
January 16, 2007 01:42 AM
benryves
Thanks all for the comments. [smile]

I found the bug (I think!) with the parallel port. The parallel port, when in EPP mode, provides two extra registers. Reading and writing to them makes the hardware do all the handshaking for you, making dumping data across to another device very easy.

However, as I don't have an intelligent device at the other end to respond to the transfer, they were timing out. This sets the error flag (which I could read happily) and also prevents the port from reading/writing data correctly - even if manually setting pins.

Running another parallel port application cleared the error flag, so I needed to work out how they did it. Whether it's correct or not, pulling nStrobe low then back high again clears the error flag on my PC, so doing that in the class constructor seems to fix it (nStrobe is used for the power pin in the adapter, so pulling it low/high each read of the port could cause problems).
January 16, 2007 05:23 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement