Project

General

Profile

---------------------------------------------------------------------------
-- (c) 2013 mark watson
-- I am happy for anyone to use this for non-commercial use.
-- If my vhdl files are used commercially or otherwise sold,
-- please contact me for explicit permission at scrameta (gmail).
-- This applies for source and binary form and derived works.
---------------------------------------------------------------------------

LIBRARY ieee;
USE ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_MISC.all;
use ieee.numeric_std.all;

LIBRARY work;

-- There is a higher level that just wires up internal ROM/RAM/joysticks to demonstrate how to use this
-- Also see board specific top levels
ENTITY atari800xl IS
PORT
(
CLK : IN STD_LOGIC; -- cycle_length*1.79MHz
RESET_N : IN STD_LOGIC;

-- VIDEO OUT - PAL/NTSC, original Atari timings approx (may be higher res)
VIDEO_VS : OUT STD_LOGIC;
VIDEO_HS : OUT STD_LOGIC;
VIDEO_CS : OUT STD_LOGIC;
COLOUR : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
VIDEO_BLANK : out std_logic;
VIDEO_BURST : out std_logic;
VIDEO_START_OF_FIELD : out std_logic;
VIDEO_ODD_LINE : out std_logic;

-- AUDIO OUT - Pokey/GTIA 1-bit and Covox all mixed
AUDIO_L : OUT std_logic_vector(15 downto 0);
AUDIO_R : OUT std_logic_vector(15 downto 0);
SIO_AUDIO : IN std_logic_vector(7 downto 0);

-- PIA
CA1_IN : IN STD_LOGIC; -- SIO Proceed
CB1_IN : IN STD_LOGIC; -- SIO IRQ
CA2_IN : IN STD_LOGIC; -- SIO Motor control
CA2_OUT : OUT STD_LOGIC;
CA2_DIR_OUT: OUT STD_LOGIC; -- 1=output mode
CB2_IN: IN STD_LOGIC;
CB2_OUT : OUT STD_LOGIC; -- SIO Command
CB2_DIR_OUT: OUT STD_LOGIC; -- 1=output mode
PORTA_IN : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- For joystick
PORTA_OUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
PORTA_DIR_OUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
PORTB_IN : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- For bank switching on XL/XE, for joystick on 800
PORTB_OUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
PORTB_DIR_OUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);

-- Pokey keyboard matrix
-- Standard component available to connect this to PS2
KEYBOARD_RESPONSE : IN STD_LOGIC_VECTOR(1 DOWNTO 0);
KEYBOARD_SCAN : OUT STD_LOGIC_VECTOR(5 DOWNTO 0);

-- Pokey pots
POT_IN : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
POT_RESET : OUT STD_LOGIC;

-- CARTRIDGE ACCESS
-- (R/W/DO on PBI)
CART_RD4 : in STD_LOGIC;
CART_RD5 : in STD_LOGIC;
CART_S4_n : out STD_LOGIC;
CART_S5_N : out STD_LOGIC;
CART_CCTL_N : out std_logic;

-- PBI
PBI_MPD_N : in STD_LOGIC;
PBI_REF_N_IN : in STD_LOGIC;
PBI_EXTSEL_N : in STD_LOGIC;
PBI_CAS : out STD_LOGIC;
PBI_RAS : out STD_LOGIC;
PBI_CAS_INHIBIT : out STD_LOGIC;
PBI_REF_N_OUT : out STD_LOGIC;
PBI_IRQ_N : IN STD_LOGIC := '1';

-- SIO
SIO_RXD : in std_logic;
SIO_TXD : out std_logic;
SIO_CLOCKIN : in std_logic :='1';
SIO_CLOCKOUT : out std_logic;
-- SIO_COMMAND_TX - see PIA PB2
-- TODO CLOCK IN/CLOCK OUT (unused almost everywhere...)

