diff --git a/arduino/kbd/kbd.ino b/arduino/kbd/kbd.ino new file mode 100644 index 0000000..db93ab7 --- /dev/null +++ b/arduino/kbd/kbd.ino @@ -0,0 +1,194 @@ +// keyboard wiring diagram: https://www.minuszerodegrees.net/5150/misc/5150_keyboard_reset.jpg + +constexpr int kClockPin = PC6; +constexpr int kDataPin = PC4; + +constexpr int kBitDelayMicros = 100; +constexpr int kCodeDelayMicros = 200; +constexpr int kKeyDelayMicros = 100; + +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + pinMode(LED_BUILTIN, OUTPUT); + + pinMode(kClockPin, INPUT); + pinMode(kDataPin, INPUT); + + Serial.println("kbd 0.1"); +} + +// 0. reset +// 0.0. clock line will be held low externally for 20 ms +// when back up, keyboard needs to clock 0xAA as a response +// +// 1. operation +// 1.0. just directly clock out scan codes of pressed keys + +void sendCode(int code) { + Serial.printf("sending code 0x%02x\n", code); + // preconditions: clock and data pins are INPUT + + while (digitalRead(kDataPin) == LOW || digitalRead(kClockPin) == LOW) { + // wait. we're not allowed to send + } + + digitalWrite(kDataPin, LOW); + digitalWrite(kClockPin, LOW); + + pinMode(kClockPin, OUTPUT); + delayMicroseconds(kBitDelayMicros); + pinMode(kClockPin, INPUT); + // delayMicroseconds(kDelayMicros); + + for (int i = 0; i < 8; i++) { + if ((code & (1 << i)) == 0) { + // send a 0 + pinMode(kDataPin, OUTPUT); + } // else do nothing, it's already a 1 + delayMicroseconds(kBitDelayMicros); + + pinMode(kClockPin, OUTPUT); + delayMicroseconds(kBitDelayMicros); + pinMode(kDataPin, INPUT); + pinMode(kClockPin, INPUT); + } + delayMicroseconds(kCodeDelayMicros); +} + +enum State { + kReady, + kMaybeReset, +}; + +constexpr int kResetDelay = 19; + +State state = State::kReady; +int lastclocklow = 0; + +constexpr int kLetterCodes[] = { + 0x1E, // 'a' + 0x30, + 0x2E, + 0x20, + 0x12, + 0x21, + 0x22, + 0x23, + 0x17, + 0x24, + 0x25, + 0x26, + 0x32, + 0x31, + 0x18, + 0x19, + 0x10, + 0x13, + 0x1F, + 0x14, + 0x16, + 0x2F, + 0x11, + 0x2D, + 0x15, + 0x2c, +}; + +constexpr int kNumberCodes[] = { + 0x0B, // '0' + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, +}; + +void sendNormalCode(int code) { + sendCode(code); + sendCode(code | 0x80); +} + +void sendShiftCode(int code) { + sendCode(42); // left shift + sendNormalCode(code); + sendCode(42 | 0x80); +} + +void sendAsciiChar(int c) { + if (c >= 'a' && c <= 'z') { + return sendNormalCode(kLetterCodes[c - 'a']); + } else if (c >= '0' && c <= '9') { + return sendNormalCode(kNumberCodes[c - '0']); + } else if (c >= 'A' && c <= 'Z') { + return sendShiftCode(kLetterCodes[c - 'A']); + } + switch (c) { + case '-': return sendNormalCode(12); + case '=': return sendNormalCode(13); + case '[': return sendNormalCode(26); + case ']': return sendNormalCode(27); + case ';': return sendNormalCode(39); + case '\'': return sendNormalCode(40); + case ',': return sendNormalCode(51); + case '.': return sendNormalCode(52); + case '/': return sendNormalCode(53); + case ' ': return sendNormalCode(57); + case '\n': return sendNormalCode(28); + + case '!': return sendShiftCode(2); + case '@': return sendShiftCode(3); + case '#': return sendShiftCode(4); + case '$': return sendShiftCode(5); + case '%': return sendShiftCode(6); + case '^': return sendShiftCode(7); + case '&': return sendShiftCode(8); + case '*': return sendShiftCode(9); + case '(': return sendShiftCode(10); + case ')': return sendShiftCode(11); + case '_': return sendShiftCode(12); + case '+': return sendShiftCode(13); + + case '{': return sendShiftCode(26); + case '}': return sendShiftCode(27); + case ':': return sendShiftCode(39); + case '"': return sendShiftCode(40); + case '<': return sendShiftCode(51); + case '>': return sendShiftCode(52); + case '?': return sendShiftCode(53); + } +} + +void loop() { + static int led_counter = 0; + static int led = HIGH; + if (led_counter > 400000) { + led_counter = 0; + led = (led == HIGH) ? LOW : HIGH; + digitalWrite(LED_BUILTIN, led); + } + led_counter += 1; + + int clockp = digitalRead(kClockPin); + if (state == State::kReady && clockp == LOW) { + state = State::kMaybeReset; + lastclocklow = millis(); + } else if (state == State::kMaybeReset) { + if (clockp == HIGH && millis() - lastclocklow > kResetDelay) { + delay(1); + state = State::kReady; + sendCode(0xaa); + Serial.println("Reset!"); + } + } + + if (Serial.available() > 0) { + int c = Serial.read(); + sendAsciiChar(c); + //delayMicroseconds(kKeyDelayMicros); + } +} diff --git a/arduino/programmer/programmer.ino b/arduino/programmer/programmer.ino new file mode 100644 index 0000000..5278b67 --- /dev/null +++ b/arduino/programmer/programmer.ino @@ -0,0 +1,272 @@ +#define LV(x) ((x) ? HIGH : LOW) + +// pinout sorta like this: https://myoldcomputer.nl/Files/Datasheet/2364-Commodore.pdf + +const int kDataPins[] = { + PA12, + PA11, + PB12, + PB11, + PB2, + PB1, + PB15, + PB14, +}; + +const int kAddressPins[] = { + D15, + D14, + D12, + D11, + D10, + D9, + D8, + D7, + D6, + D5, + D4, + D3, + D2, +}; + +const int kOutputEnablePin = PC9; +const int kWriteEnablePin = PC8; + +void setData(int direction) { + for (int i = 0; i < 8; i++) { + pinMode(kDataPins[i], direction); + } +} + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); + + setData(INPUT); + + for (int i = 0; i < 13; i++) { + pinMode(kAddressPins[i], OUTPUT); + } + + pinMode(kOutputEnablePin, OUTPUT); + digitalWrite(kOutputEnablePin, HIGH); + + pinMode(kWriteEnablePin, OUTPUT); + digitalWrite(kWriteEnablePin, HIGH); +} + +int readData() { + int out = 0; + for (int i = 0; i < 8; i++) { + if (digitalRead(kDataPins[i]) == HIGH) { + out += (1 << i); + } + } + + return out; +} + +void writeData(int data) { + for (int i = 0; i < 8; i++) { + digitalWrite(kDataPins[i], LV(data & (1 << i))); + } +} + +void writeAddress(int address) { + for (int i = 0; i < 13; i++) { + digitalWrite(kAddressPins[i], LV(address & (1 << i))); + } +} + +int read(int address) { + writeAddress(address); + delayMicroseconds(1); + digitalWrite(kOutputEnablePin, LOW); + delayMicroseconds(1); + int out = readData(); + digitalWrite(kOutputEnablePin, HIGH); + + return out; +} + +void write(int address, int data) { + writeAddress(address); + delayMicroseconds(1); + digitalWrite(kWriteEnablePin, LOW); + writeData(data); + delayMicroseconds(1); + digitalWrite(kWriteEnablePin, HIGH); +} + +void chiperase() { + write(0x5555, 0xaa); + write(0x2aaa, 0x55); + write(0x5555, 0x80); + + write(0x5555, 0xaa); + write(0x2aaa, 0x55); + write(0x5555, 0x10); + + delay(20); +} + +constexpr int kBufferSize = 256; +int address = 0; +int led_counter = 0; +int led = HIGH; +char buffer[kBufferSize] = {}; +int buffer_size = 0; + +const char* kSampleData = "this is a blarg"; + +int readnibble(const char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + + return -1; +} + +int readint8(const char* buffer) { + int n1 = readnibble(buffer[0]); + int n2 = readnibble(buffer[1]); + if (n1 < 0 || n2 < 0) return -1; + return (n1 << 4) + n2; +} + +int readint16(const char* buffer) { + int i1 = readint8(buffer); + int i2 = readint8(buffer + 2); + if (i1 < 0 || i2 < 0) return -1; + return (i1 << 8) + i2; +} + +int readpage(const char* buffer, char page[64]) { + for (int i = 0; i < 64; i++) { + int b = readint8(&buffer[i*2]); + if (b < 0) { + Serial.printf("error: wrong hex character %c%c\n", buffer[i*2], buffer[i*2+1]); + return -1; + } + page[i] = b; + } + return 0; +} + +int writepage(int address, const char page[64]) { + int start = micros(); + for (int i = 0; i < 64; i++) { + write(address + i, page[i]); + } + int end = micros(); + delay(10); + + return end - start; +} + +void dump16(int address) { + address = address & 0x1ff0; + + char buff[16]; + for (int i = 0; i < 16; i++) { + buff[i] = read(address + i); + } + + Serial.printf("%04x:", address); + for (int i = 0; i < 16; i++) { + Serial.printf(" %02x", buff[i]); + } + Serial.print(" "); + for (int i = 0; i < 16; i++) { + if (buff[i] >= 33 && buff[i] <= 127) { + Serial.printf("%c", buff[i]); + } else { + Serial.print("."); + } + } + Serial.print("\n"); +} + +void runcmd(const char* buffer, int size) { + int cmd = buffer[0]; + + if (size < 1) { + dump16(address); + address += 16; + return; + } + + if (cmd == '0') { + address = 0; + } else if (cmd == 'a') { + if (size < 5) { + Serial.println("error: incomplete address command"); + return; + } + address = readint16(buffer + 1); + } else if (cmd == 'w') { + if (size < 1 + 4 + 2) { + Serial.println("error: incomplete write command"); + return; + } + int address = readint16(buffer + 1); + int data = readint8(buffer + 5); + setData(OUTPUT); + write(address, data); + delay(10); + setData(INPUT); + Serial.printf("wrote data at 0x%04x\n", address); + dump16(address & 0x1ff0); + } else if (cmd == 'p') { + if (size < 1 + 4 + 128) { + Serial.println("error: incomplete page command"); + return; + } + int address = readint16(buffer + 1) & 0x1fc0; + char page[64]; + if (readpage(buffer + 5, page) != 0) return; + setData(OUTPUT); + int elapsed = writepage(address, page); + setData(INPUT); + Serial.printf("wrote page at 0x%04x in %d us\n", address, elapsed); + dump16(address); + dump16(address + 16); + dump16(address + 32); + dump16(address + 48); + } else if (cmd == 's') { + int addr = address & 0x1ff0; + Serial.printf("writing sample data to 0x%04x\n", addr); + setData(OUTPUT); + for (int i = 0; i < 16; i++) { + write(addr + i, kSampleData[i]); + } + setData(INPUT); + } else if (cmd == 'e') { + setData(OUTPUT); + chiperase(); + setData(INPUT); + Serial.println("chip erased"); + } +} + +void loop() { + if (led_counter > 200000) { + led_counter = 0; + led = (led == HIGH) ? LOW : HIGH; + digitalWrite(LED_BUILTIN, led); + } + led_counter += 1; + + if (Serial.available()) { + char c = Serial.read(); + if (c == '\n') { + buffer[buffer_size] = 0; + runcmd(buffer, buffer_size); + buffer_size = 0; + buffer[0] = 0; + } else { + buffer[buffer_size] = c; + buffer_size = min(kBufferSize - 1, buffer_size + 1); + } + } +} diff --git a/arduino/ramcheck/ramcheck.ino b/arduino/ramcheck/ramcheck.ino new file mode 100644 index 0000000..62eb1bf --- /dev/null +++ b/arduino/ramcheck/ramcheck.ino @@ -0,0 +1,281 @@ +// See RAM chip pinout here: https://www.digchip.com/datasheets/parts/datasheet/922/MK4116-pdf.php + +const PinName kAddressPins[] = { + PA_7, + PC_7, + PB_6, + PB_10, + PA_8, + PA_9, + PB_4, +}; + +const PinName kRasPin = PA_6; +const PinName kWritePin = PB_9; +const PinName kDinPin = PB_8; +const PinName kDoutPin = PA_10; +const PinName kCasPin = PB_3; + +void Timer1Isr(); + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + Serial.begin(115200); + + for (int i = 0; i < 7; i++) { + pinMode(pinNametoDigitalPin(kAddressPins[i]), OUTPUT); + } + + pinMode(pinNametoDigitalPin(kRasPin), OUTPUT); + pinMode(pinNametoDigitalPin(kCasPin), OUTPUT); + pinMode(pinNametoDigitalPin(kWritePin), OUTPUT); + pinMode(pinNametoDigitalPin(kDoutPin), INPUT); + pinMode(pinNametoDigitalPin(kDinPin), OUTPUT); + + digitalWrite(pinNametoDigitalPin(kRasPin), HIGH); + digitalWrite(pinNametoDigitalPin(kCasPin), HIGH); + digitalWrite(pinNametoDigitalPin(kWritePin), HIGH); + + // Instantiate HardwareTimer object. Thanks to 'new' instanciation, HardwareTimer is not destructed when setup() function is finished. + HardwareTimer *MyTim = new HardwareTimer(TIM1); + + MyTim->setOverflow(1400, MICROSEC_FORMAT); + MyTim->attachInterrupt(Timer1Isr); + MyTim->resume(); +} + +#define LV(x) ((x) ? HIGH : LOW) + +void writeAddress(int address) { + for (int i = 0; i < 7; i++) { + digitalWriteFast(kAddressPins[i], LV(address & (1 << i))); + } +} + +int read(int address) { + int row = address >> 7; + int col = address & 0x7f; + + noInterrupts(); + + writeAddress(row); + digitalWriteFast(kRasPin, LOW); + writeAddress(col); + digitalWriteFast(kCasPin, LOW); + delayMicroseconds(1); // tCAS ish + int out = digitalReadFast(kDoutPin); + + digitalWriteFast(kCasPin, HIGH); + digitalWriteFast(kRasPin, HIGH); + + interrupts(); + + return out; +} + +void write(int address, int data) { + int row = address >> 7; + int col = address & 0x7f; + + noInterrupts(); + + writeAddress(row); + digitalWriteFast(kRasPin, LOW); + digitalWriteFast(kWritePin, LOW); + digitalWriteFast(kDinPin, LV(data)); + writeAddress(col); + digitalWriteFast(kCasPin, LOW); + + for (int i = 0; i < 10; i++) asm volatile(""); // extra delay + digitalWriteFast(kWritePin, HIGH); + delayMicroseconds(1); // tCAS ish + + digitalWriteFast(kCasPin, HIGH); + digitalWriteFast(kRasPin, HIGH); + + interrupts(); +} + +void writepage(int address, const uint8_t data[16]) { + int row = address >> 7; + + noInterrupts(); + + writeAddress(row); + digitalWriteFast(kRasPin, LOW); + for (int col = 0; col < 128; col++) { + int b = data[col >> 3] & (1 << (col % 8)); + digitalWriteFast(kDinPin, LV(b)); + digitalWriteFast(kWritePin, LOW); + writeAddress(col); + digitalWriteFast(kCasPin, LOW); + + for (int i = 0; i < 10; i++) asm volatile(""); // extra delay + digitalWriteFast(kWritePin, HIGH); + delayMicroseconds(1); // tCAS ish + + digitalWriteFast(kCasPin, HIGH); + } + digitalWriteFast(kRasPin, HIGH); + + interrupts(); +} + +void readpage(int address, uint8_t data[16]) { + int row = address >> 7; + + noInterrupts(); + + writeAddress(row); + digitalWriteFast(kRasPin, LOW); + for (int col = 0; col < 128; col++) { + uint8_t& out = data[col >> 3]; + + writeAddress(col); + digitalWriteFast(kCasPin, LOW); + delayMicroseconds(1); // tCAS ish + int b = digitalReadFast(kDoutPin); + out >>= 1; + if (b == HIGH) out |= 0x80; + digitalWriteFast(kCasPin, HIGH); + } + digitalWriteFast(kRasPin, HIGH); + + interrupts(); +} + +int writeread(int address, int value) { + write(address, value); + return read(address); +} + +void refreshrow(int row) { + writeAddress(row); + digitalWriteFast(kRasPin, LOW); + delayMicroseconds(1); + digitalWriteFast(kRasPin, HIGH); +} + +void refreshall() { + for (int row = 0; row < 128; row++) { + refreshrow(row); + } +} + +void Timer1Isr() { + refreshall(); +} + +int check01(int address) { + if (writeread(address, 0) != 0) return -1; + if (writeread(address, 1) != 1) return -2; + + return 0; +} + +int checkrow(int row) { + int row_address = row << 7; + for (int i = 0; i < 128; i++) { + int ret = check01(row_address + i); + if (ret != 0) { + Serial.printf("failure at 0x%04x: %d\n", row_address + i, ret); + return ret; + } + } + + return 0; +} + +int address = 0x007f; +int led_count = 0; +int led = 0; +char cmd = '\0'; + +const int kLedInterval = 1000000; + +const uint8_t kSampleData[16] = { + 'b', 'l', 'a', 'r', 'g', ',', ' ', 'c', + 'e', 'c', 'i', ' ', 'e', 's', 't', '!', +}; + +void dump16(int address) { + address &= 0x3f80; + uint8_t data[16]; + readpage(address, data); + + Serial.printf("%04x:", address); + for (int i = 0; i < 16; i++) { + Serial.printf(" %02x", data[i]); + } + Serial.print(" "); + for (int i = 0; i < 16; i++) { + if (data[i] < 33 || data[i] > 126) { + Serial.print("."); + } else { + Serial.printf("%c", data[i]); + } + } + Serial.println(""); +} + +void filltest() { + uint8_t data[16]; + for (int row = 0; row < 128; row++) { + snprintf(reinterpret_cast(data), 16, "testing row %03d", row); + writepage(row << 7, data); + } + for (int row = 0; row < 128; row++) { + readpage(row << 7, data); + if (atoi(reinterpret_cast(&data[12])) != row) { + Serial.printf("error in row %d\n", row); + return; + } + } + Serial.println("fill test ok."); +} + +void runcmd(char cmd) { + uint8_t dat[16]; + + if (cmd == 'p') { + writepage(address, kSampleData); + dump16(address); + } else if (cmd == '\0') { + address += 128; + dump16(address); + } else if (cmd == '0') { + address = 0; + dump16(address); + } else if (cmd == 'f') { + filltest(); + } else if (cmd == 't') { + for (int row = 0; row < 128; row++) { + Serial.printf("row %d... ", row); + int ret = checkrow(row); + if (ret != 0) { + break; + } + Serial.println("ok!"); + } + } +} + +void loop() { + // put your main code here, to run repeatedly: + if (led_count > kLedInterval) { + digitalWrite(LED_BUILTIN, LV(led % 2)); + led += 1; + led_count = 0; + } + led_count += 1; + + if (Serial.available()) { + char c = Serial.read(); + if (c == '\n') { + runcmd(cmd); + cmd = '\0'; + } else { + cmd = c; + } + } +}