/*
 * ArduinoISAIO.cpp
 *
 *  Created on: Jun 30, 2017
 *      Author: oliford
 */
#include "ArduinoISAIO.h"

#include <Arduino.h>
#include <string.h>


#define SERIAL_BUFF_SIZE 64
int currentSerialChar = 0;
char serialBuffer[SERIAL_BUFF_SIZE];

void checkSerial();
void gotCommand(char * cmd);

void reset();
uint16_t inp(uint16_t addr, bool silent);
void out(uint16_t addr, uint16_t val);
void setAddress(uint16_t address);
void dataMode(int mode);
uint16_t getData();
void setData(uint16_t val);
void dumpMemory();

#define DEBUG_IO
#undef DEBUG_PINS
#undef DEBUG_SERIAL

void setup(){
	//get reset line high ASAP
	pinMode(RST, OUTPUT);
	resetHigh();

	Serial.begin(115200);
	Serial.println("Arudino ISA simple IO controller");


	pinMode(IOW, OUTPUT);
	pinMode(IOR, OUTPUT);
	pinMode(IOCS16, OUTPUT);
	pinMode(IOCHRDY, INPUT);
	pinMode(AEN, OUTPUT);
	for(int i=0; i < 14; i++)
			pinMode(ISA_A0+i, OUTPUT);

	dataMode(INPUT);

	//default state
	digitalWrite(IOW, HIGH);
	digitalWrite(IOR, HIGH);
	digitalWrite(IOCS16, HIGH);
	digitalWrite(AEN, LOW); //DMA signal, always low

	reset();

	Serial.println("Ready");
}

void loop(){
	checkSerial();

}

void checkSerial(){
	if(Serial.available()){ //only do 1 byte at a time

		char inChar = Serial.read();

		if(inChar == 0x00 || inChar == 0xFF){ //probably rubbish from line advances
				return;

		}else if (inChar == '\n' || inChar == '\\' || inChar == '\r') {

				serialBuffer[currentSerialChar] = 0x00; //terminate

				gotCommand(serialBuffer);

				currentSerialChar = 0;
		}else{
			if(currentSerialChar >= (SERIAL_BUFF_SIZE - 1)){
				Serial.println("CTL: ERROR: Overflow receiving command");
				currentSerialChar = 0;
			}else{
				serialBuffer[currentSerialChar] = inChar;
				currentSerialChar++;
			}
		}
	}
}


void gotCommand(char * cmd){
#ifdef DEBUG_SERIAL
	Serial.print("got '");
	Serial.print(serialBuffer);
	Serial.println("'");
#endif
	if(strncasecmp(serialBuffer, "IN", 2) == 0){
		int addr = strtol(cmd + 2, NULL, 16);

		inp(addr, false);

	}else if(strncasecmp(serialBuffer, "OUT", 3) == 0){
		char *sep = NULL;
		int addr = strtol(cmd+3, &sep, 16);
		if(sep == NULL)
			return;
		int val = strtol(sep + 1, NULL, 16);

		out(addr, val);
	}else if(strncasecmp(serialBuffer, "ADDR", 4) == 0){
		int addr = strtol(cmd + 4, NULL, 16);

		setAddress(addr);

	}else if(strncasecmp(serialBuffer, "DATA", 4) == 0){
		int val = strtol(cmd + 4, NULL, 16);

		setData(val);

	}else if(strncasecmp(serialBuffer, "DIN", 3) == 0){
			dataMode(INPUT);

	}else if(strncasecmp(serialBuffer, "RST", 3) == 0){
		reset();
	}else if(strncasecmp(serialBuffer, "DUMP", 4) == 0){
		dumpMemory();
	}
}

void reset(){
#ifdef DEBUG_IO
	Serial.print("Reset...");
#endif
	resetHigh();


	delay(1000);

	resetLow();
#ifdef DEBUG_IO
	Serial.println("Done");
#endif
}


uint16_t inp(uint16_t addr, bool silent){
#ifdef DEBUG_IO
	if(!silent){
		Serial.print("IN ");
		Serial.print(addr, HEX);
	}
#endif

	setAddress(addr);
	dataMode(INPUT);

	cli();

	ioCS16Low();
	ioReadLow();

	//ISA clock freq was 4 - 8 Mhz and read starts within 1/2 clock cycle
	// so 1us is more than enough
	_delay_us(1);

	//if LOW, wait for IOCHRDY to go high (with 10ms timeout)
	int countRDYHIGH=0;
	for(countRDYHIGH=0; countRDYHIGH < TIMEOUT_CHRDY; countRDYHIGH++){
			int val = digitalRead(IOCHRDY);
			if(val == HIGH)
				break;
			_delay_us(1);
	}


	uint16_t val = getData();

	ioReadHigh();
	ioCS16High();

	sei();

#ifdef DEBUG_IO
	if(!silent){
		Serial.print(" = ");
		Serial.print(val, HEX);
		Serial.print(", t_RDY = ");
		Serial.println(countRDYHIGH, DEC);
	}
#endif


	if(countRDYHIGH >= TIMEOUT_CHRDY){
		Serial.println("CHRDY_TIMOUT");
	}

	return val;
}


void out(uint16_t addr, uint16_t val){

#ifdef DEBUG_IO
	Serial.print("OUT ");
	Serial.print(addr, HEX);
	Serial.print(", ");
	Serial.print(val, HEX);
#endif

	setAddress(addr);
	dataMode(OUTPUT);
	setData(val);

	cli();

	ioCS16Low();
	ioWriteLow();

	_delay_us(1);

	//if LOW, wait for IOCHRDY to go high (with 10ms timeout)
	int countRDYHIGH=0;
	for(countRDYHIGH=0; countRDYHIGH < 10000; countRDYHIGH++){
			int val = digitalRead(IOCHRDY);
			if(val == HIGH)
				break;
			_delay_us(1);
	}

	ioWriteHigh();
	ioCS16High();

	sei();

	dataMode(INPUT);

#ifdef DEBUG_IO
	Serial.print(": t_RDY = ");
	Serial.println(countRDYHIGH, DEC);
#endif

}

void setAddress(uint16_t address){
#ifdef DEBUG_PINS
	Serial.print("setAddr ");
	Serial.println(address, HEX);
#endif
	for(int i=0; i < 14; i++){
		digitalWrite(ISA_A0+i, address & 0x0001);
		address >>= 1;
	}
}

void setData(uint16_t val){
#ifdef DEBUG_PINS
	Serial.print("setData ");
	Serial.println(val, HEX);
#endif

	dataMode(OUTPUT);
	for(int i=0; i < 16; i++){
		digitalWrite(ISA_D0+i, val & 0x0001);
		val >>= 1;
	}
}

uint16_t getData(){
	uint16_t val = 0;
	for(int i=15; i >= 0; i--){
		val <<= 1;
		val |= (digitalRead(ISA_D0+i) & 0x01);
	}
	return val;
}

void dataMode(int mode){
#ifdef DEBUG_PINS
	Serial.print("PINS: DATA ");
	Serial.println((mode == INPUT) ? "IN" : "OUT");
#endif
	for(int i=0; i < 16; i++){
		pinMode(ISA_D0 + i, mode);
	}
}

void dumpMemory(){
	out(0x300, 1);
	out(0x304, 0);
	out(0x302, 0);
	out(0x300, 0);


	for(long i=0; i < 0x100000; i++){
		uint16_t val = inp(0x302, true);
		Serial.print(val, HEX);
		if(((i+1) % 16) == 0)
			Serial.println();
		else
			Serial.print(" ");
	}

}
