Skip to content

Instantly share code, notes, and snippets.

@ctigeek
Created September 23, 2024 12:06
Show Gist options
  • Save ctigeek/1ce86a4d6be38cd79af84a040faab6ad to your computer and use it in GitHub Desktop.
Save ctigeek/1ce86a4d6be38cd79af84a040faab6ad to your computer and use it in GitHub Desktop.
Arduino 6502
void FillRom(byte *buffer) {
for (int i=0x00; i<= 0x06; i++) {
buffer[i] = 0xEA;
}
//load A with 0x77 and push
buffer[0x07] = 0xA9;
buffer[0x08] = 0x77;
buffer[0x09] = 0x48;
//load A with 0x88 and push
buffer[0x0A] = 0xA9;
buffer[0x0B] = 0x88;
buffer[0x0C] = 0x48;
//pop off stack into A twice
buffer[0x0D] = 0x68;
buffer[0x0E] = 0x68;
buffer[0x0F] = Op_JMP_abs;
buffer[0x10] = 0x00;
buffer[0x11] = 0x80;
}
// BE Bus Enable. Set high for normal operation.
const char BE = 40; // OUTPUT ONLY, Port G1
// PHI2 the clock.
const char CLOCK=41; // INPUT ONLY for now., PORT G0
//RWB Read-Write bit. When high, the CPU is reading data.
const char RWB = 42; // INPUT ONLY, port L7
//RESB Reset. When low for 2+ cycles, the CPU is reset and PC is set to FFFC-D.
const char RESB= 43; // OUTPUT ONLY, port L6
//VPB vector pull. When low, the CPU is reading the interrupt vector .
const char VPB = 44; // INPUT ONLY, port L5
//RDY READY. When low, the CPU will stop. Keep high for normal operation.
const char RDY = 45; // OUTPUT port L4
//IRQB Interrupt Request. Low will trigger an interrupt.
const char IRQB = 46; // OUTPUT ONLY port L3
//NMIB Non Maskable Interrupt. Low will trigger an interrupt.
const char NMIB = 47; // OUTPUT ONLY port L2
// Synchronize OpCode fetch. Goes high when CPU is fetching an op code.
const char SYNC = 48; // INPUT ONLY port L1
//DATA, input + output
const char D0 = 22;
const char D1 = 23; // PORT A
const char D2 = 24;
const char D3 = 25;
const char D4 = 26;
const char D5 = 27;
const char D6 = 28;
const char D7 = 29;
// Address lines
const char Ad0 = 53;
const char Ad1 = 52;
const char Ad2 = 51;
const char Ad3 = 50; // PORT B
const char Ad4 = 10;
const char Ad5 = 11;
const char Ad6 = 12;
const char Ad7 = 13;
/////////////////////////////
const char Ad8 = 37;
const char Ad9 = 36; //PORT C
const char AdA = 35;
const char AdB = 34;
const char AdC = 33;
const char AdD = 32;
const char AdE = 31;
const char AdF = 30;
//===================== Op Codes. https://www.masswerk.at/6502/6502_instruction_set.html
const uint8_t Op_NOP = 0xEA; // No Operation
const uint8_t Op_JSR = 0x20; // Jump Subroutine. + 2bytes absolute address
const uint8_t Op_RTI = 0x40; // Return from Interupt
const uint8_t Op_JMP_abs = 0x4C; // Jump absolute. + 2 bytes absolute address
const uint8_t Op_JMP_ind = 0x6C; // Jump indirect. + 2 bytes indirect address
const uint8_t Op_RTS = 0x60; // Return from subroutine.
// special addresses
const uint16_t RESET_ADDRESS_LB = 0xFFFC;
const uint16_t RESET_ADDRESS_HB = 0xFFFD;
const uint8_t RESET_VECTOR_LB = 0x00;
const uint8_t RESET_VECTOR_HB = 0x80;
//global vars to track state...
String Clock_status = "";
uint8_t RWB_status = LOW;
uint8_t VPB_status = HIGH;
uint8_t SYNC_status = HIGH;
uint16_t address = 0x00;
uint8_t data = 0x00;
const uint16_t ROM_SIZE = 0x02FF;
byte ROM[ROM_SIZE];
void setup() {
SetAddressPinsToInput();
SetDataPinsToInput();
pinMode(CLOCK, INPUT);
//set BE to high. If set to low, address and data go HI-Z
pinMode(BE, OUTPUT);
digitalWrite(BE, HIGH);
//when RWB is high, the processor is reading data. When low, writing data.
pinMode(RWB, INPUT);
// set RESB to low for 2 clock cycles
pinMode(RESB, OUTPUT);
digitalWrite(RESB, HIGH);
// this has something to do with interrupts. can be ignored for now.
pinMode(VPB, INPUT);
pinMode(RDY, OUTPUT);
digitalWrite(RDY, HIGH);
pinMode(IRQB, OUTPUT);
digitalWrite(IRQB, HIGH);
pinMode(NMIB, OUTPUT);
digitalWrite(NMIB, HIGH);
pinMode(SYNC, INPUT);
Serial.begin(57600);
Serial.println("Emulator started.");
ResetCPU();
FillRom(ROM);
}
void loop() {
if (Clock_status == "LOW") {
WaitForClockHigh();
}
else {
WaitForClockLow();
}
if (RWB_status == HIGH) { // READ from RAM or ROM or IO
if (address <= 0x3FFF) { // 0011 1111 1111 1111 16k
ReadDataLines();
Serial.println("RAM HIT!!!");
}
else if (address >= 0x8000) { // 1000 0000 0000 0000 32k
address = address & 0x7FFF;
WriteDataLines(ROM[address]);
Serial.println("ROM HIT!!!");
}
else { // ???
Serial.print("unknown address!!!!!!!!!!!!!!!");
WriteDataLines(Op_NOP);
}
}
else { // this is a memory or IO write operation
ReadDataLines();
}
if (Clock_status != "LOW") {
DumpData();
}
}
void DumpData() {
String isRWB = RWB_status == HIGH ? "READ " : "WRITE";
String isVPB = VPB_status == HIGH ? "_________" : "INTERRUPT";
String isSYNC = SYNC_status == HIGH ? "OP FETCH" : "________";
Serial.print(" CLOCK:");
Serial.print(Clock_status);
Serial.print(" RWB=");
Serial.print(isRWB);
Serial.print(" VPB=");
Serial.print(isVPB);
Serial.print(" SYNC=");
Serial.print(isSYNC);
Serial.print(" ADDRESS=");
Serial.print(address,HEX);
Serial.print(" data=");
Serial.println(data,HEX);
}
void ResetCPU() {
Serial.println("Resetting CPU.... make sure clock is running...");
digitalWrite(RESB, LOW);
WaitForClockLow();
WaitForClockHigh();
//Serial.println("Resetting: 2 more clock cycles");
WaitForClockLow();
WaitForClockHigh();
//Serial.println("Resetting: 1 more clock cycle");
WaitForClockLow();
WaitForClockHigh();
digitalWrite(RESB, HIGH);
Serial.println(" Keep the clock running....");
for (int i=25; i>=0; i--)
{
if (Clock_status == "LOW") {
WaitForClockHigh();
}
else {
WaitForClockLow();
}
if (VPB_status == LOW) {
if (address == RESET_ADDRESS_LB) {
WriteDataLines(RESET_VECTOR_LB);
}
else if (address == RESET_ADDRESS_HB) {
WriteDataLines(RESET_VECTOR_HB);
i=1;
}
else {
Serial.println("This should never happen 1111.");
WriteDataLines(0x00);
}
DumpData();
}
}
WaitForClockLow();
SetDataPinsToInput();
Serial.println("Reset complete.");
}
void WaitForClockLow() {
bool clockIsHigh = digitalRead(CLOCK) == HIGH;
while (clockIsHigh) {
clockIsHigh = digitalRead(CLOCK) == HIGH; // wait for clock to go low.
}
Clock_status = "LOW";
RWB_status = digitalRead(RWB);
VPB_status = digitalRead(VPB);
SYNC_status = digitalRead(SYNC);
address = ReadAddressLines();
// do not read data here!
}
void WaitForClockHigh() {
bool clockIsHigh = digitalRead(CLOCK) == HIGH;
while (!clockIsHigh) {
clockIsHigh = digitalRead(CLOCK) == HIGH; // wait for clock to go high.
}
Clock_status = "HIGH";
RWB_status = digitalRead(RWB);
VPB_status = digitalRead(VPB);
SYNC_status = digitalRead(SYNC);
address = ReadAddressLines();
// do not read data here!
}
void WriteDataLines(uint8_t somedata) {
DDRA = B11111111; // all write
PORTA = somedata;
data = somedata;
// digitalWrite(D0, bitRead(somedata, 0));
// digitalWrite(D1, bitRead(somedata, 1));
// digitalWrite(D2, bitRead(somedata, 2));
// digitalWrite(D3, bitRead(somedata, 3));
// digitalWrite(D4, bitRead(somedata, 4));
// digitalWrite(D5, bitRead(somedata, 5));
// digitalWrite(D6, bitRead(somedata, 6));
// digitalWrite(D7, bitRead(somedata, 7));
}
uint8_t ReadDataLines() {
DDRA = B00000000; //all input
//SetDataPinsToInput();
uint8_t byteread = PINA;
data = byteread;
return byteread;
// uint8_t byteread = 0x00;
// bitWrite(byteread, 0, digitalRead(D0));
// bitWrite(byteread, 1, digitalRead(D1));
// bitWrite(byteread, 2, digitalRead(D2));
// bitWrite(byteread, 3, digitalRead(D3));
// bitWrite(byteread, 4, digitalRead(D4));
// bitWrite(byteread, 5, digitalRead(D5));
// bitWrite(byteread, 6, digitalRead(D6));
// bitWrite(byteread, 7, digitalRead(D7));
// return byteread;
}
uint16_t ReadAddressLines() {
uint16_t addressread = PINB | (PINC << 8);
return addressread;
// uint16_t addressread = 0x0000;
// bitWrite(addressread, 0, digitalRead(Ad0));
// bitWrite(addressread, 1, digitalRead(Ad1));
// bitWrite(addressread, 2, digitalRead(Ad2));
// bitWrite(addressread, 3, digitalRead(Ad3));
// bitWrite(addressread, 4, digitalRead(Ad4));
// bitWrite(addressread, 5, digitalRead(Ad5));
// bitWrite(addressread, 6, digitalRead(Ad6));
// bitWrite(addressread, 7, digitalRead(Ad7));
// bitWrite(addressread, 8, digitalRead(Ad8));
// bitWrite(addressread, 9, digitalRead(Ad9));
// bitWrite(addressread, 10, digitalRead(AdA));
// bitWrite(addressread, 11, digitalRead(AdB));
// bitWrite(addressread, 12, digitalRead(AdC));
// bitWrite(addressread, 13, digitalRead(AdD));
// bitWrite(addressread, 14, digitalRead(AdE));
// bitWrite(addressread, 15, digitalRead(AdF));
// return addressread;
}
void SetAddressPinsToInput() {
DDRB = B00000000;
DDRC = B00000000;
// pinMode(Ad0, INPUT);
// pinMode(Ad1, INPUT);
// pinMode(Ad2, INPUT);
// pinMode(Ad3, INPUT);
// pinMode(Ad4, INPUT);
// pinMode(Ad5, INPUT);
// pinMode(Ad6, INPUT);
// pinMode(Ad7, INPUT);
// pinMode(Ad8, INPUT);
// pinMode(Ad9, INPUT);
// pinMode(AdA, INPUT);
// pinMode(AdB, INPUT);
// pinMode(AdC, INPUT);
// pinMode(AdD, INPUT);
// pinMode(AdE, INPUT);
// pinMode(AdF, INPUT);
}
void SetDataPinsToInput() {
DDRA = B00000000;
// pinMode(D0, INPUT);
// pinMode(D1, INPUT);
// pinMode(D2, INPUT);
// pinMode(D3, INPUT);
// pinMode(D4, INPUT);
// pinMode(D5, INPUT);
// pinMode(D6, INPUT);
// pinMode(D7, INPUT);
}
void SetDataPinsToOutput() {
DDRA = B11111111;
// pinMode(D0, OUTPUT);
// pinMode(D1, OUTPUT);
// pinMode(D2, OUTPUT);
// pinMode(D3, OUTPUT);
// pinMode(D4, OUTPUT);
// pinMode(D5, OUTPUT);
// pinMode(D6, OUTPUT);
// pinMode(D7, OUTPUT);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment