module alu(opcode, operand_1, operand_2, result, nzcv_old, nzcv, c_from_shifter, isWriteback);

parameter N = 32; 

input[3:0] opcode;	//opcode of operation
input wire [N-1:0] operand_1; //operands:
input wire [N-1:0] operand_2;
input wire[3:0] nzcv_old; //old nzcv
input wire c_from_shifter;//this is carry flag from shifter. so that we can update carry flag for logical instructions

output reg isWriteback; //specifies if result is to be written back
output reg[N-1:0] result; //output
output reg [3:0] nzcv; 	//update nzcv register
/*condition code register.  i.e, both read and write,  nzcv[3] = n
nzcv[0] = v
nzcv[1] = c
nzcv[2] = z
nzcv[3] = n  */

reg cin;
reg cout;
reg[N-1:0] neg;

always @(*) begin
	nzcv= nzcv_old;

	case(opcode)
		//logical and
		`AND: begin
			result = operand_1 & operand_2;
			nzcv[1] = c_from_shifter;
			isWriteback = 1;
		end
		//xor
		`EOR: begin
			result = operand_1 ^ operand_2;
			isWriteback = 1;
			nzcv[1] = c_from_shifter;
		end
		//result = op1-op2
		`SUB: begin
			neg = -operand_2;
			{cin, result[N-2:0]} = operand_1[N-2:0]+neg[N-2:0]; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+neg[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 1;
		end
		
		//result = op2-op1
		`RSB: begin
			neg = -operand_1;
			{cin, result[N-2:0]} = operand_2[N-2:0]+neg[N-2:0]; 
			{cout, result[N-1]} = cin+ operand_2[N-1]+neg[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 1;
		end
		
		//result = op1+op2
		`ADD: begin
			{cin, result[N-2:0]} = operand_1[N-2:0]+operand_2[N-2:0]; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+operand_2[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 1;
		end
		
		//result = op1+op2+c
		`ADC: begin
			{cin, result[N-2:0]} = operand_1[N-2:0]+operand_2[N-2:0]+nzcv_old[1]; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+operand_2[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 1;
		end
		
		//result = op1 – Op2 + C-1
		`SBC: begin
			neg = -operand_2;
			{cin, result[N-2:0]} = operand_1[N-2:0]+neg[N-2:0]+nzcv_old[1]-1; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+neg[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag	
			isWriteback = 1;
		end
		
		//result = op2-op1+c-1
		`RSC: begin
			neg = -operand_1;
			{cin, result[N-2:0]} = operand_2[N-2:0]+neg[N-2:0]+nzcv_old[1]-1; 
			{cout, result[N-1]} = cin+ operand_2[N-1]+neg[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 1;
		end
		
		//same as AND  but you don't write back
		`TST: begin
			result = operand_1 & operand_2;
			isWriteback = 0;
			nzcv[1] = c_from_shifter;
		end
		
		//same as EOR but you don't write back
		`TEQ: begin
			result = operand_1 ^ operand_2;
			isWriteback = 0;
			nzcv[1] = c_from_shifter;
		end
		
		//same as SUB but you don't write back
		`CMP: begin
			neg = -operand_2;
			{cin, result[N-2:0]} = operand_1[N-2:0]+neg[N-2:0]; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+neg[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 0;
		end
		
		//same as ADD but you don't write back
		`CMN: begin
			{cin, result[N-2:0]} = operand_1[N-2:0]+operand_2[N-2:0]; 
			{cout, result[N-1]} = cin+ operand_1[N-1]+operand_2[N-1];
			nzcv[1]  = cout;	//carry flag
			nzcv[0] = cin^cout; //overflow flag
			isWriteback = 0;
		end
		
		//bitwise or
		`ORR: begin
			result = operand_1 | operand_2;
			isWriteback = 1;
			nzcv[1] = c_from_shifter;
		end
		
		//move. op1 ignored
		`MOV: begin
			result = operand_2;
			isWriteback = 1;
			nzcv[1] =c_from_shifter;
		end
		
		//res = op1 & ~op2
		`BIC: begin
			result = operand_1 & (~operand_2);
			isWriteback = 1;
			nzcv[1] =c_from_shifter;
		end
		
		//move negated op2
		`MVN: begin
			result = ~operand_2;
			isWriteback = 1;
			nzcv[1] = c_from_shifter;
		end
		
	endcase

	nzcv[3] = result[N-1]; 	//n flag	
	nzcv[2] = (result == 0); // z flag
			
end

endmodule