/*
 * ROM based 4x4-bit processor.
 * 
 * Written 2011/10/28 by Alexis Kotlowy.
 * 
 * Instruction Set:
 * 	ADC - Add [X]-[X+3] to A as a binary number with carry.
 * 	ADD - Add [X]-[X+3] to A as a binary number, ignoring carry.
 * 	COM - Complement the accumulator with 1's complement.
 * 	JMP - Load the immediate word into the PC if the Skip flag is clear.
 * 	LDX - Load the immediate word into the X register.
 * 	STC - Store A to [X]-[X+3] and clear A.
 * 	CLC - Clear the carry flag
 * 	FNZ - Flag skip if A is non-zero. Else clear it.
 * 	CLS - Clear Skip Flag (unconditional JMP)
 * 	SET - Set the carry flag
 * 	AND - Logical AND with [X]-[X+3] and A
 * 	HLT - Halt execution.
 */

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>

#define ADC 0
#define COM 1
#define JMP 2
#define LDX 3
#define STC 4
#define CLC 5
#define FNZ 6
#define CLS 7
#define SET 8
#define AND 9
#define HLT 15

#define MEMSIZE 65536

int main (int argc, char *argv[]) {
	struct stat st;			// For file status
	int mem_file;
		
	uint8_t state = 0;		// Current machine state
	uint8_t shift_c;		// Shift count
	uint8_t carry = 0;		// Carry flag
	uint8_t skip = 0;		// Skip flag
	
	uint16_t pc = 0;
	uint16_t ix = 0;
	uint16_t ld_tmp = 0;
	
	uint8_t acc [4] = { 0,0,0,0 };
	uint8_t acc_tmp;
	uint8_t oper_count;
	uint8_t col_count = 0;
	uint8_t zflag = !0;
	uint8_t i;
	
	uint8_t print_out [16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
	
	uint8_t memory[MEMSIZE];
	
	if (argc != 2) {
		fprintf(stderr, "Please specify a memory image\n");
		return (1);
	}
	
	mem_file = open(argv[1], O_RDONLY);
	
	if ( !mem_file ) {
		fprintf(stderr, "Error opening file %s\n", argv[1]);
		return (1);
	}
	
	stat(argv[1], &st);

	if ( st.st_size > MEMSIZE) {
		fprintf(stderr, "File too large!\n");
		return (1);
	}

	lseek(mem_file, 0, SEEK_SET);
	read(mem_file, (void *) memory, st.st_size);

	close(mem_file);

	while (state != 1) {
		fprintf(stderr, "A = %X,%X%X%X%X\tPC = %X{%X}\tIX = %X{%X}\tSTATE = %d\n", \
			carry,acc[3],acc[2],acc[1],acc[0], pc, memory[pc], ix, memory[ix], state); 
		switch (state) {
			case 0:				// Instruction Fetch
				oper_count = 0;
				switch (memory[pc++]) {
					case ADC:	
						if (carry) state = 15;
						else state = 14;
						break;
					case COM:
						state = 2;
						break;
					case JMP:
						state = 3;
						break;
					case LDX:
						state = 5;
						break;
					case STC:
						state = 7;
						break;
					case CLC:
						state = 8;
						break;
					case FNZ:
						state = 9;
						break;
					case CLS:
						state = 11;
						break;
					case SET:
						state = 12;
						break;
					case AND:
						state = 13;
						break;
					case HLT:
						state = 1;
						break;
				}
				break;
			case 1:				// Halt state
				break;
			case 2:		//1's complement negate
				acc_tmp = (~acc[0])&0x0F;
				for (shift_c=0; shift_c<3; shift_c++)
					acc[shift_c] = acc[shift_c+1];
				acc[3]=acc_tmp;
				if (oper_count > 3) state = 0;
				break;
			case 3:		//Load PC from immeediate if skip = 0
				ld_tmp = (ld_tmp>>4)|(memory[pc++]<<12);
				if (oper_count > 3) state = 4;
				break;
			case 4:		// Load ld_tmp into PC
				if (skip == 0) pc = ld_tmp;
				state = 0;
				break;
			case 5:		//Load Index
				ld_tmp = (ld_tmp>>4)|(memory[pc++]<<12);
				if (oper_count > 3) state = 6;
				break;
			case 6:	// Load ld_tmp itno IX
				ix = ld_tmp;
				state = 0;
				break;
			case 7:		//Store and Clear
				if ((ix & 0xE000) == 0xE000) {	// Emulate printer
					if (acc[0] == 15) {
						for(i=16; i!=0;) {
							printf("%X",print_out[--i]);
							print_out[i] = 0;
						}
						printf("\n");
						col_count = 0;
					}
					else print_out[col_count++] = acc[0];
				}
				memory[ix++] = acc[0];
				for (shift_c=0; shift_c<3; shift_c++)
					acc[shift_c] = acc[shift_c+1];
				acc[3] = 0;
				if (oper_count > 3) state = 0;
				break;
			case 8:		// Clear Carry
				carry = 0;
				state = 0;
				break;
			case 9:		// Determine if Acc == 0
				skip = 0;
				if (oper_count > 4) state = 0;
				else {
					acc_tmp = acc[0];
					if (acc_tmp != 0) state = 10;
					for (shift_c=0; shift_c<3; shift_c++)
						acc[shift_c] = acc[shift_c+1];
					acc[3] = acc_tmp;
				}
				break;
			case 10:	// Non-zero, finish oper_count
					// and set skip flag.
				skip = !0;
				if (oper_count > 4) state = 0;
				else {
					acc_tmp = acc[0];
					for (shift_c=0; shift_c<3; shift_c++)
						acc[shift_c] = acc[shift_c+1];
					acc[3] = acc_tmp;
				}
				break;
			case 11:	// Clear skip flag.
				skip = 0;
				state = 0;
				break;
			case 12:	// Set carry flag
				carry = !0;
				state = 0;
				break;
			case 13:	// Logical AND
				if(oper_count > 3) state = 0;
				acc_tmp = memory[ix++] & acc[0] & 0x0F;
				for (shift_c=0; shift_c<3; shift_c++)
					acc[shift_c] = acc[shift_c+1];
				acc[3]=acc_tmp;
				break;
			case 14:	// Add Binary without carry
				carry = 0;
				acc_tmp = memory[ix++] + acc[0];
				if (acc_tmp > 15) {
					carry = !0;
					state = 15;
				}
				acc_tmp &= 0x0F;
				for (shift_c=0; shift_c<3; shift_c++)
					acc[shift_c] = acc[shift_c+1];
				acc[3] = acc_tmp;
				if (oper_count > 3) state = 0;
				break;
			case 15:	// Add binary with carry.
				carry = !0;
				acc_tmp = memory[ix++] + acc[0] + 1;
				if (acc_tmp > 15) acc_tmp = (acc_tmp - 16) & 0x0F;
				else {
					carry = 0;
					state = 14;
				}
				acc_tmp &= 0x0F;
				for (shift_c=0; shift_c<3; shift_c++)
					acc[shift_c] = acc[shift_c+1];
				acc[3] = acc_tmp;
				if (oper_count > 3) state = 0;
				break;
		}
		oper_count++;
	}
	
	printf("Execution halted. Processor status:\n");
	printf("A = %X%X%X%X  PC = %d{%d} IX = %d{%d}  STATE = %d\n", \
		acc[3],acc[2],acc[1],acc[0], pc, memory[pc], ix, memory[ix], state); 

	return 0;
}