// control signals

#include <stdio.h>

#define  a_pc		1
#define  a_ads		2
#define  pc_inc		3
#define  pc_ads		4
#define  z_mbus		5
#define  z_bus		6
#define  x_d		7
#define  x_s1		8
#define  y_s2		9
#define  alu_add	10
#define  alu_le     11
#define  alu_pass	12
#define  alu_true	13
#define  lir		14
#define  lT			15
#define  rwrite_d	16
#define  mread		17
#define  mwrite		18
#define  jmpT		19

#define  last_sig   20
#define  decode		20		// control flow of microprogram

#define  mpgm_width 21

#define  iadd		1
#define  ile		2
#define  ild		3
#define  ist		4
#define  ijmp		5
#define  ijt		6
#define  ijf		7
#define  iend		8

int mpgm[20][mpgm_width];
int mpgm_next[20];
int upc;
int PC, R[32], M[100], IR, T;					// registers, memory
int alu_p1, alu_p2, alu_out, R_in, mbus, bus;	// wires
int alu_t, m_ads;								// wires

int micro[] = {
	a_pc, mread, 0, 1,
	lir, pc_inc, decode, 0, 0,
	alu_add, x_s1, y_s2, lT, 0, 3,
	z_bus, rwrite_d, 0, 0,
	alu_le, x_s1, y_s2, lT, 0, 5,
	z_bus, rwrite_d, 0, 0,
	a_ads, mread, z_mbus, rwrite_d, 0, 0,
	x_d, alu_pass, a_ads, mwrite, 0, 0,
	pc_ads, 0, 0,
	x_d, alu_true, jmpT, 0, 0, -1
};

//  microprogram       next
//
//  010000000000000001000 1
//  000100000000001000001 0
//  000000001110000100000 3
//  000000100000000010000 0
//  000000001101000100000 5
//  000000100000000010000 0
//  001001000000000011000 0
//  001000010000100000100 0
//  000010000000000000000 0
//  000000010000010000010 0

// build microprogram from source micro[.]
void create_mpgm(void){
	int i, j, s;
	for(i = 0; i < 20; i++)			// clear mpgm[.][.]
		for(j = 0; j < mpgm_width; j++)
			mpgm[i][j] = 0;
	j = 0;							// each mpgm word
	i = 0;							// each signal from micro[.]
	s = micro[i];
	while( s >= 0 ){				// until end, s = -1
		mpgm[j][s] = 1;
		i++;						// next signal
		s = micro[i];
		if ( s == 0 ){				// end of current word
			i++;
			mpgm_next[j] = micro[i];
			j++;					// next word
			i++;
			s = micro[i];
		}
	}
}

// decoding of IR bit field
int IR_op(void){ return (IR & 0x0f8000000) >> 27; }
int IR_ads(void){ return IR & 0x03fffff; }
int IR_d(void){ return (IR & 0x07c00000) >> 22; }
int IR_s1(void){ return (IR & 0x03e0000) >> 17; }
int IR_s2(void){ return (IR & 0x01f000) >> 12; }

void signal_active(int i){
	switch(i){
	case  a_pc:		m_ads = PC; break;
	case  a_ads:   	m_ads = IR_ads(); break;
	case  pc_inc:	PC = PC + 1; break;
	case  pc_ads:	PC = IR_ads(); break;
	case  z_mbus:	R_in = mbus; break;
	case  z_bus:	R_in = bus; break;
	case  x_d:		alu_p1 = R[IR_d()]; break;
	case  x_s1:		alu_p1 = R[IR_s1()]; break;
	case  y_s2:		alu_p2 = R[IR_s2()]; break;
	case  alu_add:	alu_out = alu_p1 + alu_p2; break;
	case  alu_le:	alu_out = alu_p1 <= alu_p2; break;
	case  alu_pass: alu_out = alu_p1; break;
	case  alu_true: alu_t = alu_p1 != 0; break;
	case  lir:		IR = mbus; break;
	case  lT:		T = alu_out; break;
	case  rwrite_d: R[IR_d()] = R_in; break;
	case  mread:	mbus = M[m_ads]; break;
	case  mwrite:   M[m_ads] = mbus; break;
	case  jmpT:		if( alu_t ) PC = IR_ads(); break;
	}
}
// convert op to microprogram address (ako ROM)
int map_op[] = {
	0,		// nop
	2, 		// add
	4,		// le
	6,		// ld
	7,		// st
	8,		// jmp
	9,		// jt
	0,
	0,
	0
};

int signal_order[] = { 1,2,3,4 };

// execute one machine instruction
void exe_mpgm(void){
	int i, j;
	do{
		for(i = 1; i < last_sig; i++){		// scan each bit
			j = signal_order[i];
			if( mpgm[upc][j] == 1 ) signal_active(j);
		}
		if( mpgm[upc][decode] == 1 ) upc = map_op[IR_op()];
		else upc = mpgm_next[upc];
	}while (upc != 0);
}

void test_create_mpgm(void){
	int i, j;
	create_mpgm();
	for(i = 0; i < 10; i++){
		for(j = 0; j < mpgm_width; j++)
			printf("%d", mpgm[i][j]);
		printf(" %d\n", mpgm_next[i]);
	}
}

// an example program in assembly language

// add 1..10

// assume
// r[1] = 1,
// r[2] = 10,
// r[0] = 0
// r[3] = result, initially 0
// r[4] = tmp
// r[5] = flag

// assembly program

//      add r4 r1 r0  // set r4 1
//loop: add r3 r4 r3
//      add r4 r4 r1  // increment i
//      le r5 r4 r2   // i <= 10?
//      jt r5 loop
//      end

int pgm1[] = {
	iadd, 4, 1, 0,
	iadd, 3, 4, 3,
	iadd, 4, 4, 1,
	ile, 5, 4, 2,
	ijt, 5, 0, 1, -1
};

int format1(int op, int d, int s1, int s2){
	return (op << 27) | (d << 22) | (s1 << 17) | (s2 << 12);
}

int format2(int op, int d, int ads){
	return (op << 27) | (d << 22) | ads;
}

// convert pgm1[.] to machine code started at 0
void assemble(void){
	int i, j, op, d, s1, s2;
	i = 0;
	j = 0;
	op = pgm1[i];
	while( op >= 0 ){
		d = pgm1[i+1];
		s1 = pgm1[i+2];
		s2 = pgm1[i+3];
		if( op < 8 ) M[j] = format1(op,d,s1,s2);
		else M[j] = format2(op,d,s2);
		j++;
		i += 4;
		op = pgm1[i];
	}
}

void test_assemble(void){
	int i, op, s2;
	assemble();
	for(i = 0; i < 5; i++){
		IR = M[i];
		op = IR_op();
		if( op < 8 ) s2 = IR_s2();
		else s2 = IR_ads();
		printf("i %d op %d d %d s1 %d s2 %d\n",
		  i, op, IR_d(), IR_s1(), s2);
	}
}

int main(void){
	upc = 0;
	PC = 0;
	create_mpgm();
//	test_create_mpgm();
	test_assemble();
	printf("hello\n");
}