-- GTIA consol
CONSOL_OPTION : IN STD_LOGIC;
CONSOL_SELECT : IN STD_LOGIC;
CONSOL_START : IN STD_LOGIC;
GTIA_TRIG : IN STD_LOGIC_VECTOR(3 downto 0);

-- ANTIC lightpen
ANTIC_LIGHTPEN : IN std_logic;
ANTIC_REFRESH : out STD_LOGIC -- 1 'original' cycle high when antic doing refresh cycle...
);
END atari800xl;

ARCHITECTURE bdf_type OF atari800xl IS

-- BUS
SIGNAL BUS_ADDR : STD_LOGIC_VECTOR(15 downto 0);
SIGNAL BUS_DATA : STD_LOGIC_VECTOR(7 downto 0);

signal IO_DO : std_logic_vector(7 downto 0);
signal PBI_DO : std_logic_vector(7 downto 0);
signal BASIC_DO : std_logic_vector(7 downto 0);
signal OS_DO : std_logic_vector(7 downto 0);
signal RAM_DO : std_logic_vector(7 downto 0);

-- ANTIC
SIGNAL ANTIC_ADDR : STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL ANTIC_AN : STD_LOGIC_VECTOR(2 DOWNTO 0);
SIGNAL ANTIC_COLOUR_CLOCK_OUT : STD_LOGIC;
SIGNAL ANTIC_DO : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL ANTIC_FETCH : STD_LOGIC;
SIGNAL ANTIC_HIGHRES_COLOUR_CLOCK_OUT : STD_LOGIC;
SIGNAL ANTIC_ORIGINAL_COLOUR_CLOCK_OUT : STD_LOGIC;
SIGNAL ANTIC_RDY : STD_LOGIC;
SIGNAL BREAK_PRESSED : STD_LOGIC;
signal hcount_temp : std_logic_vector(7 downto 0);
signal vcount_temp : std_logic_vector(8 downto 0);
signal ANTIC_REFRESH_CYCLE : STD_LOGIC;

-- GTIA
SIGNAL GTIA_SOUND : STD_LOGIC;
SIGNAL CONSOL_OUT : STD_LOGIC_VECTOR(3 downto 0);
SIGNAL CONSOL_IN : STD_LOGIC_VECTOR(3 downto 0);
SIGNAL GTIA_TRIG_MERGED : STD_LOGIC_VECTOR(3 downto 0);

SIGNAL GTIA_DO : STD_LOGIC_VECTOR(7 DOWNTO 0);

-- CPU
SIGNAL CPU_6502_RESET : STD_LOGIC;
SIGNAL CPU_ADDR : STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL CPU_DO : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL CPU_FETCH : STD_LOGIC;
SIGNAL IRQ_n : STD_LOGIC;
SIGNAL NMI_n : STD_LOGIC;
SIGNAL R_W_N : STD_LOGIC;

-- POKEY
SIGNAL POKEY_IRQ : STD_LOGIC;

SIGNAL POKEY_DO : STD_LOGIC_VECTOR(7 DOWNTO 0);
signal POKEY1_CHANNEL0 : std_logic_vector(3 downto 0);
signal POKEY1_CHANNEL1 : std_logic_vector(3 downto 0);
signal POKEY1_CHANNEL2 : std_logic_vector(3 downto 0);
signal POKEY1_CHANNEL3 : std_logic_vector(3 downto 0);

-- PIA
SIGNAL PIA_DO : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL PIA_IRQA : STD_LOGIC;
SIGNAL PIA_IRQB : STD_LOGIC;
SIGNAL PORTB_OUT_INT : STD_LOGIC_VECTOR(7 downto 0);
SIGNAL PORTB_OPTIONS : STD_LOGIC_VECTOR(7 downto 0);

-- cart
signal cart_trig3_out: std_logic;

-- timing
signal shift_cpu_run_next : std_logic_vector(31 downto 0);
signal shift_cpu_run_reg : std_logic_vector(31 downto 0);
signal run_system : std_logic;
signal run_cpu : std_logic;

