
`include "hvsync_generator.v"
`include "font_cp437_8x8.v"
`include "ram.v"
`include "tile_renderer.v"
`include "sprite_scanline_renderer.v"
`include "lfsr.v"
`include "sound_generator.v"
`include "cpu16.v"

/*
A full video game console, with the following components:

  64 kilobytes (32,678 16-bit words) of RAM
  16-bit CPU running at 4.857 MHz
  32x30 tile graphics with 256 x 8 tile ROM
  32 16x16 sprites per frame with sprite ROM
  16 colors (two per tile, one per sprite)
  Two game controllers (four direction switches, two buttons)
  One paddle/analog stick controller
*/

module cpu_platform(clk, reset, hsync, vsync, 
                    hpaddle, vpaddle, 
                    switches_p1, switches_p2,
                    rgb);

  input clk, reset;
  input hpaddle, vpaddle;
  input [7:0] switches_p1;
  input [7:0] switches_p2;
  output hsync, vsync;
  output [3:0] rgb;

  wire display_on;
  wire [8:0] hpos;
  wire [8:0] vpos;
  
  // video RAM bus
  wire [15:0] ram_read;
  reg [15:0] ram_write;
  reg ram_writeenable;

  // multiplex sprite and tile RAM
  reg [15:0] tile_ram_addr;
  reg [5:0] sprite_ram_addr;
  wire tile_reading;
  wire sprite_reading;
  var [14:0] mux_ram_addr; // 15-bit RAM access
  
  // multiplexor for sprite/tile/CPU RAM
  always @(*)
    if (cpu_busy) begin
      if (sprite_reading)
        mux_ram_addr = {9'b111111100, sprite_ram_addr};
      else
        mux_ram_addr = tile_ram_addr[14:0];
    end else
      mux_ram_addr = cpu_ram_addr[14:0];
  
  // tile and sprite ROM
  wire [10:0] tile_rom_addr;
  wire [7:0] tile_rom_data;
  wire [15:0] sprite_rom_addr;
  wire [15:0] sprite_rom_data;
  
  // gfx outputs
  wire [3:0] tile_rgb;
  wire [3:0] sprite_rgb;
  
  // video sync generator
  hvsync_generator hvsync_gen(
    .clk(clk),
    .reset(reset),
    .hsync(hsync),
    .vsync(vsync),
    .display_on(display_on),
    .hpos(hpos),
    .vpos(vpos)
  );
 
  // RAM (32k x 16 bits)
  RAM_sync #(15,16) ram(
    .clk(clk),
    .dout(ram_read),
    .din(ram_write),
    .addr(mux_ram_addr),
    .we(ram_writeenable)
  );
  
  // tile graphics
  tile_renderer tile_gen(
    .clk(clk),
    .reset(reset),
    .hpos(hpos),
    .vpos(vpos),
    .ram_addr(tile_ram_addr),
    .ram_read(ram_read),
    .ram_busy(tile_reading),
    .rom_addr(tile_rom_addr),
    .rom_data(tile_rom_data),
    .rgb(tile_rgb)
  );

  // sprite scanline renderer
  sprite_scanline_renderer ssr(
    .clk(clk),
    .reset(reset),
    .hpos(hpos),
    .vpos(vpos),
    .ram_addr(sprite_ram_addr),
    .ram_data(ram_read),
    .ram_busy(sprite_reading),
    .rom_addr(sprite_rom_addr),
    .rom_data(sprite_rom_data),
    .rgb(sprite_rgb)
  );
  
  // tile ROM
  font_cp437_8x8 tile_rom(
    .addr(tile_rom_addr),
    .data(tile_rom_data)
  );

  // sprite ROM
  example_bitmap_rom bitmap_rom(
    .addr(sprite_rom_addr),
    .data(sprite_rom_data)
  );

  // sprites overlay tiles
  assign rgb = display_on
    ? (sprite_rgb>0 ? sprite_rgb : tile_rgb)
    : 0;

  // CPU
  reg cpu_hold = 0;
  wire cpu_busy;
  wire [15:0] cpu_ram_addr;
  wire busy;
  var [15:0] cpu_bus;
  wire [15:0] flags = {11'b0, vsync, hsync, vpaddle, hpaddle, display_on};
  wire [15:0] switches = {switches_p2, switches_p1};
  
  // select ROM, RAM, switches ($FFFE) or flags ($FFFF)
  /* verilator lint_off CASEOVERLAP */
  always @(*)
    casez (cpu_ram_addr)
      16'hfffe: cpu_bus = switches;
      16'hffff: cpu_bus = flags;
      16'b0???????????????: cpu_bus = ram_read;
      16'b1???????????????: cpu_bus = program_rom[cpu_ram_addr[14:0]];
    endcase

  // 16-bit CPU
  CPU16 cpu(
    .clk(clk),
    .reset(reset),
    .hold(tile_reading | sprite_reading), // hold input
    .busy(cpu_busy),			  // busy output
    .address(cpu_ram_addr),
    .data_in(cpu_bus),
    .data_out(ram_write),
    .write(ram_writeenable));

  // program ROM ($8000-$FFFE)
  reg [15:0] program_rom[0:32767];
  
  // example ROM program code
`ifdef EXT_INLINE_ASM
  initial begin
    program_rom = '{
      __asm
.arch femto16
.org 0x8000
.len 32768
      mov       sp,@$6fff
      mov	dx,@InitPageTable
      jsr	dx
      mov	ax,@$4ffe
      mov	dx,@ClearTiles
      jsr	dx
      mov	dx,@ClearSprites
      jsr	dx
      reset
InitPageTable:
      mov       ax,@$6000	; screen buffer
      mov       bx,@$7e00	; page table start
      mov	cx,#32		; 32 rows
InitPTLoop:
      mov	[bx],ax
      add	ax,#32
      inc	bx
      dec	cx
      bnz	InitPTLoop
      rts
ClearTiles:
      mov	bx,@$6000
      mov	cx,@$3c0
ClearLoop:
        mov	[bx],ax
        inc	bx
        dec	cx
        bnz	ClearLoop
      rts
ClearSprites:
        mov	bx,@$7f00
        mov	ax,#0
        mov	cx,#$40
ClearSLoop:
        mov	ax,[bx]
        add	ax,@$101
        mov	[bx],ax
        inc	bx
	dec	cx
        bnz	ClearSLoop
        rts
      __endasm
    };
  end
`endif

endmodule
