Files
CrazyRedMachine_UltimatePop…/popnController/ps2.c
T

238 lines
5.2 KiB
C

#include "ps2.h"
#if defined(ARDUINO_ARCH_AVR)
/* USER CUSTOMIZABLE SETTINGS */
#define ACK_PORT PORTD
#define ACK_DDR DDRD
#define ACK_PIN 5 // PD5 (TXLED)
/* RECOMMENDED DO NOT CHANGE */
#define INVERT_CIPO 0 // Set to 1 if CIPO is open-drain via transistor (recommended)
#define INVERT_ACK 0 // Set to 1 if ACK is open-drain via transistor (recommended)
/* END OF USER CUSTOMIZABLE SETTINGS */
// Stores a constructed packet for the PS2.
uint16_t Data = 0;
// List of available PS2 inputs.
PS2_InputList_t *PS2Input = NULL;
// Current PS2 state.
void (*PS2Handler)(uint8_t) = NULL;
void PS2_Acknowledge(void) {
// Burn a few cycles before acknowledging
asm volatile(
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
);
#if INVERT_ACK == 0
ACK_DDR |= (1<<ACK_PIN);
#else
ACK_DDR &= ~(1<<ACK_PIN);
#endif
// 40 cycles of delay should give us the same delay as a real PS1 controller
asm volatile(
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
);
#if INVERT_ACK == 0
ACK_DDR &= ~(1<<ACK_PIN);
#else
ACK_DDR |= (1<<ACK_PIN);
#endif
}
void PS2_Listen(uint8_t in);
void PS2_Addressed(uint8_t in);
void PS2_HeaderFinished(uint8_t in);
void PS2_LowerSent(uint8_t in);
uint8_t memory_card_timeout = 0;
void PS2_MemoryCardTimeout(uint8_t in) {
if (memory_card_timeout) --memory_card_timeout;
if (!memory_card_timeout)
PS2Handler = PS2_Listen;
}
void PS2_MemoryCardID2(uint8_t in) {
// If we receive a 0x01 here, revert back to the listener
--memory_card_timeout;
if (in == 0x01) {
memory_card_timeout = 0;
PS2_Listen(in);
return;
}
PS2Handler = PS2_MemoryCardTimeout;
}
void PS2_MemoryCardID1(uint8_t in) {
// If we receive a 0x01 here, revert back to the listener
--memory_card_timeout;
if (in == 0x01) {
memory_card_timeout = 0;
PS2_Listen(in);
return;
}
PS2Handler = PS2_MemoryCardID2;
}
// Memory card addressed; ignore input
void PS2_MemoryCardAddressed(uint8_t in) {
memory_card_timeout = 8;
if (in == 'R')
memory_card_timeout = 138;
if (in == 'W')
memory_card_timeout = 136;
PS2Handler = PS2_MemoryCardID1;
}
// Idle state.
void PS2_Listen(uint8_t in) {
if (in == 0x81) {
DDRB &= ~0x08;
PS2Handler = PS2_MemoryCardAddressed;
}
// Report as a digital controller when addressed
if (in == 0x01) {
DDRB |= 0x08;
#if INVERT_CIPO == 1
SPDR = ~0x41;
#else
SPDR = 0x41;
#endif
PS2Handler = PS2_Addressed;
PS2_Acknowledge();
}
}
// When polling is requested, begin responding
void PS2_Addressed(uint8_t in) {
if (in == 0x42) {
#if INVERT_CIPO == 1
SPDR = ~0x5A;
#else
SPDR = 0x5A;
#endif
PS2Handler = PS2_HeaderFinished;
PS2_Acknowledge();
}
}
// After end-of-header sent, send the first byte
void PS2_HeaderFinished(uint8_t in) {
uint8_t *data = (uint8_t *)&Data;
#if INVERT_CIPO == 1
SPDR = ~(*data);
#else
SPDR = (*data);
#endif
PS2Handler = PS2_LowerSent;
PS2_Acknowledge();
}
// After first byte sent, send the second and go back to listening.
void PS2_LowerSent(uint8_t in) {
uint8_t *data = (uint8_t *)&Data + 1;
#if INVERT_CIPO == 1
SPDR = ~(*data);
#else
SPDR = (*data);
#endif
PS2Handler = PS2_Listen;
PS2_Acknowledge();
}
void PS2_Init(void) {
cli();
#if INVERT_ACK == 0
ACK_PORT &= ~(1<<ACK_PIN);
#else
ACK_PORT |= (1<<ACK_PIN);
#endif
PS2_Acknowledge();
// Set MISO as an output pin
DDRB |= 0x08;
// Setup data on falling edge, sample on rising edge (SPI mode 3)
SPCR = (1 << CPOL) | (1 << CPHA)
// Transmit LSB first
| (1 << DORD)
// Enable interrupts for SPI
| (1 << SPIE)
// Enable SPI
| (1 << SPE);
// Set the first byte up
#if INVERT_CIPO == 1
SPDR = 0x00; // 0xFF;
#else
SPDR = 0xFF;
#endif
PS2Handler = PS2_Listen;
sei();
}
// Update the stored data packet
void PS2_Task(void) {
if (PINB & 0x01)
{
#if INVERT_CIPO == 1
SPDR = 0x00;
#else
SPDR = 0xFF;
#endif
}
PS2_InputList_t *map = PS2Input;
uint16_t new_data = 0;
while(map) {
if (!map->input || (*map->input & map->mask))
new_data |= map->buttons;
map = map->parent;
}
Data = ~new_data;
}
void PS2_MapInput(uint16_t *input, uint16_t mask, PS2_INPUT buttons) {
PS2_InputList_t *child = calloc(1, sizeof(PS2_InputList_t));
child->input = input,
child->mask = mask,
child->buttons = buttons,
child->parent = PS2Input;
PS2Input = child;
}
void PS2_AlwaysInput(PS2_INPUT buttons) {
PS2_InputList_t *child = calloc(1, sizeof(PS2_InputList_t));
child->input = NULL,
child->mask = 0,
child->buttons = buttons,
child->parent = PS2Input;
PS2Input = child;
}
// When a transfer is complete, determine what to do next
ISR(SPI_STC_vect) {
uint8_t input = SPDR;
if (input == 0x01 && (!memory_card_timeout)) PS2Handler = PS2_Listen;
PS2Handler(SPDR);
}
#endif