//============================================================================
//  Sinclair QL
//
//  Copyright (c) 2015 Till Harbaum <till@harbaum.org> 
//
//  Port to MiSTer
//  Copyright (C) 2017-2019 Sorgelig
//
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//  more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//============================================================================

module emu
(
	//Master input clock
	input         CLK_50M,

	//Async reset from top-level module.
	//Can be used as initial reset.
	input         RESET,

	//Must be passed to hps_io module
	inout  [48:0] HPS_BUS,

	//Base video clock. Usually equals to CLK_SYS.
	output        CLK_VIDEO,

	//Multiple resolutions are supported using different CE_PIXEL rates.
	//Must be based on CLK_VIDEO
	output        CE_PIXEL,

	//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
	//if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio.
	output [12:0] VIDEO_ARX,
	output [12:0] VIDEO_ARY,

	output  [7:0] VGA_R,
	output  [7:0] VGA_G,
	output  [7:0] VGA_B,
	output        VGA_HS,
	output        VGA_VS,
	output        VGA_DE,    // = ~(VBlank | HBlank)
	output        VGA_F1,
	output [1:0]  VGA_SL,
	output        VGA_SCALER, // Force VGA scaler
	output        VGA_DISABLE, // analog out is off

	input  [11:0] HDMI_WIDTH,
	input  [11:0] HDMI_HEIGHT,
	output        HDMI_FREEZE,

`ifdef MISTER_FB
	// Use framebuffer in DDRAM
	// FB_FORMAT:
	//    [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp
	//    [3]   : 0=16bits 565 1=16bits 1555
	//    [4]   : 0=RGB  1=BGR (for 16/24/32 modes)
	//
	// FB_STRIDE either 0 (rounded to 256 bytes) or multiple of pixel size (in bytes)
	output        FB_EN,
	output  [4:0] FB_FORMAT,
	output [11:0] FB_WIDTH,
	output [11:0] FB_HEIGHT,
	output [31:0] FB_BASE,
	output [13:0] FB_STRIDE,
	input         FB_VBL,
	input         FB_LL,
	output        FB_FORCE_BLANK,

`ifdef MISTER_FB_PALETTE
	// Palette control for 8bit modes.
	// Ignored for other video modes.
	output        FB_PAL_CLK,
	output  [7:0] FB_PAL_ADDR,
	output [23:0] FB_PAL_DOUT,
	input  [23:0] FB_PAL_DIN,
	output        FB_PAL_WR,
`endif
`endif

	output        LED_USER,  // 1 - ON, 0 - OFF.

	// b[1]: 0 - LED status is system status OR'd with b[0]
	//       1 - LED status is controled solely by b[0]
	// hint: supply 2'b00 to let the system control the LED.
	output  [1:0] LED_POWER,
	output  [1:0] LED_DISK,

	// I/O board button press simulation (active high)
	// b[1]: user button
	// b[0]: osd button
	output  [1:0] BUTTONS,

	input         CLK_AUDIO, // 24.576 MHz
	output [15:0] AUDIO_L,
	output [15:0] AUDIO_R,
	output        AUDIO_S,   // 1 - signed audio samples, 0 - unsigned
	output  [1:0] AUDIO_MIX, // 0 - no mix, 1 - 25%, 2 - 50%, 3 - 100% (mono)

	//ADC
	inout   [3:0] ADC_BUS,

	//SD-SPI
	output        SD_SCK,
	output        SD_MOSI,
	input         SD_MISO,
	output        SD_CS,
	input         SD_CD,

	//High latency DDR3 RAM interface
	//Use for non-critical time purposes
	output        DDRAM_CLK,
	input         DDRAM_BUSY,
	output  [7:0] DDRAM_BURSTCNT,
	output [28:0] DDRAM_ADDR,
	input  [63:0] DDRAM_DOUT,
	input         DDRAM_DOUT_READY,
	output        DDRAM_RD,
	output [63:0] DDRAM_DIN,
	output  [7:0] DDRAM_BE,
	output        DDRAM_WE,

	//SDRAM interface with lower latency
	output        SDRAM_CLK,
	output        SDRAM_CKE,
	output [12:0] SDRAM_A,
	output  [1:0] SDRAM_BA,
	inout  [15:0] SDRAM_DQ,
	output        SDRAM_DQML,
	output        SDRAM_DQMH,
	output        SDRAM_nCS,
	output        SDRAM_nCAS,
	output        SDRAM_nRAS,
	output        SDRAM_nWE,

`ifdef MISTER_DUAL_SDRAM
	//Secondary SDRAM
	//Set all output SDRAM_* signals to Z ASAP if SDRAM2_EN is 0
	input         SDRAM2_EN,
	output        SDRAM2_CLK,
	output [12:0] SDRAM2_A,
	output  [1:0] SDRAM2_BA,
	inout  [15:0] SDRAM2_DQ,
	output        SDRAM2_nCS,
	output        SDRAM2_nCAS,
	output        SDRAM2_nRAS,
	output        SDRAM2_nWE,
`endif

	input         UART_CTS,
	output        UART_RTS,
	input         UART_RXD,
	output        UART_TXD,
	output        UART_DTR,
	input         UART_DSR,

	// Open-drain User port.
	// 0 - D+/RX
	// 1 - D-/TX
	// 2..6 - USR2..USR6
	// Set USER_OUT to 1 to read from USER_IN.
	input   [6:0] USER_IN,
	output  [6:0] USER_OUT,

	input         OSD_STATUS
);

assign ADC_BUS  = 'Z;
assign USER_OUT = '1;
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0;

assign LED_USER  = mdv_led | ioctl_download | sd_act;
assign LED_DISK  = {1'b1, status[0]};
assign LED_POWER = 0;
assign BUTTONS   = 0;
assign VGA_SCALER= 0;
assign VGA_DISABLE = 0;
assign HDMI_FREEZE = 0;

video_freak video_freak
(
	.*,
	.VGA_DE_IN(VGA_DE),
	.VGA_DE(),
	.ARX((!ar) ? 12'd4 : (ar - 1'd1)),
	.ARY((!ar) ? 12'd3 : 12'd0),
	.CROP_SIZE(0),
	.CROP_OFF(0),
	.SCALE(status[14:13])
);

/////////////////  CONFIGURATION  /////////////////
`include "build_id.v" 
parameter CONF_STR = {
	"QL;;",
	"SC0,WIN,Mount HD image;",	
	"-;",
	"F2,MDV,Load MDV image;",
	"O2,MDV direction,normal,reverse;",
	"-;",
	"F4,ROM,Load OS;",
	"-;",
	"O3,Video mode,PAL,NTSC;",
	"OBC,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
	"O9A,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;",
	"ODE,Scale,Normal,V-Integer,Narrower HV-Integer,Wider HV-Integer;",
	"-;",
	"O78,CPU speed,QL,16 Mhz,24 Mhz,Full;",
	"O45,RAM,128k,640k,896k,4096k;",
	"R0,Reset & unload MDV;",
	"V,v",`BUILD_DATE
};

parameter MDV_IOCTL_INDEX = 8'd2;
parameter ROM_IOCTL_INDEX = 8'd4;

wire mdv_reverse = status[2];
wire ntsc_mode = status[3];
wire osd_reset = status[0];
wire [1:0] cpu_speed = status[8:7];
wire [1:0] scale = status[10:9];
wire ql_mode = cpu_speed == 2'b00;
wire gc_en = ram_cfg == 2'b11;
wire [1:0] ar = status[12:11];

reg [1:0] ram_cfg;			// 00 = 128k, 01 = 640k, 10 = 896k, 11 = 4096k
always @(posedge clk_sys) if (reset) ram_cfg <= status[5:4];


/////////////////  CLOCKS  ////////////////////////

wire clk_sys;
wire pll_locked;

pll pll
(
	.refclk(CLK_50M),
	.rst(0),
	.outclk_0(clk_sys),						// System clock
	.locked(pll_locked)
);

// 84MHz sys_clk
parameter FRACT_BUS_QL = 17'd11702;		// 84MHz * 11702 / 65536 = 14.999MHz
parameter FRACT_BUS_16 = 17'd24966;		// 84MHz * 24966 / 65536 = 31.999MHz
parameter FRACT_BUS_24 = 17'd37449;		// 84MHz * 37449 / 65536 = 48.000MHz
parameter FRACT_BUS_FULL = 17'h10000;	// 84MHz

parameter FRACT_SD = 17'd19505;			// 84MHz * 39010 / 65536 = 50Mhz (effectively 25Mhz SPI speed)
parameter FRACT_11M = 17'd8582;			// 84MHz * 8582 / 65536 = 10.999Mhz
parameter DIV_131k = 10'd640;				// 84MHz / 640 = 131250Hz
parameter DIV_VID = 4'd8;					// 84MHz / 8 = 10.5MHz

// 94.5MHz sys_clk
/*parameter FRACT_BUS_QL = 17'd10403;		// 94.5MHz * 10403 / 65536 = 15.000MHz
parameter FRACT_BUS_16 = 17'd22192;		// 94.5MHz * 22192 / 65536 = 31.999MHz
parameter FRACT_BUS_24 = 17'd33288;		// 94.5MHz * 33288 / 65536 = 48.000MHz
parameter FRACT_BUS_FULL = 17'h10000;	// 94.5MHz

parameter FRACT_SD = 17'd34675;			// 94.5MHz * 34675 / 65536 = 49.999MHz (effectively 25Mhz SPI speed)
parameter FRACT_11M = 17'd7629;			// 94.5MHz * 7629 / 65536 = 11.001MHz
parameter DIV_131k = 10'd720;				// 94.5MHz / 720 = 131250Hz
parameter DIV_VID = 4'd9;					// 94.5MHz / 9 = 10.5MHz*/

// 105MHz sys_clk
/*parameter FRACT_BUS_QL = 17'd9362;		// 105MHz * 9362 / 65536 = 14.999MHz
parameter FRACT_BUS_16 = 17'd19973;		// 105MHz * 19973 / 65536 = 32.000MHz
parameter FRACT_BUS_24 = 17'd29959;		// 105MHz * 29959 / 65536 = 47.999MHz
parameter FRACT_BUS_FULL = 17'h10000;	// 105MHz

parameter FRACT_SD = 17'd31208;			// 105MHz * 31208 / 65536 = 50.000MHz (effectively 25Mhz SPI speed)
parameter FRACT_11M = 17'd6866;			// 105MHz * 6866 / 65536 = 11.001MHz
parameter DIV_131k = 10'd800;				// 105MHz / 800 = 131250Hz
parameter DIV_VID = 4'd10;					// 105MHz / 10 = 10.5MHz*/

wire [16:0] fract_bus = 
	cpu_speed == 0? FRACT_BUS_QL:
	cpu_speed == 1? FRACT_BUS_16:
	cpu_speed == 2? FRACT_BUS_24:
	FRACT_BUS_FULL;

reg ce_bus_p, ce_bus_n;
reg ce_131k;									// Supposed to be 131025 Hz for SDRAM refresh and clock update
reg ce_vid;										// 10.5Mhz pixel clock
reg ce_sd;										// ~50 Mhz SD clock
reg ce_11m;

always @(negedge clk_sys)
begin
	reg bus_pol;
	reg bus_tick;
	reg [15:0] cnt_bus;
	reg [15:0] cnt_sd;
	reg [15:0] cnt_11m;
	reg [9:0] div131k;
	reg [3:0] divVid;

	if (reset) 
	begin
		bus_pol <= 0;
		cnt_bus <= 0;
		div131k <= 0;
		divVid <= 0;
	end else begin	
		div131k<= div131k + 10'd1;
		divVid <= divVid + 4'd1;
		end
	
	// CPU clock
	{bus_tick, cnt_bus} <= cnt_bus + fract_bus;
	ce_bus_p <= bus_tick && !bus_pol;
	ce_bus_n <= bus_tick && bus_pol;
	bus_pol <= bus_tick ^ bus_pol; 

	// SDRAM refresh and clock update	
	if (div131k == DIV_131k - 1) div131k <= 0;
	ce_131k <= !div131k;						
		
	// 10.5Mhz pixel clock
	if (divVid == DIV_VID - 1) divVid <= 0;	
	ce_vid <= !divVid;
	
	// QL-SD clock
	{ce_sd, cnt_sd} <= cnt_sd + FRACT_SD;
	
	// 11Mhz IPC clock
	{ce_11m, cnt_11m} <= cnt_11m + FRACT_11M;