-- mmu
signal io_select : std_logic_vector(7 downto 0);
signal CS_GTIA : std_logic;
signal CS_D1 : std_logic;
signal CS_POKEY : std_logic;
signal CS_PIA : std_logic;
signal CS_ANTIC : std_logic;
signal CS_D5 : std_logic;
signal CS_D6 : std_logic;
signal CS_D7 : std_logic;

signal ANTIC_WRITE_ENABLE : std_logic;
signal POKEY_WRITE_ENABLE : std_logic;
signal PIA_WRITE_ENABLE : std_logic;
signal PIA_READ_ENABLE : std_logic;
signal GTIA_WRITE_ENABLE : std_logic;
signal RAM_WRITE_ENABLE : std_logic;


signal CS_BASIC : std_logic;
signal CS_IO : std_logic;
signal CS_OS : std_logic;
signal CAS_INHIBIT : std_logic;
signal REF_N : std_logic;

signal PAL : std_logic;

BEGIN

PAL <= '1';

CPU_6502_RESET <= NOT(RESET_N);
cpu6502 : entity work.cpu
PORT MAP(CLK => CLK,
RESET => CPU_6502_RESET,
ENABLE => RESET_N,
IRQ_n => IRQ_n,
NMI_n => NMI_n,
MEMORY_READY => '1',
THROTTLE => run_cpu,
RDY => ANTIC_RDY,
DI => BUS_DATA(7 DOWNTO 0),
R_W_n => R_W_N,
CPU_FETCH => CPU_FETCH,
A => CPU_ADDR,
DO => CPU_DO);

antic1 : entity work.antic
GENERIC MAP(cycle_length => 32)
PORT MAP(CLK => CLK,
WR_EN => ANTIC_WRITE_ENABLE,
RESET_N => RESET_N,
MEMORY_READY_ANTIC => RUN_CPU,
MEMORY_READY_CPU => RUN_CPU,
ANTIC_ENABLE_179 => RUN_CPU,
PAL => PAL,
lightpen => ANTIC_LIGHTPEN,
ADDR => BUS_ADDR(3 DOWNTO 0),
CPU_DATA_IN => BUS_DATA(7 DOWNTO 0),
MEMORY_DATA_IN => BUS_DATA(7 DOWNTO 0),
NMI_N_OUT => NMI_n,
ANTIC_READY => ANTIC_RDY,
COLOUR_CLOCK_ORIGINAL_OUT => ANTIC_ORIGINAL_COLOUR_CLOCK_OUT,
COLOUR_CLOCK_OUT => ANTIC_COLOUR_CLOCK_OUT,
HIGHRES_COLOUR_CLOCK_OUT => ANTIC_HIGHRES_COLOUR_CLOCK_OUT,
dma_fetch_out => ANTIC_FETCH,
hcount_out => hcount_temp,
vcount_out => vcount_temp,
refresh_out => ANTIC_REFRESH_CYCLE,
AN => ANTIC_AN,
DATA_OUT => ANTIC_DO,
dma_address_out => ANTIC_ADDR);

pokey_mixer_both : entity work.pokey_mixer_mux
PORT MAP(CLK => CLK,
GTIA_SOUND => GTIA_SOUND,
SIO_AUDIO => SIO_AUDIO,
CHANNEL_L_0 => POKEY1_CHANNEL0,
CHANNEL_L_1 => POKEY1_CHANNEL1,
CHANNEL_L_2 => POKEY1_CHANNEL2,
CHANNEL_L_3 => POKEY1_CHANNEL3,
COVOX_CHANNEL_L_0 => (others=>'0'),
COVOX_CHANNEL_L_1 => (others=>'0'),
CHANNEL_R_0 => POKEY1_CHANNEL0,
CHANNEL_R_1 => POKEY1_CHANNEL1,
CHANNEL_R_2 => POKEY1_CHANNEL2,
CHANNEL_R_3 => POKEY1_CHANNEL3,
COVOX_CHANNEL_R_0 => (others=>'0'),
COVOX_CHANNEL_R_1 => (others=>'0'),
VOLUME_OUT_L => AUDIO_L,
VOLUME_OUT_R => AUDIO_R);
pia1 : entity work.pia
PORT MAP(CLK => CLK,
EN => PIA_READ_ENABLE,
WR_EN => PIA_WRITE_ENABLE,
RESET_N => RESET_N,
CA1 => CA1_IN,
CB1 => CB1_IN,
CA2_DIR_OUT => CA2_DIR_OUT,
CA2_IN => CA2_IN,
CA2_OUT => CA2_OUT,
CB2_DIR_OUT => CB2_DIR_OUT,
CB2_IN => CB2_IN,
CB2_OUT => CB2_OUT,
ADDR => BUS_ADDR(1 DOWNTO 0),
CPU_DATA_IN => BUS_DATA(7 DOWNTO 0),
IRQA_N => PIA_IRQA,
IRQB_N => PIA_IRQB,
DATA_OUT => PIA_DO,
PORTA_IN => PORTA_IN,
PORTA_DIR_OUT => PORTA_DIR_OUT,
PORTA_OUT => PORTA_OUT,
PORTB_IN => PORTB_IN,
PORTB_DIR_OUT => PORTB_DIR_OUT,
PORTB_OUT => PORTB_OUT_INT);

PORTB_OPTIONS <= PORTB_OUT_INT;
PORTB_OUT <= PORTB_OUT_INT;
GTIA_TRIG_MERGED <= cart_trig3_out & GTIA_TRIG(2 downto 0); -- NOTE, inputs ignored, careful when adding 4 joystick support

pokey1 : entity work.pokey
PORT MAP(CLK => CLK,
ENABLE_179 => run_system,
WR_EN => POKEY_WRITE_ENABLE,
RESET_N => RESET_N,
SIO_IN1 => SIO_RXD,
SIO_IN2 => '1',
SIO_IN3 => '1',
SIO_CLOCKIN => SIO_CLOCKIN,
ADDR => BUS_ADDR(3 DOWNTO 0),
DATA_IN => BUS_DATA(7 DOWNTO 0),
keyboard_response => KEYBOARD_RESPONSE,
POT_IN => POT_IN,
IRQ_N_OUT => POKEY_IRQ,
SIO_OUT1 => SIO_TXD,
SIO_OUT2 => open,
SIO_OUT3 => open,
SIO_CLOCKOUT => SIO_CLOCKOUT,
POT_RESET => POT_RESET,
CHANNEL_0_OUT => POKEY1_CHANNEL0,
CHANNEL_1_OUT => POKEY1_CHANNEL1,
CHANNEL_2_OUT => POKEY1_CHANNEL2,
CHANNEL_3_OUT => POKEY1_CHANNEL3,
DATA_OUT => POKEY_DO,
keyboard_scan => KEYBOARD_SCAN);

CONSOL_IN <= '1'&CONSOL_OPTION&CONSOL_SELECT&CONSOL_START;
gtia1 : entity work.gtia
PORT MAP(CLK => CLK,
WR_EN => GTIA_WRITE_ENABLE,
ANTIC_FETCH => ANTIC_FETCH, -- for first pmg fetch
CPU_ENABLE_ORIGINAL => run_system, -- for subsequent pmg fetches
RESET_N => RESET_N,
PAL => PAL,
COLOUR_CLOCK_ORIGINAL => ANTIC_ORIGINAL_COLOUR_CLOCK_OUT,
COLOUR_CLOCK => ANTIC_COLOUR_CLOCK_OUT,
COLOUR_CLOCK_HIGHRES => ANTIC_HIGHRES_COLOUR_CLOCK_OUT,
CONSOL_OUT => CONSOL_OUT,
CONSOL_IN => CONSOL_IN,
TRIG => GTIA_TRIG_MERGED,
ADDR => BUS_ADDR(4 DOWNTO 0),
AN => ANTIC_AN,
CPU_DATA_IN => BUS_DATA(7 DOWNTO 0),
MEMORY_DATA_IN => BUS_DATA(7 DOWNTO 0),
VSYNC => VIDEO_VS,
HSYNC => VIDEO_HS,
CSYNC => VIDEO_CS,
BLANK => VIDEO_BLANK,
BURST => VIDEO_BURST,
START_OF_FIELD => VIDEO_START_OF_FIELD,
ODD_LINE => VIDEO_ODD_LINE,
COLOUR_out => COLOUR,
DATA_OUT => GTIA_DO);

