2018-12-07 06:09:46 -05:00
|
|
|
|
// Protovision 4 player interface / Classical Games adapter (1997)
|
|
|
|
|
// Arduino Pro Micro
|
|
|
|
|
// https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters
|
|
|
|
|
// https://www.protovision.games/hardw/build4player.php?language=en
|
|
|
|
|
// http://cloud.cbm8bit.com/penfold42/joytester.zip
|
|
|
|
|
// https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926
|
2018-12-07 07:31:50 -05:00
|
|
|
|
// http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
|
2018-12-07 06:09:46 -05:00
|
|
|
|
// http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf
|
|
|
|
|
// http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf
|
|
|
|
|
// http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png
|
|
|
|
|
// https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf
|
|
|
|
|
// https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf
|
|
|
|
|
|
|
|
|
|
// TXD (INT3,PD3) and RXD (INT2,PD2) to userport L.
|
|
|
|
|
// INT2(RXD) is used for rising edge and INT3(TXD) for falling edge.
|
|
|
|
|
// Interrupts takes only less than 1us to change output port state after select-signal.
|
|
|
|
|
|
|
|
|
|
// Joystick port 3
|
|
|
|
|
// GND = GND (8)
|
|
|
|
|
#define up1 (~PD & _BV(7)) // 6,PD7 = (1)
|
|
|
|
|
#define down1 (~PC & _BV(6)) // 5,PC6 = (2)
|
|
|
|
|
#define left1 (~PD & _BV(4)) // 4,PD4 = (3)
|
|
|
|
|
#define right1 (~PD & _BV(0)) // 3,PD0 = (4)
|
|
|
|
|
#define fire1 (~PD & _BV(1)) // 2,PD1 = (6)
|
|
|
|
|
|
|
|
|
|
// Joystick port 4
|
|
|
|
|
// GND = GND (8)
|
|
|
|
|
#define up2 (~PF & _BV(7)) // A0,PF7 = (1)
|
|
|
|
|
#define down2 (~PF & _BV(6)) // A1,PF6 = (2)
|
|
|
|
|
#define left2 (~PF & _BV(5)) // A2,PF5 = (3)
|
|
|
|
|
#define right2 (~PF & _BV(4)) // A3,PF4 = (4)
|
|
|
|
|
#define fire2 (~PE & _BV(6)) // 7,PE6 = (6)
|
|
|
|
|
|
|
|
|
|
// Arduino <-> Userport
|
|
|
|
|
// VCC = +5V (2)
|
|
|
|
|
// GND = GND (A)
|
|
|
|
|
// TXD+RXD = Select = PB7 (L)
|
|
|
|
|
#define upC 1 // 15,PB1 = PB0 (C)
|
|
|
|
|
#define downC 3 // 14,PB3 = PB1 (D)
|
|
|
|
|
#define leftC 2 // 16,PB2 = PB2 (E)
|
|
|
|
|
#define rightC 6 // 10,PB6 = PB3 (F)
|
|
|
|
|
#define fire1C 4 // 8,PB4 = PB4 (H)
|
|
|
|
|
#define fire2C 5 // 9,PB5 = PB5 (J)
|
|
|
|
|
|
2018-12-07 07:31:50 -05:00
|
|
|
|
// LEDS (inverted):
|
2018-12-07 06:09:46 -05:00
|
|
|
|
// RX = D17,PB0
|
|
|
|
|
// TX = -,PD5
|
|
|
|
|
|
2018-12-07 07:41:02 -05:00
|
|
|
|
//volatile uint16_t last_interrupt;
|
2018-12-07 06:10:59 -05:00
|
|
|
|
volatile uint8_t *ptr;
|
2018-12-07 06:09:46 -05:00
|
|
|
|
|
|
|
|
|
ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3
|
|
|
|
|
asm volatile(
|
2018-12-07 07:31:50 -05:00
|
|
|
|
//" push r0 \n" // save register r0
|
|
|
|
|
//" lds r0, output1 \n"
|
|
|
|
|
" out %[pin], %[gpio] \n" // GPIOR0 address is 30, so we can use it directly with out-command (which works with 0-31)
|
|
|
|
|
//" pop r0 \n" // restore previous r0
|
2018-12-07 06:09:46 -05:00
|
|
|
|
" rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue
|
2018-12-07 07:31:50 -05:00
|
|
|
|
:: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0)));
|
2018-12-07 06:09:46 -05:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 07:41:02 -05:00
|
|
|
|
//ISR(INT2_vect_part_2) { ptr = &GPIOR0; last_interrupt = TCNT1; }
|
|
|
|
|
ISR(INT2_vect_part_2) { ptr = &GPIOR0; }
|
2018-12-07 06:09:46 -05:00
|
|
|
|
|
|
|
|
|
ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4
|
|
|
|
|
asm volatile(
|
|
|
|
|
" push r0 \n" // save register r0
|
2018-12-07 07:31:50 -05:00
|
|
|
|
" lds r0, %[gpio] \n" // GPIOR1 address is 42 and out-command works only with 0-31
|
2018-12-07 07:33:38 -05:00
|
|
|
|
" out %[pin], r0 \n" // so we need lds-command and r0 register
|
2018-12-07 06:09:46 -05:00
|
|
|
|
" pop r0 \n" // restore previous r0
|
|
|
|
|
" rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue
|
2018-12-07 07:31:50 -05:00
|
|
|
|
:: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1)));
|
2018-12-07 06:09:46 -05:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-07 07:41:02 -05:00
|
|
|
|
//ISR(INT3_vect_part_2) { ptr = &GPIOR1; last_interrupt = TCNT1; }
|
|
|
|
|
ISR(INT3_vect_part_2) { ptr = &GPIOR1; }
|
2018-12-07 06:09:46 -05:00
|
|
|
|
|
|
|
|
|
void setup() {
|
2018-12-07 07:34:48 -05:00
|
|
|
|
ptr = &GPIOR0;
|
2018-12-07 07:31:50 -05:00
|
|
|
|
|
2018-12-07 06:09:46 -05:00
|
|
|
|
pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input
|
|
|
|
|
pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input
|
|
|
|
|
DDRB = 0xff; //all PB-ports are outputs
|
|
|
|
|
DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups
|
2018-12-07 07:31:50 -05:00
|
|
|
|
DDRD = B00100000; PORTD = B11010011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup), TX-LED on
|
2018-12-07 06:09:46 -05:00
|
|
|
|
|
|
|
|
|
TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR)
|
|
|
|
|
|
|
|
|
|
EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx)
|
|
|
|
|
EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx)
|
|
|
|
|
|
|
|
|
|
//Serial.begin(115200);
|
2018-12-07 07:33:38 -05:00
|
|
|
|
//PORTD &= ~_BV(5); // TX-LED on
|
2018-12-07 06:09:46 -05:00
|
|
|
|
|
|
|
|
|
// We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock".
|
2018-12-07 07:41:02 -05:00
|
|
|
|
/*TIMSK1 = 0; // disable timer1 interrupts
|
2018-12-07 06:09:46 -05:00
|
|
|
|
TCCR1A = 0;
|
|
|
|
|
TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us.
|
2018-12-07 07:41:02 -05:00
|
|
|
|
TCNT1 = 0; // reset Timer1 counter*/
|
2018-12-07 06:09:46 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
volatile uint8_t joy1, joy2;
|
|
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
|
uint8_t PF, PD, PC, PE;
|
|
|
|
|
PF = PINF; PD = PIND; PC = PINC; PE = PINE;
|
2018-12-07 07:31:50 -05:00
|
|
|
|
joy1 = 0xff; joy2 = 0xff; // all signals are inverted
|
2018-12-07 06:09:46 -05:00
|
|
|
|
if (up1) bitClear(joy1,upC);
|
|
|
|
|
if (down1) bitClear(joy1,downC);
|
|
|
|
|
if (left1) bitClear(joy1,leftC);
|
|
|
|
|
if (right1) bitClear(joy1,rightC);
|
|
|
|
|
if (up2) bitClear(joy2,upC);
|
|
|
|
|
if (down2) bitClear(joy2,downC);
|
|
|
|
|
if (left2) bitClear(joy2,leftC);
|
|
|
|
|
if (right2) bitClear(joy2,rightC);
|
|
|
|
|
if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); }
|
|
|
|
|
if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); }
|
2018-12-07 07:31:50 -05:00
|
|
|
|
|
|
|
|
|
GPIOR0 = joy1; GPIOR1 = joy2;
|
|
|
|
|
|
2018-12-07 07:33:59 -05:00
|
|
|
|
PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer?
|
2018-12-07 07:31:50 -05:00
|
|
|
|
//delayMicroseconds(10);
|
2018-12-07 06:09:46 -05:00
|
|
|
|
}
|