#include <avr/io.h>

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

#include <util/delay.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;
}

void loop()
{
	// 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;

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

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

	if(!endstop_x)
		portd_new &= ~pin_xstep;

	if(!endstop_y)
		portd_new &= ~pin_ystep;

	if(!endstop_z)
		portd_new &= ~pin_zstep;

	if(!endstop_a)
		portd_new &= ~pin_astep;

	// write out new values
	PORTB = (PORTB & ~portb_use) | portb_new;
	PORTC = (PORTC & ~portc_use) | portc_new;
	PORTD = (PORTD & ~portd_use) | portd_new;

	_delay_us(5000);

	// Reset step bits
	portd_new |= (pin_xstep | pin_ystep | pin_zstep | pin_astep);

	// write out new values
	PORTB = (PORTB & ~portb_use) | portb_new;
	PORTC = (PORTC & ~portc_use) | portc_new;
	PORTD = (PORTD & ~portd_use) | portd_new;

	_delay_us(5000);

	// if there is no more work remaining, shut down the Arduino
	if(all_done)
		exit(0);
}

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