359 lines
11 KiB
VHDL
359 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;
|
|
|
|
entity apple2 is
|
|
port (
|
|
CLK_14M : in std_logic; -- 14.31818 MHz master clock
|
|
CLK_2M : out std_logic;
|
|
CPU_WAIT : in 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(17 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);
|
|
pcDebugOut : out unsigned(15 downto 0);
|
|
opcodeDebugOut : out unsigned(7 downto 0);
|
|
laudio : out std_logic_vector(7 downto 0);
|
|
raudio : out std_logic_vector(7 downto 0);
|
|
mb_enabled : in std_logic;
|
|
speaker : out std_logic -- One-bit speaker output
|
|
);
|
|
end apple2;
|
|
|
|
architecture rtl of apple2 is
|
|
|
|
component ramcard is
|
|
port ( mclk28: in std_logic;
|
|
reset_in: in std_logic;
|
|
addr: in std_logic_vector(15 downto 0);
|
|
ram_addr: out std_logic_vector(17 downto 0);
|
|
we: in std_logic;
|
|
card_ram_we: out std_logic;
|
|
card_ram_rd: out std_logic;
|
|
bank1: out std_logic
|
|
);
|
|
end component;
|
|
|
|
-- 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 D_OUT: unsigned(7 downto 0);
|
|
signal A : unsigned(15 downto 0);
|
|
signal we : 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
|
|
|
|
-- ramcard
|
|
signal card_addr : unsigned(17 downto 0);
|
|
signal card_ram_rd : std_logic;
|
|
signal card_ram_we : std_logic;
|
|
signal ram_card_read : std_logic;
|
|
signal ram_card_write : std_logic;
|
|
|
|
signal psg_irq_n : std_logic;
|
|
signal psg_do : unsigned(7 downto 0);
|
|
|
|
signal ioselect : std_logic_vector(7 downto 0);
|
|
signal devselect : std_logic_vector(7 downto 0);
|
|
|
|
signal R_W_n : std_logic;
|
|
|
|
begin
|
|
|
|
CLK_2M <= Q3;
|
|
PRE_PHASE_ZERO <= PRE_PHASE_ZERO_sig;
|
|
|
|
ram_addr <= card_addr when PHASE_ZERO = '1' else "00" & VIDEO_ADDRESS;
|
|
ram_we <= ((we and RAM_SELECT) or (we and ram_card_write)) 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;
|
|
D <= D_OUT;
|
|
|
|
IO_SELECT <= ioselect;
|
|
DEVICE_SELECT <= devselect;
|
|
|
|
-- 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';
|
|
ioselect <= (others => '0');
|
|
devselect <= (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" =>
|
|
devselect(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" =>
|
|
ioselect(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' or ram_card_read = '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
|
|
psg_do when (devselect(4) = '1' or ioselect(4) = '1') and mb_enabled = '1' else
|
|
PD; -- Peripherals
|
|
|
|
LD194 <= LD194_I;
|
|
|
|
timing : entity work.timing_generator port map (
|
|
CLK_14M => CLK_14M,
|
|
CLK_7M => CLK_7M,
|
|
CAS_N => CAS_N,
|
|
RAS_N => RAS_N,
|
|
Q3 => Q3,
|
|
AX => AX,
|
|
PHI0 => PHASE_ZERO,
|
|
PRE_PHI0 => PRE_PHASE_ZERO_sig,
|
|
COLOR_REF => 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 => VBL,
|
|
HBL => 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);
|
|
|
|
cpu : entity work.cpu65xx
|
|
generic map (
|
|
pipelineOpcode => false,
|
|
pipelineAluMux => false,
|
|
pipelineAluOut => false)
|
|
port map (
|
|
clk => Q3,
|
|
enable => (not PRE_PHASE_ZERO_sig) and (not CPU_WAIT),
|
|
reset => reset,
|
|
nmi_n => '1',
|
|
irq_n => psg_irq_n,
|
|
di => D_IN,
|
|
do => D_OUT,
|
|
addr => A,
|
|
we => we,
|
|
debugPc => pcDebugOut,
|
|
debugOpcode => opcodeDebugOut
|
|
);
|
|
|
|
-- Original Apple had asynchronous ROMs. We use a synchronous ROM
|
|
-- that needs its address earlier, hence the odd clock.
|
|
roms : entity work.roms port map (
|
|
address => std_logic_vector(rom_addr),
|
|
clock => CLK_14M,
|
|
unsigned(q) => rom_out);
|
|
|
|
-- ramcard
|
|
ram_card_D: component ramcard
|
|
port map
|
|
(
|
|
mclk28 => CLK_14M,
|
|
reset_in => reset,
|
|
addr => std_logic_vector(A),
|
|
unsigned(ram_addr) => card_addr,
|
|
we => we,
|
|
card_ram_we => card_ram_we,
|
|
card_ram_rd => card_ram_rd,
|
|
bank1 => open
|
|
);
|
|
|
|
ram_card_read <= ROM_SELECT and card_ram_rd;
|
|
ram_card_write <= ROM_SELECT and card_ram_we;
|
|
|
|
mb : work.mockingboard
|
|
port map (
|
|
CLK_VIA => not Q3,
|
|
CLK_PSG => not PHASE_ZERO,
|
|
I_P2_H => not PHASE_ZERO,
|
|
I_RESET_L => not reset,
|
|
I_ENA_H => mb_enabled,
|
|
|
|
I_ADDR => std_logic_vector(A)(7 downto 0),
|
|
I_DATA => std_logic_vector(D_OUT),
|
|
unsigned(O_DATA) => psg_do,
|
|
I_RW_L => not we,
|
|
I_IOSEL_L => not ioselect(4),
|
|
O_IRQ_L => psg_irq_n,
|
|
O_AUDIO_L => laudio,
|
|
O_AUDIO_R => raudio
|
|
);
|
|
|
|
end rtl;
|