-- BBC Micro for Altera DE1
--
-- Copyright (c) 2011 Mike Stirling
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
--   this list of conditions and the following disclaimer.
--
-- * Redistributions in synthesized form must reproduce the above copyright
--   notice, this list of conditions and the following disclaimer in the
--   documentation and/or other materials provided with the distribution.
--
-- * Neither the name of the author nor the names of other contributors may
--   be used to endorse or promote products derived from this software without
--   specific prior written agreement from the author.
--
-- * License is granted for non-commercial use only.  A fee may not be charged
--   for redistributions as source code or in synthesized/hardware form without 
--   specific prior written agreement from the author.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- BBC B Micro
--
-- Terasic DE1 top-level
--
-- (C) 2011 Mike Stirling

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

-- Generic top-level entity for Altera DE1 board
entity bbc_micro_de1 is
generic (
	-- ROM offset
	-- The 4MB Flash is used in 16KB banks as a simple mechanism for
	-- different machines to address different parts of the ROM, saving
	-- on re-flashing each time a new machine is run on the board.
	-- This generic sets the upper 8 address bits.
	-- Note that the lower bits may be ignored by the implementation,
	-- e.g. where ROMs are bigger than 16K or where multiple banks
	-- are required.  In this case it is important to ensure that the
	-- ROM images are aligned correctly (such that these ignored bits are 0).
	--
	-- For the BBC the ROMs start in bank 8 (the first 8 banks are used by
	-- the Spectrum project).  The particular bank is selected by the sideways
	-- ROM paging register, and bank 7 is used for the MOS.  Recommended layout
	-- is:
	--
	-- 0 Sideways
	-- 1 Sideways
	-- 2 Sideways - SuperMMC (DFS)
	-- 3 Sideways - BASIC
	-- 4 Not used
	-- 5 Not used
	-- 6 Not used
	-- 7 MOS
	ROM_OFFSET			:	std_logic_vector(7 downto 0) := "00001000"
	);
port (
	-- Clocks
	CLOCK_24	:	in	std_logic_vector(1 downto 0);
	CLOCK_27	:	in	std_logic_vector(1 downto 0);
	CLOCK_50	:	in	std_logic;
	EXT_CLOCK	:	in	std_logic;
	
	-- Switches
	SW			:	in	std_logic_vector(9 downto 0);
	-- Buttons
	KEY			:	in	std_logic_vector(3 downto 0);
	
	-- 7 segment displays
	HEX0		:	out	std_logic_vector(6 downto 0);
	HEX1		:	out	std_logic_vector(6 downto 0);
	HEX2		:	out	std_logic_vector(6 downto 0);
	HEX3		:	out	std_logic_vector(6 downto 0);
	-- Red LEDs
	LEDR		:	out	std_logic_vector(9 downto 0);
	-- Green LEDs
	LEDG		:	out	std_logic_vector(7 downto 0);
	
	-- VGA
	VGA_R		:	out	std_logic_vector(3 downto 0);
	VGA_G		:	out	std_logic_vector(3 downto 0);
	VGA_B		:	out	std_logic_vector(3 downto 0);
	VGA_HS		:	out	std_logic;
	VGA_VS		:	out	std_logic;
	
	-- Serial
	UART_RXD	:	in	std_logic;
	UART_TXD	:	out	std_logic;
	
	-- PS/2 Keyboard
	PS2_CLK		:	inout	std_logic;
	PS2_DAT		:	inout	std_logic;
	
	-- I2C
	I2C_SCLK	:	inout	std_logic;
	I2C_SDAT	:	inout	std_logic;
	
	-- Audio
	AUD_XCK		:	out		std_logic;
	AUD_BCLK	:	out		std_logic;
	AUD_ADCLRCK	:	out		std_logic;
	AUD_ADCDAT	:	in		std_logic;
	AUD_DACLRCK	:	out		std_logic;
	AUD_DACDAT	:	out		std_logic;
	
	-- SRAM
	SRAM_ADDR	:	out		std_logic_vector(17 downto 0);
	SRAM_DQ		:	inout	std_logic_vector(15 downto 0);
	SRAM_CE_N	:	out		std_logic;
	SRAM_OE_N	:	out		std_logic;
	SRAM_WE_N	:	out		std_logic;
	SRAM_UB_N	:	out		std_logic;
	SRAM_LB_N	:	out		std_logic;
	
	-- SDRAM
	DRAM_ADDR	:	out		std_logic_vector(11 downto 0);
	DRAM_DQ		:	inout	std_logic_vector(15 downto 0);
	DRAM_BA_0	:	in		std_logic;
	DRAM_BA_1	:	in		std_logic;
	DRAM_CAS_N	:	in		std_logic;
	DRAM_CKE	:	in		std_logic;
	DRAM_CLK	:	in		std_logic;
	DRAM_CS_N	:	in		std_logic;
	DRAM_LDQM	:	in		std_logic;
	DRAM_RAS_N	:	in		std_logic;
	DRAM_UDQM	:	in		std_logic;
	DRAM_WE_N	:	in		std_logic;
	
	-- Flash
	FL_ADDR		:	out		std_logic_vector(21 downto 0);
	FL_DQ		:	inout	std_logic_vector(7 downto 0);
	FL_RST_N	:	out		std_logic;
	FL_OE_N		:	out		std_logic;
	FL_WE_N		:	out		std_logic;
	FL_CE_N		:	out		std_logic;
	
	-- SD card (SPI mode)
	SD_nCS		:	out		std_logic;
	SD_MOSI		:	out		std_logic;
	SD_SCLK		:	out		std_logic;
	SD_MISO		:	in		std_logic;
	
	-- GPIO
	GPIO_0		:	inout	std_logic_vector(35 downto 0);
	GPIO_1		:	inout	std_logic_vector(35 downto 0)
	);
end entity;

