<?
require_once "lib/utils.php";
require_once "lib/mux.php";
require_once "lib/counter.php";
counterC(16);
counterModV(16);
mux(4);
?>

#
# Add / Sub
#
<?
require_once "addsub.php";
?>

#
# Register
#

<?=listV("out",0,16)?> = reg(w,<?=listV("in",0,16)?>){

<? for($i=0;$i<16;$i++){ ?>
	inst in2[<?=$i?>] = mux(out[<?=$i?>],in[<?=$i?>],w)
	out[<?=$i?>] = Z(in2[<?=$i?>])
<? } ?>

}

#
# Main
#

<?
function pW($i){return listV("W${i}",0,16);}

function pW2($j){
	$t = array();
	for($i=15;$i>=0;$i--){
		$t[$i] = "W${i}[${j}]";
	}
	return implode(",",$t);
}

function pInput2($j){
	$t = array();
	for($i=15;$i>=0;$i--){
		$t[$i] = "input${i}[${j}]";
	}
	return implode(",",$t);
}


function pWsrc1(){return listV("Wsrc1",0,16);}
function pWsrc2(){return listV("Wsrc2",0,16);}
function pWdst(){return listV("Wdst",0,16);}
function pInstr($i=-1){
	if($i==-1){
		return listV("instr",0,16);
	}else{
		return listV("instr",4*$i,4*($i+1));
	}
}
function pSelRes(){return listV("selRes",0,4);}
function pPC(){return listV("pc",0,16);}
?>

