TITLE "FORTH-CPU version 7";

include "../fcpu7/stack.inc";
include "../fcpu7/alu_5.inc";
include "../fcpu7/alu_6.inc";
include "../fcpu7/alu_7.inc";

constant nop = 0;
constant pop = 2;
constant push = 1;
constant swap = 3;

PARAMETERS (
        WIDTH = 16,
        DEPTH = 8
);
SUBDESIGN fcpu (
        CLKi : input;  -- ясное дело - синхронизация!
        DI[WIDTH-1..0] : input; -- данные, считываемые из ОЗУ
        DO[WIDTH-1..0] : output; -- данные для запппписи в ОЗУ
        ADR[WIDTH-1..0] : output; -- адрес ОЗУ
        RD,WR,CS : output; -- управляющие сигналы ОЗУ
        RESET	: input;
)
VARIABLE
	CLK : node;
    RCMD[WIDTH-1..0] : DFF;
    RStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    DStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    PC[WIDTH-1..0] : DFFE;
    TR[WIDTH-1..0] : DFFE;
    CMD[3..0] : NODE;
    PREF[3..0]: NODE;
    ALU5 : alu_5 with(WIDTH=WIDTH);
    ALU6 : alu_6 with(WIDTH=WIDTH);
    ALU7 : alu_7 with(WIDTH=WIDTH);
    XR		: DFF;
BEGIN
	

DEFAULTS
    RStack.cmd[] = GND; -- по умолчанию nop
    DStack.cmd[] = GND; -- по умолчанию nop
    PC[].ena = GND;  -- PC не меняется
    TR[].ena = GND;  -- TR не меняется
    RD	= VCC;
    WR	= VCC;
    CS	= VCC;
    XR.d = GND;
END DEFAULTS;

	CLK = GLOBAL(CLKi);
	(RCMD[].clrn,PC[].clrn) = DFF(RESET,CLK,,);

    RCMD[].clk = clk;
    RStack.clk = clk;
    DStack.clk = clk;
    PC[].clk = clk;
    TR[].clk = clk;
    ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[];
    ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[];
    ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[];
    PREF[] = RCMD[7..4];
    XR.clk = clk;

CASE CMD[3..0] IS
    WHEN 0 => -- команда 0 NOP
        RCMD[] = DI[]; RD = GND;
        PC[] = PC[] + 1; PC[].ena = VCC;
    WHEN 1 => -- команда 1 LIT
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        PC[] = PC[] + 1; PC[].ena = VCC;
        TR[] = DI[]; TR[].ena = VCC; RD = GND;
        DStack.di[] = TR[];
        DStack.cmd[] = push;
    WHEN 2 => -- команда 2 CALL
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        PC[] = DI[]; PC[].ena = VCC; RD = GND;
        RStack.cmd[] = push; 
        RStack.di[] = PC[] + 1;
    WHEN 3 => -- команда 3 RET
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
		PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop;
    WHEN 4 => -- команда 4 ?BRANCH
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        IF (TR[] == 0) THEN
            PC[] = PC[] + 1;
        ELSE
            PC[] = DI[]; RD = GND;
        END IF;
        PC[].ena = VCC;
    WHEN 5 => -- команда 5 DUP*
        RCMD[] = (B"00000000",RCMD[WIDTH-1..8]);
        TR[] = ALU5.res[]; TR[].ena = VCC;
	    DStack.di[] = TR[]; DStack.cmd[] = push;
    WHEN 6 => -- команда 6 DROP*
        RCMD[] = (B"00000000",RCMD[WIDTH-1..8]);
        TR[] = ALU6.res[]; TR[].ena = VCC;
        DStack.di[] = TR[]; DStack.cmd[] = pop;
    WHEN 7 => -- команда 7 SWAP*
        RCMD[] = (B"00000000",RCMD[WIDTH-1..8]);
        TR[] = ALU7.res[]; TR[].ena = VCC;
        DStack.di[] = TR[]; DStack.cmd[] = swap;
    WHEN 8 => -- @ -- call*
        RCMD[] = (RCMD[WIDTH-1..4],B"1001");
        PC[] = TR[]; PC[].ena = VCC;
        RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC
    WHEN 9 => -- продолжение @ - ret-@
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop;
        TR[] = DI[]; TR[].ena = VCC; RD = GND;
    WHEN 10 => -- ! -- call*
        RCMD[] = (RCMD[WIDTH-1..4],B"1011");
        PC[] = TR[]; PC[].ena = VCC;
        RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC
        TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop;
    WHEN 11 => -- продолжение ! ret-!
        WR = GND;
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop;
        TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop;
    WHEN 12 => -- >R
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        RStack.di[] = TR[]; RStack.cmd[] = push;
        TR[] = DStack.do[]; TR[].ena = VCC;
        DStack.cmd[] = pop;
    WHEN 13 => -- R>
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
        DStack.di[] = TR[]; DStack.cmd[] = push;
        TR[] = RStack.do[]; TR[].ena = VCC;
        RStack.cmd[] = pop;
    WHEN 14 => -- MOVE
		PC[] = RStack.do[]; RStack.cmd[] = swap; PC[].ena = VCC;
		RStack.di[] = PC[] + 1;
		XR = !XR;

		IF !XR THEN
			TR[] = DI[]; TR[].ena = VCC; RD = GND;
			DStack.di[] = TR[]; DStack.cmd[] = push;
		ELSE
			DStack.cmd[] = pop; 
			TR[] = DStack.do[] - 1; WR = GND; TR[].ena = VCC;
		END IF;
		-- return to normal execute 
		IF (TR[] == 0) and !XR THEN
			RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
	        ELSE
			RCMD[] = RCMD[];
		END IF;
	WHEN 15 => 
		-- NEXT (= RET&CALL)
		RCMD[] = (RCMD[WIDTH-1..4],B"0010"); -- next command - CALL
		-- all for RET
		PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop;
		
END CASE; 
	CS = WR and RD;
    CMD[] = RCMD[3..0];
	ADR[] = PC[];
	DO[] = TR[];

END;