architecture rtl of bbc_micro_de1 is

------------------------------
-- PLL (32 MHz master clock)
------------------------------

component pll32 IS
	PORT
	(
		areset		: IN STD_LOGIC  := '0';
		inclk0		: IN STD_LOGIC  := '0';
		c0			: OUT STD_LOGIC ;
		locked		: OUT STD_LOGIC 
	);
end component;

---------
-- CPU
---------

component T65 is
	port(
		Mode    : in  std_logic_vector(1 downto 0);      -- "00" => 6502, "01" => 65C02, "10" => 65C816
		Res_n   : in  std_logic;
		Enable  : in  std_logic;
		Clk     : in  std_logic;
		Rdy     : in  std_logic;
		Abort_n : in  std_logic;
		IRQ_n   : in  std_logic;
		NMI_n   : in  std_logic;
		SO_n    : in  std_logic;
		R_W_n   : out std_logic;
		Sync    : out std_logic;
		EF      : out std_logic;
		MF      : out std_logic;
		XF      : out std_logic;
		ML_n    : out std_logic;
		VP_n    : out std_logic;
		VDA     : out std_logic;
		VPA     : out std_logic;
		A       : out std_logic_vector(23 downto 0);
		DI      : in  std_logic_vector(7 downto 0);
		DO      : out std_logic_vector(7 downto 0)
	);
end component;

-----------------
-- MC6845 CRTC
-----------------

component mc6845 is
port (
	CLOCK		:	in	std_logic;
	CLKEN		:	in	std_logic;
	nRESET		:	in	std_logic;

	-- Bus interface
	ENABLE		:	in	std_logic;
	R_nW		:	in	std_logic;
	RS			:	in	std_logic;
	DI			:	in	std_logic_vector(7 downto 0);
	DO			:	out	std_logic_vector(7 downto 0);

	-- Display interface
	VSYNC		:	out	std_logic;
	HSYNC		:	out	std_logic;
	DE			:	out	std_logic;
	CURSOR		:	out	std_logic;
	LPSTB		:	in	std_logic;
	
	-- Memory interface
	MA			:	out	std_logic_vector(13 downto 0);
	RA			:	out	std_logic_vector(4 downto 0)
	);
end component;

-------------------------
-- "VIDPROC" Video ULA
-------------------------

component vidproc is
port (
	CLOCK		:	in	std_logic;
	-- Clock enable qualifies display cycles (interleaved with CPU cycles)
	CLKEN		:	in	std_logic;
	nRESET		:	in	std_logic;
	
	-- Clock enable output to CRTC
	CLKEN_CRTC	:	out	std_logic;
	
	-- Bus interface
	ENABLE		:	in	std_logic;
	A0			:	in	std_logic;
	-- CPU data bus (for register writes)
	DI_CPU		:	in	std_logic_vector(7 downto 0);
	-- Display RAM data bus (for display data fetch)
	DI_RAM		:	in	std_logic_vector(7 downto 0);
	
	-- Control interface
	nINVERT		:	in	std_logic;
	DISEN		:	in	std_logic;
	CURSOR		:	in	std_logic;
	
	-- Video in (teletext mode)
	R_IN		:	in	std_logic;
	G_IN		:	in	std_logic;
	B_IN		:	in	std_logic;
	
	-- Video out
	R			:	out	std_logic;
	G			:	out	std_logic;
	B			:	out	std_logic
	);
end component;

--------------------------------
-- SAA5050 Teletext Generator
--------------------------------

component saa5050 is
port (
	CLOCK		:	in	std_logic;	
	-- 6 MHz dot clock enable
	CLKEN		:	in	std_logic;
	-- Async reset
	nRESET		:	in	std_logic;
	
	-- Character data input (in the bus clock domain)
	DI_CLOCK	:	in	std_logic;
	DI_CLKEN	:	in	std_logic;
	DI			:	in	std_logic_vector(6 downto 0);
	
	-- Timing inputs
	-- General line reset (not used)
	GLR			:	in	std_logic; -- /HSYNC
	-- Data entry window - high during VSYNC.
	-- Resets ROM row counter and drives 'flash' signal
	DEW			:	in	std_logic; -- VSYNC
	-- Character rounding select - high during even field
	CRS			:	in	std_logic; -- FIELD
	-- Load output shift register enable - high during active video
	LOSE		:	in	std_logic; -- DE
	
	-- Video out
	R			:	out	std_logic;
	G			:	out	std_logic;
	B			:	out	std_logic;
	Y			:	out	std_logic
	);
end component;

--------------
-- 6522 VIA
--------------

component M6522 is
  port (

    I_RS              : in    std_logic_vector(3 downto 0);
    I_DATA            : in    std_logic_vector(7 downto 0);
    O_DATA            : out   std_logic_vector(7 downto 0);
    O_DATA_OE_L       : out   std_logic;

    I_RW_L            : in    std_logic;
    I_CS1             : in    std_logic;
    I_CS2_L           : in    std_logic;

    O_IRQ_L           : out   std_logic; -- note, not open drain
    -- port a
    I_CA1             : in    std_logic;
    I_CA2             : in    std_logic;
    O_CA2             : out   std_logic;
    O_CA2_OE_L        : out   std_logic;

    I_PA              : in    std_logic_vector(7 downto 0);
    O_PA              : out   std_logic_vector(7 downto 0);
    O_PA_OE_L         : out   std_logic_vector(7 downto 0);

    -- port b
    I_CB1             : in    std_logic;
    O_CB1             : out   std_logic;
    O_CB1_OE_L        : out   std_logic;

    I_CB2             : in    std_logic;
    O_CB2             : out   std_logic;
    O_CB2_OE_L        : out   std_logic;

    I_PB              : in    std_logic_vector(7 downto 0);
    O_PB              : out   std_logic_vector(7 downto 0);
    O_PB_OE_L         : out   std_logic_vector(7 downto 0);

	-- FIXME: Revisit timing in this component.  Does it really need a 4x clock?
    I_P2_H            : in    std_logic; -- high for phase 2 clock  ____----__
    RESET_L           : in    std_logic;
    ENA_4             : in    std_logic; -- clk enable (4x system clock rate)
    CLK               : in    std_logic
    );
