#include <avr/io.h>
#include <avr/interrupt.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

// PORTB
uint8_t const pin_adir  = 1U << 0;

#define portb_use pin_adir
#define portb_out pin_adir

// PORTC
uint8_t const pin_xend  = 1U << 3;
uint8_t const pin_yend  = 1U << 2;
uint8_t const pin_zend  = 1U << 1;
uint8_t const pin_aend  = 1U << 0;
uint8_t const pin_enab  = 1U << 5;

#define portc_use (pin_xend | pin_yend | pin_zend | pin_aend | pin_enab)
#define portc_out pin_enab

// PORTD
uint8_t const pin_xstep = 1U << 4;		// temporarily D4
uint8_t const pin_xdir  = 1U << 1;
uint8_t const pin_ystep = 1U << 2;
uint8_t const pin_ydir  = 1U << 3;
uint8_t const pin_zstep = 1U << 5;
uint8_t const pin_zdir  = 1U << 6;
uint8_t const pin_astep = 1U << 7;

#define portd_use (pin_xstep | pin_xdir | pin_ystep | pin_ydir | \
	pin_zstep | pin_zdir | pin_astep)
#define portd_out (pin_xstep | pin_xdir | pin_ystep | pin_ydir | \
	pin_zstep | pin_zdir | pin_astep)

void setup()
{
	// pins are low active, set outputs high
	PORTB = (PORTB & ~portb_use) | portb_out;
	PORTC = (PORTC & ~portc_use) | portc_out;
	PORTD = (PORTD & ~portd_use) | portd_out;

	// set up inputs and outputs
	DDRB = (DDRB & ~portb_use) | portb_out;
	DDRC = (DDRC & ~portc_use) | portc_out;
	DDRD = (DDRD & ~portd_use) | portd_out;

	cli();

	TCCR0A = 1 << WGM01;
	TCCR0B = (1 << CS01) | (1 << CS00);
	TCNT0  = 0;
	OCR0A = 124;
	TIMSK0 |= (1 << OCIE0A);

	sei();

}

ISR(TIMER0_COMPA_vect)
{
	// start with all bits set (inactive)
	static uint8_t portb_new = portb_out;
	static uint8_t portc_new = portc_out;
	static uint8_t portd_new = portd_out;

	// write out values -- first thing so we get guaranteed timing
	PORTB = (PORTB & ~portb_use) | portb_new;
	PORTC = (PORTC & ~portc_use) | portc_new;
	PORTD = (PORTD & ~portd_use) | portd_new;

	// read status bits
	uint8_t const pinc = PINC;

	// map to internal function
	bool const endstop_x = !(pinc & pin_xend);
	bool const endstop_y = !(pinc & pin_yend);
	bool const endstop_z = !(pinc & pin_zend);
	bool const endstop_a = !(pinc & pin_aend);

	// if all four endstops are hit, the program is done
	bool const all_done = endstop_x && endstop_y && endstop_z && endstop_a;

	// if there is work to be done, enable the steppers
	if(!all_done)
		portc_new &= ~pin_enab;
	else
		portc_new |= pin_enab;

	if(!endstop_x)
		portd_new ^= pin_xstep;
	else
		portd_new |= pin_xstep;

	if(!endstop_y)
		portd_new ^= pin_ystep;
	else
		portd_new |= pin_ystep;

	if(!endstop_z)
		portd_new ^= pin_zstep;
	else
		portd_new |= pin_zstep;

	if(!endstop_a)
		portd_new ^= pin_astep;
	else
		portd_new |= pin_astep;
}

void loop()
{
}

int main(void)
{
	setup();
	for(;;)
		loop();
}