`define YES
`include "revision.v"

module lfsre(
    input clk,
    output reg [16:0] lfsr);
wire d0;

xnor(d0,lfsr[16],lfsr[13]);

always @(posedge clk) begin
    lfsr <= {lfsr[15:0],d0};
end
endmodule

module oldram256x1s(
  input d,
  input we,
  input wclk,
  input [7:0] a,
  output o);

  wire sel0 = (a[7:6] == 0);
  wire o0;
  RAM64X1S r0(.O(o0), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE(sel0 & we));

  wire sel1 = (a[7:6] == 1);
  wire o1;
  RAM64X1S r1(.O(o1), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE(sel1 & we));

  wire sel2 = (a[7:6] == 2);
  wire o2;
  RAM64X1S r2(.O(o2), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE(sel2 & we));

  wire sel3 = (a[7:6] == 3);
  wire o3;
  RAM64X1S r3(.O(o3), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE(sel3 & we));

  assign o = (a[7] == 0) ? ((a[6] == 0) ? o0 : o1) : ((a[6] == 0) ? o2 : o3);
endmodule

module ring64(
  input clk,
  input i,
  output o);

  wire o0, o1, o2;
  SRL16E ring0( .CLK(clk), .CE(1), .D(i),  .A0(1), .A1(1), .A2(1), .A3(1), .Q(o0));
  SRL16E ring1( .CLK(clk), .CE(1), .D(o0), .A0(1), .A1(1), .A2(1), .A3(1), .Q(o1));
  SRL16E ring2( .CLK(clk), .CE(1), .D(o1), .A0(1), .A1(1), .A2(1), .A3(1), .Q(o2));
  SRL16E ring3( .CLK(clk), .CE(1), .D(o2), .A0(1), .A1(1), .A2(1), .A3(1), .Q(o));
endmodule