end component;

-----------------------------
-- PS/2 Keyboard Emulation
-----------------------------

component keyboard is
port (
	CLOCK		:	in	std_logic;
	nRESET		:	in	std_logic;
	CLKEN_1MHZ	:	in	std_logic;
	
	-- PS/2 interface
	PS2_CLK		:	in	std_logic;
	PS2_DATA	:	in	std_logic;
	
	-- If 1 then column is incremented automatically at
	-- 1 MHz rate
	AUTOSCAN	:	in	std_logic;
	
	COLUMN		:	in	std_logic_vector(3 downto 0);
	ROW			:	in	std_logic_vector(2 downto 0);
	
	-- 1 when currently selected key is down (AUTOSCAN disabled)
	KEYPRESS	:	out	std_logic;
	-- 1 when any key is down (except row 0)
	INT			:	out	std_logic;
	-- BREAK key output - 1 when pressed
	BREAK_OUT	:	out	std_logic;
	
	-- DIP switch inputs
	DIP_SWITCH	:	in	std_logic_vector(7 downto 0)
	);
end component;

-----------------------------
-- SN76489 sound generator
-----------------------------

component sn76489_top is

  generic (
    clock_div_16_g : integer := 1
  );
  port (
    clock_i    : in  std_logic;
    clock_en_i : in  std_logic;
    res_n_i    : in  std_logic;
    ce_n_i     : in  std_logic;
    we_n_i     : in  std_logic;
    ready_o    : out std_logic;
    d_i        : in  std_logic_vector(0 to 7);
    aout_o     : out signed(0 to 7)
  );

end component;

component i2s_intf is
generic(
	mclk_rate : positive := 12000000;
	sample_rate : positive := 8000;
	preamble : positive := 1; -- I2S
	word_length : positive := 16
	);

port (
	-- 2x MCLK in (e.g. 24 MHz for WM8731 USB mode)
	CLK			:	in	std_logic;
	nRESET		:	in	std_logic;
	
	-- Parallel IO
	PCM_INL		:	out	std_logic_vector(word_length - 1 downto 0);
	PCM_INR		:	out	std_logic_vector(word_length - 1 downto 0);
	PCM_OUTL	:	in	std_logic_vector(word_length - 1 downto 0);
	PCM_OUTR	:	in	std_logic_vector(word_length - 1 downto 0);
	
	-- Codec interface (right justified mode)
	-- MCLK is generated at half of the CLK input
	I2S_MCLK	:	out	std_logic;
	-- LRCLK is equal to the sample rate and is synchronous to
	-- MCLK.  It must be related to MCLK by the oversampling ratio
	-- given in the codec datasheet.
	I2S_LRCLK	:	out	std_logic;
	
	-- Data is shifted out on the falling edge of BCLK, sampled
	-- on the rising edge.  The bit rate is determined such that
	-- it is fast enough to fit preamble + word_length bits into
	-- each LRCLK half cycle.  The last cycle of each word may be 
	-- stretched to fit to LRCLK.  This is OK at least for the 
	-- WM8731 codec.
	-- The first falling edge of each timeslot is always synchronised
	-- with the LRCLK edge.
	I2S_BCLK	:	out	std_logic;
	-- Output bitstream
	I2S_DOUT	:	out	std_logic;
	-- Input bitstream
	I2S_DIN		:	in	std_logic
	);
end component;

component i2c_loader is
generic (
	-- Address of slave to be loaded
	device_address : integer := 16#1a#;
	-- Number of retries to allow before stopping
	num_retries : integer := 0;
	-- Length of clock divider in bits.  Resulting bus frequency is
	-- CLK/2^(log2_divider + 2)
	log2_divider : integer := 6
);

port (
	CLK			:	in	std_logic;
	nRESET		:	in	std_logic;
	
	I2C_SCL		:	inout	std_logic;
	I2C_SDA		:	inout	std_logic;
	
	IS_DONE		:	out std_logic;
	IS_ERROR	:	out	std_logic
	);
end component;

component debugger is
generic (
	-- Set this for a reasonable half flash duration relative to the
	-- clock frequency
	flash_divider	:	natural := 24
	);
port (
	CLOCK		:	in	std_logic;
	nRESET		:	in	std_logic;
	-- CPU clock enable in
	CLKEN_IN	:	in	std_logic;
	-- Gated clock enable back out to CPU
	CLKEN_OUT	:	out	std_logic;
	-- CPU IRQ in
	nIRQ_IN		:	in	std_logic;
	-- Gated IRQ back out to CPU (no interrupts when single stepping)
	nIRQ_OUT	:	out	std_logic;
	
	-- CPU
	A_CPU		:	in	std_logic_vector(15 downto 0);
	R_nW		:	in	std_logic;
	SYNC		:	in	std_logic;
	
	-- Aux bus input for display in hex
	AUX_BUS		:	in	std_logic_vector(15 downto 0);	
	
	-- Controls
	-- RUN or HALT CPU
	RUN			:	in	std_logic;
	-- Push button to single-step in HALT mode
	nSTEP		:	in	std_logic;
	-- Push button to cycle display mode
	nMODE		:	in	std_logic;
	-- Push button to cycle display digit in edit mode
	nDIGIT		:	in	std_logic;
	-- Push button to cycle digit value in edit mode
	nSET		:	in	std_logic;
	
	-- Output to display
	DIGIT3		:	out	std_logic_vector(6 downto 0);
	DIGIT2		:	out	std_logic_vector(6 downto 0);
	DIGIT1		:	out	std_logic_vector(6 downto 0);
	DIGIT0		:	out	std_logic_vector(6 downto 0);
	
	LED_BREAKPOINT	:	out	std_logic;
	LED_WATCHPOINT	:	out	std_logic
	);
end component;

-------------
-- Signals
-------------

-- Master clock - 32 MHz
signal pll_reset	:	std_logic;
signal pll_locked	:	std_logic;
signal clock		:	std_logic;
signal hard_reset_n	:	std_logic;
signal reset_n		:	std_logic;

-- Clock enable counter
-- CPU and video cycles are interleaved.  The CPU runs at 2 MHz (every 16th
-- cycle) and the video subsystem is enabled on every odd cycle.
signal clken_counter	:	unsigned(4 downto 0);
signal cpu_cycle		:	std_logic; -- Qualifies all 2 MHz cycles
signal cpu_cycle_mask	:	std_logic; -- Set to mask CPU cycles until 1 MHz cycle is complete
signal cpu_clken		:	std_logic; -- 2 MHz cycles in which the CPU is enabled
signal cpu_debug_clken	:	std_logic; -- CPU clken return from hardware debugger
-- IO cycles are out of phase with the CPU
signal vid_clken		:	std_logic; -- 16 MHz video cycles
signal mhz4_clken		:	std_logic; -- Used by 6522
signal mhz2_clken		:	std_logic; -- Used for latching CPU address for clock stretch
signal mhz1_clken		:	std_logic; -- 1 MHz bus and associated peripherals, 6522 phase 2
-- SAA5050 needs a 6 MHz clock enable relative to a 24 MHz clock
signal ttxt_clken_counter	:	unsigned(1 downto 0);
signal ttxt_clken		:	std_logic;

-- Debugger connections
signal debug_irq_in_n	:	std_logic;
signal debug_aux		:	std_logic_vector(15 downto 0);

-- CPU signals
signal cpu_mode			:	std_logic_vector(1 downto 0);
signal cpu_ready		:	std_logic;
signal cpu_abort_n		:	std_logic;
signal cpu_irq_n		:	std_logic;
signal cpu_nmi_n		:	std_logic;
signal cpu_so_n			:	std_logic;
signal cpu_r_nw			:	std_logic;
signal cpu_sync			:	std_logic;
signal cpu_ef			:	std_logic;
signal cpu_mf			:	std_logic;
signal cpu_xf			:	std_logic;
signal cpu_ml_n			:	std_logic;
signal cpu_vp_n			:	std_logic;
signal cpu_vda			:	std_logic;
signal cpu_vpa			:	std_logic;
signal cpu_a			:	std_logic_vector(23 downto 0);
signal cpu_di			:	std_logic_vector(7 downto 0);
signal cpu_do			:	std_logic_vector(7 downto 0);

-- CRTC signals
signal crtc_clken		:	std_logic;
signal crtc_do			:	std_logic_vector(7 downto 0);
signal crtc_vsync		:	std_logic;
signal crtc_hsync		:	std_logic;
signal crtc_de			:	std_logic;
signal crtc_cursor		:	std_logic;
signal crtc_lpstb		:	std_logic := '0';
signal crtc_ma			:	std_logic_vector(13 downto 0);
signal crtc_ra			:	std_logic_vector(4 downto 0);

-- Decoded display address after address translation for hardware
-- scrolling
signal display_a		:	std_logic_vector(14 downto 0);

-- "VIDPROC" signals
signal vidproc_invert_n	:	std_logic;
signal vidproc_disen	:	std_logic;
signal r_in				:	std_logic;
signal g_in				:	std_logic;
signal b_in				:	std_logic;
signal r_out			:	std_logic;
signal g_out			:	std_logic;
signal b_out			:	std_logic;

-- SAA5050 signals
signal ttxt_glr			:	std_logic;
signal ttxt_dew			:	std_logic;
signal ttxt_crs			:	std_logic;
signal ttxt_lose		:	std_logic;
signal ttxt_r			:	std_logic;
signal ttxt_g			:	std_logic;
signal ttxt_b			:	std_logic;
signal ttxt_y			:	std_logic;

-- System VIA signals
signal sys_via_do		:	std_logic_vector(7 downto 0);
signal sys_via_do_oe_n	:	std_logic;
signal sys_via_irq_n	:	std_logic;
signal sys_via_ca1_in	:	std_logic := '0';
signal sys_via_ca2_in	:	std_logic := '0';
signal sys_via_ca2_out	:	std_logic;
signal sys_via_ca2_oe_n	:	std_logic;
signal sys_via_pa_in	:	std_logic_vector(7 downto 0);
signal sys_via_pa_out	:	std_logic_vector(7 downto 0);
signal sys_via_pa_oe_n	:	std_logic_vector(7 downto 0);
signal sys_via_cb1_in	:	std_logic := '0';
signal sys_via_cb1_out	:	std_logic;
signal sys_via_cb1_oe_n	:	std_logic;
signal sys_via_cb2_in	:	std_logic := '0';
signal sys_via_cb2_out	:	std_logic;
signal sys_via_cb2_oe_n	:	std_logic;
signal sys_via_pb_in	:	std_logic_vector(7 downto 0);
signal sys_via_pb_out	:	std_logic_vector(7 downto 0);
signal sys_via_pb_oe_n	:	std_logic_vector(7 downto 0);