GTIA_SOUND <= CONSOL_OUT(3);

irq_glue1 : entity work.irq_glue
PORT MAP(pokey_irq => POKEY_IRQ,
pia_irqa => PIA_IRQA,
pia_irqb => PIA_IRQB,
pbi_irq => PBI_IRQ_N,
combined_irq => IRQ_n);

process(ANTIC_FETCH, ANTIC_ADDR, CPU_ADDR)
begin
BUS_ADDR <= CPU_ADDR;
if (ANTIC_FETCH = '1') then
BUS_ADDR <= ANTIC_ADDR;
end if;
end process;

PBI_REF_N_OUT <= not(ANTIC_REFRESH_CYCLE);
REF_N <= PBI_REF_N_IN and not(ANTIC_REFRESH_CYCLE);
PBI_CAS_INHIBIT <= CAS_INHIBIT;


-- TODO
-- implement in timing_6502, along with driving bus every cycle (for snoopers)
--PBI_CAS : out STD_LOGIC; -- high with RAS, low (unless extsel/inhibited) depends on read write. phi2 low-> cas low (read)=300-370, write=425-?
--PBI_RAS : out STD_LOGIC; -- high slightly after PHI2 high, low slight before phi low. phi2 low -> raw low 210-305

mmu1: entity work.mmu
PORT MAP
(
ADDR => BUS_ADDR(15 downto 11),
REF_N => REF_N,
RD4 => CART_RD4,
RD5 => CART_RD5,
MPD_N => PBI_MPD_N,
REN => PORTB_OUT_INT(0),
BE_N => PORTB_OUT_INT(1),
MAP_N => PORTB_OUT_INT(7),
S4_N => CART_S4_N,
S5_N => CART_S5_N,
BASIC => CS_BASIC,
IO => CS_IO,
OS => CS_OS,
CI => CAS_INHIBIT --Disable RAM
);

--74ls138
decode_addr1 : entity work.complete_address_decoder
generic map(width=>3)
port map (addr_in=>BUS_ADDR(10 downto 8), addr_decoded=>io_select);

CS_GTIA <= CS_IO and io_select(0);
CS_D1 <= CS_IO and io_select(1); -- PBI regs
CS_POKEY <= CS_IO and io_select(2);
CS_PIA <= CS_IO and io_select(3);
CS_ANTIC <= CS_IO and io_select(4); -- Antic decodes bus itself
CS_D5 <= CS_IO and io_select(5); -- CART CTRL
CS_D6 <= CS_IO and io_select(6); -- PBI RAM?
CS_D7 <= CS_IO and io_select(7); -- PBI RAM?

ANTIC_WRITE_ENABLE <= CS_ANTIC AND NOT(R_W_N) and run_cpu;
POKEY_WRITE_ENABLE <= CS_POKEY AND NOT(R_W_N) and run_cpu;
PIA_WRITE_ENABLE <= CS_PIA AND NOT(R_W_N) and run_cpu;
PIA_READ_ENABLE <= CS_PIA AND R_W_N and run_system;
GTIA_WRITE_ENABLE <= CS_GTIA AND NOT(R_W_N) and run_cpu;

