#include <avr/pgmspace.h>
// 2400 for the authentic experience, 9600 for something more enjoyable
#define BAUD 2400
#include <util/setbaud.h>
#include "rom.h"
#define CLOCK 53
#define RESET 52
#define RW 51
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
byte ram[0x1000];
byte pia[0x14];
#define ADDR ((addrh << 8) | addrl)
static uint8_t memRead() {
const uint8_t addrh = PINC;
const uint8_t addrl = PINA;
const uint16_t addr = ADDR;
uint8_t val;
switch (addr >> 12) {
default:
// Nothing in the address space, just return zero.
return 0x00;
case 0x0:
val = ram[addr];
return val;
case 0xd:
// Fake 6821
switch (addrl) {
case 0x10:
val = 0x80 | UDR0;
break;
case 0x11:
val = UCSR0A & _BV(RXC0);
break;
case 0x12:
val = UCSR0A & _BV(UDRE0) ? 0x00 : 0xFF;
break;
default:
printf("pia: invalid read at %04x\n", addr);
val = 0;
break;
}
return val;
case 0xe:
val = pgm_read_byte(erom + (addr & 0x0fff));
return val;
case 0xf:
val = pgm_read_byte(from + (addr & 0x0fff));
return val;
}
}
static void memWrite() {
const uint8_t addrh = PINC;
const uint8_t addrl = PINA;
const uint16_t addr = ADDR;
const uint8_t val = PINL;
switch (addr >> 12) {
default:
// Nothing in the address space ignore the write;
break;
case 0x0:
ram[addr] = val;
break;
case 0xd:
switch (addrl) {
default:
printf("pia: invalid write at %04x: %02x\n", addr, val);
break;
case 0x12:
putchar(val & 0x7f);
break;
case 0x11:
// Ignore write to $D011, PIA is not configurable.
case 0x13:
// Ignore write to $D013, PIA is not configurable.
break;
}
break;
}
}
int uart_putchar(char c, FILE *stream) {
if (c == '\r') {
uart_putchar('\n', stream);
}
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
return 1;
}
static FILE uartout = {0} ;
void uart_init(void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= _BV(U2X0);
#else
UCSR0A &= ~(_BV(U2X0));
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
stdout = &uartout ;
}
void setup() {
uart_init();
printf("RESET\n");
pinMode(CLOCK, OUTPUT);
pinMode(RESET, OUTPUT);
pinMode(RW, INPUT);
digitalWrite(RESET, HIGH);
// Setup PORTC to read the low byte of the address
DDRC = 0x00;
PORTC = 0x00;
// Setup PORTA to do the same for the high byte
DDRA = 0x00;
PORTA = 0x00;
// Send reset pulse
digitalWrite(RESET, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(CLOCK, LOW);
digitalWrite(CLOCK, HIGH);
digitalWrite(RESET, HIGH);
}
void loop() {
while (true) {
cli();
cbi(PORTB, 0);
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"); // 4 * 62.5ns delay @ 16mhz
sbi(PORTB, 0);
sei();
if (PINB & _BV(PB2)) {
// read cycle
DDRL = 0xff;
PORTL = memRead();
} else {
// write cycle
DDRL = 0x00;
PORTL = 0;
memWrite();
}
}
}