SMPC
The SMPC (System Management & Peripheral Control) is a Hitachi 4-bit MCU with built-in program ROM. The actual part number is HD404920FS, but the chip is branded with a Sega custom part number of 315-5744.
Contents
Tasks
The SMPC carries out the following tasks:
- Turn on/off other parts of the system. (CPU's, custom chips, etc.)
- Maintain internal timekeeping functions.
- Change the system clock speed generated by the PLL.
- Poll peripherals in the I/O ports.
- Provide SH-2 interface to I/O ports for direct programming.
The SMPC is battery backed, and earlier models of the Saturn have a button in the battery compartment to force a SMPC reset.
Memory map
Address | Name | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | What's there |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x20100001 | IREG0 | Input registers for command issuing. Whatever data the SMPC needs for processing command goes here. | ||||||||||||||||
0x20100003 | IREG1 | See IREG0 | ||||||||||||||||
0x20100005 | IREG2 | See IREG0 | ||||||||||||||||
0x20100007 | IREG3 | See IREG0 | ||||||||||||||||
0x20100009 | IREG4 | See IREG0 | ||||||||||||||||
0x2010000B | IREG5 | See IREG0 | ||||||||||||||||
0x2010000D | IREG6 | See IREG0 | ||||||||||||||||
0x2010001F | COMREG | The command that's supposed to be issued by the SMPC | ||||||||||||||||
0x20100021 | OREG0 | Ouput Register for the command. If it supports it, it'll output any return data here | ||||||||||||||||
0x20100023 | OREG1 | See OREG0 | ||||||||||||||||
0x20100025 | OREG2 | See OREG0 | ||||||||||||||||
0x20100027 | OREG3 | See OREG0 | ||||||||||||||||
0x20100029 | OREG4 | See OREG0 | ||||||||||||||||
0x2010002B | OREG5 | See OREG0 | ||||||||||||||||
0x2010002D | OREG6 | See OREG0 | ||||||||||||||||
0x2010002F | OREG7 | See OREG0 | ||||||||||||||||
0x20100031 | OREG8 | See OREG0 | ||||||||||||||||
0x20100033 | OREG9 | See OREG0 | ||||||||||||||||
0x20100035 | OREG10 | See OREG0 | ||||||||||||||||
0x20100037 | OREG11 | See OREG0 | ||||||||||||||||
0x20100039 | OREG12 | See OREG0 | ||||||||||||||||
0x2010003B | OREG13 | See OREG0 | ||||||||||||||||
0x2010003D | OREG14 | See OREG0 | ||||||||||||||||
0x2010003F | OREG15 | See OREG0 | ||||||||||||||||
0x20100041 | OREG16 | See OREG0 | ||||||||||||||||
0x20100043 | OREG17 | See OREG0 | ||||||||||||||||
0x20100045 | OREG18 | See OREG0 | ||||||||||||||||
0x20100047 | OREG19 | See OREG0 | ||||||||||||||||
0x20100049 | OREG20 | See OREG0 | ||||||||||||||||
0x2010004B | OREG21 | See OREG0 | ||||||||||||||||
0x2010004D | OREG22 | See OREG0 | ||||||||||||||||
0x2010004F | OREG23 | See OREG0 | ||||||||||||||||
0x20100051 | OREG24 | See OREG0 | ||||||||||||||||
0x20100053 | OREG25 | See OREG0 | ||||||||||||||||
0x20100055 | OREG26 | See OREG0 | ||||||||||||||||
0x20100057 | OREG27 | See OREG0 | ||||||||||||||||
0x20100059 | OREG28 | See OREG0 | ||||||||||||||||
0x2010005B | OREG29 | See OREG0 | ||||||||||||||||
0x2010005D | OREG30 | See OREG0 | ||||||||||||||||
0x2010005F | OREG31 | See OREG0 | ||||||||||||||||
0x20100061 | SR | Status Register | ||||||||||||||||
0x20100063 | SF | Status Flag. Shows the status of the SMPC command. Normally you set this to 1 when issuing out a command, and then the SMPC clears it when it's finished. | ||||||||||||||||
0x20100075 | PDR1 | Port Data Register for Port 1. | ||||||||||||||||
0x20100077 | PDR2 | Port Data Register for Port 2. | ||||||||||||||||
0x20100079 | DDR1 | Data Direction Register for Port 1. Controls direction of each bit in PDR(they can either be read or write). | ||||||||||||||||
0x2010007B | DDR2 | Data Direction Register for Port 2. See DDR1. | ||||||||||||||||
0x2010007D | IOSEL | Input/Output Select Register. Use this to control whether the SMPC automatically polls the peripheral port or whether it's done manually through the DDR/PDR registers | ||||||||||||||||
0x2010007F | EXLE | External Latch Enable Register. Enables input from the VDP2 external latch or peripheral data bit 6 to trigger SCU Pad interrupt. |
Commands
Command | Value | Description | Input registers | Output registers |
---|---|---|---|---|
MSHON | 0x0 | Resets and enables the SH-2 Master CPU. | None | OREG31 |
SSHON | 0x2 | Resets and enables the SH-2 Slave CPU. | None | OREG31 |
SSHOFF | 0x3 | Disables the SH-2 Slave CPU. | None | OREG31 |
SNDON | 0x6 | Resets and enables the Motorola C68K (sound) CPU. | None | OREG31 |
SNDOFF | 0x7 | Disables the Motorola C68K (sound) CPU. | None | OREG31 |
CDON | 0x8 | Resets and enables the CD Block. | None | OREG31 |
CDOFF | 0x9 | Disables the CD Block. | None | OREG31 |
NETLINKON | 0xA | Resets and enables Netlink execution. | None | OREG31 |
NETLINKOFF | 0xB | Disables Netlink execution. | None | OREG31 |
SYSRES | 0xD | Resets the System. | None | OREG31 |
CKCHG352 | 0xE | Changes the system clockspeed | None | OREG31 |
CKCHG320 | 0xF | Changes the system clockspeed | None | OREG31 |
INTBACK | 0x10 | Fetches the SMPC status and peripheral data. | IREG0~IREG2 | OREG0~OREG31 |
SETTIME | 0x16 | Sets the date and time for the RTC | IREG0~IREG6 | OREG31 |
SETSMEM | 0x17 | Sets the 4-byte battery-backed memory contained on the SMPC(which is used by the bios for language settings, etc. | IREG0~IREG3 | OREG31 |
NMIREQ | 0x18 | Sends an NMI request to the Master SH2 | None | OREG31 |
RESENAB | 0x19 | Enables NMI requests to be sent when the Reset button is pressed. | None | OREG31 |
RESDISA | 0x1A | Disables NMI requests to be sent when the Reset button is pressed. | None | OREG31 |
I/O Ports
The Saturn has two 7-bit I/O ports. Here's a diagram of the port itself and a list of pin assignments. The names on the left are the Sega Genesis style naming conventions.
Left Right .-----------------. | 1 2 3 4 5 6 7 8 9 | +-=-=-=-=-=-=-=-=-=-+
Pin 1 - +5v Pin 2 - D2 (Left) Pin 3 - D3 (Right) Pin 4 - D4 (TL) Pin 5 - D5 (TR) Pin 6 - D6 (TH) Pin 7 - D0 (Up) Pin 8 - D1 (Down) Pin 9 - Ground
SMPC Control Mode
Finish me
SH-2 Direct Mode
The SMPC uses the following registers for I/O port control:
- 0x20100075 - PDR1 I/O port #1 pin output level (1=high, 0=low)
- 0x20100077 - PDR2 I/O port #2 pin output level (1=high, 0=low)
- 0x20100079 - DDR1 I/O port #1 pin direction (1=output, 0=input)
- 0x2010007B - DDR2 I/O port #2 pin direction (1=output, 0=input)
For each register, bits 6-0 correspond to TH,TR,TL,D3,D2,D1,D0. Bit 7 is unused and will latch whatever value was written to it.
Pins defined as an output through DDRx will return the value you set in PDRx. Pins defined as an input return whatever value was sent to that pin by the peripheral in use.
For an empty I/O port, each bit returns 1. (so $7F would be read from PDRx assuming the software hadn't set bit 7 beforehand)
0x2010007D - IOSEL
D1 - I/O port #2 D0 - I/O port #1
The lower two bits direct the SMPC to poll or ignore the corresponding I/O port. (0= poll, 1= ignore)
This doesn't stop you from doing direct I/O anyway, but is used to prevent a conflict since the SMPC will attempt to poll it at the same time.
Unused bits latch whatever value was last written.
Saturn Control Pad
The standard Saturn control pad can be read using direct I/O. To do this, set up the following registers like so:
DDR1 = 0x60 Port 1 - pins TH,TR as outputs, TL,D3-D0 are inputs DDR2 = 0x60 Port 2 - pins TH,TR as outputs, TL,D3-D0 are inputs IOSEL = 0x03 Use direct I/O mode instead of SMPC polling
You can then get pad data by writing a 2-bit value to the TH,TR pins and reading the lower four bits of either input port. The values read back are the following:
When PDRx = 0x60
D4 : Always '1' D3 : Left shoulder button (0=pressed, 1=released) D2 : Always '1' D1 : Always '0' D0 : Always '0'
When PDRx = 0x40
D4 : Always '1' D3 : Start button (0=pressed, 1=released) D2 : Button A (0=pressed, 1=released) D1 : Button C (0=pressed, 1=released) D0 : Button B (0=pressed, 1=released)
When PDRx = 0x20
D4 : Always '1' D3 : Right direction (0=pressed, 1=released) D2 : Left direction (0=pressed, 1=released) D1 : Down direction (0=pressed, 1=released) D0 : Up direction (0=pressed, 1=released)
When PDRx = 0x00
D4 : Always '1' D3 : Right shoulder button (0=pressed, 1=released) D2 : Button X (0=pressed, 1=released) D1 : Button Y (0=pressed, 1=released) D0 : Button Z (0=pressed, 1=released)
A small delay is needed between changing the state of PDR1 and reading the new button data. The pad I have seems to need no delay, but perhaps 3rd party controllers using slower logic may need more time before they return new data after switching the input source.
Three-line Handshake
Some peripherals use what Sega call a "3-line handshake" protocol for data transfer. In this protocol, TH and TR are outputs, and TL and D3-D0 are inputs. TH acts like an active low chip select for the connected controller, and TR acts like a data toggle. TL acts like an acknowledge and data is returned on D3-D0. The data on D3-D0 is valid when TL equals TR. When TL does not equal TR, the value of D3-D0 is undefined.
Assuming DDR1=0x60, PDR1=0x00, IOSEL=0x00, here's how to identify a 3-line handshake device:
Write to PDRx | Read from PDRx |
---|---|
60 | D1 : 0, D0 : 1 |
20 | D1 : 0, D0 : 1 |
To read the device, start by setting TH and TR to 1. Then set both TH and TR to 0, and wait until TL becomes 0. The first nibble of data is now available on D3-D0. Next, set TR to 1 and wait until TL becomes 1, then read the next nibble. Repeat this until all the data is read. Once finished, set TH and TR to 1.
The first returned nibble is the device ID, the next nibble indicates the number of data bytes following. Note that this may not be true of all devices.
NiGHTS Analog Pad
The controller that comes packaged with the NiGHTS video game has the same buttons as a standard controller, but also has an analog thumb pad, and the two shoulder buttons return analog as well as digital values.
A switch on the pad selects analog pad mode and digital pad mode, I'll describe how both work.
Analog mode
The pad has some hardware that does the A-D conversion of the analog inputs and sends the data in nibble sized units through the I/O port using the 3-line handshake protocol as described above.
This table lists the values returned by the controller when idle:
Nibble | Read from PDRx | Description |
---|---|---|
1 | 1 | Device ID? |
2 | 6 | Data bytes? |
3 | F | Direction keys: right, left, down, up |
4 | F | Buttons: Start, A, C, B |
5 | F | Buttons: Right shoulder, X, Y, Z |
6 | F | Buttons: Left shoulder, 1, 1, 1 |
7 | 8 | Analog pad X axis MSB |
8 | 0 | Analog pad X axis LSB |
9 | 8 | Analog pad Y axis MSB |
A | 0 | Analog pad Y axis LSB |
B | 0 | Right shoulder MSB |
C | 0 | Right shoulder LSB |
D | 0 | Left shoulder MSB |
E | 0 | Left shoulder LSB |
F | 0 | No data |
10..n | 1 | The controller stops sending data, TL stays at 1 |
The digital buttons return '0' when pressed and '1' when released.
The two shoulder buttons return an unsigned value of $00 (not pressed) to $FF (fully pressed). The digital shoulder button bits return '0' when the analog value goes above around $90, and remain at that value until the analog value drops to below around $56.
The analog pad axis return $80,$80 at the center, $00 at the topmost or leftmost positions, and $FF at the bottommost or rightmost positions.
The analog pad is in a circular shape, so some settings (such as $00,$FF for all the way left and down) are impossible to get.
Digital mode
In this mode the analog thumb pad is disabled, and the shoulder buttons return digital values only. The pad is programmed the same way as before, and here's another table:
Nibble | Read from PDRx | Description |
---|---|---|
1 | 0 | Device ID? |
2 | 2 | Data bytes? |
3 | F | Direction keys: right, left, down, up |
4 | F | Buttons: Start, A, C, B |
5 | F | Buttons: Right shoulder, X, Y, Z |
6 | F | Buttons: Left shoulder, 1, 1, 1 |
7..n | 1 | The controller stops sending data, TL stays at 1 |
When in digital mode, the pad does not return the same kind of data that the regular Saturn 6-button pads do, so it has to be read using the 3-line handshake protocol. So don't think of digital mode as being 100% compatible with a standard Saturn controller.