// 
// FIXME:
// there defines must be enabled in the project
// settings to avoid conflicts with different
// development platforms
//
//`define ICE40
//

module top(
    input  clk25,           // 25 MHz master clock
    input  uart_rx,
    output uart_tx,
    output uart_cts,

    output [7:0] led,       // what do these do?
    output [7:0] ledx       // what do these do?
);
    //////////////////////////////////////////////////////////////////////////
    // Registers and Wires

    reg [15:0] ab;
    wire [7:0] dbi;
    reg [7:0] dbo;
    reg we;

    //////////////////////////////////////////////////////////////////////////
    // Clocks
    reg cpu_clken;

    // FIXME:
    // the clocks here should come from higher up 
    // the hierarchy, i.e. generated at the board
    // level.
    //
    // if cpu_clken is a simple block,
    // keep it here but make it generic.
    
    `ifdef ICE40
    clocks my_clocks(
        .clk(clk),
        .clk25(clk25),
        .cpu_clken(cpu_clken)
    );
    `endif
    
    // generate clock enable once every 
    // 25 clocks. This will (hopefully) make
    // the 6502 run at 1 MHz or 1Hz
    //
    reg [4:0] clk_div;
    always @(posedge clk25)
    begin
        if (clk_div == 25)
            clk_div <= 0;
        else
            clk_div <= clk_div + 1'b1;

        cpu_clken <= (clk_div[4:0] == 0);
    end

    //////////////////////////////////////////////////////////////////////////
    // Reset
    wire reset;
    reg hard_reset;
    reg [5:0] reset_cnt;
    wire pwr_up_reset = &reset_cnt;

    always @(posedge clk25)
    begin
        if (cpu_clken)
        begin
            if (!pwr_up_reset)
                reset_cnt <= reset_cnt + 1;

            hard_reset <= pwr_up_reset;
        end
    end

    assign reset = ~hard_reset;

    //////////////////////////////////////////////////////////////////////////
    // 6502
    wire [7:0] dbo_c;
    wire [15:0] ab_c;
    wire we_c;
    reg [7:0] dbi_c;

    cpu my_cpu (
        .clk    (clk25),
        .reset  (reset),
        .AB     (ab_c),
        .DI     (dbi_c),
        .DO     (dbo_c),
        .WE     (we_c),
        .IRQ    (1'b0),
        .NMI    (1'b0),
        .RDY    (cpu_clken)
    );

    always @(posedge clk25)
    begin
        if (cpu_clken)
        begin
            ab <= ab_c;
            dbo <= dbo_c;
            dbi_c <= dbi;
            we <= we_c;
        end
    end

    //////////////////////////////////////////////////////////////////////////
    // RAM and ROM

    wire ram_cs =   (ab[15:13] ==  3'b000);            // 0x0000 -> 0x1FFF
    wire uart_cs =  (ab[15:2]  == 14'b11010000000100); // 0xD010 -> 0xD013
    wire basic_cs = (ab[15:12] ==  4'b1110);           // 0xE000 -> 0xEFFF
    wire rom_cs =   (ab[15:8]  ==  8'b11111111);       // 0xFF00 -> 0xFFFF

    // RAM
    wire [7:0] ram_dout;
    ram my_ram (
        .clk(clk25),
        .address(ab[12:0]),
        .w_en(we & ram_cs),
        .din(dbo),
        .dout(ram_dout)
    );

    // WozMon ROM
    wire [7:0] rom_dout;
    rom_wozmon my_rom_wozmon (
        .clk(clk25),
        .address(ab[7:0]),
        .dout(rom_dout)
    );

    // UART
    wire [7:0] uart_dout;
    uart my_uart (
        .clk(clk25),

        .uart_rx(uart_rx),
        .uart_tx(uart_tx),
        .uart_cts(uart_cts),

        .enable(uart_cs & cpu_clken),
        .address(ab[1:0]),
        .w_en(we & uart_cs),
        .din(dbo),
        .dout(uart_dout),
        .led(led)
    );

    // link up chip selected device to cpu input
    assign dbi = ram_cs  ? ram_dout : 
                 rom_cs  ? rom_dout :
                 uart_cs ? uart_dout :
                 8'hFF;

    assign ledx = ab[7:0];

//    always @(posedge clk25)
//    begin
//        if (cpu_clken)
//        begin
//            led <= ab[7:0];
//            ledx <= ~ab[15:8];
//        end
//    end

//    reg [7:0] ram[0:8191] /* synthesis syn_ramstyle = "block_ram" */;
//    reg [7:0] rom[0:255] /* synthesis syn_ramstyle = "block_ram" */;
//    reg [7:0] basic[0:4095] /* synthesis syn_ramstyle = "block_ram" */;
//    
//    initial begin
//        $readmemh("../roms/ram.hex", ram, 0, 8191);
//        $readmemh("../roms/rom.hex", rom, 0, 255);
//        $readmemh("../roms/basic.hex", basic, 0, 4095);
//    end
//
//    always @(posedge clk_25)
//    begin
//        if (phi_clk_en)
//        begin
//            if (res)
//            begin
//                case(ab)
//                    default:
//                    begin
//                        if (ab[15:12] == 4'b0000 || ab[15:12] == 4'b0001)
//                        begin
//                            // 0x0000 -> 0x1FFF - RAM
//                            dbi <= ram[ab[12:0]];
//                            if (~rw) ram[ab[12:0]] <= dbo;
//                        end
//                        else if (ab[15:12] == 4'b1110)
//                        begin
//                            // 0xE000 -> 0xEFFF - BASIC
//                            dbi <= basic[ab[11:0]];
//                        end
//                        else if (ab[15:8] == 8'b11111111)
//                        begin
//                            // 0xFF00 -> 0xFFFF - ROM
//                            dbi <= rom[ab[7:0]];
//                        end
//                        else
//                            // unknown address return zero
//                            dbi <= 8'h0;
//                    end
//
//                endcase
//            end
//        end
//    end
endmodule
