module xyz_peppergray_Potato1_top(
  input [7:0] io_in,
  output [7:0] io_out
);

  localparam INSTR_WITH   = 4;
  localparam INSTR_NUM    = 9;
  localparam CNTRL_WITH   = 9;
  localparam CMD_WITH     = 8;
  localparam LOOPCTR_WITH = 16;

  localparam CTRL_X_INC  = 0;
  localparam CTRL_X_DEC  = 1;
  localparam CTRL_A_INC  = 2;
  localparam CTRL_A_DEC  = 3;
  localparam CTRL_PUT    = 4;
  localparam CTRL_GET    = 5;
  localparam CTRL_LOOP   = 6;
  localparam CTRL_DONE   = 7;
  localparam CTRL_HALT   = 8;

  localparam X_PC_INC  = 0;
  localparam X_PC_DEC  = 1;

  localparam CMD_OFFSET = 2;

  wire Clock   = io_in[0];
  wire Reset_n = io_in[1];

  reg ZeroFlag;
  reg IOWait;
  reg [INSTR_WITH-1:0] Instruction;

  /* Input */  
  always @(posedge Clock or negedge Reset_n) begin
    if(~Reset_n) begin
      Instruction <= 4'b1111; /* Halt */
      ZeroFlag    <= 0;
      IOWait      <= 0;
    end
    else begin
      Instruction <= io_in[7:4];
      ZeroFlag    <= io_in[3];
      IOWait      <= (IOActivity && io_in[2]);
    end
  end

  /* Instruction Decode */  
  reg [INSTR_NUM-1:0] MicroInstruction;
  
  always @ * begin
    case(Instruction)
      4'b0000: begin MicroInstruction <= (1 << CTRL_X_INC); end
      4'b0001: begin MicroInstruction <= (1 << CTRL_X_DEC); end
      4'b0010: begin MicroInstruction <= (1 << CTRL_A_INC); end
      4'b0011: begin MicroInstruction <= (1 << CTRL_A_DEC); end
      4'b0100: begin MicroInstruction <= (1 << CTRL_PUT);   end
      4'b0101: begin MicroInstruction <= (1 << CTRL_GET);   end
      4'b0110: begin MicroInstruction <= (1 << CTRL_LOOP);  end
      4'b0111: begin MicroInstruction <= (1 << CTRL_DONE);  end
      4'b1111: begin MicroInstruction <= (1 << CTRL_HALT);  end
      default: begin MicroInstruction <= 0; /* CTRL_NOP */  end
    endcase
  end

  /* Loop Control */
  reg reverse;
  reg skipCmd;  
  wire Reverse = setReverse | (reverse & ~clrReverse);
  wire SkipCmd = setSkipCmd | (skipCmd & ~clrSkipCmd);

  wire Loop = MicroInstruction[CTRL_LOOP];
  wire Done = MicroInstruction[CTRL_DONE];

  wire setSkipCmd_L =!reverse && !skipCmd & ZeroFlag;
  wire clrReverse_L = reverse && markMatch;
  wire clrSkipCmd_L = skipCmd && clrReverse;

  wire setReverse_D = !reverse && !skipCmd && !ZeroFlag;
  wire setSkipCmd_D = setReverse;
  wire clrSkipCmd_D = skipCmd && markMatch;

  wire setSkipCmd = Loop ? setSkipCmd_L : Done ? setSkipCmd_D : 0;
  wire clrSkipCmd = Loop ? clrSkipCmd_L : Done ? clrSkipCmd_D : 0;
  wire setReverse = Done ? setReverse_D : 0;
  wire clrReverse = Loop ? clrReverse_L : 0;

  wire Count = !((!reverse && setReverse) || (reverse && clrReverse));
  wire Up    = Count && (reverse ? Done : Loop);
  wire Down  = Count && (reverse ? Loop : Done);
  wire Store = setSkipCmd;
  
  reg [LOOPCTR_WITH-1:0] LoopCounter;
  reg [LOOPCTR_WITH-1:0] LoopJmpMark;
  wire markMatch     = (LoopJmpMark == LoopCounter);

  always @(negedge Clock or negedge Reset_n) begin
    if(~Reset_n) begin
      LoopCounter <= 0;
      LoopJmpMark <= 0;
      reverse     <= 0;
      skipCmd     <= 0;
    end
    else begin
      LoopCounter <= LoopCounter + (Count ? (Up ? 1 : Down ? -1 : 0) : 0);
      LoopJmpMark <= (Store ? LoopCounter + (Up ? 1 : Down ? -1 : 0) : LoopJmpMark);
      reverse     <= clrReverse ? 0 : setReverse ? 1 : reverse;
      skipCmd     <= clrSkipCmd ? 0 : setSkipCmd ? 1 : skipCmd;
    end
  end

  /* Execution Control */
  reg [CNTRL_WITH-1:0] Control;
  
  always @ * begin
    Control = IOWait ? Control : SkipCmd ? 0 : MicroInstruction;
  end

  /* ProgramCounter */
  wire [1:0] Control_PC;
    
  assign Control_PC[X_PC_INC] = ~Reverse & ~(Control[CTRL_HALT] | IOWait);
  assign Control_PC[X_PC_DEC] =  Reverse & ~(Control[CTRL_HALT] | IOWait);

  /* Output */
  reg [CMD_WITH-1:0] Command;
  assign io_out = Command;

  wire IOActivity = (Command[CMD_OFFSET + CTRL_GET] || Command[CMD_OFFSET + CTRL_PUT]);

  always @(negedge Clock or negedge Reset_n) begin
    if(~Reset_n) begin
      Command <= 0;
    end
    else begin
      Command <= { Control[5:0], Control_PC[1:0]};
    end
  end

endmodule