changeOut, changeIn, <?for($i=0;$i<8;$i++){print(listV("output$i",0,16).",");}?> = main(<?for($i=0;$i<8;$i++){print(listV("input$i",0,16).",");}?>){

	#
	# External interfaces
	#

	# Outputs (real outputs + timer period)

<? for($i=0;$i<9;$i++){ ?>
	inst <?=listV("output$i",0,16)?> = reg(<?="outputW[$i]"?>,<?=pWsrc2()?>)
	outputW[<?=$i?>] = outputW2[<?=$i?>] & changeOut2
<? } ?>
	rom <?=listV("outputW2",0,16)?> = selector4(<?=pInstr(1)?>) 

	# Inputs
	
	# -- Real inputs
<? for($i=10;$i<16;$i++){ ?>
<? for($j=0;$j<16;$j++){ ?>
	input<?=$i?>[<?=$j?>] = 0
<? } ?>	
<? } ?>

	# -- Timer period
<? for($j=0;$j<16;$j++){ ?>
	input<?=8?>[<?=$j?>] = output<?=8?>[<?=$j?>]
<? } ?>	

	# Change out/in

	changeOut = Z(changeOut ^ changeOut1)
	changeIn = Z(changeIn) ^ changeIn1

	changeOut2 = instr[15] & instr[14] & ~instr[13] & instr[12] # instr[15-12]=13 - =1 when write something out
	changeOut1 = changeOut2 & ~instr[7] # =1 when write something out on port 0-7
	changeIn1 = instr[15] & ~instr[14] & ~instr[13] & ~instr[12] & ~instr[3] & instr[2] & ~instr[1] & instr[0] & ~instr[7] # idem for "read"
	# instr[15-12]=8 and instr[3-0]=5 and instr[7]=0

	# RAM
	
	ram <?=listV("ramLh",0,16)?> = RAM(<?=pWsrc1()?>,weRam,<?=pWsrc2()?>,<?=pWsrc1()?>)
	weRam = instr[15] & instr[14] & ~instr[13] & ~instr[12]

	# Timer

	changeTimerOut = changeOut2 & instr[7] & ~instr[6] & ~instr[5] & instr[4]

	inst <?=listV("input9",0,16)?>,wake = counterModV16(1,changeTimerOut,<?=pWsrc2()?>,<?=listV("output8",0,16)?>)

	#
	# Real Microprocessor - Registers / Control Unit / ALU
	#

	# Registers
<? for($i=0;$i<16;$i++){ ?>
	inst <?=pW($i)?> = reg(<?="Ww[$i]"?>, <?=pWdst()?>)
<? } ?>

	# Zero / Carry

	inst carry1 = mux(carry,carry0,carryW)
	carry = Z(carry1)

	inst zero1 = mux(zero,zero0,zeroW)
	zero = Z(zero1)

	carryWalu = (~instr[15] & ~instr[14])
	carryWsr = (instr[15] & ~instr[14] & ~instr[13] & ~instr[12] & ~instr[3] & ~instr[2] & instr[1])	
	carryW = carryWalu | carryWsr
	# carryW = 1 when instr=3,2,1 or 0   or  sra/srl 8-2/3
	
	inst carry0 = mux(carryAlu,Wsrc1[0],carryWsr)
	
	zeroW = ~instr[15] | (instr[15] & ~instr[14] & ~instr[13] & ~instr[12] & ~instr[3]) | (instr[15] & ~instr[14] & instr[13])
	# zeroW = 1 when instr ALU ((less than 8) or 8/(less than 8)) or lhi (10) or lli (11)
	zero0 = ~(<?=listV("Wdst",0,16,"|")?>)


	# Select src1 and src2

<? for($i=0;$i<16;$i++){ ?>
	inst <?="Wsrc1[$i]"?> = mux4(<?=pW2($i)?>,<?=pInstr(1)?>)
	inst <?="Wsrc2[$i]"?> = mux4(<?=pW2($i)?>,<?=pInstr(0)?>)
	inst <?="WdstOld[$i]"?> = mux4(<?=pW2($i)?>,<?=pInstr(2)?>)
<? } ?>

	# Select dst

	rom <?=listV("Ww",0,16)?> = selector4(<?=pInstr(2)?>)

	# Computation (ALU/...)

<? for($i=0;$i<16;$i++){ ?>
	<?="Wand[$i]"?> = <?="Wsrc1[$i]"?> & <?="Wsrc2[$i]"?> 
	<?="Wor[$i]"?> = <?="Wsrc1[$i]"?> | <?="Wsrc2[$i]"?> 
	<?="Wxor[$i]"?> = <?="Wsrc1[$i]"?> ^ <?="Wsrc2[$i]"?> 
	<?="Wnot[$i]"?> = ~ <?="Wsrc1[$i]"?> 
	<?="Wsrl[$i]"?> = <?if($i<15){?>Wsrc1[<?=$i+1?>]<?}else{?>0<?}?> 
	<?="Wsra[$i]"?> = <?if($i<15){?>Wsrc1[<?=$i+1?>]<?}else{?>Wsrc1[15]<?}?> 
<? if($i<8){ ?>
	<?="HiK[$i]"?> = <?="WdstOld[$i]"?> 
<? }else{ ?>
	<?="HiK[$i]"?> = <?="instr[".($i-8)."]"?> 
<? } ?> 
<? if($i>=8){ ?>
	<?="LoK[$i]"?> = <?="WdstOld[$i]"?> 
<? }else{ ?>
	<?="LoK[$i]"?> = <?="instr[".($i)."]"?> 
<? } ?>
	inst <?="Input[$i]"?> = mux4(<?=pInput2($i)?>,<?=pInstr(1)?>)
<? } ?>

	uAdd = instr[12] & carry
	uSub = ~ instr[12]
	inst uAddSub = mux(uAdd,uSub,sAddSub)
	sAddSub = instr[13]
	inst <?=listV("Waddsub",0,16)?>, carryAlu = addsub (<?=pWsrc1()?>,<?=pWsrc2()?>,uAddSub,sAddSub)

	# Jump

	jmp = (instr[15] & ~instr[14] & ~instr[13] & instr[12]) & ~((instr[1] & ~(zero ^ instr[0])) | (instr[2] & ~(carry ^ instr[0])))

	# Select instruction

<? for($i=0;$i<16;$i++){ ?>
	inst <?="Wdst[$i]"?> = mux4(<?="0,0,WdstOld[$i],WdstOld[$i],LoK[$i],HiK[$i],Wret[$i],Winstr8[$i],0,Wand[$i],Wor[$i],Wxor[$i],Waddsub[$i],Waddsub[$i],Waddsub[$i],Waddsub[$i]"?>, <?=pInstr(3)?>)
	inst <?="Wret[$i]"?> = mux(<?="WdstOld[$i]"?>,<?="nextPC[$i]"?>,instr[3])
	inst <?="Winstr8[$i]"?> = mux4(<?="0,0,0,0,0,0,0,WdstOld[$i],0,0,Input[$i],ramLh[$i],Wsrl[$i],Wsra[$i],Wnot[$i],Wsrc1[$i]"?>,<?=pInstr(0)?>)
<? } ?>

	# Sleep

	nsleep = wake | ~(instr[15] & ~instr[14] & ~instr[13] & ~instr[12] & instr[3] & ~instr[2] & ~instr[1] & ~instr[0]) # nsleep=0 in sleep mode (8-8)

	rom <?=pInstr()?>  = romInstr(<?=pPC()?>)
	inst <?=pPC()?>, c, <?=listV("nextPC",0,16)?> = counterC16(nsleep,jmp,<?=pWsrc1()?>)
}