RAM_WRITE_ENABLE <= NOT(R_W_N) and NOT(CAS_INHIBIT) and run_cpu;

process(BUS_ADDR, GTIA_DO, POKEY_DO, PIA_DO, ANTIC_DO, PBI_DO)
begin
case (BUS_ADDR(10 downto 8)) is
when "000" =>
IO_DO <= GTIA_DO;
when "010" =>
IO_DO <= POKEY_DO;
when "011" =>
IO_DO <= PIA_DO;
when "100" =>
IO_DO <= ANTIC_DO;
when others =>
IO_DO <= PBI_DO; -- D1,D5,D6,D7
end case;
end process;

-- CAS_INHIBIT -> RAM disabled. e.g. IO, ROM etc.
-- So PBI can reply if its IO and there is no chip selected - D1,D6,D7
process(R_W_N, CS_BASIC, CS_IO, CS_OS, CAS_INHIBIT, PBI_EXTSEL_N, IO_DO, RAM_DO, OS_DO, BASIC_DO, PBI_DO, CPU_DO)
variable casesig : std_logic_vector(5 downto 0);
begin
BUS_DATA <= (others=>'1');
casesig := R_W_N&CS_BASIC&CS_IO&CS_OS&CAS_INHIBIT&PBI_EXTSEL_N;
case casesig is
when
"000000"|"000001"|"000010"|"000011"|"000100"|"000101"|"000110"|"000111"|
"001000"|"001001"|"001010"|"001011"|"001100"|"001101"|"001110"|"001111"|
"010000"|"010001"|"010010"|"010011"|"010100"|"010101"|"010110"|"010111"|
"011000"|"011001"|"011010"|"011011"|"011100"|"011101"|"011110"|"011111"
=>
BUS_DATA <= CPU_DO;
when "110010"|"110011" =>
BUS_DATA <= BASIC_DO;
when "101010"|"101011" =>
BUS_DATA <= IO_DO;
when "100110"|"100111" =>
BUS_DATA <= OS_DO;
when "100010"|"100011" =>
BUS_DATA <= PBI_DO; -- i.e. CAS inhibited, nothing selected (e.g. REF_N is low)
when "100001" =>
BUS_DATA <= RAM_DO; -- i.e. RAM access, no EXTSEL
when "100000" =>
BUS_DATA <= PBI_DO; -- i.e. RAM access, but EXTSEL asserted
when others =>
BUS_DATA <= (others=>'1');
end case;
end process;

-- 00000000001111111111122222222233
-- 01234567890123456789012345678901
-- 00000000000000000000000000000001 run antic and CPU and kick off pbi/cart cycle
-- simple shift reg will do it
process(clk, reset_n)
begin
if (reset_n='0') then
shift_cpu_run_reg <= "00000000000000000000000000000001";
elsif (clk'event and clk='1') then
shift_cpu_run_reg <= shift_cpu_run_next;
end if;
end process;
shift_cpu_run_next <= shift_cpu_run_reg(30 downto 0)&shift_cpu_run_reg(31);
run_system <= shift_cpu_run_reg(31);
run_cpu <= run_system and not(ANTIC_FETCH or ANTIC_REFRESH_CYCLE);

-- Internal rom/ram
internalromram1 : entity work.internalromram_simple
PORT MAP (
clock => CLK,
reset_n => RESET_N,

ROM_ADDR => "000000"&BUS_ADDR,
ROM_REQUEST_COMPLETE => open,
ROM_REQUEST => '1',
BASIC_DATA => BASIC_DO,
OS_DATA => OS_DO,
RAM_ADDR => "000"&BUS_ADDR,
RAM_WR_ENABLE => RAM_WRITE_ENABLE,
RAM_DATA_IN => BUS_DATA(7 downto 0),
RAM_REQUEST_COMPLETE => open,
RAM_REQUEST => '1',
RAM_DATA => RAM_DO(7 downto 0)
);

END bdf_type;
(18-18/63)