Created
August 22, 2021 01:49
-
-
Save MMcM/36fed5189a22cd381af2c6e26cd922a6 to your computer and use it in GitHub Desktop.
Coleco Adam Keyboard as ASCII virtual serial port using Teensy 3.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Wiring for Teensy3.2: | |
* Teensy 1(TX1) -- RJ 1 | |
* Teensy 2 -- RJ 2 (optional) | |
* Teensy GND -- RJ 3,4,5 | |
* Teensy Vin -- RJ 6 | |
* | |
* Note that a real Coleco keyboard cable is reversed. | |
* (E.g., https://raw.githubusercontent.com/Kalidomra/AdamNet-Drive-Emulator/master/Connection%20Diagram.jpg) | |
* It is best to check that 3,4,5 are connected to one another on the Teensy end. | |
*/ | |
// See https://console5.com/techwiki/images/b/b5/Coleco_ADAM_Technical_Reference_Manual.pdf, Chapter 3.3. | |
// The high nibble is the message type and the low nibble is the address. | |
// Since we only send to device 1, the keyboard, we bake that in. | |
const uint8_t MN_RESET = 0x01; // command.control (reset) | |
const uint8_t MN_STATUS = 0x11; // command.control (status) | |
const uint8_t MN_ACK = 0x21; // command.control (ack) | |
const uint8_t MN_CLR = 0x31; // command.control (clr) | |
const uint8_t MN_RECEIVE = 0x41; // command.control (receive) | |
const uint8_t MN_CANCEL = 0x51; // command.control (cancel) | |
const uint8_t MN_SEND = 0x61; // command.data (send) | |
const uint8_t MN_NACK = 0x71; // command.control (nack) | |
const uint8_t MN_READY = 0xD1; // command.control (ready) | |
const uint8_t NM_STATUS = 0x81; // response.control (status) | |
const uint8_t NM_ACK = 0x91; // response.control (ack) | |
const uint8_t NM_CANCEL = 0xA1; // response.control (cancel) | |
const uint8_t NM_SEND = 0xB1; // response.data (send) | |
const uint8_t NM_NACK = 0xC1; // response.control (nack) | |
#define DEBUG_ADAMNET 0 | |
void send_command(uint8_t command) { | |
#if DEBUG_ADAMNET | |
Serial.print("S: "); | |
Serial.println(command, HEX); | |
#endif | |
UART0_C3 |= (1 << 5); // Switch Transmit pin direction to output. | |
Serial1.write(command); | |
Serial1.flush(); // Wait for transmit complete. | |
UART0_C3 &= ~(1 << 5); // Switch Transmit pin direction back to input. | |
} | |
bool receive_response(uint8_t *buffer, uint8_t expected_length) { | |
uint8_t len = Serial1.readBytes(buffer, expected_length); | |
#if DEBUG_ADAMNET | |
Serial.print("R:"); | |
for (uint8_t i = 0; i < len; i++) { | |
Serial.print(" "); | |
Serial.print(buffer[i], HEX); | |
} | |
Serial.println(); | |
#endif | |
return len == expected_length; | |
} | |
void setup() { | |
Serial.begin(115200); | |
while (!Serial) { | |
} | |
Serial1.begin(62500, SERIAL_8N1_RXINV_TXINV); | |
UART0_C1 |= (1 << 7) | (1 << 5); // LOOPS RSRC, enabling single-wire mode. | |
Serial1.setTimeout(100); | |
// Hard RESET. | |
pinMode(2, OUTPUT); | |
digitalWrite(2, HIGH); | |
delay(100); | |
// Soft RESET. Mostly turns off LOCK. | |
send_command(MN_RESET); | |
} | |
const uint32_t RECEIVE_INTERVAL = 10; | |
void loop() { | |
static uint32_t last_receive_time = 0; | |
if (millis() - last_receive_time > RECEIVE_INTERVAL) { | |
uint8_t buf[8]; | |
send_command(MN_RECEIVE); | |
if (receive_response(buf, 1)) { | |
if (buf[0] == NM_ACK) { | |
send_command(MN_CLR); // Clear to Send | |
if (receive_response(buf, 5) && buf[0] == NM_SEND) { | |
if (buf[1] == 0 && buf[2] == 1 && buf[3] == buf[4]) { | |
// Length 1, checksum okay. | |
send_command(MN_ACK); | |
#if DEBUG_ADAMNET | |
char ch = buf[3]; | |
Serial.print("A: "); | |
Serial.print(ch, HEX); | |
if (ch > 0x20 && ch < 0x7F) { | |
Serial.print(" "); | |
Serial.write(ch); | |
} | |
Serial.println(); | |
#else | |
Serial.write(buf[3]); | |
#endif | |
last_receive_time = millis(); | |
} else { | |
send_command(MN_NACK); | |
// Try again right away. | |
} | |
} | |
} else if (buf[0] == NM_NACK) { | |
// No input available, wait a bit. | |
last_receive_time = millis(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment