#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* LMS operation */
/* operazioni di Input/Output */
#define READ 10
#define WRITE 11

/* Operazioni di  caricamento */
#define LOAD 20
#define STORE 21

/* Operazioni aritmetiche */
#define ADD 30
#define SUBTRACT 31
#define DIVIDE 32
#define MULTIPLY 33


/* Operazioni di trasferimento del controllo */
#define BRANCH 40
#define BRANCHNEG 41
#define BRANCHZERO 42
#define HALT 43

/* Numero totale delle operazioni supportate */
#define NUM_OP 12



//* Dimensione memoria */
#define SIZE_MEM 100

void warning();
void dump();
void type_istructiones();
int get_op_code(const int *instructionRegister);
int get_operand(const int *instructionRegister);
int in_array(const int array[], const int value, const int size);
void execute(const int begin, const int end);
void fatal_error();

int memory[ SIZE_MEM ] = { 0 }; /* memoria */
int accumulator = +0000; /* registro */
int instructionCounter = 00; /* numero di istruzione ion corso */
int instructionRegister = +0000; /* valore composto dal operationCode e dall' operando */
int operationCode = 00; /* codice di una delle operazioni LMS */
int operand = 00; /* locazione di memoria su cui l'operationCode deve agire*/

int main()
{
	warning();
	type_istructiones( "" );
	execute(0, instructionCounter);
	dump();
	return 0;

}

/* avviso iniziale */
void warning(){
	
	printf("\t*** Welcome to Simpletron! ***\n");
	printf("\t*** Please enter your program one instruction ***\n");
	printf("\t*** (or data word) at a time. I will type the ***\n");	
	printf("\t*** locatione number and a question mark(?). ***\n");	
	printf("\t*** You then type the word for that locaction. ***\n");	
	printf("\t*** Type the sentinel -99999 to stop entering ***\n");	
	printf("\t*** your program. ***\n");		
	printf("\n");
	
}

/* Dump della memoria */
void dump(){
	
	int i;
	
	printf("\nREGISTERS\n");
	printf("Accumulator:        %+10.4d\n", accumulator);
	printf("instructionCounter:   %10.2d\n", instructionCounter);	
	printf("instructionRegister: %+10.4d\n", instructionRegister);	
	printf("operationCode:      %10.2d\n", operationCode);	
	printf("operand:             %10.2d\n", operand);	
	printf("\n");	
	printf("MEMORY:\n  ");
	
	for (i = 0; i < 10; i++)
		printf(" %5d", i);
	
	for (i = 0; i < SIZE_MEM; i++){
	
		if (i % 10 == 0) printf("\n%.2d ", i);
		printf("%+.4d ", memory[ i ]);		
		
	}	
	
	printf("\n");
	
}

/* immissione istruzioni */
void type_istructiones( char filename[] ){
	
	if ( strcmp( filename, "" ) == 0 ) {
		
		int valid_instructions[NUM_OP] = { READ, WRITE, LOAD, STORE, ADD, SUBTRACT,
										DIVIDE, MULTIPLY, BRANCH, BRANCHNEG,
										BRANCHZERO, HALT };
	
		while (instructionCounter <= 100){
		
			printf("%.2d ? ", instructionCounter);
			scanf("%d", &instructionRegister);
		
			if (instructionRegister >= 0 && instructionRegister <= 9999 
				&& (in_array(valid_instructions, get_op_code(&instructionRegister), NUM_OP)
						|| instructionRegister == 0000 )){
			
				memory[instructionCounter] = instructionRegister;
				instructionCounter++;
		
			}
		
			else if (instructionRegister == -99999)
				break;
		
			else printf("*** Invalid value ***\n");
		
		}
	
	} 
	
	printf("*** Program loading completed. ***\n");
	
}

/* Restituisce operationCode prendendolo dall' instructionRegister*/
int get_op_code(const int *instructionRegister){
	
	return *instructionRegister / 100;
	
}


/* Restituisce operand prendendolo dall'instructionRegister */
int get_operand(const int *instructionRegister){
	
	return *instructionRegister % 100;
	
}

/* Restituisce 1 se l'elemento è presente nell'array, altrimenti 0 */
int in_array(const int array[], const int value, const int size){
	
	int i;
	for (i = 0; i < size; i++)
		if (array[i] == value) return 1;
		
	return 0;
	
}

/* Messaggio di errore fatale */
void fatal_error(){
	
	printf("*** Simpletron execution abnormally terminated ***\n");
	dump();
	getchar();
	do { getchar();
    } while( getchar() != '\n' );
	exit( EXIT_FAILURE );
	
}

/* Esegue le istruzioni immesse */
void execute(const int begin, const int end){

	printf("*** Program execution begins ***\n");	
	
	int i;
	
	for (i = begin; i < end; i++){
		
		instructionRegister = memory[ i ];
		operationCode = get_op_code( &instructionRegister );
		operand = get_operand( &instructionRegister );
		
		switch (operationCode){
			
			case READ:
				printf("? ");
				scanf("%d", &memory[ operand ]);
				while (memory[ operand ] > 9999 && memory[ operand ] < -9999)
					scanf("%d", &memory[ operand ]);
				break;
			
			case WRITE:
				printf("%d\n", memory[ operand ]);
				break;
			
			case LOAD:
				accumulator = memory[ operand ];
				break;
			
			case STORE:
				memory[ operand ] = accumulator;
				break;
			
			case ADD:
				accumulator += memory[ operand ];
				break;
			
			case SUBTRACT:
				accumulator -= memory[ operand ];
				break;
			
			case DIVIDE:
				if (memory[ operand ] == 0) {
				
					printf("*** Attempt to divide by zero ***\n");
					fatal_error();
					break;
					
				} else {
					
					accumulator /= memory[ operand ];
					break;
					
					}
			
			case MULTIPLY:
				accumulator *= memory[ operand ];
				break;
			
			case BRANCH:
				i = --operand;
				break;
			
			case BRANCHNEG:
				if (accumulator < 0) i = --operand;
				break;
			
			case BRANCHZERO:
				if (accumulator == 0) i = --operand;
				break;
				
			case HALT:
				printf("*** Simpletron execution terminated ***\n");
				dump();
				getchar();
				while( getchar() != '\n' ) {
                       getchar();
                       }
				/* exit( EXIT_SUCCESS ); */
				break;
		
			default:
				/* printf("\n*** Hai inserito un operationCode non valido ***"); */
				/* fatal_error(); */
				break;
		
		}
		
		
	}
	
}

