Skip to content

Instantly share code, notes, and snippets.

@tuzz
Created December 30, 2024 21:58
Show Gist options
  • Save tuzz/2899cdf7a4e745e94837a1a661593945 to your computer and use it in GitHub Desktop.
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 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