Apple_II_vhdl/src/apple2.vhd

364 lines
11 KiB
VHDL

-------------------------------------------------------------------------------
--
-- Top level of an Apple ][+
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.T65_Pack.all;
entity apple2 is
generic (
use_bios_rom : boolean := true; -- use test bios
use_auto_rom : boolean := false; -- use apple II roms
use_cpu65xx_core : boolean := false;
use_T65_core : boolean := true
);
port (
CLK_14M : in std_logic; -- 14.31818 MHz master clock
CLK_2M : out std_logic;
PRE_PHASE_ZERO : out std_logic;
FLASH_CLK : in std_logic; -- approx. 2 Hz flashing char clock
reset : in std_logic;
ADDR : out unsigned(15 downto 0); -- CPU address
ram_addr : out unsigned(15 downto 0); -- RAM address
D : out unsigned(7 downto 0); -- Data to RAM
ram_do : in unsigned(7 downto 0); -- Data from RAM
PD : in unsigned(7 downto 0); -- Data to CPU from peripherals
ram_we : out std_logic; -- RAM write enable
VIDEO : out std_logic;
COLOR_LINE : out std_logic;
HBL : out std_logic;
VBL : out std_logic;
LD194 : out std_logic;
K : in unsigned(7 downto 0); -- Keyboard data
READ_KEY : out std_logic; -- Processor has read key
AN : out std_logic_vector(3 downto 0); -- Annunciator outputs
-- GAMEPORT input bits:
-- 7 6 5 4 3 2 1 0
-- pdl3 pdl2 pdl1 pdl0 pb3 pb2 pb1 casette
GAMEPORT : in std_logic_vector(7 downto 0);
PDL_STROBE : out std_logic; -- Pulses high when C07x read
STB : out std_logic; -- Pulses high when C04x read
IO_SELECT : out std_logic_vector(7 downto 0);
DEVICE_SELECT : out std_logic_vector(7 downto 0);
debugPc : out unsigned(15 downto 0);
debugOpcode : out unsigned(7 downto 0);
debugA : out unsigned(7 downto 0);
debugX : out unsigned(7 downto 0);
debugY : out unsigned(7 downto 0);
debugS : out unsigned(7 downto 0);
speaker : out std_logic -- One-bit speaker output
);
end apple2;
architecture rtl of apple2 is
-- Clocks
signal CLK_7M : std_logic;
signal Q3, RAS_N, CAS_N, AX : std_logic;
signal PHASE_ZERO, PRE_PHASE_ZERO_sig : std_logic;
signal COLOR_REF : std_logic;
-- From the timing generator
signal VIDEO_ADDRESS : unsigned(15 downto 0);
signal LDPS_N : std_logic;
signal H0, VA, VB, VC, V2, V4 : std_logic;
signal BLANK, LD194_I : std_logic;
signal HIRES : std_logic; -- from video generator B11 p6
-- Soft switches
signal soft_switches : std_logic_vector(7 downto 0) := "00000000";
signal TEXT_MODE : std_logic;
signal MIXED_MODE : std_logic;
signal PAGE2 : std_logic;
signal HIRES_MODE : std_logic;
-- CPU signals
signal D_IN : unsigned(7 downto 0);
signal A : unsigned(15 downto 0);
signal A_BIG : unsigned(23 downto 0);
signal we : std_logic;
signal R_W_n : std_logic;
-- Main ROM signals
signal rom_out : unsigned(7 downto 0);
signal rom_addr : unsigned(13 downto 0);
-- Address decoder signals
signal RAM_SELECT : std_logic := '1';
signal KEYBOARD_SELECT : std_logic := '0';
signal SPEAKER_SELECT : std_logic;
signal SOFTSWITCH_SELECT : std_logic;
signal ROM_SELECT : std_logic;
signal GAMEPORT_SELECT : std_logic;
signal IO_STROBE : std_logic;
-- Speaker signal
signal speaker_sig : std_logic := '0';
signal DL : unsigned(7 downto 0); -- Latched RAM data
signal DEBUG : T_t65_dbg;
begin
CLK_2M <= Q3;
PRE_PHASE_ZERO <= PRE_PHASE_ZERO_sig;
ram_addr <= A when PHASE_ZERO = '1' else VIDEO_ADDRESS;
ram_we <= we and not RAS_N when PHASE_ZERO = '1' else '0';
-- Latch RAM data on the rising edge of RAS
RAM_data_latch : process (CLK_14M)
begin
if rising_edge(CLK_14M) then
if AX = '1' and CAS_N = '0' and RAS_N = '0' then
DL <= ram_do;
end if;
end if;
end process;
ADDR <= A;
-- Address decoding
rom_addr <= (A(13) and A(12)) & (not A(12)) & A(11 downto 0);
address_decoder: process (A)
begin
ROM_SELECT <= '0';
RAM_SELECT <= '0';
KEYBOARD_SELECT <= '0';
READ_KEY <= '0';
SPEAKER_SELECT <= '0';
SOFTSWITCH_SELECT <= '0';
GAMEPORT_SELECT <= '0';
PDL_STROBE <= '0';
STB <= '0';
IO_SELECT <= (others => '0');
DEVICE_SELECT <= (others => '0');
IO_STROBE <= '0';
case A(15 downto 14) is
when "00" | "01" | "10" => -- 0000 - BFFF
RAM_SELECT <= '1';
when "11" => -- C000 - FFFF
case A(13 downto 12) is
when "00" => -- C000 - CFFF
case A(11 downto 8) is
when x"0" => -- C000 - C0FF
case A(7 downto 4) is
when x"0" => -- C000 - C00F
KEYBOARD_SELECT <= '1';
when x"1" => -- C010 - C01F
READ_KEY <= '1';
when x"3" => -- C030 - C03F
SPEAKER_SELECT <= '1';
when x"4" =>
STB <= '1';
when x"5" => -- C050 - C05F
SOFTSWITCH_SELECT <= '1';
when x"6" => -- C060 - C06F
GAMEPORT_SELECT <= '1';
when x"7" => -- C070 - C07F
PDL_STROBE <= '1';
when x"8" | x"9" | x"A" | -- C080 - C0FF
x"B" | x"C" | x"D" | x"E" | x"F" =>
DEVICE_SELECT(TO_INTEGER(A(6 downto 4))) <= '1';
when others => null;
end case;
when x"1" | x"2" | x"3" | -- C100 - C7FF
x"4" | x"5" | x"6" | x"7" =>
IO_SELECT(TO_INTEGER(A(10 downto 8))) <= '1';
when x"8" | x"9" | x"A" | -- C800 - CFFF
x"B" | x"C" | x"D" | x"E" | x"F" =>
IO_STROBE <= '1';
when others => null;
end case;
when "01" | "10" | "11" => -- D000 - FFFF
ROM_SELECT <= '1';
when others =>
null;
end case;
when others => null;
end case;
end process address_decoder;
speaker_ctrl: process (Q3)
begin
if rising_edge(Q3) then
if PRE_PHASE_ZERO_sig = '1' and SPEAKER_SELECT = '1' then
speaker_sig <= not speaker_sig;
end if;
end if;
end process speaker_ctrl;
softswitches: process (Q3)
begin
if rising_edge(Q3) then
if PRE_PHASE_ZERO_sig = '1' and SOFTSWITCH_SELECT = '1' then
soft_switches(TO_INTEGER(A(3 downto 1))) <= A(0);
end if;
end if;
end process softswitches;
TEXT_MODE <= soft_switches(0);
MIXED_MODE <= soft_switches(1);
PAGE2 <= soft_switches(2);
HIRES_MODE <= soft_switches(3);
AN <= soft_switches(7 downto 4);
speaker <= speaker_sig;
D_IN <= DL when RAM_SELECT = '1' else -- RAM
K when KEYBOARD_SELECT = '1' else -- Keyboard
GAMEPORT(TO_INTEGER(A(2 downto 0))) & "0000000" -- Gameport
when GAMEPORT_SELECT = '1' else
rom_out when ROM_SELECT = '1' else -- ROMs
PD; -- Peripherals
LD194 <= LD194_I;
timing : entity work.timing_generator port map (
CLK_14M => CLK_14M,
CLK_7M_out => CLK_7M,
CAS_N_out => CAS_N,
RAS_N_out => RAS_N,
Q3_out => Q3,
AX_out => AX,
PHI0_out => PHASE_ZERO,
PRE_PHI0_out => PRE_PHASE_ZERO_sig,
COLOR_REF_out => COLOR_REF,
TEXT_MODE => TEXT_MODE,
PAGE2 => PAGE2,
HIRES => HIRES,
VIDEO_ADDRESS => VIDEO_ADDRESS,
H0 => H0,
VA => VA,
VB => VB,
VC => VC,
V2 => V2,
V4 => V4,
VBL_out => VBL,
HBL_out => HBL,
BLANK => BLANK,
LDPS_N => LDPS_N,
LD194 => LD194_I);
video_display : entity work.video_generator port map (
CLK_14M => CLK_14M,
CLK_7M => CLK_7M,
AX => AX,
CAS_N => CAS_N,
TEXT_MODE => TEXT_MODE,
PAGE2 => PAGE2,
HIRES_MODE => HIRES_MODE,
MIXED_MODE => MIXED_MODE,
H0 => H0,
VA => VA,
VB => VB,
VC => VC,
V2 => V2,
V4 => V4,
BLANK => BLANK,
DL => DL,
LDPS_N => LDPS_N,
LD194 => LD194_I,
FLASH_CLK => FLASH_CLK,
HIRES => HIRES,
VIDEO => VIDEO,
COLOR_LINE => COLOR_LINE);
cpu65xx_core: if use_cpu65xx_core generate
cpu : entity work.cpu65xx
generic map (
pipelineOpcode => false,
pipelineAluMux => false,
pipelineAluOut => false)
port map (
clk => Q3,
enable => not PRE_PHASE_ZERO_sig,
reset => reset,
nmi_n => '1',
irq_n => '1',
di => D_IN,
do => D,
addr => A,
we => we,
debugPc => debugPc,
debugOpcode => debugOpcode,
debugA => debugA,
debugX => debugX,
debugY => debugY,
debugS => debugS
);
end generate;
T65_core: if use_T65_core generate
cpu : entity work.T65 port map (
Mode => "00",
Abort_n => '1',
SO_n => '1',
Res_n => not reset,
Enable => not PRE_PHASE_ZERO_sig,
Clk => Q3,
Rdy => '1',
IRQ_n => '1',
NMI_n => '1',
R_W_n => R_W_n,
Sync => open,
EF => open,
MF => open,
XF => open,
ML_n => open,
VP_n => open,
VDA => open,
VPA => open,
unsigned(A(23 downto 0)) => A_BIG,
DI(7 downto 0) => std_logic_vector(D_IN),
unsigned(DO(7 downto 0)) => D,
DEBUG => DEBUG
);
A <= A_BIG( 15 downto 0);
we <= not R_W_n;
debugOpcode <= unsigned(DEBUG.I); -- instruction
debugA <= unsigned(DEBUG.A); -- A reg
debugX <= unsigned(DEBUG.X); -- X reg
debugY <= unsigned(DEBUG.Y); -- Y reg
debugS <= unsigned(DEBUG.S); -- stack pointer
--<= DEBUG.P; -- processor flags
end generate;
-- Original Apple had asynchronous ROMs. We use a synchronous ROM
-- that needs its address earlier, hence the odd clock.
auto_rom: if use_auto_rom generate
-- apple II roms
roms : entity work.apple_II_auto_rom port map (
addr => std_logic_vector(rom_addr),
clk => CLK_14M,
unsigned(DATA) => rom_out);
end generate;
bios_rom: if use_bios_rom generate
-- test bios
roms : entity work.bios_rom port map (
addr => std_logic_vector(rom_addr),
clk => CLK_14M,
unsigned(DATA) => rom_out);
end generate;
end rtl;