-- User VIA signals
signal user_via_do		:	std_logic_vector(7 downto 0);
signal user_via_do_oe_n	:	std_logic;
signal user_via_irq_n	:	std_logic;
signal user_via_ca1_in	:	std_logic := '0';
signal user_via_ca2_in	:	std_logic := '0';
signal user_via_ca2_out	:	std_logic;
signal user_via_ca2_oe_n	:	std_logic;
signal user_via_pa_in	:	std_logic_vector(7 downto 0);
signal user_via_pa_out	:	std_logic_vector(7 downto 0);
signal user_via_pa_oe_n	:	std_logic_vector(7 downto 0);
signal user_via_cb1_in	:	std_logic := '0';
signal user_via_cb1_out	:	std_logic;
signal user_via_cb1_oe_n	:	std_logic;
signal user_via_cb2_in	:	std_logic := '0';
signal user_via_cb2_out	:	std_logic;
signal user_via_cb2_oe_n	:	std_logic;
signal user_via_pb_in	:	std_logic_vector(7 downto 0);
signal user_via_pb_out	:	std_logic_vector(7 downto 0);
signal user_via_pb_oe_n	:	std_logic_vector(7 downto 0);

-- IC32 latch on System VIA
signal ic32				:	std_logic_vector(7 downto 0);
signal sound_enable_n	:	std_logic;
signal speech_read_n	:	std_logic;
signal speech_write_n	:	std_logic;
signal keyb_enable_n	:	std_logic;
signal disp_addr_offs	:	std_logic_vector(1 downto 0);
signal caps_lock_led_n	:	std_logic;
signal shift_lock_led_n	:	std_logic;

-- Keyboard
signal keyb_column		:	std_logic_vector(3 downto 0);
signal keyb_row			:	std_logic_vector(2 downto 0);
signal keyb_out			:	std_logic;
signal keyb_int			:	std_logic;
signal keyb_break		:	std_logic;

-- Sound generator
signal sound_ready		:	std_logic;
signal sound_di			:	std_logic_vector(7 downto 0);
signal sound_ao			:	signed(7 downto 0);
signal pcm_inl			:	std_logic_vector(15 downto 0);
signal pcm_inr			:	std_logic_vector(15 downto 0);

-- Memory enables
signal ram_enable		:	std_logic;		-- 0x0000
signal rom_enable		:	std_logic;		-- 0x8000 (BASIC/sideways ROMs)
signal mos_enable		:	std_logic;		-- 0xC000
-- IO region enables
signal io_fred			:	std_logic;		-- 0xFC00 (1 MHz bus)
signal io_jim			:	std_logic;		-- 0xFD00 (1 MHz bus)
signal io_sheila		:	std_logic;		-- 0xFE00 (System peripherals)
-- SHIELA
signal crtc_enable		:	std_logic;		-- 0xFE00-FE07
signal acia_enable		:	std_logic;		-- 0xFE08-FE0F
signal serproc_enable	:	std_logic;		-- 0xFE10-FE1F
signal vidproc_enable	:	std_logic;		-- 0xFE20-FE2F
signal romsel_enable	:	std_logic;		-- 0xFE30-FE3F
signal sys_via_enable	:	std_logic;		-- 0xFE40-FE5F
signal user_via_enable	:	std_logic;		-- 0xFE60-FE7F
signal fddc_enable		:	std_logic;		-- 0xFE80-FE9F
signal adlc_enable		:	std_logic;		-- 0xFEA0-FEBF (Econet)
signal adc_enable		:	std_logic;		-- 0xFEC0-FEDF
signal tube_enable		:	std_logic;		-- 0xFEE0-FEFF

-- ROM select latch
signal romsel			:	std_logic_vector(3 downto 0);

signal mhz1_enable		:	std_logic;		-- Set for access to any 1 MHz peripheral

begin
	-------------------------
	-- COMPONENT INSTANCES
	-------------------------

	-- 32 MHz master clock
	pll:	pll32 port map (
		pll_reset,
		CLOCK_24(0),
		clock,
		pll_locked );
		
	-- Hardware debugger block (single-step, breakpoints)
	debug:	debugger port map (
		clock,
		hard_reset_n,
		cpu_clken,
		cpu_debug_clken,
		debug_irq_in_n,
		cpu_irq_n,
		cpu_a(15 downto 0), cpu_r_nw, cpu_sync,
		debug_aux,
		SW(8), -- RUN
		KEY(3), -- STEP
		KEY(2), -- MODE
		KEY(1), -- DIGIT
		KEY(0), -- SET
		HEX3, HEX2, HEX1, HEX0,
		LEDR(3), -- BREAKPOINT
		LEDR(2) -- WATCHPOINT
		);

	-- 6502 CPU
	cpu : T65 port map (
		cpu_mode,
		reset_n,
		cpu_debug_clken,
		clock,
		cpu_ready,
		cpu_abort_n,
		cpu_irq_n,
		cpu_nmi_n,
		cpu_so_n,
		cpu_r_nw,
		cpu_sync,
		cpu_ef,
		cpu_mf,
		cpu_xf,
		cpu_ml_n,
		cpu_vp_n,
		cpu_vda,
		cpu_vpa,
		cpu_a,
		cpu_di,
		cpu_do );
		
	crtc : mc6845 port map (
		clock,
		crtc_clken,
		reset_n,
		crtc_enable,
		cpu_r_nw,
		cpu_a(0),
		cpu_do,
		crtc_do,
		crtc_vsync,
		crtc_hsync,
		crtc_de,
		crtc_cursor,
		crtc_lpstb,
		crtc_ma,
		crtc_ra );
		
	video_ula : vidproc port map (
		clock,
		vid_clken,
		reset_n,
		crtc_clken,
		vidproc_enable,
		cpu_a(0),
		cpu_do,
		SRAM_DQ(7 downto 0),
		vidproc_invert_n,
		vidproc_disen,
		crtc_cursor,
		r_in, g_in, b_in,
		r_out, g_out, b_out
		);
		
	teletext : saa5050 port map (
		CLOCK_24(0), -- This runs at 6 MHz, which we can't derive from the 32 MHz clock
		ttxt_clken,
		reset_n,
		clock, -- Data input is synchronised from the bus clock domain
		vid_clken,
		SRAM_DQ(6 downto 0),
		ttxt_glr,
		ttxt_dew,
		ttxt_crs,
		ttxt_lose,
		ttxt_r, ttxt_g, ttxt_b, ttxt_y
		);
		
	-- System VIA
	system_via : m6522 port map (
		cpu_a(3 downto 0),
		cpu_do,
		sys_via_do,
		sys_via_do_oe_n,
		cpu_r_nw,
		sys_via_enable,
		'0', -- nCS2
		sys_via_irq_n,
		sys_via_ca1_in,
		sys_via_ca2_in,
		sys_via_ca2_out,
		sys_via_ca2_oe_n,
		sys_via_pa_in,
		sys_via_pa_out,
		sys_via_pa_oe_n,
		sys_via_cb1_in,
		sys_via_cb1_out,
		sys_via_cb1_oe_n,
		sys_via_cb2_in,
		sys_via_cb2_out,
		sys_via_cb2_oe_n,
		sys_via_pb_in,
		sys_via_pb_out,
		sys_via_pb_oe_n,
		mhz1_clken,
		hard_reset_n, -- System VIA is reset by power on reset only
		mhz4_clken,
		clock
		);
	
	-- User VIA
	user_via : m6522 port map (
		cpu_a(3 downto 0),
		cpu_do,
		user_via_do,
		user_via_do_oe_n,
		cpu_r_nw,
		user_via_enable,
		'0', -- nCS2
		user_via_irq_n,
		user_via_ca1_in,
		user_via_ca2_in,
		user_via_ca2_out,
		user_via_ca2_oe_n,
		user_via_pa_in,
		user_via_pa_out,
		user_via_pa_oe_n,
		user_via_cb1_in,
		user_via_cb1_out,
		user_via_cb1_oe_n,
		user_via_cb2_in,
		user_via_cb2_out,
		user_via_cb2_oe_n,
		user_via_pb_in,
		user_via_pb_out,
		user_via_pb_oe_n,
		mhz1_clken,
		reset_n,
		mhz4_clken,
		clock
		);
		
	-- Keyboard
	keyb : keyboard port map (
		clock, hard_reset_n, mhz1_clken,
		PS2_CLK, PS2_DAT,
		keyb_enable_n,
		keyb_column,
		keyb_row,
		keyb_out,
		keyb_int,
		keyb_break,
		SW(7 downto 0)
		);
		
	-- Sound generator (and drive logic for I2S codec)
	sound : sn76489_top port map (
		clock, mhz4_clken,
		reset_n, '0', sound_enable_n,
		sound_ready, sound_di,
		sound_ao
		);
	i2s : i2s_intf port map (
		CLOCK_24(0), reset_n,
		pcm_inl, pcm_inr,
		std_logic_vector(sound_ao) & "00000000",
		std_logic_vector(sound_ao) & "00000000",
		AUD_XCK, AUD_DACLRCK,
		AUD_BCLK, AUD_DACDAT, AUD_ADCDAT
		);
	i2c : i2c_loader 
		generic map (
			log2_divider => 7
		)
		port map (
			clock, reset_n,
			I2C_SCLK, I2C_SDAT,
			LEDR(5), -- IS_DONE
			LEDR(4) -- IS_ERROR
		);
	
	-- Asynchronous reset
	-- PLL is reset by external reset switch
	pll_reset <= not SW(9);
	-- Keyboard and System VIA are reset by external reset switch or PLL being out of lock
	hard_reset_n <= not (pll_reset or not pll_locked);
	-- Rest of system is reset by all of the above plus the keyboard BREAK key
	reset_n <= hard_reset_n and not keyb_break;
		
	-- Clock enable generation - 32 MHz clock split into 32 cycles
	-- CPU is on 0 and 16 (but can be masked by 1 MHz bus accesses)
	-- Video is on all odd cycles (16 MHz)
	-- 1 MHz cycles are on cycle 31 (1 MHz)	
	vid_clken <= clken_counter(0); -- 1,3,5...
	mhz4_clken <= clken_counter(0) and clken_counter(1) and clken_counter(2); -- 7/15/23/31
	mhz2_clken <= mhz4_clken and clken_counter(3); -- 15/31
	mhz1_clken <= mhz2_clken and clken_counter(4); -- 31
	cpu_cycle <= not (clken_counter(0) or clken_counter(1) or clken_counter(2) or clken_counter(3)); -- 0/16
	cpu_clken <= cpu_cycle and not cpu_cycle_mask;
	
	clk_gen: process(clock,reset_n)
	begin
		if reset_n = '0' then
			clken_counter <= (others => '0');
		elsif rising_edge(clock) then
			clken_counter <= clken_counter + 1;
		end if;
	end process;
	
	cycle_stretch: process(clock,reset_n)
	begin
		if reset_n = '0' then
			cpu_cycle_mask <= '0';
		elsif rising_edge(clock) and mhz2_clken = '1' then
			if mhz1_enable = '1' and cpu_cycle_mask = '0' then
				-- Block CPU cycles until 1 MHz cycle has completed
				cpu_cycle_mask <= '1';
			end if;
			if mhz1_clken = '1' then
				-- CPU can run again
				-- FIXME: This may not be correct in terms of CPU cycles, but it
				-- should work
				cpu_cycle_mask <= '0';
			end if;
		end if;
	end process;
	
	ttxt_clk_gen: process(CLOCK_24(0),reset_n)
	begin
		if reset_n = '0' then
			ttxt_clken_counter <= (others => '0');
		elsif rising_edge(CLOCK_24(0)) then
			ttxt_clken_counter <= ttxt_clken_counter + 1;
		end if;
	end process;
	
	-- 6 MHz clock enable for SAA5050
	ttxt_clken <= '1' when ttxt_clken_counter = 0 else '0';

	-- CPU configuration and fixed signals
	cpu_mode <= "00"; -- 6502
	cpu_ready <= '1';
	cpu_abort_n <= '1';
	cpu_nmi_n <= '1';
	cpu_so_n <= '1';
	
	-- Address decoding
	-- 0x0000 = 32 KB SRAM
	-- 0x8000 = 16 KB BASIC/Sideways ROMs
	-- 0xC000 = 16 KB MOS ROM
	--
	-- IO regions are mapped into a hole in the MOS.  There are three regions:
	-- 0xFC00 = FRED
	-- 0xFD00 = JIM
	-- 0xFE00 = SHEILA
	ram_enable <= not cpu_a(15);
	rom_enable <= cpu_a(15) and not cpu_a(14);
	mos_enable <= cpu_a(15) and cpu_a(14) and not (io_fred or io_jim or io_sheila);
	io_fred <= '1' when cpu_a(15 downto 8) = "11111100" else '0';
	io_jim <= '1' when cpu_a(15 downto 8) = "11111101" else '0';
	io_sheila <= '1' when cpu_a(15 downto 8) = "11111110" else '0';
	-- The following IO regions are accessed at 1 MHz and hence will stall the
	-- CPU accordingly
	mhz1_enable <= io_fred or io_jim or
		adc_enable or sys_via_enable or user_via_enable or
		serproc_enable or acia_enable or crtc_enable;
	
	-- SHEILA address demux
	-- All the system peripherals are mapped into this page as follows:
	-- 0xFE00 - 0xFE07 = MC6845 CRTC
	-- 0xFE08 - 0xFE0F = MC6850 ACIA (Serial/Tape)
	-- 0xFE10 - 0xFE1F = Serial ULA
	-- 0xFE20 - 0xFE2F = Video ULA
	-- 0xFE30 - 0xFE3F = Paged ROM select latch
	-- 0xFE40 - 0xFE5F = System VIA (6522)
	-- 0xFE60 - 0xFE7F = User VIA (6522)
	-- 0xFE80 - 0xFE9F = 8271 Floppy disc controller
	-- 0xFEA0 - 0xFEBF = 68B54 ADLC for Econet
	-- 0xFEC0 - 0xFEDF = uPD7002 ADC
	-- 0xFEE0 - 0xFEFF = Tube ULA
	process(cpu_a,io_sheila)
	begin
		-- All regions normally de-selected
		crtc_enable <= '0';
		acia_enable <= '0';
		serproc_enable <= '0';
		vidproc_enable <= '0';
		romsel_enable <= '0';
		sys_via_enable <= '0';
		user_via_enable <= '0';
		fddc_enable <= '0';
		adlc_enable <= '0';
		adc_enable <= '0';
		tube_enable <= '0';
		
		if io_sheila = '1' then
			case cpu_a(7 downto 5) is
				when "000" =>
					-- 0xFE00
					if cpu_a(4) = '0' then
						if cpu_a(3) = '0' then
							-- 0xFE00
							crtc_enable <= '1';
						else
							-- 0xFE08
							acia_enable <= '1';
						end if;
					else
						-- 0xFE10
						serproc_enable <= '1';
					end if;
				when "001" =>
					-- 0xFE20
					if cpu_a(4) = '0' then
						-- 0xFE20
						vidproc_enable <= '1';
					else
						-- 0xFE30
						romsel_enable <= '1';
					end if;
				when "010" => sys_via_enable <= '1';	-- 0xFE40
				when "011" => user_via_enable <= '1';	-- 0xFE60
				when "100" => fddc_enable <= '1';		-- 0xFE80
				when "101" => adlc_enable <= '1';		-- 0xFEA0
				when "110" => adc_enable <= '1';		-- 0xFEC0
				when "111" => tube_enable <= '1';		-- 0xFEE0
				when others =>
					null;
			end case;
		end if;
	end process;
	
	-- CPU data bus mux and interrupts
	cpu_di <=
		SRAM_DQ(7 downto 0) when ram_enable = '1' else
		FL_DQ		when rom_enable = '1' else
		FL_DQ		when mos_enable = '1' else
		crtc_do		when crtc_enable = '1' else
		"00000010"	when acia_enable = '1' else
		sys_via_do	when sys_via_enable = '1' else
		user_via_do	when user_via_enable = '1' else
		(others => '0'); -- un-decoded locations are pulled down by RP1
	debug_irq_in_n <= sys_via_irq_n and user_via_irq_n; -- route IRQ through debugger
	--cpu_irq_n <= sys_via_irq_n and user_via_irq_n;
	
	-- ROMs are in external flash and split into 16K slots (since this also suits other
	-- computers that might be run on the same board).
	-- The first 8 slots are allocated for use here, and the first 4 are decoded as
	-- the sideways ROMs.  Slot 7 is used for the MOS.
	FL_RST_N <= reset_n;
	FL_CE_N <= '0';
	FL_OE_N <= '0';
	FL_WE_N <= '1';
	FL_ADDR(21 downto 17) <= ROM_OFFSET(7 downto 3);
	FL_ADDR(16 downto 14) <=
		"111" when mos_enable = '1' else
		"0" & romsel(1 downto 0);
	FL_ADDR(13 downto 0) <= cpu_a(13 downto 0);		
		
	-- SRAM bus
	SRAM_UB_N <= '1';
	SRAM_LB_N <= '0';
	SRAM_CE_N <= '0';
	SRAM_OE_N <= '0';
	SRAM_DQ(15 downto 8) <= (others => '0');
	
	-- Synchronous outputs to SRAM
	process(clock,reset_n)
	variable ram_write : std_logic;
	begin		
		ram_write := ram_enable and not cpu_r_nw;
	
		if reset_n = '0' then
			SRAM_WE_N <= '1';
			SRAM_DQ(7 downto 0) <= (others => 'Z');
		elsif rising_edge(clock) then
			-- Default to inputs
			SRAM_DQ(7 downto 0) <= (others => 'Z');
			
			-- Register SRAM signals to outputs (clock must be at least 2x CPU clock)
			if vid_clken = '1' then
				-- Fetch data from previous CPU cycle
				SRAM_WE_N <= not ram_write;
				SRAM_ADDR <= "00" & cpu_a(15 downto 0);
				if ram_write = '1' then
					SRAM_DQ(7 downto 0) <= cpu_do;
				end if;
			else
				-- Fetch data from previous display cycle
				SRAM_WE_N <= '1';
				SRAM_ADDR <= "000" & display_a;
			end if;
		end if;
	end process;
	
	-- Address translation logic for calculation of display address
	process(crtc_ma,crtc_ra,disp_addr_offs)
	variable aa : unsigned(3 downto 0);
	begin
		if crtc_ma(12) = '0' then
			-- No adjustment
			aa := unsigned(crtc_ma(11 downto 8));
		else
			-- Address adjusted according to screen mode to compensate for
			-- wrap at 0x8000.
			case disp_addr_offs is
			when "00" =>
				-- Mode 3 - restart at 0x4000
				aa := unsigned(crtc_ma(11 downto 8)) + 8;
			when "01" =>
				-- Mode 6 - restart at 0x6000
				aa := unsigned(crtc_ma(11 downto 8)) + 12;
			when "10" =>
				-- Mode 0,1,2 - restart at 0x3000
				aa := unsigned(crtc_ma(11 downto 8)) + 6;
			when "11" =>
				-- Mode 4,5 - restart at 0x5800
				aa := unsigned(crtc_ma(11 downto 8)) + 11;
			when others =>
				null;
			end case;
		end if;
		
		if crtc_ma(13) = '0' then
			-- HI RES
			display_a <= std_logic_vector(aa(3 downto 0)) & crtc_ma(7 downto 0) & crtc_ra(2 downto 0);
		else
			-- TTX VDU
			display_a <= std_logic(aa(3)) & "1111" & crtc_ma(9 downto 0);
		end if;
	end process;
	
	-- VIDPROC
	vidproc_invert_n <= '1';
	vidproc_disen <= crtc_de and not crtc_ra(3); -- DISEN is masked off by RA(3) for MODEs 3 and 6
	r_in <= ttxt_r;
	g_in <= ttxt_g;
	b_in <= ttxt_b;
	
	-- SAA5050
	ttxt_glr <= not crtc_hsync;
	ttxt_dew <= crtc_vsync;
	ttxt_crs <= not crtc_ra(0);
	ttxt_lose <= crtc_de;
	
	-- CRTC drives video out (CSYNC on HSYNC output, VSYNC high)
	VGA_HS <= not (crtc_hsync xor crtc_vsync);
	VGA_VS <= '1';
	VGA_R <= r_out & r_out & r_out & r_out;
	VGA_G <= g_out & g_out & g_out & g_out;
	VGA_B <= b_out & b_out & b_out & b_out;
	
	-- Connections to System VIA
	-- ADC
	sys_via_cb1_in <= '1'; -- /EOC
	-- CRTC
	sys_via_ca1_in <= crtc_vsync;
	sys_via_cb2_in <= crtc_lpstb;
	-- Keyboard
	sys_via_ca2_in <= keyb_int;
	sys_via_pa_in(7) <= keyb_out;
	sys_via_pa_in(6 downto 0) <= sys_via_pa_out(6 downto 0); -- Must loop back output pins or keyboard won't work
	keyb_column <= sys_via_pa_out(3 downto 0);
	keyb_row <= sys_via_pa_out(6 downto 4);
	-- Sound
	sound_di <= sys_via_pa_out;
	-- Others (idle until missing bits implemented)
	sys_via_pb_in(7 downto 4) <= (others => '1');
	
	-- Connections to User VIA (user port is output on green LEDs)
	user_via_ca1_in <= '1'; -- Pulled up
	--LEDG <= user_via_pb_out;
	
	-- MMBEEB
	user_via_cb1_in <= user_via_pb_out(1);
	SD_SCLK <= user_via_pb_out(1); -- SCLK
	SD_MOSI <= user_via_pb_out(0); -- SDO
	SD_nCS <= '0'; -- CS
	user_via_cb2_in <= SD_MISO; -- SDI
	user_via_pb_in <= user_via_pb_out;
	
	-- ROM select latch
	process(clock,reset_n)
	begin
		if reset_n = '0' then
			romsel <= (others => '0');
		elsif rising_edge(clock) then
			if romsel_enable = '1' and cpu_r_nw = '0' then
				romsel <= cpu_do(3 downto 0);
			end if;
		end if;
	end process;
	
	-- IC32 latch
	sound_enable_n <= ic32(0);
	speech_write_n <= ic32(1);
	speech_read_n <= ic32(2);
	keyb_enable_n <= ic32(3);
	disp_addr_offs <= ic32(5 downto 4);
	caps_lock_led_n <= ic32(6);
	shift_lock_led_n <= ic32(7);
	
	process(clock,reset_n)
	variable bit_num : integer;
	begin
		bit_num := to_integer(unsigned(sys_via_pb_out(2 downto 0)));
	
		if reset_n = '0' then
			ic32 <= (others => '0');
		elsif rising_edge(clock) then
			ic32(bit_num) <= sys_via_pb_out(3);
		end if;
	end process;
	
	-- Keyboard LEDs
	LEDR(0) <= not caps_lock_led_n;
	LEDR(1) <= not shift_lock_led_n;
	
	-----------------
	-- DEBUG STUFF
	-----------------
	
	GPIO_0(0) <= not (crtc_hsync xor crtc_vsync);
	GPIO_0(1) <= crtc_de;

end architecture;