module ram256x1s(
  input d,
  input we,
  input wclk,
  input [7:0] a,
  output o);

  wire [1:0] rsel = a[7:6];
  wire [3:0] oo;
  genvar i;
  generate 
    for (i = 0; i < 4; i=i+1) begin : ramx
    RAM64X1S r0(.O(oo[i]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE((rsel == i) & we));
    end
  endgenerate

  assign o = oo[rsel];
endmodule

module ram448x1s(
  input d,
  input we,
  input wclk,
  input [8:0] a,
  output o);

  wire [2:0] rsel = a[8:6];
  wire [7:0] oo;
  genvar i;
  generate 
    for (i = 0; i < 7; i=i+1) begin : ramx
      RAM64X1S r0(.O(oo[i]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE((rsel == i) & we));
    end
  endgenerate

  assign o = oo[rsel];
endmodule

module ram400x1s(
  input d,
  input we,
  input wclk,
  input [8:0] a,
  output o);

  wire [2:0] rsel = a[8:6];
  wire [6:0] oo;
  genvar i;
  generate 
    for (i = 0; i < 6; i=i+1) begin : ramx
      RAM64X1S r0(.O(oo[i]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d), .WCLK(wclk), .WE((rsel == i) & we));
    end
  endgenerate
  RAM16X1S r6(.O(oo[6]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .D(d), .WCLK(wclk), .WE((rsel == 6) & we));

  assign o = oo[rsel];
endmodule

module ram256x8s(
  input [7:0] d,
  input we,
  input wclk,
  input [7:0] a,
  output [7:0] o);
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      ram256x1s ramx(
        .d(d[i]),
        .we(we),
        .wclk(wclk),
        .a(a),
        .o(o[i]));
    end
  endgenerate
endmodule

module ram32x8s(
  input [7:0] d,
  input we,
  input wclk,
  input [4:0] a,
  output [7:0] o);
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      RAM32X1S r0(.O(o[i]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .D(d[i]), .WCLK(wclk), .WE(we));
    end
  endgenerate
endmodule

module ram64x8s(
  input [7:0] d,
  input we,
  input wclk,
  input [5:0] a,
  output [7:0] o);
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      RAM64X1S r0(.O(o[i]), .A0(a[0]), .A1(a[1]), .A2(a[2]), .A3(a[3]), .A4(a[4]), .A5(a[5]), .D(d[i]), .WCLK(wclk), .WE(we));
    end
  endgenerate
endmodule

module mRAM32X1D(
  input D,
  input WE,
  input WCLK,
  input A0,     // port A
  input A1,
  input A2,
  input A3,
  input A4,
  input DPRA0,  // port B
  input DPRA1,
  input DPRA2,
  input DPRA3,
  input DPRA4,
  output DPO,   // port A out
  output SPO);  // port B out

  parameter INIT = 32'b0;
  wire hDPO;
  wire lDPO;
  wire hSPO;
  wire lSPO;
  RAM16X1D
    #( .INIT(INIT[15:0]) ) 
    lo(
    .D(D),
    .WE(WE & !A4),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPO(lDPO),
    .SPO(lSPO));
  RAM16X1D
    #( .INIT(INIT[31:16]) ) 
  hi(
    .D(D),
    .WE(WE & A4),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPO(hDPO),
    .SPO(hSPO));
  assign DPO = DPRA4 ? hDPO : lDPO;
  assign SPO = A4 ? hSPO : lSPO;
endmodule

module mRAM64X1D(
  input D,
  input WE,
  input WCLK,
  input A0,     // port A
  input A1,
  input A2,
  input A3,
  input A4,
  input A5,
  input DPRA0,  // port B
  input DPRA1,
  input DPRA2,
  input DPRA3,
  input DPRA4,
  input DPRA5,
  output DPO,   // port A out
  output SPO);  // port B out

  parameter INIT = 64'b0;
  wire hDPO;
  wire lDPO;
  wire hSPO;
  wire lSPO;
  mRAM32X1D
    #( .INIT(INIT[31:0]) ) 
    lo(
    .D(D),
    .WE(WE & !A5),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPO(lDPO),
    .SPO(lSPO));
  mRAM32X1D
    #( .INIT(INIT[63:32]) ) 
  hi(
    .D(D),
    .WE(WE & A5),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPO(hDPO),
    .SPO(hSPO));
  assign DPO = DPRA5 ? hDPO : lDPO;
  assign SPO = A5 ? hSPO : lSPO;
endmodule

module mRAM128X1D(
  input D,
  input WE,
  input WCLK,
  input A0,     // port A
  input A1,
  input A2,
  input A3,
  input A4,
  input A5,
  input A6,
  input DPRA0,  // port B
  input DPRA1,
  input DPRA2,
  input DPRA3,
  input DPRA4,
  input DPRA5,
  input DPRA6,
  output DPO,   // port A out
  output SPO);  // port B out
  parameter INIT = 128'b0;

  wire hDPO;
  wire lDPO;
  wire hSPO;
  wire lSPO;
  mRAM64X1D
    #( .INIT(INIT[63:0]) ) 
    lo(
    .D(D),
    .WE(WE & !A6),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .A5(A5),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPRA5(DPRA5),
    .DPO(lDPO),
    .SPO(lSPO));
  mRAM64X1D
    #( .INIT(INIT[127:64]) ) 
    hi(
    .D(D),
    .WE(WE & A6),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .A5(A5),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPRA5(DPRA5),
    .DPO(hDPO),
    .SPO(hSPO));
  assign DPO = DPRA6 ? hDPO : lDPO;
  assign SPO = A6 ? hSPO : lSPO;
endmodule


module mRAM256X1D(
  input D,
  input WE,
  input WCLK,
  input A0,     // port A
  input A1,
  input A2,
  input A3,
  input A4,
  input A5,
  input A6,
  input A7,
  input DPRA0,  // port B
  input DPRA1,
  input DPRA2,
  input DPRA3,
  input DPRA4,
  input DPRA5,
  input DPRA6,
  input DPRA7,
  output DPO,   // port A out
  output SPO);  // port B out

  wire hDPO;
  wire lDPO;
  wire hSPO;
  wire lSPO;
  mRAM128X1D
    lo(
    .D(D),
    .WE(WE & !A7),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .A5(A5),
    .A6(A6),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPRA5(DPRA5),
    .DPRA6(DPRA6),
    .DPO(lDPO),
    .SPO(lSPO));
  mRAM128X1D
  hi(
    .D(D),
    .WE(WE & A7),
    .WCLK(WCLK),
    .A0(A0),
    .A1(A1),
    .A2(A2),
    .A3(A3),
    .A4(A4),
    .A5(A5),
    .A6(A6),
    .DPRA0(DPRA0),
    .DPRA1(DPRA1),
    .DPRA2(DPRA2),
    .DPRA3(DPRA3),
    .DPRA4(DPRA4),
    .DPRA5(DPRA5),
    .DPRA6(DPRA6),
    .DPO(hDPO),
    .SPO(hSPO));
  assign DPO = DPRA7 ? hDPO : lDPO;
  assign SPO = A7 ? hSPO : lSPO;
endmodule

module ram32x8d(
  input [7:0] ad,
  input wea,
  input wclk,
  input [4:0] a,
  input [4:0] b,
  output [7:0] ao,
  output [7:0] bo
  );
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      mRAM32X1D ramx(
        .D(ad[i]),
        .WE(wea),
        .WCLK(wclk),
        .A0(a[0]),
        .A1(a[1]),
        .A2(a[2]),
        .A3(a[3]),
        .A4(a[4]),
        .DPRA0(b[0]),
        .DPRA1(b[1]),
        .DPRA2(b[2]),
        .DPRA3(b[3]),
        .DPRA4(b[4]),
        .SPO(ao[i]),
        .DPO(bo[i]));
    end
  endgenerate
endmodule

module ram64x8d(
  input [7:0] ad,
  input wea,
  input wclk,
  input [5:0] a,
  input [5:0] b,
  output [7:0] ao,
  output [7:0] bo
  );
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      mRAM64X1D ramx(
        .D(ad[i]),
        .WE(wea),
        .WCLK(wclk),
        .A0(a[0]),
        .A1(a[1]),
        .A2(a[2]),
        .A3(a[3]),
        .A4(a[4]),
        .A5(a[5]),
        .DPRA0(b[0]),
        .DPRA1(b[1]),
        .DPRA2(b[2]),
        .DPRA3(b[3]),
        .DPRA4(b[4]),
        .DPRA5(b[5]),
        .SPO(ao[i]),
        .DPO(bo[i]));
    end
  endgenerate
endmodule

// Same but latched read port, for CPU
module ram32x8rd(
  input wclk,
  input [15:0] ad,
  input wea,
  input [4:0] a,
  input [4:0] b,
  output reg [15:0] ao,
  output reg [15:0] bo
  );
  wire [15:0] _ao;
  wire [15:0] _bo;
  always @(posedge wclk)
  begin
    ao <= _ao;
    bo <= _bo;
  end
  genvar i;
  generate 
    for (i = 0; i < 16; i=i+1) begin : ramx
      mRAM32X1D ramx(
        .D(ad[i]),
        .WE(wea),
        .WCLK(wclk),
        .A0(a[0]),
        .A1(a[1]),
        .A2(a[2]),
        .A3(a[3]),
        .A4(a[4]),
        .DPRA0(b[0]),
        .DPRA1(b[1]),
        .DPRA2(b[2]),
        .DPRA3(b[3]),
        .DPRA4(b[4]),
        .SPO(_ao[i]),
        .DPO(_bo[i]));
    end
  endgenerate
endmodule

module ram128x8rd(
  input wclk,
  input [15:0] ad,
  input wea,
  input [6:0] a,
  input [6:0] b,
  output reg [15:0] ao,
  output reg [15:0] bo
  );
  wire [15:0] _ao;
  wire [15:0] _bo;
  always @(posedge wclk)
  begin
    ao <= _ao;
    bo <= _bo;
  end
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      mRAM128X1D ramx(
        .D(ad[i]),
        .WE(wea),
        .WCLK(wclk),
        .A0(a[0]),
        .A1(a[1]),
        .A2(a[2]),
        .A3(a[3]),
        .A4(a[4]),
        .A5(a[5]),
        .A6(a[6]),
        .DPRA0(b[0]),
        .DPRA1(b[1]),
        .DPRA2(b[2]),
        .DPRA3(b[3]),
        .DPRA4(b[4]),
        .DPRA5(b[5]),
        .DPRA6(b[6]),
        .SPO(_ao[i]),
        .DPO(_bo[i]));
    end
  endgenerate
endmodule

module ram256x8rd(
  input wclk,
  input [7:0] ad,
  input wea,
  input [7:0] a,
  input [7:0] b,
  output reg [7:0] ao,
  output reg [7:0] bo
  );
  wire [7:0] _ao;
  wire [7:0] _bo;
  always @(posedge wclk)
  begin
    ao <= _ao;
    bo <= _bo;
  end
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      mRAM256X1D ramx(
        .D(ad[i]),
        .WE(wea),
        .WCLK(wclk),
        .A0(a[0]),
        .A1(a[1]),
        .A2(a[2]),
        .A3(a[3]),
        .A4(a[4]),
        .A5(a[5]),
        .A6(a[6]),
        .A7(a[7]),
        .DPRA0(b[0]),
        .DPRA1(b[1]),
        .DPRA2(b[2]),
        .DPRA3(b[3]),
        .DPRA4(b[4]),
        .DPRA5(b[5]),
        .DPRA6(b[6]),
        .DPRA7(b[7]),
        .SPO(_ao[i]),
        .DPO(_bo[i]));
    end
  endgenerate
endmodule

module ram448x9s(
  input [8:0] d,
  input we,
  input wclk,
  input [8:0] a,
  output [8:0] o);
  genvar i;
  generate 
    for (i = 0; i < 9; i=i+1) begin : ramx
      ram448x1s ramx(
        .d(d[i]),
        .we(we),
        .wclk(wclk),
        .a(a),
        .o(o[i]));
    end
  endgenerate
endmodule

module ram400x9s(
  input [8:0] d,
  input we,
  input wclk,
  input [8:0] a,
  output [8:0] o);
  genvar i;
  generate 
    for (i = 0; i < 9; i=i+1) begin : ramx
      ram400x1s ramx(
        .d(d[i]),
        .we(we),
        .wclk(wclk),
        .a(a),
        .o(o[i]));
    end
  endgenerate
endmodule

module ram400x8s(
  input [7:0] d,
  input we,
  input wclk,
  input [8:0] a,
  output [7:0] o);
  genvar i;
  generate 
    for (i = 0; i < 8; i=i+1) begin : ramx
      ram400x1s ramx(
        .d(d[i]),
        .we(we),
        .wclk(wclk),
        .a(a),
        .o(o[i]));
    end
  endgenerate
endmodule

module ram400x7s(
  input [6:0] d,
  input we,
  input wclk,
  input [8:0] a,
  output [6:0] o);
  genvar i;
  generate 
    for (i = 0; i < 7; i=i+1) begin : ramx
      ram400x1s ramx(
        .d(d[i]),
        .we(we),
        .wclk(wclk),
        .a(a),
        .o(o[i]));
    end
  endgenerate
endmodule

// SPI can be many things, so to be clear, this implementation:
//   MSB first
//   CPOL 0, leading edge when SCK rises
//   CPHA 0, sample on leading, setup on trailing

module SPI_memory(
  input clk,
  input SCK, input MOSI, output MISO, input SSEL,
  output wire [15:0] raddr,   // read address
  output reg [15:0] waddr,    // write address
  output reg [7:0] data_w,
  input [7:0] data_r,
  output reg we,
  output reg re,
  output mem_clk
);
  reg [15:0] paddr;
  reg [4:0] count;
  wire [4:0] _count = (count == 23) ? 16 : (count + 1);

  assign mem_clk = clk;

  // sync SCK to the FPGA clock using a 3-bits shift register
  reg [2:0] SCKr;  always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
  wire SCK_risingedge = (SCKr[2:1]==2'b01);  // now we can detect SCK rising edges
  wire SCK_fallingedge = (SCKr[2:1]==2'b10);  // and falling edges

  // same thing for SSEL
  reg [2:0] SSELr;  always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
  wire SSEL_active = ~SSELr[1];  // SSEL is active low
  wire SSEL_startmessage = (SSELr[2:1]==2'b10);  // message starts at falling edge
  wire SSEL_endmessage = (SSELr[2:1]==2'b01);  // message stops at rising edge

// and for MOSI
  reg [1:0] MOSIr;  always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
  wire MOSI_data = MOSIr[1];

  assign raddr = (count[4] == 0) ? {paddr[14:0], MOSI} : paddr;

  always @(posedge clk)
  begin
    if (~SSEL_active) begin
      count <= 0;
      re <= 0;
      we <= 0;
    end else
      if (SCK_risingedge) begin
        if (count[4] == 0) begin
          we <= 0;
          paddr <= raddr;
          re <= (count == 15);
        end else begin
          data_w <= {data_w[6:0], MOSI_data};
          if (count == 23) begin
            we <= paddr[15];
            re <= !paddr[15];
            waddr <= paddr;
            paddr <= paddr + 1;
          end else begin
            we <= 0;
            re <= 0;
          end
        end
        count <= _count;
      end
      if (SCK_fallingedge) begin
        re <= 0;
        we <= 0;
      end
  end

  reg readbit;
  always @*
  begin
    case (count[2:0])
    3'd0: readbit <= data_r[7];
    3'd1: readbit <= data_r[6];
    3'd2: readbit <= data_r[5];
    3'd3: readbit <= data_r[4];
    3'd4: readbit <= data_r[3];
    3'd5: readbit <= data_r[2];
    3'd6: readbit <= data_r[1];
    3'd7: readbit <= data_r[0];
    endcase
  end
  assign MISO = readbit;

endmodule

// This is a Delta-Sigma Digital to Analog Converter
`define MSBI 12 // Most significant Bit of DAC input, 12 means 13-bit

module dac(DACout, DACin, Clk, Reset);
output DACout;         // This is the average output that feeds low pass filter
reg DACout;            // for optimum performance, ensure that this ff is in IOB
input [`MSBI:0] DACin; // DAC input (excess 2**MSBI)
input Clk;
input Reset;
reg [`MSBI+2:0] DeltaAdder; // Output of Delta adder
reg [`MSBI+2:0] SigmaAdder; // Output of Sigma adder
reg [`MSBI+2:0] SigmaLatch; // Latches output of Sigma adder
reg [`MSBI+2:0] DeltaB; // B input of Delta adder

always @(SigmaLatch) DeltaB = {SigmaLatch[`MSBI+2], SigmaLatch[`MSBI+2]} << (`MSBI+1);
always @(DACin or DeltaB) DeltaAdder = DACin + DeltaB;
always @(DeltaAdder or SigmaLatch) SigmaAdder = DeltaAdder + SigmaLatch;
always @(posedge Clk or posedge Reset)
begin
  if (Reset) begin
    SigmaLatch <= #1 1'b1 << (`MSBI+1);
    DACout <= #1 1'b0;
  end else begin
    SigmaLatch <= #1 SigmaAdder;
    DACout <= #1 SigmaLatch[`MSBI+2];
  end
end
endmodule

module top(
  input clka,
  output [2:0] vga_red,
  output [2:0] vga_green,
  output [2:0] vga_blue,
  output vga_hsync_n,
  output vga_vsync_n,

  input SCK,  // arduino 13
  input MOSI, // arduino 11
  inout MISO, // arduino 12
  input SSEL, // arduino 9
  inout AUX,  // arduino 2
  output AUDIOL,
  output AUDIOR,

  output flashMOSI,
  input  flashMISO,
  output flashSCK,
  output flashSSEL

  );


  wire mem_clk;
  wire [7:0] host_mem_data_wr;
  reg [7:0] mem_data_rd;
  reg [7:0] latched_mem_data_rd;
  wire [14:0] mem_w_addr;   // Combined write address
  wire [14:0] mem_r_addr;   // Combined read address
  wire [14:0] host_mem_w_addr;
  wire [14:0] host_mem_r_addr;
  wire host_mem_wr;
  wire mem_rd;

  wire vga_clk;
  ck_div #(.DIV_BY(2), .MULT_BY(4)) vga_ck_gen(.ck_in(clka), .ck_out(vga_clk));

  wire [15:0] j1_insn;
  wire [12:0] j1_insn_addr;
  wire [15:0] j1_mem_addr;
  wire [15:0] j1_mem_dout;
  wire j1_mem_wr;

  wire [7:0] j1insnl_read;
  wire [7:0] j1insnh_read;
  wire [7:0] mem_data_rd0;
  wire [7:0] mem_data_rd1;
  wire [7:0] mem_data_rd2;
  wire [7:0] mem_data_rd3;
  wire [7:0] mem_data_rd4;
  wire [7:0] mem_data_rd5;

  wire gdMISO;

  always @(posedge vga_clk)
  if (mem_rd)
    latched_mem_data_rd <= mem_data_rd;

  SPI_memory spi1(
    .clk(vga_clk),
    .SCK(SCK), .MOSI(MOSI), .MISO(gdMISO), .SSEL(SSEL),
    .raddr(host_mem_r_addr),
    .waddr(host_mem_w_addr),
    .data_w(host_mem_data_wr),
    .data_r(latched_mem_data_rd),
    .we(host_mem_wr),
    .re(mem_rd),
    .mem_clk(mem_clk));
  wire host_busy = host_mem_wr | mem_rd;
  wire mem_wr =            host_busy ? host_mem_wr : j1_mem_wr;
  wire [7:0] mem_data_wr = host_busy ? host_mem_data_wr : j1_mem_dout;
  wire [14:0] mem_addr =   host_busy ? (host_mem_wr ? host_mem_w_addr : host_mem_r_addr) : j1_mem_addr;
  assign mem_w_addr =      host_busy ? host_mem_w_addr : j1_mem_addr;
  assign mem_r_addr =      host_busy ? host_mem_r_addr : j1_mem_addr;

  reg signed [15:0] sample_l;
  reg signed [15:0] sample_r;
  reg [6:0] modvoice = 64;
  reg [14:0] bg_color;
  reg [7:0] pin2mode = 0;
  wire pin2f = (pin2mode == 8'h46);
  wire pin2j = (pin2mode == 8'h4A);

  wire flashsel = (AUX == 0) & pin2f;

  assign MISO = SSEL ? (flashsel ? flashMISO : 1'bz) : gdMISO;
  // assign MISO = SSEL ? (1'bz ) : gdMISO;
  // PULLUP MISO_pullup(.O(MISO));
  // IOBUF MISO_iobuf(
  //   .I(gdMISO),
  //   .IO(MISO),
  //   .T(SSEL));


  // user-visible registers
  reg [7:0] frames;
  reg [8:0] scrollx;
  reg [8:0] scrolly;
  reg jkmode;

  wire [7:0] palette16l_read;
  wire [7:0] palette16h_read;
  wire [4:0] palette16_addr;
  wire [15:0] palette16_data;

  // 11'b00001xxxxx0: low
  wire palette16_wr = (mem_wr & (mem_w_addr[14:11] == 5) & (mem_w_addr[10:6] == 1));
  ram32x8d palette16l(
    .a(mem_addr[5:1]),
    .wclk(mem_clk),
    .wea((mem_w_addr[0] == 0) & palette16_wr),
    .ad(mem_data_wr),
    .ao(palette16l_read),
    .b(palette16_addr),
    .bo(palette16_data[7:0]));
  ram32x8d palette16h(
    .a(mem_addr[5:1]),
    .wclk(mem_clk),
    .wea((mem_w_addr[0] == 1) & palette16_wr),
    .ad(mem_data_wr),
    .ao(palette16h_read),
    .b(palette16_addr),
    .bo(palette16_data[15:8]));

  wire [7:0] palette4l_read;
  wire [7:0] palette4h_read;
  wire [4:0] palette4_addr;
  wire [15:0] palette4_data;

  // 11'b00010xxxxx0: low
  wire palette4_wr = (mem_wr & (mem_w_addr[14:11] == 5) & (mem_w_addr[10:6] == 2));
  ram32x8d palette4l(
    .a(mem_addr[5:1]),
    .wclk(mem_clk),
    .wea((mem_w_addr[0] == 0) & palette4_wr),
    .ad(mem_data_wr),
    .ao(palette4l_read),
    .b(palette4_addr),
    .bo(palette4_data[7:0]));
  ram32x8d palette4h(
    .a(mem_addr[5:1]),
    .wclk(mem_clk),
    .wea((mem_w_addr[0] == 1) & palette4_wr),
    .ad(mem_data_wr),
    .ao(palette4h_read),
    .b(palette4_addr),
    .bo(palette4_data[15:8]));

  // Generate CounterX and CounterY
  // A single line is 1040 clocks.  Line pair is 2080 clocks.

  reg [10:0] CounterX;
  reg [9:0] CounterY;
  wire CounterXmaxed = (CounterX==1040);

  always @(posedge vga_clk)
  if(CounterXmaxed)
    CounterX <= 0;
  else
    CounterX <= CounterX + 1;
  wire lastline = (CounterY == 665);
  wire [9:0] _CounterY = lastline ? 0 : (CounterY + 1);

  always @(posedge vga_clk)
  if (CounterXmaxed) begin
    CounterY <= _CounterY;
    if (lastline)
      frames <= frames + 1;
  end

  reg [12:0] comp_workcnt; // Compositor work address
  reg comp_workcnt_lt_400;
  always @(posedge vga_clk)
  begin
    if (CounterXmaxed & (CounterY[0] == 0)) begin
      comp_workcnt <= 0;
      comp_workcnt_lt_400 <= 1;
    end else begin
      comp_workcnt <= comp_workcnt + 1;
      if (comp_workcnt == 399)
        comp_workcnt_lt_400 <= 0;
    end
  end

// horizontal
//   Front porch  56
//   Sync         120
//   Back porch   64

// vertical
//   Front porch  37 lines
//   Sync         6
//   Back porch   23

// `define HSTART (53 + 120 + 61)
`define HSTART 0

  reg vga_HS, vga_VS, vga_active;
  always @(posedge vga_clk)
  begin
    vga_HS <= ((800 + 61) <= CounterX) & (CounterX < (800 + 61 + 120));
    vga_VS <= (35 <= CounterY) & (CounterY < (35 + 6));
    vga_active = ((`HSTART + 1) <= CounterX) & (CounterX < (`HSTART + 1 + 800)) & ((35 + 6 + 21) < CounterY) & (CounterY <= (35 + 6 + 21 + 600));
  end

  wire [10:0] xx = (CounterX - `HSTART);
  wire [10:0] xx_1 = (CounterX - `HSTART + 1);
  wire [10:0] yy = (CounterY + 2 - (35 + 6 + 21 + 1));    // yy range 0-665

  wire [10:0] column = comp_workcnt + scrollx;
  wire [10:0] row    = yy[10:1] + scrolly;
  wire [7:0] glyph;

  wire [11:0] picaddr = {row[8:3], column[8:3]};


  wire en_pic = (mem_addr[14:12] == 0);
  RAM_PICTURE picture(
    .dia(0), .doa(glyph), .wea(0),     .ena(1), .clka(vga_clk), .addra(picaddr),
    .dib(mem_data_wr), .dob(mem_data_rd0),             .web(mem_wr), .enb(en_pic), .clkb(mem_clk), .addrb(mem_addr));

  reg [2:0] _column;
  always @(posedge vga_clk)
    _column = column;

  wire en_chr = (mem_addr[14:12] == 1);
  wire [1:0] charout;
  RAM_CHR chars(
    .dia(0), .doa(charout), .wea(0),     .ena(1), .clka(vga_clk), .addra({glyph, row[2:0], _column[2], ~_column[1:0]}),
    .dib(mem_data_wr), .dob(mem_data_rd1),             .web(mem_wr), .enb(en_chr), .clkb(mem_clk), .addrb(mem_addr));

  reg [7:0] _glyph;
  always @(posedge vga_clk)
    _glyph <= glyph;

  wire [4:0] bg_r;
  wire [4:0] bg_g;
  wire [4:0] bg_b;

  wire en_pal = (mem_addr[14:11] == 4'b0100);
  wire [15:0] char_matte;
  RAM_PAL charpalette(
    .DIA(mem_data_wr),
    .WEA(mem_wr),
    .ENA(en_pal),
    .CLKA(mem_clk),
    .ADDRA(mem_addr),
    .DOA(mem_data_rd2),
    .SSRA(0),

    .DIB(0),
    .WEB(0),
    .ENB(1),
    .CLKB(vga_clk),
    .ADDRB({_glyph, charout}),
    .DOB(char_matte),
    .SSRB(0)
  );
  // wire [4:0] bg_mix_r = bg_color[14:10] + char_matte[14:10];
  // wire [4:0] bg_mix_g = bg_color[9:5]   + char_matte[9:5];
  // wire [4:0] bg_mix_b = bg_color[4:0]   + char_matte[4:0];
  // wire [14:0] char_final = char_matte[15] ? {bg_mix_r, bg_mix_g, bg_mix_b} : char_matte[14:0];
  wire [14:0] char_final = char_matte[15] ? bg_color : char_matte[14:0];

  reg [7:0] mem_data_rd_reg;

  // Collision detection RAM is readable during vblank
  // writes coll_d to coll_w_addr during render
  wire coll_rd = (yy >= 600); // 1 means reading
  wire [7:0] coll_d;
  wire [7:0] coll_w_addr;
  wire coll_we;
  wire [7:0] coll_addr = coll_rd ? mem_r_addr[7:0] : coll_w_addr;
  wire [7:0] coll_o;
  ram256x8s coll(.o(coll_o), .a(coll_addr), .d(coll_d), .wclk(vga_clk), .we(~coll_rd & coll_we));
  wire [7:0] screenshot_rd;

  wire [7:0] voicefl_read;
  wire [7:0] voicefh_read;
  wire [7:0] voicela_read;
  wire [7:0] voicera_read;

  reg j1_reset = 0;
  reg spr_disable = 0;
  reg spr_page = 0;

  // Screenshot notes
  // three states, controlled by screenshot_primed, _done:
  //  primed done                         composer.A
  //  0      0     screenshot disabled    write(comp_write)
  //  1      0     screenshot primed      write(comp_write)
  //  1      1     screenshot done        read(mem_r_addr[9:1])

  reg [8:0] screenshot_yy;  // 9 bits, 0-400
  reg screenshot_primed;
  reg screenshot_done;
  wire screenshot_reset;
  wire [8:0] public_yy = coll_rd ? 300 : yy[10:1];

  always @(posedge vga_clk)
  begin
    if (screenshot_reset)
      screenshot_done <= 0;
    else if (CounterXmaxed & screenshot_primed & !screenshot_done & (public_yy == screenshot_yy)) begin
      screenshot_done <= 1;
    end
  end

  always @(mem_data_rd_reg)
  begin
    casex (mem_r_addr[10:0])
    11'h000: mem_data_rd_reg <= 8'h6d;  // Gameduino ident
    11'h001: mem_data_rd_reg <= `REVISION;
    11'h002: mem_data_rd_reg <= frames;
    11'h003: mem_data_rd_reg <= coll_rd;  // called VBLANK, but really "is coll readable?"
    11'h004: mem_data_rd_reg <= scrollx[7:0];
    11'h005: mem_data_rd_reg <= scrollx[8];
    11'h006: mem_data_rd_reg <= scrolly[7:0];
    11'h007: mem_data_rd_reg <= scrolly[8];
    11'h008: mem_data_rd_reg <= jkmode;
    11'h009: mem_data_rd_reg <= j1_reset;
    11'h00a: mem_data_rd_reg <= spr_disable;
    11'h00b: mem_data_rd_reg <= spr_page;
    11'h00c: mem_data_rd_reg <= pin2mode;
    11'h00e: mem_data_rd_reg <= bg_color[7:0];
    11'h00f: mem_data_rd_reg <= bg_color[14:8];
    11'h010: mem_data_rd_reg <= sample_l[7:0];
    11'h011: mem_data_rd_reg <= sample_l[15:8];
    11'h012: mem_data_rd_reg <= sample_r[7:0];
    11'h013: mem_data_rd_reg <= sample_r[15:8];
    11'h014: mem_data_rd_reg <= modvoice;
    11'h01e: mem_data_rd_reg <= public_yy[7:0];
    11'h01f: mem_data_rd_reg <= {screenshot_done, 6'b000000, public_yy[8]};

    11'b00001xxxxx0: mem_data_rd_reg <= palette16l_read;
    11'b00001xxxxx1: mem_data_rd_reg <= palette16h_read;
    11'b00010xxxxx0: mem_data_rd_reg <= palette4l_read;
    11'b00010xxxxx1: mem_data_rd_reg <= palette4h_read;
    11'b001xxxxxxxx:
      mem_data_rd_reg <= coll_rd ? coll_o : 8'hff;
    11'b010xxxxxx00: mem_data_rd_reg <= voicefl_read;
    11'b010xxxxxx01: mem_data_rd_reg <= voicefh_read;
    11'b010xxxxxx10: mem_data_rd_reg <= voicela_read;
    11'b010xxxxxx11: mem_data_rd_reg <= voicera_read;
    11'b011xxxxxxx0: mem_data_rd_reg <= j1insnl_read;
    11'b011xxxxxxx1: mem_data_rd_reg <= j1insnh_read;
    11'b1xxxxxxxxxx: mem_data_rd_reg <= screenshot_rd;
    // default: mem_data_rd_reg <= 0;
    endcase
  end

  reg [17:0] soundcounter;
  always @(posedge vga_clk)
    soundcounter <= soundcounter + 1;
  wire [5:0] viN = soundcounter + 1;
  wire [5:0] vi = soundcounter;

  wire voice_wr = mem_wr & (mem_w_addr[14:11] == 5) & (mem_w_addr[10:8] == 3'b010);
  wire voicefl_wr = voice_wr & (mem_w_addr[1:0] == 2'b00);
  wire voicefh_wr = voice_wr & (mem_w_addr[1:0] == 2'b01);
  wire voicela_wr = voice_wr & (mem_w_addr[1:0] == 2'b10);
  wire voicera_wr = voice_wr & (mem_w_addr[1:0] == 2'b11);

  wire [7:0] voicela_data;
  wire [7:0] voicera_data;
  wire [7:0] voicefl_data;
  wire [7:0] voicefh_data;
  wire [5:0] voice_addr = mem_addr[7:2];

  ram64x8d voicefl(
    .a(voice_addr),
    .wclk(mem_clk),
    .wea(voicefl_wr),
    .ad(mem_data_wr),
    .ao(voicefl_read),
    .b(viN),
    .bo(voicefl_data));

  ram64x8d voicefh(
    .a(voice_addr),
    .wclk(mem_clk),
    .wea(voicefh_wr),
    .ad(mem_data_wr),
    .ao(voicefh_read),
    .b(viN),
    .bo(voicefh_data));

  ram64x8d voicela(
    .a(voice_addr),
    .wclk(mem_clk),
    .wea(voicela_wr),
    .ad(mem_data_wr),
    .ao(voicela_read),
    .b(viN),
    .bo(voicela_data));

  ram64x8d voicera(
    .a(voice_addr),
    .wclk(mem_clk),
    .wea(voicera_wr),
    .ad(mem_data_wr),
    .ao(voicera_read),
    .b(viN),
    .bo(voicera_data));

  assign screenshot_reset = mem_wr & (mem_w_addr[14:11] == 5) & (mem_w_addr[10:0] == 11'h01f);
  always @(posedge mem_clk)
  begin
    if (mem_wr & mem_w_addr[14:11] == 5)
      casex (mem_w_addr[10:0])
      11'h004: scrollx[7:0]   <= mem_data_wr;
      11'h005: scrollx[8]     <= mem_data_wr;
      11'h006: scrolly[7:0]   <= mem_data_wr;
      11'h007: scrolly[8]     <= mem_data_wr;
      11'h008: jkmode         <= mem_data_wr;
      11'h009: j1_reset       <= mem_data_wr;
      11'h00a: spr_disable    <= mem_data_wr;
      11'h00b: spr_page       <= mem_data_wr;
      11'h00c: pin2mode       <= mem_data_wr;
      11'h00e: bg_color[7:0]  <= mem_data_wr;
      11'h00f: bg_color[14:8] <= mem_data_wr;
      11'h010: sample_l[7:0]  <= mem_data_wr;
      11'h011: sample_l[15:8] <= mem_data_wr;
      11'h012: sample_r[7:0]  <= mem_data_wr;
      11'h013: sample_r[15:8] <= mem_data_wr;
      11'h014: modvoice       <= mem_data_wr;

      11'h01e: screenshot_yy[7:0] <= mem_data_wr;
      11'h01f: begin screenshot_primed <= mem_data_wr[7];
               screenshot_yy[8] <= mem_data_wr; end
      endcase
  end

  /*
    0000-0fff Picture
    1000-1fff Character
    2000-27ff Character
    2800-2fff (Unused)
    3000-37ff Sprite values
    3800-3fff Sprite palette
    4000-7fff Sprite image
  */
  always @*
  begin
    case (mem_addr[14:11]) // 2K pages
    4'h0: mem_data_rd <= mem_data_rd0;  // pic
    4'h1: mem_data_rd <= mem_data_rd0;
    4'h2: mem_data_rd <= mem_data_rd1;  // chr
    4'h3: mem_data_rd <= mem_data_rd1;
    4'h4: mem_data_rd <= mem_data_rd2;  // pal
    4'h5: mem_data_rd <= mem_data_rd_reg;
    4'h6: mem_data_rd <= mem_data_rd3;  // sprval
    4'h7: mem_data_rd <= mem_data_rd4;  // sprpal
    4'h8: mem_data_rd <= mem_data_rd5;  // sprimg
    4'h9: mem_data_rd <= mem_data_rd5;  // sprimg
    4'ha: mem_data_rd <= mem_data_rd5;  // sprimg
    4'hb: mem_data_rd <= mem_data_rd5;  // sprimg
    4'hc: mem_data_rd <= mem_data_rd5;  // sprimg
    4'hd: mem_data_rd <= mem_data_rd5;  // sprimg
    4'he: mem_data_rd <= mem_data_rd5;  // sprimg
    4'hf: mem_data_rd <= mem_data_rd5;  // sprimg

    default: mem_data_rd <= 8'h97;
    endcase
  end

  // Sprite memory

  // Stage 1: scan for valid
  reg [8:0] s1_count;

  wire en_sprval = (mem_addr[14:11] == 4'b0110);
  wire [31:0] sprval_data;
  RAM_SPRVAL sprval(
    .DIA(mem_data_wr),
    .WEA(mem_wr),
    .ENA(en_sprval),
    .CLKA(mem_clk),
    .ADDRA(mem_addr),
    .DOA(mem_data_rd3),
    .SSRA(0),

    .DIB(0),
    .WEB(0),
    .ENB(1),
    .CLKB(vga_clk),
    .ADDRB({spr_page, s1_count[7:0]}),
    .DOB(sprval_data),
    .SSRB(0)
  );
  wire [9:0] sprpal_addr;
  wire [15:0] sprpal_data;
  wire en_sprpal = (mem_addr[14:11] == 4'b0111);
  RAM_SPRPAL sprpal(
    .DIA(mem_data_wr),
    .WEA(mem_wr),
    .ENA(en_sprpal),
    .CLKA(mem_clk),
    .ADDRA(mem_addr),
    .DOA(mem_data_rd4),
    .SSRA(0),

    .DIB(0),
    .WEB(0),
    .ENB(1),
    .CLKB(vga_clk),
    .ADDRB(sprpal_addr),
    .DOB(sprpal_data),
    .SSRB(0)
  );
  wire [13:0] sprimg_readaddr;
  wire [7:0] sprimg_data;
  wire en_sprimg = (mem_addr[14] == 1'b1);
//  ram16K_8_8 sprimg(
  RAM_SPRIMG sprimg(
    .dia(mem_data_wr),
    .wea(mem_wr),
    .ena(en_sprimg),
    .clka(mem_clk),
    .addra(mem_addr),
    .doa(mem_data_rd5),
    .ssra(0),

    .dib(0),
    .web(0),
    .enb(1),
    .clkb(vga_clk),
    .addrb(sprimg_readaddr),
    .dob(sprimg_data),
    .ssrb(0)
  );

  // Stage 1: scan for valid

  reg s1_consider;      // Consider the sprval on the next cycle
  wire s2_room;         // Does s2 fifo have room for one entry?
  always @(posedge vga_clk)
  begin
    if (comp_workcnt == 0) begin
      s1_count <= 0;
      s1_consider <= 0;
    end else
      if ((s1_count <= 255) & s2_room) begin
        s1_count <= s1_count + 1;
        s1_consider <= 1;
      end else begin
        s1_consider <= 0;
      end
  end
  wire [31:0] s1_out = sprval_data;
  wire [8:0] s1_y_offset = yy[9:1] - s1_out[24:16];
  wire s1_visible = (spr_disable == 0) & (s1_y_offset[8:4] == 0);
  wire s1_valid = s1_consider & s1_visible;
  reg [8:0] s1_id;
  always @(posedge vga_clk)
    s1_id <= s1_count;

  // Stage 2: fifo
  wire [4:0] s2_fullness;
  wire s3_read;
  wire [40:0] s2_out;
  fifo #(40) s2(.clk(vga_clk),
                .wr(s1_valid), .datain({s1_id, s1_out}),
                .rd(s3_read), .dataout(s2_out),
                .fullness(s2_fullness));
  assign s2_room = (s2_fullness < 14);

  wire s2_valid = s2_fullness != 0;

  // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
  //    [     image     ] [          y             ]       [PAL] [  ROT ] [           x            ]

  /* Stage 3: read sprimg
    consume s2_out on last cycle 
    out: s3_valid, s3_pal, s3_out, s3_compaddr
  */

  reg [4:0] s3_state;
  reg [3:0] s3_pal;
  reg [31:0] s3_in;
  assign s3_read = (s3_state == 15);
  always @(posedge vga_clk)
  begin
    if (comp_workcnt < 403)
      s3_state <= 16;
    else if (s3_state == 16) begin
      if (s2_valid) begin
        s3_state <= 0;
        s3_in <= s2_out;
      end
    end else begin
      s3_state <= s3_state + 1;
    end
    s3_pal <= s2_out[15:12];
  end
  wire [3:0] s3_yoffset = yy[4:1] - s2_out[19:16];
  wire [3:0] s3_prev_state = (s3_state == 16) ? 0 : (s3_state + 1);
  wire [3:0] readx = (s2_out[9] ? s3_yoffset : s3_prev_state) ^ {4{s2_out[10]}};
  wire [3:0] ready = (s2_out[9] ? s3_prev_state : s3_yoffset) ^ {4{s2_out[11]}};
  assign sprimg_readaddr = {s2_out[30:25], ready, readx};
  wire [7:0] s3_out = sprimg_data;
  wire [8:0] s3_compaddr = s2_out[8:0] + s3_state;
  wire s3_valid = (s3_state != 16) & (s3_compaddr < 400);
  reg [8:0] s3_id;
  reg s3_jk;
  always @(posedge vga_clk)
  begin
    s3_id <= s2_out[40:32];
    s3_jk <= s2_out[31];
  end

  /* Stage 4: read sprpal
    out: s4_valid, s4_out, s4_compaddr
  */
  reg [15:0] sprpal4;
  reg [15:0] sprpal16;
  assign sprpal_addr = {s3_pal[1:0], s3_out};
  reg [8:0] s4_compaddr;
  reg s4_valid;
  reg [8:0] s4_id;
  reg s4_jk;
  wire [3:0] subfield4 = s3_pal[1] ? s3_out[7:4] : s3_out[3:0];
  wire [1:0] subfield2 = s3_pal[2] ? (s3_pal[1] ? s3_out[7:6] : s3_out[5:4]) : (s3_pal[1] ? s3_out[3:2] : s3_out[1:0]);
  assign palette4_addr = {s3_pal[0], subfield2};
  assign palette16_addr = {s3_pal[0], subfield4};
  always @(posedge vga_clk)
  begin
    s4_compaddr <= s3_compaddr;
    s4_valid <= s3_valid;
    s4_id <= s3_id;
    s4_jk <= s3_jk;
    sprpal4 <= palette4_data;
    sprpal16 <= palette16_data;
  end
  wire [15:0] s4_out = s3_pal[3] ? sprpal4 : ((s3_pal[3:2] == 0) ? sprpal_data : sprpal16);

  // Common signals for collision and composite
  wire sprite_write = s4_valid & !s4_out[15];  // transparency

  // Collision detect
  // Have 400x9 occupancy buffer.  If NEW overwrites a fragment OLD
  // (and their groups differ) write OLD to coll[NEW].
  // Reset coll to FF at start of frame.
  // Reset occ to FF at start of line, since sprites drawn 0->ff,
  // ff is impossible and means empty.

  wire coll_scrub = (yy == 0) & (comp_workcnt < 256);
  wire [8:0] occ_addr = comp_workcnt_lt_400 ? comp_workcnt : s4_compaddr;
  wire [8:0] occ_d = comp_workcnt_lt_400 ? 9'hff : {s4_jk, s4_id[7:0]};
  wire [8:0] oldocc;
  wire occ_w = comp_workcnt_lt_400 | sprite_write;
  ram400x9s occ(.o(oldocc), .a(occ_addr), .d(occ_d), .wclk(vga_clk), .we(occ_w));
  assign coll_d = coll_scrub ? 8'hff : oldocc[7:0];
  assign coll_w_addr = coll_scrub ? comp_workcnt : s4_id[7:0];
  // old contents 0xff, never write
  // jkmode=1 and JK's differ, allow write
  wire overwriting = (oldocc[7:0] != 8'hff);
  wire jkpass = (jkmode == 0) | (oldocc[8] ^ s4_jk);
  assign coll_we = coll_scrub | ((403 <= comp_workcnt) & sprite_write & overwriting & jkpass);

  // Composite

  wire comp_read = yy[1];   // which block is reading
  wire [8:0] comp_scanout = xx[9:1];
  wire [8:0] comp_write = (comp_workcnt < 403) ? (comp_workcnt - 3) : s4_compaddr;
  wire comp_part1 = (3 <= comp_workcnt) & (comp_workcnt < 403);
`ifdef NELLY
  wire [14:0] comp_out0;
  wire [14:0] comp_out1;
  RAMB16_S18_S18 composer(
    .DIPA(0),
    .DIA(comp_part1 ? char_final : s4_out),
    .WEA(comp_read == 1 & (comp_part1 | sprite_write)),
    .ENA(1),
    .CLKA(vga_clk),
    .ADDRA({1'b0, (comp_read == 0) ? comp_scanout : comp_write[8:0]}),
    .DOA(comp_out0),
    .SSRA(0),

    .DIPB(0),
    .DIB(comp_part1 ? char_final : s4_out),
    .WEB(comp_read == 0 & (comp_part1 | sprite_write)),
    .ENB(1),
    .CLKB(vga_clk),
    .ADDRB({1'b1, (comp_read == 1) ? comp_scanout : comp_write[8:0]}),
    .DOB(comp_out1),
    .SSRB(0)
  );
  wire [14:0] comp_out = comp_read ? comp_out1 : comp_out0;
`else
  wire ss = screenshot_primed & screenshot_done;  // screenshot readout mode
  wire [15:0] screenshot_rdHL;
  wire [14:0] comp_out;
  // Port A is the write, or read when screenshot is ready
  // Port B is scanout
  RAMB16_S18_S18
  composer(
    .DIPA(0),
    .DIA(comp_part1 ? char_final : s4_out),
    .WEA(!ss & (comp_part1 | sprite_write)),
    .ENA(1),
    .CLKA(vga_clk),
    .ADDRA(ss ? {screenshot_yy[0], mem_r_addr[9:1]} : {comp_read, comp_write[8:0]}),
    .DOA(screenshot_rdHL),
    .SSRA(0),

    .DIPB(0),
    .DIB(0),
    .WEB(0),
    .ENB(1),
    .CLKB(vga_clk),
    .ADDRB({!comp_read, comp_scanout}),
    .DOB(comp_out),
    .SSRB(0)
  );
  assign screenshot_rd = mem_r_addr[0] ? screenshot_rdHL[15:8] : screenshot_rdHL[7:0];
`endif
  assign {bg_r,bg_g,bg_b} = comp_out;

  // Figure out from above signals when composite completes
  // by detecting pipeline idle
  wire composite_complete = (comp_workcnt > 403) & (s1_count == 256) & !s1_consider & !s2_valid & (s3_state == 16) & !s4_valid;

  // Signal generation

  wire [16:0] lfsr;
  lfsre lfsr0(
    .clk(vga_clk),
    .lfsr(lfsr));

  wire [1:0] dith;
  // 0 2
  // 3 1
  assign dith = {(xx[0]^yy[0]), yy[0]};
  wire [5:0] dith_r = (bg_r + dith);
  wire [5:0] dith_g = (bg_g + dith);
  wire [5:0] dith_b = (bg_b + dith);
  wire [2:0] f_r = {3{dith_r[5]}} | dith_r[4:2];
  wire [2:0] f_g = {3{dith_g[5]}} | dith_g[4:2];
  wire [2:0] f_b = {3{dith_b[5]}} | dith_b[4:2];

  wire [2:0] ccc = { charout[1], charout[0], charout[0] };
  assign vga_red =    vga_active ? f_r : 0;
  assign vga_green =  vga_active ? f_g : 0;
  assign vga_blue =   vga_active ? f_b : 0;
  assign vga_hsync_n = ~vga_HS;
  assign vga_vsync_n = ~vga_VS;

  /*
  An 18-bit counter, multiplied by the frequency gives a single bit
  pulse that advances the voice's wave counter.
  Frequency of 4 means 1Hz, 16385 means 4095.75Hz.
  18-bit counter increments every 1/(2**18), or 262144 Hz.
  */
`define MASTERFREQ (1 << 22)
  reg [27:0] d;
  wire [27:0] dInc = d[27] ? (`MASTERFREQ) : (`MASTERFREQ - 50000000);
  wire [27:0] dN = d + dInc;

  reg [16:0] soundmaster;   // This increments at MASTERFREQ Hz
  always @(posedge vga_clk)
  begin
    d = dN;
    // clock B tick whenever d[27] is zero
    if (dN[27] == 0) begin
      soundmaster <= soundmaster + 1;
    end
  end

  wire [6:0] hsin;
  wire [6:0] wavecounter;
  wire [6:0] note = wavecounter[6:0];

`ifdef YES
RAM64X1S #(.INIT(64'b0000000000110101100010001111010010010111100010001101011000000000) /* 0 */
) sin0(.O(hsin[0]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0101010101101100011100010101001111100101010001110001101101010101) /* 1 */
) sin1(.O(hsin[1]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0110011001001001010101001100111111111001100101010100100100110011) /* 2 */
) sin2(.O(hsin[2]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0010110100100100110011000011111111111110000110011001001001011010) /* 3 */
) sin3(.O(hsin[3]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0001110011100011110000111111111111111111111000011110001110011100) /* 4 */
) sin4(.O(hsin[4]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0000001111100000001111111111111111111111111111100000001111100000) /* 5 */
) sin5(.O(hsin[5]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
RAM64X1S #(.INIT(64'b0000000000011111111111111111111111111111111111111111110000000000) /* 6 */
) sin6(.O(hsin[6]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]), .D(0), .WCLK(vga_clk), .WE(0));
`else
ROM64X1 #(.INIT(64'b0000000000110101100010001111010010010111100010001101011000000000) /* 0 */) sin0(.O(hsin[0]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0101010101101100011100010101001111100101010001110001101101010101) /* 1 */) sin1(.O(hsin[1]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0110011001001001010101001100111111111001100101010100100100110011) /* 2 */) sin2(.O(hsin[2]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0010110100100100110011000011111111111110000110011001001001011010) /* 3 */) sin3(.O(hsin[3]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0001110011100011110000111111111111111111111000011110001110011100) /* 4 */) sin4(.O(hsin[4]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0000001111100000001111111111111111111111111111100000001111100000) /* 5 */) sin5(.O(hsin[5]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
ROM64X1 #(.INIT(64'b0000000000011111111111111111111111111111111111111111110000000000) /* 6 */) sin6(.O(hsin[6]), .A0(note[0]), .A1(note[1]), .A2(note[2]), .A3(note[3]), .A4(note[4]), .A5(note[5]));
`endif

  wire signed [7:0] sin = note[6] ? (8'h00 - hsin) : hsin;

  wire voiceshape = voicefh_data[7];
  wire [14:0] voicefreq = {voicefh_data[6:0], voicefl_data};

  wire [35:0] derived = {1'b0, soundmaster} * voicefreq;

  wire highfreq = voicefreq[14];
  wire newpulse = highfreq ? derived[18] : derived[17]; // pulse is a square wave frequency 64*f
  wire oldpulse;
  wire [6:0] nextwavecounter = voiceshape ? lfsr : (wavecounter + (highfreq ? 2 : 1));
  wire [6:0] _wavecounter = (newpulse != oldpulse) ? nextwavecounter : wavecounter;

`ifdef NELLY
  ram64x8s voicestate(
    .a(viN),
    .we(1),
    .wclk(vga_clk),
    .d({_wavecounter, newpulse}),
    .o({wavecounter, oldpulse}));
`else
  wire [8:0] vsi = {_wavecounter, newpulse};
  wire [8:0] vso;
  ring64 vs0(.clk(vga_clk), .i(vsi[0]), .o(vso[0]));
  ring64 vs1(.clk(vga_clk), .i(vsi[1]), .o(vso[1]));
  ring64 vs2(.clk(vga_clk), .i(vsi[2]), .o(vso[2]));
  ring64 vs3(.clk(vga_clk), .i(vsi[3]), .o(vso[3]));
  ring64 vs4(.clk(vga_clk), .i(vsi[4]), .o(vso[4]));
  ring64 vs5(.clk(vga_clk), .i(vsi[5]), .o(vso[5]));
  ring64 vs6(.clk(vga_clk), .i(vsi[6]), .o(vso[6]));
  ring64 vs7(.clk(vga_clk), .i(vsi[7]), .o(vso[7]));
  assign wavecounter = vso[7:1];
  assign oldpulse = vso[0];
`endif

  wire signed [8:0] lamp = voicela_data;
  wire signed [8:0] ramp = voicera_data;
  reg signed [35:0] lmodulated;
  reg signed [35:0] rmodulated;
  always @(posedge vga_clk)
  begin
    lmodulated = lamp * sin;
    rmodulated = ramp * sin;
  end

  // Have lmodulated and rmodulated

  reg signed [15:0] lacc;
  reg signed [15:0] racc;
  wire signed [15:0] lsum = lacc + lmodulated[15:0];
  wire signed [15:0] rsum = racc + rmodulated[15:0];
  wire signed [31:0] lprod = lacc * lmodulated;
  wire signed [31:0] rprod = racc * rmodulated;
  wire zeroacc = (vi == 63);
  wire [15:0] _lacc = zeroacc ? sample_l : ((vi == modvoice) ? lprod[30:15] : lsum);
  wire [15:0] _racc = zeroacc ? sample_r : ((vi == modvoice) ? rprod[30:15] : rsum);
  reg signed [12:0] lvalue;
  reg signed [12:0] rvalue;
  always @(posedge vga_clk)
  begin
    if (vi == 63) begin
      lvalue <= lsum[15:3] /* + sample_l + 32768 */;
      rvalue <= rsum[15:3] /* + sample_r + 32768 */;
    end
    lacc <= _lacc;
    racc <= _racc;
  end

  wire signed [7:0] dither = soundcounter;
//  wire [15:0] dither = {soundcounter[0],
//                       soundcounter[1],
//                       soundcounter[2],
//                       soundcounter[3],
//                       soundcounter[4],
//                       soundcounter[5],
//                       soundcounter[6],
//                       soundcounter[7],
//                       soundcounter[8],
//                       soundcounter[9],
//                       soundcounter[10],
//                       soundcounter[11],
//                       soundcounter[12],
//                       soundcounter[13],
//                       soundcounter[14],
//                       soundcounter[15]
//                       };

`ifdef NELLY
  wire lau_out = lvalue >= dither;
  wire rau_out = rvalue >= dither;

  assign AUDIOL = lau_out;
  assign AUDIOR = rau_out;
`else
  wire [12:0] ulvalue = lvalue ^ 4096;
  wire [12:0] urvalue = rvalue ^ 4096;
  dac ldac(AUDIOL, ulvalue, vga_clk, 0);
  dac rdac(AUDIOR, urvalue, vga_clk, 0);
`endif

  reg [2:0] busyhh;
  always @(posedge vga_clk) busyhh = { busyhh[1:0], host_busy };

  // J1 Peripherals
  reg [7:0] icap_i;   // ICAP in
  reg icap_write;
  reg icap_ce;
  reg icap_clk;

  wire [7:0] icap_o;  // ICAP out
  wire  icap_busy;

  reg j1_p2_dir = 1;    // pin defaults to input
  reg j1_p2_o = 1;

  ICAP_SPARTAN3A ICAP_SPARTAN3A_inst (
    .O(icap_o),
    .BUSY(icap_busy),
    .I(icap_i),
    .WRITE(icap_write),
    .CE(icap_ce),
    .CLK(icap_clk));

  reg dna_read;
  reg dna_shift;
  reg dna_clk;
  wire dna_dout;
  DNA_PORT dna(
    .DOUT(dna_dout),
    .DIN(0),
    .READ(dna_read),
    .SHIFT(dna_shift),
    .CLK(dna_clk));

  wire j1_wr;

  reg [8:0] YYLINE;
  wire [9:0] yyline = (CounterY + 4 - (35 + 6 + 21 + 1));
  always @(posedge vga_clk)
  begin
    // if (xx == 400)
    if (composite_complete)
      YYLINE = yyline[9:1];
  end

  reg [15:0] freqhz = 8000;
  reg [7:0] freqtick;

  reg [26:0] freqd;
  wire [26:0] freqdN = freqd + freqhz - (freqd[26] ? 0 : 50000000);
  always @(posedge vga_clk)
  begin
    freqd = freqdN;
    if (freqd[26] == 0)
      freqtick = freqtick + 1;
  end

  reg [15:0] local_j1_read;
  always @*
  begin
    case ({j1_mem_addr[4:1], 1'b0})
    5'h00: local_j1_read <= YYLINE;
    5'h02: local_j1_read <= icap_o;
    5'h0c: local_j1_read <= freqtick;
    5'h0e: local_j1_read <= AUX;
    5'h12: local_j1_read <= lfsr;
    5'h14: local_j1_read <= soundcounter;
    5'h16: local_j1_read <= flashMISO;
    5'h18: local_j1_read <= dna_dout;

    // default: local_j1_read <= 16'hffff;
    endcase
  end

  wire [0:7] j1_mem_dout_be = j1_mem_dout;
  reg j1_flashMOSI;
  reg j1_flashSCK;
  reg j1_flashSSEL;

  always @(posedge vga_clk)
  begin
    if (j1_wr & (j1_mem_addr[15] == 1))
      case ({j1_mem_addr[4:1], 1'b0})
      // 5'h06: icap_i <= j1_mem_dout;
      // 5'h08: icap_write <= j1_mem_dout;
      // 5'h0a: icap_ce <= j1_mem_dout;
      // 5'h0c: icap_clk <= j1_mem_dout;
      5'h06: {icap_write,icap_ce,icap_clk,icap_i} <= j1_mem_dout;
      5'h08: {dna_read, dna_shift, dna_clk} <= j1_mem_dout;
      5'h0a: freqhz <= j1_mem_dout;
      5'h0e: j1_p2_o <= j1_mem_dout;
      5'h10: j1_p2_dir <= j1_mem_dout;
      5'h18: j1_flashMOSI <= j1_mem_dout;
      5'h1a: j1_flashSCK <= j1_mem_dout;
      5'h1c: j1_flashSSEL <= j1_mem_dout;
      endcase
  end

  assign j1_mem_wr = j1_wr & (j1_mem_addr[15] == 0);
  j0 j(.sys_clk_i(vga_clk),
       .sys_rst_i(j1_reset),
       .insn(j1_insn),
       .insn_addr(j1_insn_addr),
       .mem_wr(j1_wr),
       .mem_addr(j1_mem_addr),
       .mem_dout(j1_mem_dout),
       .mem_din(j1_mem_addr[15] ? local_j1_read : mem_data_rd),
       .pause(host_busy | (busyhh != 0))
       );

  // 0x2b00: j1 insn RAM
  wire jinsn_wr = (mem_wr & (mem_w_addr[14:8] == 6'h2b));
  RAM_CODEL jinsnl(.b(j1_insn_addr), .bo(j1_insn[7:0]),
    .a(mem_addr[7:1]),
    .wclk(vga_clk),
    .wea((mem_w_addr[0] == 0) & jinsn_wr),
    .ad(mem_data_wr),
    .ao(j1insnl_read));
  RAM_CODEH jinsnh(.b(j1_insn_addr), .bo(j1_insn[15:8]),
    .a(mem_addr[7:1]),
    .wclk(vga_clk),
    .wea((mem_w_addr[0] == 1) & jinsn_wr),
    .ad(mem_data_wr),
    .ao(j1insnh_read));

  assign flashMOSI = pin2j ? j1_flashMOSI : MOSI;
  assign flashSCK = pin2j ? j1_flashSCK : SCK;
  assign flashSSEL = pin2f ? AUX : (pin2j ? j1_flashSSEL : 1);

  assign AUX = (pin2j & (j1_p2_dir == 0)) ? j1_p2_o : 1'bz;

endmodule // top
