Created
December 30, 2024 21:58
-
-
Save tuzz/2899cdf7a4e745e94837a1a661593945 to your computer and use it in GitHub Desktop.
Arduino Microcode Programmer - For my variation of Ben Eater's 8-bit computer
This file contains hidden or 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
/** | |
* This sketch programs the microcode EEPROMs for the 8-bit breadboard computer | |
* It includes support for a flags register with carry and zero flags | |
* See this video for more: https://youtu.be/Zg1NdPKoosU | |
*/ | |
#define SHIFT_DATA 2 | |
#define SHIFT_CLK 3 | |
#define SHIFT_LATCH 4 | |
#define EEPROM_D0 5 | |
#define EEPROM_D7 12 | |
#define WRITE_EN 13 | |
#define HLT 0b1000000000000000 // Halt clock | |
#define MI 0b0100000000000000 // Memory address register in | |
#define RI 0b0010000000000000 // RAM data in | |
#define RO 0b0001000000000000 // RAM data out | |
#define IO 0b0000100000000000 // Instruction register out | |
#define II 0b0000010000000000 // Instruction register in | |
#define AI 0b0000001000000000 // A register in | |
#define AO 0b0000000100000000 // A register out | |
#define EO 0b0000000010000000 // ALU out | |
#define SU 0b0000000001000000 // ALU subtract | |
#define BI 0b0000000000100000 // B register in | |
#define OI 0b0000000000010000 // Output register in | |
#define CE 0b0000000000001000 // Program counter enable | |
#define CO 0b0000000000000100 // Program counter out | |
#define J 0b0000000000000010 // Jump (program counter in) | |
#define FI 0b0000000000000001 // Flags in | |
#define FLAGS_Z0C0 0 | |
#define FLAGS_Z0C1 1 | |
#define FLAGS_Z1C0 2 | |
#define FLAGS_Z1C1 3 | |
#define JC 0b0111 | |
#define JZ 0b1000 | |
const PROGMEM uint16_t UCODE_TEMPLATE[16][8] = { | |
{ MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0 }, // 0000 - NOP | |
{ MI|CO, RO|II|CE, IO|MI, RO|AI, 0, 0, 0, 0 }, // 0001 - LDA | |
{ MI|CO, RO|II|CE, IO|MI, RO|BI, EO|AI|FI, 0, 0, 0 }, // 0010 - ADD | |
{ MI|CO, RO|II|CE, IO|MI, RO|BI, EO|AI|SU|FI, 0, 0, 0 }, // 0011 - SUB | |
{ MI|CO, RO|II|CE, IO|MI, AO|RI, 0, 0, 0, 0 }, // 0100 - STA | |
{ MI|CO, RO|II|CE, IO|AI, 0, 0, 0, 0, 0 }, // 0101 - LDI | |
{ MI|CO, RO|II|CE, IO|J, 0, 0, 0, 0, 0 }, // 0110 - JMP | |
{ MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0 }, // 0111 - JC | |
{ MI|CO, RO|II|CE, 0, 0, 0, 0, 0, 0 }, // 1000 - JZ | |
{ MI|CO, RO|II|CE, EO|AI, EO|SU|BI, EO|SU|AI, 0, 0, 0 }, // 1001 - SWP | |
{ MI|CO, RO|II|CE, IO|BI, EO|AI|FI, 0, 0, 0, 0 }, // 1010 - ADI | |
{ MI|CO, RO|II|CE, IO|BI, EO|SU|AI|FI, 0, 0, 0, 0 }, // 1011 - SUI | |
{ MI|CO, RO|II|CE, EO|AI|FI, 0, 0, 0, 0, 0 }, // 1100 - ADP | |
{ MI|CO, RO|II|CE, EO|SU|AI|FI, 0, 0, 0, 0, 0 }, // 1101 - SUP | |
{ MI|CO, RO|II|CE, AO|OI, 0, 0, 0, 0, 0 }, // 1110 - OUT | |
{ MI|CO, RO|II|CE, HLT, 0, 0, 0, 0, 0 }, // 1111 - HLT | |
}; | |
uint16_t ucode[4][16][8]; | |
void initUCode() { | |
// ZF = 0, CF = 0 | |
memcpy_P(ucode[FLAGS_Z0C0], UCODE_TEMPLATE, sizeof(UCODE_TEMPLATE)); | |
// ZF = 0, CF = 1 | |
memcpy_P(ucode[FLAGS_Z0C1], UCODE_TEMPLATE, sizeof(UCODE_TEMPLATE)); | |
ucode[FLAGS_Z0C1][JC][2] = IO|J; | |
// ZF = 1, CF = 0 | |
memcpy_P(ucode[FLAGS_Z1C0], UCODE_TEMPLATE, sizeof(UCODE_TEMPLATE)); | |
ucode[FLAGS_Z1C0][JZ][2] = IO|J; | |
// ZF = 1, CF = 1 | |
memcpy_P(ucode[FLAGS_Z1C1], UCODE_TEMPLATE, sizeof(UCODE_TEMPLATE)); | |
ucode[FLAGS_Z1C1][JC][2] = IO|J; | |
ucode[FLAGS_Z1C1][JZ][2] = IO|J; | |
} | |
/* | |
* Output the address bits and outputEnable signal using shift registers. | |
*/ | |
void setAddress(int address, bool outputEnable) { | |
shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, (address >> 8) | (outputEnable ? 0x00 : 0x80)); | |
shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, address); | |
digitalWrite(SHIFT_LATCH, LOW); | |
digitalWrite(SHIFT_LATCH, HIGH); | |
digitalWrite(SHIFT_LATCH, LOW); | |
} | |
/* | |
* Read a byte from the EEPROM at the specified address. | |
*/ | |
byte readEEPROM(int address) { | |
for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) { | |
pinMode(pin, INPUT); | |
} | |
setAddress(address, /*outputEnable*/ true); | |
byte data = 0; | |
for (int pin = EEPROM_D7; pin >= EEPROM_D0; pin -= 1) { | |
data = (data << 1) + digitalRead(pin); | |
} | |
return data; | |
} | |
/* | |
* Write a byte to the EEPROM at the specified address. | |
*/ | |
void writeEEPROM(int address, byte data) { | |
setAddress(address, /*outputEnable*/ false); | |
for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) { | |
pinMode(pin, OUTPUT); | |
} | |
for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) { | |
digitalWrite(pin, data & 1); | |
data = data >> 1; | |
} | |
digitalWrite(WRITE_EN, LOW); | |
delayMicroseconds(1); | |
digitalWrite(WRITE_EN, HIGH); | |
delay(10); | |
} | |
/* | |
* Read the contents of the EEPROM and print them to the serial monitor. | |
*/ | |
void printContents(int start, int length) { | |
for (int base = start; base < length; base += 16) { | |
byte data[16]; | |
for (int offset = 0; offset <= 15; offset += 1) { | |
data[offset] = readEEPROM(base + offset); | |
} | |
char buf[80]; | |
sprintf(buf, "%03x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", | |
base, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], | |
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]); | |
Serial.println(buf); | |
} | |
} | |
void setup() { | |
// put your setup code here, to run once: | |
initUCode(); | |
pinMode(SHIFT_DATA, OUTPUT); | |
pinMode(SHIFT_CLK, OUTPUT); | |
pinMode(SHIFT_LATCH, OUTPUT); | |
digitalWrite(WRITE_EN, HIGH); | |
pinMode(WRITE_EN, OUTPUT); | |
Serial.begin(57600); | |
// Program data bytes | |
Serial.print("Programming EEPROM"); | |
// Program the 8 high-order bits of microcode into the first 128 bytes of EEPROM | |
for (int address = 0; address < 1024; address += 1) { | |
int flags = (address & 0b1100000000) >> 8; | |
int byte_sel = (address & 0b0010000000) >> 7; | |
int instruction = (address & 0b0001111000) >> 3; | |
int step = (address & 0b0000000111); | |
if (byte_sel) { | |
writeEEPROM(address, ucode[flags][instruction][step]); | |
} else { | |
writeEEPROM(address, ucode[flags][instruction][step] >> 8); | |
} | |
if (address % 64 == 0) { | |
Serial.print("."); | |
} | |
} | |
Serial.println(" done"); | |
// Read and print out the contents of the EERPROM | |
Serial.println("Reading EEPROM"); | |
printContents(0, 1024); | |
} | |
void loop() { | |
// put your main code here, to run repeatedly: | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment