module nes_top (
  input               clk,                  // 100MHz system clock signal
  input               rst,                  // reset push button
  input               i_console_reset,      // console reset
  input       [3:0]   i_mute_control,       // switches

  output      [9:0]   o_image_width,        // width of the generated image
  output      [9:0]   o_image_height,       // height of the generated image
  output              o_hsync,              // vga hsync signal
  output              o_vsync,              // vga vsync signal
  output      [2:0]   o_red,                // vga red signal
  output      [2:0]   o_green,              // vga green signal
  output      [1:0]   o_blue,               // vga blue signal

  // Joypad signals.
  input       [7:0]   i_jp1_state,          //State of joypad 1
  input       [7:0]   i_jp2_state,          //State of joypad 2

  output              o_audio,              // pwm output audio channel

  input               i_hci_reset,
  input       [7:0]   i_hci_opcode,
  input               i_hci_opcode_strobe,

  output      [15:0]  o_hci_opcode_status,
  output              o_hci_opcode_ack,

  input       [15:0]  i_hci_address,
  input       [31:0]  i_hci_count,

  input               i_hci_data_strobe,
  output              o_hci_sm_ready,
  input       [7:0]   i_hci_data,

  output              o_hci_data_strobe,
  input               i_hci_host_ready,
  output      [7:0]   o_hci_data
);

//
// System Memory Buses
//
wire [ 7:0] cpumc_din;
wire [15:0] cpumc_a;
wire        cpumc_r_nw;

wire [ 7:0] ppumc_din;
wire [13:0] ppumc_a;
wire        ppumc_wr;

//
// RP2A03: Main processing chip including CPU, APU, joypad control, and sprite DMA control.
//
wire        rp2a03_rdy;
wire [ 7:0] rp2a03_din;
wire        rp2a03_nnmi;
wire [ 7:0] rp2a03_dout;
wire [15:0] rp2a03_a;
wire        rp2a03_r_nw;
wire        rp2a03_brk;
wire [ 3:0] rp2a03_dbgreg_sel;
wire [ 7:0] rp2a03_dbgreg_din;
wire        rp2a03_dbgreg_wr;
wire [ 7:0] rp2a03_dbgreg_dout;

rp2a03 rp2a03_blk(
  .clk_in         (clk                ),
  .rst_in         (rst                ),
  .rdy_in         (rp2a03_rdy         ),
  .nres_in        (~i_console_reset   ),
  .nnmi_in        (rp2a03_nnmi        ),

  .r_nw_out       (rp2a03_r_nw        ),
  .a_out          (rp2a03_a           ),

  .d_in           (rp2a03_din         ),
  .d_out          (rp2a03_dout        ),
  .brk_out        (rp2a03_brk         ),

  .i_jp1_state    (i_jp1_state        ),
  .i_jp2_state    (i_jp2_state        ),
  .mute_in        (i_mute_control     ),

  .audio_out      (o_audio            ),
  .dbgreg_sel_in  (rp2a03_dbgreg_sel  ),
  .dbgreg_d_in    (rp2a03_dbgreg_din  ),
  .dbgreg_wr_in   (rp2a03_dbgreg_wr   ),
  .dbgreg_d_out   (rp2a03_dbgreg_dout )
);

//
// CART: cartridge emulator
//
wire        cart_prg_nce;
wire [ 7:0] cart_prg_dout;
wire [ 7:0] cart_chr_dout;
wire        cart_ciram_nce;
wire        cart_ciram_a10;
wire [39:0] cart_cfg;
wire        cart_cfg_upd;

cart cart_blk(
  .clk_in        (clk            ),
  .cfg_in        (cart_cfg       ),
  .cfg_upd_in    (cart_cfg_upd   ),
  .prg_nce_in    (cart_prg_nce   ),
  .prg_a_in      (cpumc_a[14:0]  ),
  .prg_r_nw_in   (cpumc_r_nw     ),
  .prg_d_in      (cpumc_din      ),
  .prg_d_out     (cart_prg_dout  ),
  .chr_a_in      (ppumc_a        ),
  .chr_r_nw_in   (~ppumc_wr      ),
  .chr_d_in      (ppumc_din      ),
  .chr_d_out     (cart_chr_dout  ),
  .ciram_nce_out (cart_ciram_nce ),
  .ciram_a10_out (cart_ciram_a10 )
);

assign cart_prg_nce = ~cpumc_a[15];

//
// WRAM: internal work ram
//
wire       wram_en;
wire [7:0] wram_dout;

wram wram_blk(
  .clk_in  (clk           ),
  .en_in   (wram_en       ),
  .r_nw_in (cpumc_r_nw    ),
  .a_in    (cpumc_a[10:0] ),
  .d_in    (cpumc_din     ),
  .d_out   (wram_dout     )
);

assign wram_en = (cpumc_a[15:13] == 0);

//
// VRAM: internal video ram
//
wire [10:0] vram_a;
wire [ 7:0] vram_dout;

vram vram_blk(
  .clk_in  (clk             ),
  .en_in   (~cart_ciram_nce ),
  .r_nw_in (~ppumc_wr       ),
  .a_in    (vram_a          ),
  .d_in    (ppumc_din       ),
  .d_out   (vram_dout       )
);

//
// PPU: picture processing unit block.
//
wire [ 2:0] ppu_ri_sel;     // ppu register interface reg select
wire        ppu_ri_ncs;     // ppu register interface enable
wire        ppu_ri_r_nw;    // ppu register interface read/write select
wire [ 7:0] ppu_ri_din;     // ppu register interface data input
wire [ 7:0] ppu_ri_dout;    // ppu register interface data output

wire [13:0] ppu_vram_a;     // ppu video ram address bus
wire        ppu_vram_wr;    // ppu video ram read/write select
wire [ 7:0] ppu_vram_din;   // ppu video ram data bus (input)
wire [ 7:0] ppu_vram_dout;  // ppu video ram data bus (output)

wire        ppu_nvbl;       // ppu /VBL signal.

// PPU snoops the CPU address bus for register reads/writes.  Addresses 0x2000-0x2007
// are mapped to the PPU register space, with every 8 bytes mirrored through 0x3FFF.
assign ppu_ri_sel  = cpumc_a[2:0];
assign ppu_ri_ncs  = (cpumc_a[15:13] == 3'b001) ? 1'b0 : 1'b1;
assign ppu_ri_r_nw = cpumc_r_nw;
assign ppu_ri_din  = cpumc_din;

ppu ppu_blk(
  .clk_in      (clk           ),
  .rst_in      (rst           ),
  .ri_sel_in   (ppu_ri_sel    ),
  .ri_ncs_in   (ppu_ri_ncs    ),
  .ri_r_nw_in  (ppu_ri_r_nw   ),
  .ri_d_in     (ppu_ri_din    ),

  //VGA
  .o_image_width  (o_image_width), // width of the generated image
  .o_image_height (o_image_height),// height of the generated image

  .vram_d_in   (ppu_vram_din  ),
  .hsync_out   (o_hsync   ),
  .vsync_out   (o_vsync   ),
  .r_out       (o_red     ),
  .g_out       (o_green   ),
  .b_out       (o_blue    ),

  //VRAM
  .ri_d_out    (ppu_ri_dout   ),
  .nvbl_out    (ppu_nvbl      ),
  .vram_a_out  (ppu_vram_a    ),
  .vram_d_out  (ppu_vram_dout ),
  .vram_wr_out (ppu_vram_wr   )
);

assign vram_a = { cart_ciram_a10, ppumc_a[9:0] };

//
// HCI: host communication interface block.  Interacts with NesDbg software through serial port.
//
wire        hci_active;
wire [ 7:0] hci_cpu_din;
wire [ 7:0] hci_cpu_dout;
wire [15:0] hci_cpu_a;
wire        hci_cpu_r_nw;
wire [ 7:0] hci_ppu_vram_din;
wire [ 7:0] hci_ppu_vram_dout;
wire [15:0] hci_ppu_vram_a;
wire        hci_ppu_vram_wr;



nes_hci hci_blk(
  .clk                  (clk          ),
  .rst                  (rst                 ),

  //Host Interface
  .i_reset_sm           (i_hci_reset         ),
  .i_opcode             (i_hci_opcode        ),
  .i_opcode_strobe      (i_hci_opcode_strobe ),

  .o_opcode_status      (o_hci_opcode_status ),
  .o_opcode_ack         (o_hci_opcode_ack    ),

  .i_address            (i_hci_address       ),
  .i_count              (i_hci_count         ),


  .i_data_strobe        (i_hci_data_strobe   ),
  .o_hci_ready          (o_hci_sm_ready      ),
  .i_data               (i_hci_data          ),


  .o_data_strobe        (o_hci_data_strobe   ),
  .i_host_ready         (i_hci_host_ready    ),
  .o_data               (o_hci_data          ),

  //NES Interface
  .i_cpu_break          (rp2a03_brk          ),
  .o_cpu_r_nw           (hci_cpu_r_nw        ),     //CPU Read/!Write Pin
  .o_cpu_address        (hci_cpu_a           ),
  .i_cpu_din            (hci_cpu_din         ),
  .o_cpu_dout           (hci_cpu_dout        ),

  .o_dbg_active         (hci_active          ),
  .o_cpu_dbg_reg_wr     (rp2a03_dbgreg_wr    ),
  .o_cpu_dbg_reg_sel    (rp2a03_dbgreg_sel   ),
  .i_cpu_dbg_reg_din    (rp2a03_dbgreg_dout  ),
  .o_cpu_dbg_reg_dout   (rp2a03_dbgreg_din   ),

  .o_ppu_vram_wr        (hci_ppu_vram_wr     ),
  .o_ppu_vram_address   (hci_ppu_vram_a      ),
  .i_ppu_vram_din       (hci_ppu_vram_din    ),
  .o_ppu_vram_dout      (hci_ppu_vram_dout   ),

  .o_cart_cfg           (cart_cfg            ),
  .o_cart_cfg_update    (cart_cfg_upd        )

);

/*
Moved this hear for testing
wire [7:0] jp_dout;

jp jp_blk(
  .clk         (clk           ),
  .rst         (rst           ),

  .i_wr        (~cpumc_r_nw   ),
  .i_addr      (cpumc_a       ),
  .i_din       (cpumc_din[0]  ),
  .o_dout      (jp_dout       ),

  .i_jp1_state (i_jp1_state   ),
  .i_jp2_state (i_jp2_state   )
);
*/


/*
hci hci_blk(
  .clk            (clk         ),
  .rst            (rst                ),
  .rx             (RXD                ),
  .tx             (TXD                ),
  .active         (hci_active         ),

  .cpu_din        (hci_cpu_din        ),
  .cpu_r_nw       (hci_cpu_r_nw       ),
  .cpu_a          (hci_cpu_a          ),
  .cpu_dout       (hci_cpu_dout       ),

  .brk            (rp2a03_brk         ),

  .cpu_dbgreg_sel (rp2a03_dbgreg_sel  ),
  .cpu_dbgreg_out (rp2a03_dbgreg_din  ),
  .cpu_dbgreg_wr  (rp2a03_dbgreg_wr   ),
  .cpu_dbgreg_in  (rp2a03_dbgreg_dout ),

  .ppu_vram_din   (hci_ppu_vram_din   ),
  .ppu_vram_wr    (hci_ppu_vram_wr    ),
  .ppu_vram_a     (hci_ppu_vram_a     ),
  .ppu_vram_dout  (hci_ppu_vram_dout  ),

  .cart_cfg       (cart_cfg           ),
  .cart_cfg_upd   (cart_cfg_upd       )
);
*/

// Mux cpumc signals from rp2a03 or hci blk, depending on debug break state (hci_active).
assign rp2a03_rdy  = (hci_active) ? 1'b0         : 1'b1;
assign cpumc_a     = (hci_active) ? hci_cpu_a    : rp2a03_a;
assign cpumc_r_nw  = (hci_active) ? hci_cpu_r_nw : rp2a03_r_nw;
assign cpumc_din   = (hci_active) ? hci_cpu_dout : rp2a03_dout;

assign rp2a03_din  = cart_prg_dout | wram_dout | ppu_ri_dout;
assign hci_cpu_din = cart_prg_dout | wram_dout | ppu_ri_dout;

// Mux ppumc signals from ppu or hci blk, depending on debug break state (hci_active).
assign ppumc_a          = (hci_active) ? hci_ppu_vram_a[13:0] : ppu_vram_a;
assign ppumc_wr         = (hci_active) ? hci_ppu_vram_wr      : ppu_vram_wr;
assign ppumc_din        = (hci_active) ? hci_ppu_vram_dout    : ppu_vram_dout;

assign ppu_vram_din     = cart_chr_dout | vram_dout;
assign hci_ppu_vram_din = cart_chr_dout | vram_dout;

// Issue NMI interupt on PPU vertical blank.
assign rp2a03_nnmi = ppu_nvbl;


endmodule