end

//////////////// QL RAM timing ////////////////////

wire ram_delay_dtack;

ql_timing ql_timing(
	.*,
	.enable(ql_mode)
);

/////////////////  HPS  ///////////////////////////

wire [31:0] status;
wire  [1:0] buttons;

wire [15:0] joystick_0, joystick_1;
wire        ioctl_download;
wire  [7:0] ioctl_index;
wire        ioctl_wr;
wire [24:0] ioctl_addr;
wire [15:0] ioctl_dout = {ioctl_data[7:0], ioctl_data[15:8]};
wire [15:0] ioctl_data;
reg         ioctl_wait = 0;

wire [31:0] sd_lba;
wire        sd_rd;
wire        sd_wr;
wire        sd_ack;
wire  [7:0] sd_buff_addr;
wire [15:0] sd_buff_dout;
wire [15:0] sd_buff_din;
wire        sd_buff_wr;
wire        img_mounted;
wire        img_readonly;
wire [63:0] img_size;

wire [24:0] ps2_mouse;
wire [10:0] ps2_key;
wire [32:0] TIMESTAMP;

wire        forced_scandoubler;
wire [21:0] gamma_bus;

hps_io #(.CONF_STR(CONF_STR), .WIDE(1)) hps_io
(
	.clk_sys(clk_sys),
	.HPS_BUS(HPS_BUS),

	.buttons(buttons),
	.status(status),
	.forced_scandoubler(forced_scandoubler),
	.gamma_bus(gamma_bus),
	
	.TIMESTAMP(TIMESTAMP),

	.ioctl_download(ioctl_download),
	.ioctl_index(ioctl_index),
	.ioctl_wr(ioctl_wr),
	.ioctl_addr(ioctl_addr),
	.ioctl_dout(ioctl_data),
	.ioctl_wait(ioctl_wait),

	.sd_lba('{sd_lba}),
	.sd_rd(sd_rd),
	.sd_wr(sd_wr),
	.sd_ack(sd_ack),
	.sd_buff_addr(sd_buff_addr),
	.sd_buff_dout(sd_buff_dout),
	.sd_buff_din('{sd_buff_din}),
	.sd_buff_wr(sd_buff_wr),
	.img_mounted(img_mounted),
	.img_readonly(img_readonly),
	.img_size(img_size),
	
	.ps2_key(ps2_key),
	.ps2_mouse(ps2_mouse),

	.joystick_0(joystick_0),
	.joystick_1(joystick_1)
);

/////////////////  SD  ////////////////////////////

// SD card emulator
reg vsd_sel = 0;
always @(posedge clk_sys) if (img_mounted) vsd_sel <= |img_size;

wire vsd_miso;
wire sd_clk;
wire sd_mosi;
wire sd_miso = sd_phys_cs? vsd_miso: SD_MISO;
wire sd_cs1, sd_cs2;
wire sd_phys_cs = vsd_sel? sd_cs2: sd_cs1;	// Physical SD as card2 if vitual SD is enabled

sd_card #(.WIDE(1)) sd_card
(
	.*,

	.clk_spi(clk_sys),
	.sdhc(1),
	.sck(sd_clk),
	.ss(sd_cs1 || ~vsd_sel),
	.mosi(sd_mosi),
	.miso(vsd_miso)
);

assign SD_CS   = sd_phys_cs;
assign SD_SCK  = sd_phys_cs? 1'd0: sd_clk;
assign SD_MOSI = sd_phys_cs? 1'd0: sd_mosi;

// QL-SD interface
wire qlsd_en  = (!gc_en || rom_shadow) && cpu_rom && cpu_rd;
wire qlsd_reg = qlsd_en && (cpu_addr[15:4] == 12'hfee || cpu_addr[15:4] == 12'hfef);
wire qlsd_rd  = qlsd_en && (cpu_addr[15:0] == 16'hfee4);  // only one register actually returns data
wire qlsd_dat = qlsd_en &&  cpu_addr[15:8] == 8'hff;
wire qlsd_sel = qlsd_reg || qlsd_dat;
wire [7:0] qlsd_dout;
wire qlsd_dtack;

qlromext qlromext
(
	.clk		( clk_sys    		),

	.ce_sd	( ce_sd    			),
	.dtack   ( qlsd_dtack      ),

	.romoel  ( !qlsd_sel 		),
	.a       ( cpu_addr[15:0]	),
	.d       ( qlsd_dout       ),
	.sd_do   ( sd_miso         ),
	.sd_cs1l ( sd_cs1          ),
	.sd_cs2l ( sd_cs2          ),
	.sd_clk  ( sd_clk          ),
	.sd_di   ( sd_mosi         ),
	.io2     ( 1'b0            )
); 


// SD led
reg sd_act;

always @(posedge clk_sys) begin
	reg old_mosi, old_miso;
	integer timeout = 0;

	old_mosi <= sd_mosi;
	old_miso <= sd_miso;

	sd_act <= 0;
	if (timeout < 4000000) begin
		timeout <= timeout + 1;
		sd_act <= 1;
	end

	if ((old_mosi ^ sd_mosi) || (old_miso ^ sd_miso)) timeout <= 0;
end

/////////////////  RESET  /////////////////////////

reg [11:0] reset_cnt;
wire reset = (reset_cnt != 0);
always @(posedge clk_sys) begin
	if(RESET || buttons[1] || osd_reset || !pll_locked || rom_download)
		reset_cnt <= 12'hfff;
	else if(ce_bus_p && reset_cnt != 0)
		reset_cnt <= reset_cnt - 1'd1;
end

/////////////////  SDRAM  /////////////////////////

wire [23:0] sdram_addr = { 3'b000, cpu_addr[21:1]};
wire [15:0] sdram_din  = cpu_dout;
wire        sdram_wr   = cpu_as && !cpu_rw && (cpu_ram || rom_shadow_write);
wire        sdram_oe   = cpu_rd && (cpu_ram || rom_shadow_read);
wire			sdram_uds  = cpu_uds;
wire			sdram_lds  = cpu_lds;
wire [15:0] sdram_dout;
wire			sdram_dtack;

assign SDRAM_CKE = 1;
sdram sdram
(
	.*,

   // system interface
   .clk		( clk_sys      ),
	.refresh	( ce_131k		),
   .init  	( !pll_locked  ),

   // cpu interface
   .din		( sdram_din    ),
   .addr		( sdram_addr   ),
   .we		( sdram_wr     ),
   .oe		( sdram_oe     ),
   .uds		( sdram_uds    ),
   .lds		( sdram_lds    ),
   .dout		( sdram_dout   ),
	.dtack 	( sdram_dtack	)
);

//////////////  GoldCard registers  ///////////////

// GoldCard style ROM shadow RAM
reg rom_shadow;
wire rom_shadow_read = cpu_rd && cpu_os_rom && rom_shadow;
wire rom_shadow_write = cpu_as && !cpu_rw && cpu_os_rom && !rom_shadow;

always @(posedge clk_sys)
begin
	if (reset) 
		rom_shadow <= 0;
	else if (gc_io && cpu_wr && cpu_uds && !cpu_lds)
	begin
		if (cpu_addr[7:0] == 8'h60)
			rom_shadow <= 1;				// glo_rena: enable shadow RAM
		else if (cpu_addr[7:0] == 8'h64)
			rom_shadow <= 0;				// glo_rdis: disable shadow RAM
	end
end

//////////////////  ROM  //////////////////////////

wire rom_download = ioctl_download && (!ioctl_index || ioctl_index == ROM_IOCTL_INDEX);
wire rom_ioctl_write = ioctl_wr && (!ioctl_index || ioctl_index == ROM_IOCTL_INDEX);

wire [15:0] ql_rom_dout;
dpram #(15) ql_rom
(
	.wrclock		( clk_sys				),
	.wraddress	( ioctl_addr[15:1] 	),
	.wren			( rom_ioctl_write	 	),
	.byteena_a	( 2'b11					),
	.data			( ioctl_dout			),

	.rdclock		( clk_sys				),
	.rdaddress	( cpu_addr[15:1]		),
	.q				( ql_rom_dout			)
);


///////////  MisterGoldCard boot ROM  /////////////

wire [15:0] gc_rom_dout;
mgc_rom gc_rom (
	.clock		( clk_sys			),
	.address		( cpu_addr[15:1]	),
	.q				( gc_rom_dout		)
);

/////////////////  VRAM  //////////////////////////	

wire [15:0] vram_dout;
dpram #(15) vram
(
	.wrclock(clk_sys),
	.wraddress	( cpu_addr[15:1] ),
	.wren			( cpu_wr && (cpu_addr[23:16] == 2) ),
	.byteena_a	( {cpu_uds, cpu_lds}	),
	.data			( cpu_dout ),

	.rdclock(clk_sys),
	.rdaddress(video_addr),
	.q(vram_dout)
);

/////////////////  ZX8301  ////////////////////////

wire video_r, video_g, video_b;
wire HS, VS;
wire HBlank, VBlank, ce_pix;

reg HSync, VSync;
always @(posedge CLK_VIDEO) begin
	HSync <= HS;
	if(~HSync & HS) VSync <= VS;
end

assign VGA_SL = scale ? scale - 1'd1 : 2'd0;
assign VGA_F1 = 0;

assign CLK_VIDEO = clk_sys;

video_mixer #(.HALF_DEPTH(1), .GAMMA(1)) video_mixer
(
	.*,
	.scandoubler(scale || forced_scandoubler),
	.hq2x(scale==1),
	.freeze_sync(),
	
	.R({4{video_r}}),
	.G({4{video_g}}),
	.B({4{video_b}})
);

wire [14:0] video_addr;

// The zx8301 has only one write-only register at $18063
wire zx8301_ce = ql_io && ({cpu_addr[6:5], cpu_addr[1]} == 3'b111) && cpu_wr && cpu_lds;

reg [7:0] mc_stat;
always @(posedge clk_sys)
begin
	if (reset) 
	begin
		mc_stat <= 8'h00;
	end
	else if (zx8301_ce) 
	begin
		mc_stat <= cpu_dout[7:0];
	end
end

zx8301 zx8301
(
	.reset   ( reset      ),

	.clk     ( clk_sys    ),
	.ce      ( ce_vid     ),
	.ce_out  ( ce_pix     ),

	.ntsc    ( ntsc_mode  ),
	.mc_stat ( mc_stat    ),

	.addr    ( video_addr ),
	.din     ( vram_dout  ),

	.hs      ( HS         ),
	.vs      ( VS         ),
	.r       ( video_r    ),
	.g       ( video_g    ),
	.b       ( video_b    ),
	.HBlank  ( HBlank     ),
	.VBlank  ( VBlank     )
);

/////////////////  ZX8302  ////////////////////////

wire zx8302_sel = cpu_io && ql_io && !cpu_addr[6];
wire [1:0] zx8302_addr = {cpu_addr[5], cpu_addr[1]};
wire [15:0] zx8302_dout;

wire mdv_download = (ioctl_index == MDV_IOCTL_INDEX) && ioctl_download;

wire audio;
assign AUDIO_L = {15{audio}};
assign AUDIO_R = {15{audio}};
assign AUDIO_S = 0;
assign AUDIO_MIX = 0;

wire mdv_led;

zx8302 zx8302
(
	.reset        ( reset        ),
	.reset_mdv    ( osd_reset    ),
	.clk          ( clk_sys      ),
	.ce_11m       ( ce_11m       ),

	.xint         ( qimi_irq     ),
	.ipl          ( cpu_ipl      ),
	.led          ( mdv_led      ),
	.audio        ( audio        ),
	
	// CPU connection
	.cep          ( ce_bus_p     ),
	.cen          ( ce_bus_n     ),

	.ce_131k      ( ce_131k      ),
	.rtc_data     ( TIMESTAMP    ),

	.cpu_sel      ( zx8302_sel   ),
	.cpu_wr       ( cpu_wr       ),
	.cpu_addr     ( zx8302_addr  ),
	.cpu_uds		  ( cpu_uds 	  ),
	.cpu_lds      ( cpu_lds		  ),
	.cpu_din      ( cpu_dout     ),
   .cpu_dout     ( zx8302_dout  ),

	// joysticks 
	.js0          ( joystick_0[4:0] ),
	.js1          ( joystick_1[4:0] ),

	.ps2_key      ( ps2_key      ),
	
	.vs           ( VS           ),

	.mdv_reverse  ( mdv_reverse  ),

	.mdv_download ( mdv_download ),
	.mdv_dl_wr    ( ioctl_wr && mdv_download),
	.mdv_dl_data  ( ioctl_dout   ),
	.mdv_dl_addr  ( ioctl_addr[17:1] )
);

/////////////////  MOUSE  /////////////////////////

// qimi is at 1bfxx
wire qimi_sel = cpu_io && ql_io && (cpu_addr[13:8] == 6'b111111);
wire [7:0] qimi_data;
wire qimi_irq;
	
qimi qimi
(
   .reset     ( reset          ),
	.clk       ( clk_sys        ),
	.cep       ( ce_bus_p       ),
	.cen       ( ce_bus_n       ),

	.cpu_sel   ( qimi_sel       ),
	.cpu_addr  ( { cpu_addr[5], cpu_addr[1] } ),
	.cpu_data  ( qimi_data      ),
	.irq       ( qimi_irq       ),
	
	.ps2_mouse ( ps2_mouse      )
);

/////////////////  CPU  ///////////////////////////

wire ram_128 = !ram_cfg;
wire ram_512 = ram_cfg == 2'b01;
wire ram_768 = ram_cfg == 2'b10;
wire ram_4k  = &ram_cfg;

wire [23:0] cpu_addr_mask =
	ram_128? 			  24'h03FFFF:		// Wrap address space at 256kb (128KB RAM)
	ram_512 || ram_768? 24'h0FFFFF:		// Wrap address space at 1MB (640 + 896KB RAM)
							  24'h7FFFFF;		// Wrap address space at 8MB (4MB RAM + extended I/O space)

// Address decoding
wire cpu_rd   = cpu_as &&  cpu_rw && (cpu_uds || cpu_lds);
wire cpu_wr   = cpu_as && !cpu_rw && (cpu_uds || cpu_lds);
wire cpu_io   = cpu_rd || cpu_wr;
wire ql_io    = {cpu_addr[23:14], 2'b00} == 12'h018; 							// internal IO 	$18000-$1bfff
wire cpu_bram = cpu_addr[23:17] == 5'b00001; 	       						// 128k RAM   		$20000-$3ffff
wire cpu_ram512 = ram_512 && !cpu_addr[23:20] && ^cpu_addr[19:18];		// ExtRAM 512k  	$40000-$bffff
wire cpu_ram768 = ram_768 && !cpu_addr[23:20] && |cpu_addr[19:18];		// ExtRAM 768k		$40000-$fffff
wire cpu_ram4k  = ram_4k  && !cpu_addr[23:22] && |cpu_addr[21:18];		// ExtRAM 4096k	$40000-$3fffff
wire cpu_xram = cpu_ram512 || cpu_ram768 || cpu_ram4k;						// ExtRAM 512k/768k/4096k
wire cpu_ram  = (cpu_bram || cpu_xram || gc_ram1 || gc_ram2);				// any RAM
wire cpu_rom  = cpu_addr[23:16] == 8'h00;	 										// 64k     ROM $0000-$ffff
wire cpu_ext_rom = {cpu_addr[23:14], 2'b00} == 12'h00C;						// 16k ext ROM $c000-$ffff
wire cpu_os_rom  = cpu_rom && !cpu_ext_rom;										// 48k OS  ROM $0000-$bfff

// Additional (Super)GoldCard spaces
wire gc_io  = gc_en && ({cpu_addr[23:8]} == 16'h01C0);						// GoldCard IO $1c000-$1c0ff
wire gc_ram1 = gc_en && {cpu_addr[23:15], 3'b000} == 12'h010;				// 32kb additional SuperGoldCard RAM $10000-$17fff
wire gc_ram2 = gc_en && {cpu_addr[23:14], 2'b00} == 12'h01C && !gc_io; 	// 16kb additional SuperGoldCard RAM $1c100-$1ffff
wire gc_boot_rom = gc_en && cpu_addr[23:16] == 8'h04;							// Another copy of the boot ROM $040000-$04ffff
wire gc_os_rom = gc_en && cpu_addr[23:16] == 8'h40;							// SuperGoldCard copy of QL ROM $400000-$40ffff
//wire gc_ext_io = gc_en && {cpu_addr[23:18], 2'b00} == 8'h4c;				// SuperGoldCard extended I/O $4c0000-$4fffff

wire [15:0] io_dout = 
	qimi_sel? {qimi_data, qimi_data}:
	zx8302_sel? zx8302_dout:
	16'h0000;	

// Data bus on GC boot (shadow RAM disabled)
wire [15:0] gc_dout_boot =
	cpu_rom? gc_rom_dout:								// 000000..00ffff: GC ROM
	gc_boot_rom? gc_rom_dout:							// 040000..04ffff: Another copy of GC ROM
	gc_os_rom? ql_rom_dout:								// 400000..40ffff: Original OS ROM
	cpu_ram? sdram_dout:
	16'hffff;

// Data bus if GC RAM shadow is enabled
wire [15:0] gc_dout_shadow = 
	cpu_os_rom? sdram_dout:								// 000000..007fff: Shadow-RAM
	qlsd_rd? {qlsd_dout, qlsd_dout}:  				// 00fee4        : QL-SD maps into rom area
	cpu_ext_rom? ql_rom_dout:							// 00c000..00ffff: EXT-ROM space
	gc_os_rom? ql_rom_dout:								// 400000..40ffff: Original OS ROM
	cpu_ram? sdram_dout:
	16'hffff;

// Gold Card mode data bus
wire [15:0] gc_dout = 
	rom_shadow? gc_dout_shadow: gc_dout_boot;
	
// QL mode data bus
wire [15:0] ql_dout =
	qlsd_rd? {qlsd_dout, qlsd_dout}:    			// 00fee4        : QL-SD maps into rom area
	cpu_rom? ql_rom_dout:								// 000000..00ffff: OS ROM + EXT ROM
	cpu_ram? sdram_dout:
	16'hffff;

// Bring it all together
wire [15:0] cpu_din =
	ql_io? io_dout:										// 18000..1bfff: Always mapped
	gc_en? gc_dout:											// GC-mode memory spaces
	ql_dout;													// QL-mode memory spaces


wire cpu_dtack =
	qlsd_sel? qlsd_dtack:
	rom_shadow_read || rom_shadow_write? sdram_dtack:
	cpu_ram? sdram_dtack && !ram_delay_dtack:
	!ram_delay_dtack;

// Debugging only
//reg [23:0] cpu_addr_reg  /* synthesis noprune */;
//always @(posedge clk_sys) begin
//	cpu_addr_reg <= cpu_addr;	
//end

wire [23:1] cpu_addr16;
wire [23:0] cpu_addr = {cpu_addr16, !cpu_uds && cpu_lds} & cpu_addr_mask;
wire [15:0] cpu_dout;
wire [1:0] cpu_ipl;
wire cpu_uds_n;
wire cpu_lds_n;
wire cpu_uds = !cpu_uds_n;
wire cpu_lds = !cpu_lds_n;
wire cpu_as_n;
wire cpu_as = !cpu_as_n;
wire cpu_rw;
wire [2:0] cpu_fc;
wire cpu_int_ack = &cpu_fc;

fx68k fx68k
(
	.clk				( clk_sys    	),
	.HALTn			( 1'b1			),
	.extReset		( reset			),
	.pwrUp			( reset			),
	.enPhi1			( ce_bus_p		),
	.enPhi2			( ce_bus_n		),
	
	.eRWn				( cpu_rw 		), 
	.ASn				( cpu_as_n		),
	.UDSn				( cpu_uds_n		),
	.LDSn				( cpu_lds_n		),
	
	.FC0				( cpu_fc[0]		),
	.FC1				( cpu_fc[1]		),
	.FC2				( cpu_fc[2]		),
	
	.BGn				(					),
	.DTACKn			( ~cpu_dtack	),
	.VPAn				( ~cpu_int_ack	),
	.BERRn			( 1				),
	.BRn				( 1				),
	.BGACKn			( 1				),
	.IPL0n			( cpu_ipl[0]	),
	.IPL1n			( cpu_ipl[1]	),
	.IPL2n			( cpu_ipl[0]	),		// ipl 0 and 2 are tied together on 68008
	.iEdb				( cpu_din		),
	.oEdb				( cpu_dout		),
	.eab				( cpu_addr16	)
);

endmodule
