2017-05-29 20:19:38 +00:00
|
|
|
#include <SPI.h>
|
|
|
|
#include <MCP23S17.h>
|
|
|
|
|
2017-06-01 20:37:16 +00:00
|
|
|
#define DEBUG 0
|
|
|
|
#define KBD_INTERRUPT_ENABLE true
|
|
|
|
#define KBD_SEND_TIMEOUT 23
|
2017-06-01 19:35:01 +00:00
|
|
|
|
2017-05-29 20:19:38 +00:00
|
|
|
#define IO_SS 10
|
2017-06-01 19:35:01 +00:00
|
|
|
|
2017-05-30 20:33:47 +00:00
|
|
|
#define IO_VIDEO 0
|
|
|
|
#define IO_VIDEO_D0 0
|
|
|
|
#define IO_VIDEO_D6 6
|
2017-06-01 19:35:01 +00:00
|
|
|
#define VIDEO_RDA 5
|
|
|
|
#define VIDEO_DA 3
|
2017-05-30 20:33:47 +00:00
|
|
|
|
|
|
|
#define IO_KBD 1
|
2017-05-29 20:19:38 +00:00
|
|
|
#define IO_KBD_D0 8
|
|
|
|
#define IO_KBD_D6 14
|
|
|
|
#define IO_KBD_DA 15
|
|
|
|
#define KBD_READY 2
|
|
|
|
#define KBD_STROBE 4
|
|
|
|
|
|
|
|
MCP23S17 bridge(&SPI, IO_SS, 0);
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
Serial.begin(115200);
|
|
|
|
configure_pins();
|
|
|
|
configure_bridge();
|
2017-05-30 22:35:14 +00:00
|
|
|
output_status();
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void configure_pins() {
|
|
|
|
pinMode(KBD_READY, INPUT);
|
|
|
|
pinMode(VIDEO_DA, INPUT);
|
|
|
|
pinMode(KBD_STROBE, OUTPUT);
|
|
|
|
pinMode(VIDEO_RDA, OUTPUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void configure_bridge() {
|
|
|
|
bridge.begin();
|
|
|
|
|
|
|
|
/* Configure video section */
|
2017-05-30 20:33:47 +00:00
|
|
|
for (int i = IO_VIDEO_D0; i <= IO_VIDEO_D6; i++) {
|
2017-05-29 20:19:38 +00:00
|
|
|
bridge.pinMode(i, INPUT);
|
|
|
|
}
|
2017-05-30 20:33:47 +00:00
|
|
|
bridge.pinMode(7, INPUT_PULLUP);
|
2017-05-29 20:19:38 +00:00
|
|
|
|
|
|
|
/* Configure keyboard section */
|
|
|
|
for (int i = 8; i <= 15; i++) {
|
|
|
|
bridge.pinMode(i, OUTPUT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-30 22:35:14 +00:00
|
|
|
void output_status() {
|
2017-06-01 19:35:01 +00:00
|
|
|
Serial.println("RC6502 Apple 1 Replica");
|
|
|
|
|
|
|
|
debug_value("Video DA", digitalRead(VIDEO_DA));
|
|
|
|
debug_value("Video D0-D6", bridge.readPort(IO_VIDEO) & 127);
|
|
|
|
debug_value("Keyboard RDY", digitalRead(KBD_READY));
|
|
|
|
}
|
2017-05-30 22:35:14 +00:00
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void debug_value(String description, byte value) {
|
|
|
|
debug_value(description, value, 1);
|
2017-05-30 22:35:14 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void debug_value(String description, byte value, int level) {
|
2017-06-01 20:37:16 +00:00
|
|
|
if (DEBUG < level) return;
|
2017-06-01 19:35:01 +00:00
|
|
|
Serial.print(description);
|
|
|
|
Serial.print(": ");
|
|
|
|
print_hex(value);
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void print_hex(byte value) {
|
|
|
|
print_hex(value, true);
|
|
|
|
}
|
2017-05-29 20:19:38 +00:00
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void print_hex(byte value, bool newline) {
|
|
|
|
if (value <= 0xF) {
|
|
|
|
Serial.print("0x0");
|
|
|
|
} else {
|
|
|
|
Serial.print("0x");
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|
2017-06-01 19:35:01 +00:00
|
|
|
|
|
|
|
if (newline) Serial.println(value, HEX);
|
|
|
|
else Serial.print(value, HEX);
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void serial_receive() {
|
|
|
|
if (Serial.available() > 0) {
|
|
|
|
int c = Serial.read();
|
|
|
|
debug_value("PIA RX", c, 10);
|
|
|
|
pia_send(c);
|
|
|
|
}
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|
|
|
|
|
2017-06-01 19:35:01 +00:00
|
|
|
void pia_send(int c) {
|
2017-05-30 20:33:47 +00:00
|
|
|
/* Make sure STROBE signal is off */
|
|
|
|
digitalWrite(KBD_STROBE, LOW);
|
2017-06-01 19:35:01 +00:00
|
|
|
c = map_to_ascii(c);
|
2017-05-30 20:33:47 +00:00
|
|
|
|
|
|
|
/* Output the actual keys as long as it's supported */
|
|
|
|
if (c < 96) {
|
|
|
|
bridge.writePort(IO_KBD, c | 128);
|
2017-06-01 20:37:16 +00:00
|
|
|
|
2017-05-30 20:33:47 +00:00
|
|
|
digitalWrite(KBD_STROBE, HIGH);
|
2017-06-01 20:37:16 +00:00
|
|
|
if (KBD_INTERRUPT_ENABLE) {
|
|
|
|
byte timeout;
|
|
|
|
|
|
|
|
/* Wait for KBD_READY (CA2) to go HIGH */
|
|
|
|
timeout = KBD_SEND_TIMEOUT;
|
|
|
|
while(digitalRead(KBD_READY) != HIGH) {
|
|
|
|
delay(1);
|
|
|
|
if (timeout == 0) break;
|
|
|
|
else timeout--;
|
|
|
|
}
|
|
|
|
digitalWrite(KBD_STROBE, LOW);
|
|
|
|
|
|
|
|
/* Wait for KBD_READY (CA2) to go LOW */
|
|
|
|
timeout = KBD_SEND_TIMEOUT;
|
|
|
|
while(digitalRead(KBD_READY) != LOW) {
|
|
|
|
delay(1);
|
|
|
|
if (timeout == 0) break;
|
|
|
|
else timeout--;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
delay(KBD_SEND_TIMEOUT);
|
|
|
|
digitalWrite(KBD_STROBE, LOW);
|
2017-05-30 20:33:47 +00:00
|
|
|
}
|
2017-06-01 19:35:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char map_to_ascii(int c) {
|
|
|
|
/* Convert ESC key */
|
|
|
|
if (c == 203) {
|
|
|
|
c = 27;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ctrl A-Z */
|
|
|
|
if (c > 576 && c < 603) {
|
|
|
|
c -= 576;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert lowercase keys to UPPERCASE */
|
|
|
|
if (c > 96 && c < 123) {
|
|
|
|
c -= 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void serial_transmit() {
|
|
|
|
digitalWrite(VIDEO_RDA, HIGH);
|
|
|
|
|
|
|
|
if (digitalRead(VIDEO_DA) == HIGH) {
|
|
|
|
char c = bridge.readPort(IO_VIDEO) & 127;
|
|
|
|
debug_value("PIA TX", c, 10);
|
|
|
|
digitalWrite(VIDEO_RDA, LOW);
|
|
|
|
|
2017-06-01 21:02:36 +00:00
|
|
|
delay(12);
|
2017-06-01 19:35:01 +00:00
|
|
|
send_ascii(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char send_ascii(char c) {
|
2017-06-02 18:14:22 +00:00
|
|
|
if (DEBUG >= 5) Serial.print("[");
|
2017-06-01 19:35:01 +00:00
|
|
|
switch (c) {
|
|
|
|
case 0x0d: Serial.println(); /* Replace CR with LF */
|
|
|
|
default:
|
|
|
|
Serial.print(c);
|
2017-05-30 20:33:47 +00:00
|
|
|
}
|
2017-06-02 18:14:22 +00:00
|
|
|
if (DEBUG >= 5) Serial.print("]");
|
2017-05-30 20:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-05-29 20:19:38 +00:00
|
|
|
void loop() {
|
2017-05-30 20:33:47 +00:00
|
|
|
serial_receive();
|
|
|
|
serial_transmit();
|
2017-05-29 20:19:38 +00:00
|
|
|
}
|