add more source

This commit is contained in:
Stefan 2016-01-05 13:40:50 +01:00
parent 5258fcf4d9
commit b77730fcec
15 changed files with 2514 additions and 1 deletions

11
CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
cmake_minimum_required (VERSION 2.8)
project (Tools)
find_package(PkgConfig)
include(CheckIncludeFiles)
add_subdirectory(tools)

View File

@ -1 +1,16 @@
# Apple_II_vhdl
This is a reconstruction of an 1980s-era Apple ][+ implemented in VHDL for
FPGAs.
The source code is copied from
Stephen A. Edwards, sedwards@cs.columbia.edu
http://www1.cs.columbia.edu/~sedwards
Build ToDO
- Download the Apple II roms from the web
-

38
make_roms.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh
# SHA1 checksum
export rom_path_src=./roms
export rom_path=./build
#export tools_path=../build2/romgen
export romgen_path=./romgen_src
#mkdir $rom_path
sha1sum $rom_path_src/apple_II.rom
sha1sum $rom_path_src/APPLE2.ROM
sha1sum $rom_path_src/bios.rom
#341-011-D0 Applesoft BASIC D0
#341-012-D8 Applesoft BASIC D8
#341-013-E0 Applesoft BASIC E0
#341-014-E8 Applesoft BASIC E8
#341-015-F0 Applesoft BASIC F0
#341-020-F8 Autostart Monitor
rm $rom_path_src/apple_II_auto.bin
# autostart rom
cat $rom_path_src/341011d0.bin $rom_path_src/341012d8.bin $rom_path_src/341013e0.bin $rom_path_src/341014e8.bin $rom_path_src/341015f0.bin $rom_path_src/341020f8.bin >> $rom_path_src/apple_II_auto.bin
$romgen_path/romgen $rom_path_src/apple_II_auto.bin apple_II_auto_rom 14 a r > $rom_path/apple_II_auto_rom.vhd
$romgen_path/romgen $rom_path_src/apple_II.rom apple_II_rom 14 a r > $rom_path/apple_II_rom.vhd
$romgen_path/romgen $rom_path_src/APPLE2.ROM APPLE2_ROM 14 a r > $rom_path/APPLE2_ROM.vhd
# test rom
$romgen_path/romgen $rom_path_src/bios.rom bios_rom 14 a r > $rom_path/bios_rom.vhd

45
src/Debouncer.vhd Normal file
View File

@ -0,0 +1,45 @@
-- (C) Rui T. Sousa from http://sweet.ua.pt/~a16360
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Debouncer is
generic (Delay : positive);
port (
Clock : in STD_LOGIC;
Reset : in STD_LOGIC;
Input : in STD_LOGIC;
Output : out STD_LOGIC
);
end Debouncer;
architecture Behavioral of Debouncer is
signal DelayCounter : natural range 0 to Delay;
signal Internal : STD_LOGIC;
begin
process(Clock, Reset)
begin
if rising_edge(Clock) then
if Reset = '1' then
Output <= '0';
Internal <= '0';
DelayCounter <= 0;
else
if Input /= Internal then
Internal <= Input;
DelayCounter <= 0;
elsif DelayCounter = Delay then
Output <= Internal;
else
DelayCounter <= DelayCounter + 1;
end if;
end if;
end if;
end process;
end Behavioral;

71
src/dac.vhd Normal file
View File

@ -0,0 +1,71 @@
-------------------------------------------------------------------------------
--
-- Delta-Sigma DAC
--
-- $Id: dac.vhd,v 1.1 2005/10/25 21:09:42 arnim Exp $
--
-- Refer to Xilinx Application Note XAPP154.
--
-- This DAC requires an external RC low-pass filter:
--
-- dac_o 0---XXXXX---+---0 analog audio
-- 3k3 |
-- === 4n7
-- |
-- GND
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity dac is
generic (
msbi_g : integer := 7
);
port (
clk_i : in std_logic;
res_n_i : in std_logic;
dac_i : in std_logic_vector(msbi_g downto 0);
dac_o : out std_logic
);
end dac;
library ieee;
use ieee.numeric_std.all;
architecture rtl of dac is
signal DACout_q : std_logic;
signal DeltaAdder_s,
SigmaAdder_s,
SigmaLatch_q,
DeltaB_s : unsigned(msbi_g+2 downto 0);
begin
DeltaB_s(msbi_g+2 downto msbi_g+1) <= SigmaLatch_q(msbi_g+2) &
SigmaLatch_q(msbi_g+2);
DeltaB_s(msbi_g downto 0) <= (others => '0');
DeltaAdder_s <= unsigned('0' & '0' & dac_i) + DeltaB_s;
SigmaAdder_s <= DeltaAdder_s + SigmaLatch_q;
seq: process (clk_i, res_n_i)
begin
if res_n_i = '0' then
SigmaLatch_q <= to_unsigned(2**(msbi_g+1), SigmaLatch_q'length);
DACout_q <= '0';
elsif clk_i'event and clk_i = '1' then
SigmaLatch_q <= SigmaAdder_s;
DACout_q <= SigmaLatch_q(msbi_g+2);
end if;
end process seq;
dac_o <= DACout_q;
end rtl;

288
src/disk_ii.vhd Normal file
View File

@ -0,0 +1,288 @@
-------------------------------------------------------------------------------
--
-- Disk II emulator
--
-- This is read-only and only feeds "pre-nibblized" data to the processor
-- It has a single-track buffer and only supports one drive (1).
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
--
-------------------------------------------------------------------------------
--
-- Each track is represented as 0x1A00 bytes
-- Each disk image consists of 35 * 0x1A00 bytes = 0x38A00 (227.5 K)
--
-- X = $60 for slot 6
--
-- Off On
-- C080,X C081,X Phase 0 Head Stepper Motor Control
-- C082,X C083,X Phase 1
-- C084,X C085,X Phase 2
-- C086,X C087,X Phase 3
-- C088,X C089,X Motor On
-- C08A,X C08B,X Select Drive 2 (select drive 1 when off)
-- C08C,X C08D,X Q6 (Shift/load?)
-- C08E,X C08F,X Q7 (Write request to drive)
--
--
-- Q7 Q6
-- 0 0 Read
-- 0 1 Sense write protect
-- 1 0 Write
-- 1 1 Load Write Latch
--
-- Reading a byte:
-- LDA $C08E,X set read mode
-- ...
-- READ LDA $C08C,X
-- BPL READ
--
-- Sense write protect:
-- LDA $C08D,X
-- LDA $C08E,X
-- BMI PROTECTED
--
-- Writing
-- STA $C08F,X set write mode
-- ..
-- LDA DATA
-- STA $C08D,X load byte to write
-- STA $C08C,X write byte to disk
--
-- Data bytes must be written in 32 cycle loops.
--
-- There are 70 phases for the head stepper and and 35 tracks,
-- i.e., two phase changes per track.
--
-- The disk spins at 300 rpm; one new bit arrives every 4 us
-- The processor's clock is 1 MHz = 1 us, so it takes 8 * 4 = 32 cycles
-- for a new byte to arrive
--
-- This corresponds to dividing the 2 MHz signal by 64 to get the byte clock
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity disk_ii is
port (
CLK_14M : in std_logic;
CLK_2M : in std_logic;
PRE_PHASE_ZERO : in std_logic;
IO_SELECT : in std_logic; -- e.g., C600 - C6FF ROM
DEVICE_SELECT : in std_logic; -- e.g., C0E0 - C0EF I/O locations
RESET : in std_logic;
A : in unsigned(15 downto 0);
D_IN : in unsigned(7 downto 0); -- From 6502
D_OUT : out unsigned(7 downto 0); -- To 6502
TRACK : out unsigned(5 downto 0); -- Current track (0-34)
track_addr : out unsigned(13 downto 0);
D1_ACTIVE : out std_logic; -- Disk 1 motor on
D2_ACTIVE : out std_logic; -- Disk 2 motor on
ram_write_addr : in unsigned(13 downto 0); -- Address for track RAM
ram_di : in unsigned(7 downto 0); -- Data to track RAM
ram_we : in std_logic -- RAM write enable
);
end disk_ii;
architecture rtl of disk_ii is
signal motor_phase : std_logic_vector(3 downto 0);
signal drive_on : std_logic;
signal drive2_select : std_logic;
signal q6, q7 : std_logic;
signal rom_dout : unsigned(7 downto 0);
-- Current phase of the head. This is in half-steps to assign
-- a unique position to the case, say, when both phase 0 and phase 1 are
-- on simultaneously. phase(7 downto 2) is the track number
signal phase : unsigned(7 downto 0); -- 0 - 139
-- Storage for one track worth of data in "nibblized" form
type track_ram is array(0 to 6655) of unsigned(7 downto 0);
-- Double-ported RAM for holding a track
signal track_memory : track_ram;
signal ram_do : unsigned(7 downto 0);
-- Lower bit indicates whether disk data is "valid" or not
-- RAM address is track_byte_addr(14 downto 1)
-- This makes it look to the software like new data is constantly
-- being read into the shift register, which indicates the data is
-- not yet ready.
signal track_byte_addr : unsigned(14 downto 0);
signal read_disk : std_logic; -- When C08C accessed
begin
interpret_io : process (CLK_2M)
begin
if rising_edge(CLK_2M) then
if reset = '1' then
motor_phase <= (others => '0');
drive_on <= '0';
drive2_select <= '0';
q6 <= '0';
q7 <= '0';
else
if PRE_PHASE_ZERO = '1' and DEVICE_SELECT = '1' then
if A(3) = '0' then -- C080 - C087
motor_phase(TO_INTEGER(A(2 downto 1))) <= A(0);
else
case A(2 downto 1) is
when "00" => drive_on <= A(0); -- C088 - C089
when "01" => drive2_select <= A(0); -- C08A - C08B
when "10" => q6 <= A(0); -- C08C - C08D
when "11" => q7 <= A(0); -- C08E - C08F
when others => null;
end case;
end if;
end if;
end if;
end if;
end process;
D1_ACTIVE <= drive_on and not drive2_select;
D2_ACTIVE <= drive_on and drive2_select;
-- There are two cases:
--
-- Current phase is odd (between two poles)
-- |
-- V
-- -3-2-1 0 1 2 3
-- X X X X
-- 0 1 2 3
--
--
-- Current phase is even (under a pole)
-- |
-- V
-- -4-3-2-1 0 1 2 3 4
-- X X X X X
-- 0 1 2 3 0
--
update_phase : process (CLK_14M)
variable phase_change : integer;
variable new_phase : integer;
variable rel_phase : std_logic_vector(3 downto 0);
begin
if rising_edge(CLK_14M) then
if reset = '1' then
phase <= TO_UNSIGNED(70, 8); -- Deliberately odd to test reset
else
phase_change := 0;
new_phase := TO_INTEGER(phase);
rel_phase := motor_phase;
case phase(2 downto 1) is
when "00" =>
rel_phase := rel_phase(1 downto 0) & rel_phase(3 downto 2);
when "01" =>
rel_phase := rel_phase(2 downto 0) & rel_phase(3);
when "10" => null;
when "11" =>
rel_phase := rel_phase(0) & rel_phase(3 downto 1);
when others => null;
end case;
if phase(0) = '1' then -- Phase is odd
case rel_phase is
when "0000" => phase_change := 0;
when "0001" => phase_change := -3;
when "0010" => phase_change := -1;
when "0011" => phase_change := -2;
when "0100" => phase_change := 1;
when "0101" => phase_change := -1;
when "0110" => phase_change := 0;
when "0111" => phase_change := -1;
when "1000" => phase_change := 3;
when "1001" => phase_change := 0;
when "1010" => phase_change := 1;
when "1011" => phase_change := -3;
when "1111" => phase_change := 0;
when others => null;
end case;
else -- Phase is even
case rel_phase is
when "0000" => phase_change := 0;
when "0001" => phase_change := -2;
when "0010" => phase_change := 0;
when "0011" => phase_change := -1;
when "0100" => phase_change := 2;
when "0101" => phase_change := 0;
when "0110" => phase_change := 1;
when "0111" => phase_change := 0;
when "1000" => phase_change := 0;
when "1001" => phase_change := 1;
when "1010" => phase_change := 2;
when "1011" => phase_change := -2;
when "1111" => phase_change := 0;
when others => null;
end case;
end if;
if new_phase + phase_change <= 0 then
new_phase := 0;
elsif new_phase + phase_change > 139 then
new_phase := 139;
else
new_phase := new_phase + phase_change;
end if;
phase <= TO_UNSIGNED(new_phase, 8);
end if;
end if;
end process;
TRACK <= phase(7 downto 2);
-- Dual-ported RAM holding the contents of the track
track_storage : process (CLK_14M)
begin
if rising_edge(CLK_14M) then
if ram_we = '1' then
track_memory(to_integer(ram_write_addr)) <= ram_di;
end if;
ram_do <= track_memory(to_integer(track_byte_addr(14 downto 1)));
end if;
end process;
-- Go to the next byte when the disk is accessed or if the counter times out
read_head : process (CLK_2M)
variable byte_delay : unsigned(5 downto 0); -- Accounts for disk spin rate
begin
if rising_edge(CLK_2M) then
if reset = '1' then
track_byte_addr <= (others => '0');
byte_delay := (others => '0');
else
byte_delay := byte_delay - 1;
if (read_disk = '1' and PRE_PHASE_ZERO = '1') or byte_delay = 0 then
byte_delay := (others => '0');
if track_byte_addr = X"33FE" then
track_byte_addr <= (others => '0');
else
track_byte_addr <= track_byte_addr + 1;
end if;
end if;
end if;
end if;
end process;
rom : entity work.disk_ii_rom port map (
addr => A(7 downto 0),
clk => CLK_14M,
dout => rom_dout);
read_disk <= '1' when DEVICE_SELECT = '1' and A(3 downto 0) = x"C" else
'0'; -- C08C
D_OUT <= rom_dout when IO_SELECT = '1' else
ram_do when read_disk = '1' and track_byte_addr(0) = '0' else
(others => '0');
track_addr <= track_byte_addr(14 downto 1);
end rtl;

204
src/grp_debouncer.vhd Normal file
View File

@ -0,0 +1,204 @@
-----------------------------------------------------------------------------------------------------------------------
-- Author: Jonny Doin, jdoin@opencores.org, jonnydoin@gmail.com
--
-- Create Date: 09:56:30 07/06/2011
-- Module Name: grp_debouncer - RTL
-- Project Name: basic functions
-- Target Devices: Spartan-6
-- Tool versions: ISE 13.1
-- Description:
--
-- This block is a generic multiple input debouncing circuit.
-- It handles multiple inputs, like mechanical switch inputs, and outputs a debounced, stable registered version of the inputs.
-- A 'new_data' one-cycle strobe is also available, to sync downstream logic.
--
-- CONCEPTUAL CIRCUIT
-- ==================
--
-- W
-- /----------------/----------------\
-- | |
-- | |
-- | ______ ______ | _____
-- | W | | W |fdr | W | W |cmp \
-- \----/---| +1 |---/----| |--/--+----/----| \
-- | | | | | \
-- ------ | | \ |
-- | | | = |-----\
-- |> R | / | |
-- ---+-- | / |
-- | CNT_VAL---| / |
-- | |____/ |
-- | |
-- \------------\ |
-- | |
-- N ____ | |
-- /-------/---)) \ ____ | |
-- | ))XOR |-----) \ | |
-- | /------))___/ )OR |-----/ |
-- | | /---)___/ |
-- | | | |
-- | | \----------\ |
-- | | N | |
-- | \--------/-----------\ +----------------------+---------\
-- | | | |
-- \---\ | | |
-- ______ | ______ | | ______ |
-- | fd | | | fd | | | |fde | |
-- [data_i]----/-----| |---/---+---/----| |---/---+----)---| |---/---+---/-----------)------------------------[data_o]
-- N | | N N | | N | | | | N | N |
-- | | | | | \---|CE | | |
-- | | | | | | | | |
-- [clk_i]----> |> | |> | | |> | | | ____ ______
-- ------ ------ | ------ | N ____ \---| \ | fd |
-- | \---/---)) \ |AND |-----| |----[strb_o]
-- | ))XOR |-----|___/ | |
-- \-------------------------/---))___/ | |
-- N | |
-- |> |
-- ------
--
--
-- PIPELINE LOGIC
-- ==============
--
-- This debouncer circuit detects edges in an input signal, and waits the signal to stabilize for the designated time
-- before transferring the stable signal to the registered output.
-- A one-clock-cyle strobe is pulsed at the output to signalize a new data available.
-- The core clock should be the system clock, to optimize use of global clock resources.
--
-- GROUP DEBOUNCING
-- ================
--
-- A change in state in any bit in the input word causes reload of the delay counter, and the output word is updated only
-- when all bits are stable for the specified period. Therefore, the grouping of signals and delay selection should match
-- behaviour of the selected signals.
--
-- RESOURCES USED
-- ==============
--
-- The number of registers inferred is: 3*N + (LOG(CNT_VAL)/LOG(2)) + 1 registers.
-- The number of LUTs inferred is roughly: ((4*N+2)/6)+2.
-- The slice distribution will vary, and depends on the control set restrictions and LUT-FF pairs resulting from map+p&r.
--
-- This design was originally targeted to a Spartan-6 platform, synthesized with XST and normal constraints.
-- Verification in silicon was done on a Digilent Atlys board with a Spartan-6 FPGA @100MHz clock.
-- The VHDL dialect used is VHDL'93, accepted largely by all synthesis tools.
--
------------------------------ COPYRIGHT NOTICE -----------------------------------------------------------------------
--
--
-- Author(s): Jonny Doin, jdoin@opencores.org, jonnydoin@gmail.com
--
-- Copyright (C) 2011 Jonny Doin
-- -----------------------------
--
-- This source file may be used and distributed without restriction provided that this copyright statement is not
-- removed from the file and that any derivative work contains the original copyright notice and the associated
-- disclaimer.
--
-- This source file is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
-- General Public License as published by the Free Software Foundation; either version 2.1 of the License, or
-- (at your option) any later version.
--
-- This source 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 Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General Public License along with this source; if not, download
-- it from http://www.gnu.org/licenses/lgpl.txt
--
------------------------------ REVISION HISTORY -----------------------------------------------------------------------
--
-- 2011/07/06 v0.01.0010 [JD] started development. verification of synthesis circuit inference.
-- 2011/07/07 v1.00.0020 [JD] verification in silicon. operation at 100MHz, tested on the Atlys board (Spartan-6 LX45).
-- 2011/08/10 v1.01.0025 [JD] added one pipeline delay to new data strobe output.
-- 2011/09/19 v1.01.0030 [JD] changed range for internal counter (cnt_reg, cnt_next) to avoid adder flipover (Altera/ModelSim).
--
-----------------------------------------------------------------------------------------------------------------------
-- TODO
-- ====
--
-- The circuit can easily be extended to have a signature of which inputs changed at the data out port.
--
-----------------------------------------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity grp_debouncer is
Generic (
N : positive := 8; -- input bus width
CNT_VAL : positive := 10000); -- clock counts for debounce period
Port (
clk_i : in std_logic := 'X'; -- system clock
data_i : in std_logic_vector (N-1 downto 0) := (others => 'X'); -- noisy input data
data_o : out std_logic_vector (N-1 downto 0); -- registered stable output data
strb_o : out std_logic -- strobe for new data available
);
end grp_debouncer;
architecture rtl of grp_debouncer is
-- datapath pipeline
signal reg_A, reg_B : std_logic_vector (N-1 downto 0) := (others => '0'); -- debounce edge detectors
signal reg_out : std_logic_vector (N-1 downto 0) := (others => '0'); -- registered output
signal dat_strb : std_logic := '0'; -- data transfer strobe
signal strb_reg : std_logic := '0'; -- registered strobe
signal strb_next : std_logic := '0'; -- lookahead strobe
signal dat_diff : std_logic := '0'; -- edge detector
-- debounce counter
signal cnt_reg : integer range CNT_VAL + 1 downto 0 := 0; -- debounce period counter
signal cnt_next : integer range CNT_VAL + 1 downto 0 := 0; -- combinatorial signal
begin
--=============================================================================================
-- DEBOUNCE COUNTER LOGIC
--=============================================================================================
-- This counter is implemented as a up-counter with reset and final count detection via compare,
-- instead of a down-counter with preset and final count detection via nonzero detection.
-- This is better for Spartan-6 and Virtex-6 CLB architecture, because it uses less control sets.
--
-- cnt_reg register transfer logic
cnt_reg_proc: process (clk_i) is
begin
if clk_i'event and clk_i = '1' then
cnt_reg <= cnt_next;
end if;
end process cnt_reg_proc;
-- cnt_next combinatorial logic
cnt_next_proc: cnt_next <= 0 when dat_diff = '1' or dat_strb = '1' else cnt_reg + 1;
-- final count combinatorial logic
final_cnt_proc: dat_strb <= '1' when cnt_reg = CNT_VAL else '0';
--=============================================================================================
-- DATAPATH SIGNAL PIPELINE
--=============================================================================================
-- input pipeline logic
pipeline_proc: process (clk_i) is
begin
if clk_i'event and clk_i = '1' then
-- edge detection pipeline
reg_A <= data_i;
reg_B <= reg_A;
-- new data strobe pipeline delay
strb_reg <= strb_next;
end if;
-- output data pipeline
if clk_i'event and clk_i = '1' then
if dat_strb = '1' then
reg_out <= reg_B;
end if;
end if;
end process pipeline_proc;
-- edge detector
edge_detector_proc: dat_diff <= '1' when reg_A /= reg_B else '0';
-- lookahead new data strobe
next_strobe_proc: strb_next <= '1' when ((reg_out /= reg_B) and dat_strb = '1') else '0';
--=============================================================================================
-- OUTPUT LOGIC
--=============================================================================================
-- connect output ports
data_o_proc: data_o <= reg_out;
strb_o_proc: strb_o <= strb_reg;
end rtl;

502
src/keyboard_apple.vhd Normal file
View File

@ -0,0 +1,502 @@
-------------------------------------------------------------------------------
--
-- PS/2 Keyboard interface for the Apple ][
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
-- After an original by Alex Freed
-- i18n & French keyboard by Michel Stempin
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity keyboard_apple is
generic (
--KEYMAP : string := "EN-us" -- English US keymap
--KEYMAP : string := "FR-fr" -- French keymap
KEYMAP : string := "DE-de" -- German keymap
);
port (
PS2_Clk : inout std_logic; -- From PS/2 port
PS2_Data : inout std_logic; -- From PS/2 port
CLK_14M : in std_logic;
read : in std_logic; -- Read strobe
reset : in std_logic;
image_out : out unsigned(9 downto 0); -- image number
debug_info_on: out std_logic; -- switch debug on/off
out_color_bw : out std_logic; -- switch color bw
K : out unsigned(7 downto 0) -- Latched, decoded keyboard data
);
end keyboard_apple;
architecture rtl of keyboard_apple is
signal code, latched_code : unsigned(7 downto 0);
signal ScanCode : std_logic_vector(9 downto 0);
signal code_available : std_logic;
signal ascii : unsigned(7 downto 0); -- decoded
signal shifted_code : unsigned(11 downto 0);
signal key_pressed : std_logic; -- Key pressed & not read
signal ctrl, shift, alt : std_logic;
signal image : unsigned(9 downto 0); -- disk image number
signal debug_info : std_logic := '0'; -- debug info on off
signal color_bw : std_logic := '0'; -- switch color / bw mode
-- Special PS/2 keyboard codes
constant KEY_UP_CODE : unsigned(7 downto 0) := X"F0";
constant EXTENDED_CODE : unsigned(7 downto 0) := X"E0";
constant LEFT_SHIFT : unsigned(7 downto 0) := X"12";
constant RIGHT_SHIFT : unsigned(7 downto 0) := X"59";
constant LEFT_CTRL : unsigned(7 downto 0) := X"14";
constant ALT_GR : unsigned(7 downto 0) := X"11";
begin
inst_ps2 : entity work.Keyboard
port map (
Clock => CLK_14M,
Reset => reset,
PS2Clock => PS2_Clk,
PS2Data => PS2_Data,
CodeReady => code_available,
ScanCode => ScanCode);
K <= key_pressed & "00" & ascii(4 downto 0) when ctrl = '1' else
key_pressed & ascii(6 downto 0);
code <= unsigned(ScanCode( 7 downto 0));
shift_ctrl : process (CLK_14M, reset)
begin
if rising_edge(CLK_14M) then
if reset = '1' then
shift <= '0';
ctrl <= '0';
else
if code_available = '1' then
if ScanCode(8) = '0' then -- make
if code = LEFT_SHIFT or code = RIGHT_SHIFT then
shift <= '1';
elsif code = LEFT_CTRL then
ctrl <= '1';
elsif code = ALT_GR then
alt <= '1';
end if;
elsif ScanCode(8) = '1' then -- break
if code = LEFT_SHIFT or code = RIGHT_SHIFT then
shift <= '0';
elsif code = LEFT_CTRL then
ctrl <= '0';
elsif code = ALT_GR then
alt <= '0';
end if;
end if;
end if;
end if;
end if;
end process shift_ctrl;
fsm : process (CLK_14M, reset)
begin
if rising_edge(CLK_14M) then
if reset = '1' then
latched_code <= (others => '0');
key_pressed <= '0';
end if;
if read = '1' then
key_pressed <= '0';
end if;
if code_available = '1' and ScanCode(8) = '1' then
if code = X"06" then -- F2
image <= image +1;
elsif code = X"04" then -- F3
image <= image - 1;
elsif code = X"07" then -- F12
debug_info <= not debug_info;
elsif code = X"0B" then -- F6
color_bw <= not color_bw;
else
-- normal key
latched_code <= code ;
key_pressed <= '1';
end if;
end if;
end if;
end process fsm;
image_out <= image;
debug_info_on <= debug_info;
out_color_bw <= color_bw;
-- PS/2 scancode to ASCII translation
shifted_code <= "00" & alt & shift & latched_code;
DE_de: if KEYMAP = "DE-de" generate
with shifted_code select
ascii <=
X"08" when X"066", -- Backspace ("backspace" key)
X"08" when X"166", -- Backspace ("backspace" key)
X"09" when X"00d", -- Horizontal Tab
X"09" when X"10d", -- Horizontal Tab
X"0d" when X"05a", -- Carriage return ("enter" key)
X"0d" when X"15a", -- Carriage return ("enter" key)
X"1b" when X"076", -- Escape ("esc" key)
X"1b" when X"176", -- Escape ("esc" key)
X"20" when X"029", -- Space
X"20" when X"129", -- Space
X"21" when X"116", -- !
X"22" when X"11e", -- "
X"23" when X"126", -- #
X"24" when X"125", -- $
X"25" when X"12e", --
X"26" when X"136", -- &
X"27" when X"052", --
X"28" when X"13e", -- (
X"29" when X"146", -- )
X"2a" when X"15B", -- *
X"2b" when X"05B", -- +
X"2c" when X"041", -- ,
X"2d" when X"04a", -- -
X"2e" when X"049", -- .
X"2f" when X"13d", -- /
X"30" when X"045", -- 0
X"31" when X"016", -- 1
X"32" when X"01e", -- 2
X"33" when X"026", -- 3
X"34" when X"025", -- 4
X"35" when X"02e", -- 5
X"36" when X"036", -- 6
X"37" when X"03d", -- 7
X"38" when X"03e", -- 8
X"39" when X"046", -- 9
X"3a" when X"14c", -- :
X"3b" when X"141", -- ;
X"3c" when X"0C2", -- <
X"3d" when X"145", -- =
X"3e" when X"1C2", -- >
X"3f" when X"14a", -- ?
--X"40" when X"11e", -- @
X"41" when X"11c", -- A
X"42" when X"132", -- B
X"43" when X"121", -- C
X"44" when X"123", -- D
X"45" when X"124", -- E
X"46" when X"12b", -- F
X"47" when X"134", -- G
X"48" when X"133", -- H
X"49" when X"143", -- I
X"4a" when X"13b", -- J
X"4b" when X"142", -- K
X"4c" when X"14b", -- L
X"4d" when X"13a", -- M
X"4e" when X"131", -- N
X"4f" when X"144", -- O
X"50" when X"14d", -- P
X"51" when X"115", -- Q
X"52" when X"12d", -- R
X"53" when X"11b", -- S
X"54" when X"12c", -- T
X"55" when X"13c", -- U
X"56" when X"12a", -- V
X"57" when X"11d", -- W
X"58" when X"122", -- X
X"59" when X"11a", -- Y
X"5a" when X"135", -- Z
X"5b" when X"2be", -- [
X"5c" when X"05d", -- \
X"5d" when X"246", -- ]
--X"5e" when X"136", -- ^
X"5f" when X"14e", -- _
X"60" when X"00e", -- `
X"41" when X"01c", -- A
X"42" when X"032", -- B
X"43" when X"021", -- C
X"44" when X"023", -- D
X"45" when X"024", -- E
X"46" when X"02b", -- F
X"47" when X"034", -- G
X"48" when X"033", -- H
X"49" when X"043", -- I
X"4a" when X"03b", -- J
X"4b" when X"042", -- K
X"4c" when X"04b", -- L
X"4d" when X"03a", -- M
X"4e" when X"031", -- N
X"4f" when X"044", -- O
X"50" when X"04d", -- P
X"51" when X"015", -- Q
X"52" when X"02d", -- R
X"53" when X"01b", -- S
X"54" when X"02c", -- T
X"55" when X"03c", -- U
X"56" when X"02a", -- V
X"57" when X"01d", -- W
X"58" when X"022", -- X
X"59" when X"01a", -- Y
X"5a" when X"035", -- Z
X"7b" when X"23d", -- {
X"7c" when X"15d", -- |
X"7d" when X"254", -- }
X"7e" when X"10e", -- ~
X"7f" when X"071", -- (Delete OR DEL on numeric keypad)
X"15" when X"074", -- right arrow (cntrl U)
X"08" when X"06b", -- left arrow (BS)
X"0B" when X"075", -- (up arrow)
X"0A" when X"072", -- (down arrow, ^J, LF)
X"7f" when X"171", -- (Delete OR DEL on numeric keypad)
X"00" when others;
end generate DE_de;
EN_us: if KEYMAP = "EN-us" generate
with shifted_code select
ascii <=
X"08" when X"066", -- Backspace ("backspace" key)
X"08" when X"166", -- Backspace ("backspace" key)
X"09" when X"00d", -- Horizontal Tab
X"09" when X"10d", -- Horizontal Tab
X"0d" when X"05a", -- Carriage return ("enter" key)
X"0d" when X"15a", -- Carriage return ("enter" key)
X"1b" when X"076", -- Escape ("esc" key)
X"1b" when X"176", -- Escape ("esc" key)
X"20" when X"029", -- Space
X"20" when X"129", -- Space
X"21" when X"116", -- !
X"22" when X"152", -- "
X"23" when X"126", -- #
X"24" when X"125", -- $
X"25" when X"12e", --
X"26" when X"13d", --
X"27" when X"052", --
X"28" when X"146", --
X"29" when X"145", --
X"2a" when X"13e", -- *
X"2b" when X"155", -- +
X"2c" when X"041", -- ,
X"2d" when X"04e", -- -
X"2e" when X"049", -- .
X"2f" when X"04a", -- /
X"30" when X"045", -- 0
X"31" when X"016", -- 1
X"32" when X"01e", -- 2
X"33" when X"026", -- 3
X"34" when X"025", -- 4
X"35" when X"02e", -- 5
X"36" when X"036", -- 6
X"37" when X"03d", -- 7
X"38" when X"03e", -- 8
X"39" when X"046", -- 9
X"3a" when X"14c", -- :
X"3b" when X"04c", -- ;
X"3c" when X"141", -- <
X"3d" when X"055", -- =
X"3e" when X"149", -- >
X"3f" when X"14a", -- ?
X"40" when X"11e", -- @
X"41" when X"11c", -- A
X"42" when X"132", -- B
X"43" when X"121", -- C
X"44" when X"123", -- D
X"45" when X"124", -- E
X"46" when X"12b", -- F
X"47" when X"134", -- G
X"48" when X"133", -- H
X"49" when X"143", -- I
X"4a" when X"13b", -- J
X"4b" when X"142", -- K
X"4c" when X"14b", -- L
X"4d" when X"13a", -- M
X"4e" when X"131", -- N
X"4f" when X"144", -- O
X"50" when X"14d", -- P
X"51" when X"115", -- Q
X"52" when X"12d", -- R
X"53" when X"11b", -- S
X"54" when X"12c", -- T
X"55" when X"13c", -- U
X"56" when X"12a", -- V
X"57" when X"11d", -- W
X"58" when X"122", -- X
X"59" when X"135", -- Y
X"5a" when X"11a", -- Z
X"5b" when X"054", -- [
X"5c" when X"05d", -- \
X"5d" when X"05b", -- ]
X"5e" when X"136", -- ^
X"5f" when X"14e", -- _
X"60" when X"00e", -- `
X"41" when X"01c", -- A
X"42" when X"032", -- B
X"43" when X"021", -- C
X"44" when X"023", -- D
X"45" when X"024", -- E
X"46" when X"02b", -- F
X"47" when X"034", -- G
X"48" when X"033", -- H
X"49" when X"043", -- I
X"4a" when X"03b", -- J
X"4b" when X"042", -- K
X"4c" when X"04b", -- L
X"4d" when X"03a", -- M
X"4e" when X"031", -- N
X"4f" when X"044", -- O
X"50" when X"04d", -- P
X"51" when X"015", -- Q
X"52" when X"02d", -- R
X"53" when X"01b", -- S
X"54" when X"02c", -- T
X"55" when X"03c", -- U
X"56" when X"02a", -- V
X"57" when X"01d", -- W
X"58" when X"022", -- X
X"59" when X"035", -- Y
X"5a" when X"01a", -- Z
X"7b" when X"154", -- {
X"7c" when X"15d", -- |
X"7d" when X"15b", -- }
X"7e" when X"10e", -- ~
X"7f" when X"071", -- (Delete OR DEL on numeric keypad)
X"15" when X"074", -- right arrow (cntrl U)
X"08" when X"06b", -- left arrow (BS)
X"0B" when X"075", -- (up arrow)
X"0A" when X"072", -- (down arrow, ^J, LF)
X"7f" when X"171", -- (Delete OR DEL on numeric keypad)
X"00" when others;
end generate EN_us;
FR_fr: if KEYMAP = "FR-fr" generate
with shifted_code select
ascii <=
X"08" when X"066", -- Backspace ("backspace" key)
X"08" when X"166", -- Backspace ("backspace" key)
X"09" when X"00d", -- Horizontal Tab
X"09" when X"10d", -- Horizontal Tab
X"0d" when X"05a", -- Carriage return ("enter" key)
X"0d" when X"15a", -- Carriage return ("enter" key)
X"1b" when X"076", -- Escape ("esc" key)
X"1b" when X"176", -- Escape ("esc" key)
X"20" when X"029", -- Space
X"20" when X"129", -- Space
X"21" when X"04a", -- !
X"22" when X"026", -- "
X"23" when X"226", -- #
X"24" when X"05b", -- $
X"25" when X"152", -- %
X"26" when X"016", -- &
X"27" when X"025", -- '
X"28" when X"02e", -- (
X"29" when X"04e", -- )
X"2a" when X"05d", -- *
X"2b" when X"155", -- +
X"2c" when X"03a", -- ,
X"2d" when X"036", -- -
X"2e" when X"141", -- .
X"2f" when X"149", -- /
X"30" when X"145", -- 0
X"31" when X"116", -- 1
X"32" when X"11e", -- 2
X"33" when X"126", -- 3
X"34" when X"125", -- 4
X"35" when X"12e", -- 5
X"36" when X"136", -- 6
X"37" when X"13d", -- 7
X"38" when X"13e", -- 8
X"39" when X"146", -- 9
X"3a" when X"049", -- :
X"3b" when X"041", -- ;
X"3c" when X"061", -- <
X"3d" when X"055", -- =
X"3e" when X"161", -- >
X"3f" when X"13a", -- ?
X"40" when X"245", -- @
X"41" when X"115", -- A
X"42" when X"132", -- B
X"43" when X"121", -- C
X"44" when X"123", -- D
X"45" when X"124", -- E
X"46" when X"12b", -- F
X"47" when X"134", -- G
X"48" when X"133", -- H
X"49" when X"143", -- I
X"4a" when X"13b", -- J
X"4b" when X"142", -- K
X"4c" when X"14b", -- L
X"4d" when X"14c", -- M
X"4e" when X"131", -- N
X"4f" when X"144", -- O
X"50" when X"14d", -- P
X"51" when X"11c", -- Q
X"52" when X"12d", -- R
X"53" when X"11b", -- S
X"54" when X"12c", -- T
X"55" when X"13c", -- U
X"56" when X"12a", -- V
X"57" when X"11a", -- W
X"58" when X"122", -- X
X"59" when X"135", -- Y
X"5a" when X"11d", -- Z
X"5b" when X"22e", -- [
X"5c" when X"23e", -- \
X"5d" when X"24e", -- ]
X"5e" when X"054", -- ^
X"5f" when X"03e", -- _
X"60" when X"23d", -- `
X"41" when X"015", -- A
X"42" when X"032", -- B
X"43" when X"021", -- C
X"44" when X"023", -- D
X"45" when X"024", -- E
X"46" when X"02b", -- F
X"47" when X"034", -- G
X"48" when X"033", -- H
X"49" when X"043", -- I
X"4a" when X"03b", -- J
X"4b" when X"042", -- K
X"4c" when X"04b", -- L
X"4d" when X"04c", -- M
X"4e" when X"031", -- N
X"4f" when X"044", -- O
X"50" when X"04d", -- P
X"51" when X"01c", -- Q
X"52" when X"02d", -- R
X"53" when X"01b", -- S
X"54" when X"02c", -- T
X"55" when X"03c", -- U
X"56" when X"02a", -- V
X"57" when X"01a", -- W
X"58" when X"022", -- X
X"59" when X"035", -- Y
X"5a" when X"01d", -- Z
X"7b" when X"225", -- {
X"7c" when X"236", -- |
X"7d" when X"255", -- }
X"7e" when X"21e", -- ~
X"7f" when X"071", -- (Delete OR DEL on numeric keypad)
X"15" when X"074", -- right arrow (cntrl U)
X"08" when X"06b", -- left arrow (BS)
X"0B" when X"075", -- (up arrow)
X"0A" when X"072", -- (down arrow, ^J, LF)
X"7f" when X"171", -- (Delete OR DEL on numeric keypad)
X"00" when others;
end generate FR_fr;
end rtl;

112
src/papilio_duo.ucf Normal file
View File

@ -0,0 +1,112 @@
# UCF file for the Papilio DUO board
# Generated by pin_converter, written by Kevin Lindsey
# https://github.com/thelonious/papilio_pins/tree/development/pin_converter
## Prohibit the automatic placement of pins that are connected to VCC or GND for configuration.
CONFIG PROHIBIT=P144;
CONFIG PROHIBIT=P69;
CONFIG PROHIBIT=P60;
PIN "pll/clkout2_buf.O" CLOCK_DEDICATED_ROUTE = FALSE;
NET clk LOC="P94" |IOSTANDARD=LVTTL ; # CLK
#NET RX LOC="P46" | IOSTANDARD=LVTTL; # RX
#NET TX LOC="P141" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # TX
#NET ARDUINO_RESET LOC="P139" | IOSTANDARD=LVTTL; # ARDUINO_RESET
#NET RS232_RX LOC="P116" | IOSTANDARD=LVTTL; # A0
#NET RS232_TX LOC="P117" | IOSTANDARD=LVTTL; # A1
NET sd_dat LOC="P118" |IOSTANDARD=LVTTL; # A2
NET sd_cmd LOC="P115" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # B0
NET sd_clk LOC="P114" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # B1
NET sd_dat3 LOC="P112" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # B2
NET ps2_dat LOC="P120" | IOSTANDARD=LVTTL |PULLUP; # A4
NET ps2_clk LOC="P121" | IOSTANDARD=LVTTL |PULLUP; # A5
#NET JOYSTICK1_5 LOC="P123" | IOSTANDARD=LVTTL; # A6
#NET JOYSTICK1_9 LOC="P124" | IOSTANDARD=LVTTL; # A7
#NET JOYSTICK1_4 LOC="P126" | IOSTANDARD=LVTTL; # A8
#NET JOYSTICK1_3 LOC="P127" | IOSTANDARD=LVTTL; # A9
#NET JOYSTICK1_7 LOC="P131" | IOSTANDARD=LVTTL; # A10
#NET JOYSTICK1_2 LOC="P132" | IOSTANDARD=LVTTL; # A11
#NET JOYSTICK1_6 LOC="P133" | IOSTANDARD=LVTTL; # A12
#NET JOYSTICK1_1 LOC="P134" | IOSTANDARD=LVTTL; # A13
#NET SD_nCS LOC="P112" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # B2
#NET SW_LEFT LOC="P111" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # B3
#NET SW_UP LOC="P105" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # B4
NET RESET_I LOC="P102" |IOSTANDARD=LVTTL; # B5
#NET SW_DOWN LOC="P101" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # B6
#NET SW_RIGHT LOC="P100" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # B7
NET vga_hs LOC="P99" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C0
NET vga_vs LOC="P97" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C1
NET vga_b(0) LOC="P93" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C2
NET O_AUDIO_L LOC="P88" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # C3
NET O_AUDIO_R LOC="P85" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # C4
NET vga_b(1) LOC="P83" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C5
NET vga_b(2) LOC="P81" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C6
NET vga_b(3) LOC="P79" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C7
NET vga_g(0) LOC="P75" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C8
NET vga_g(1) LOC="P67" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C9
NET vga_g(2) LOC="P62" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C10
NET vga_g(3) LOC="P59" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C11
NET vga_r(3) LOC="P57" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C12
NET vga_r(2) LOC="P55" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C13
NET vga_r(1) LOC="P50" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C14
NET vga_r(0) LOC="P47" |IOSTANDARD=LVTTL |DRIVE=8 |SLEW=FAST; # C15
#NET JOYSTICK2_5 LOC="P98" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D0
#NET JOYSTICK2_4 LOC="P95" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D1
#NET JOYSTICK2_3 LOC="P92" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D2
#NET JOYSTICK2_2 LOC="P87" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D3
#NET JOYSTICK2_1 LOC="P84" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D4
#NET JOYSTICK2_6 LOC="P82" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D5
#NET JOYSTICK2_7 LOC="P80" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D6
#NET JOYSTICK2_9 LOC="P78" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D7
#NET PS2_CLK2 LOC="P74" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D8
#NET PS2_DAT2 LOC="P66" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D9
#NET AUDIO2_RIGHT LOC="P61" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D10
#NET AUDIO2_LEFT LOC="P58" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # D11
NET LED(0) LOC="P56" | IOSTANDARD=LVTTL | DRIVE=8; # D12
NET LED(1) LOC="P51" | IOSTANDARD=LVTTL | DRIVE=8; # D13
NET LED(2) LOC="P48" | IOSTANDARD=LVTTL | DRIVE=8; # D14
NET LED(3) LOC="P39" | IOSTANDARD=LVTTL | DRIVE=8; # D15
NET sram_addr(0) LOC="P7" |IOSTANDARD=LVTTL | DRIVE=8 | IOB=TRUE |SLEW=FAST ; # SRAM_ADDR0
NET sram_addr(1) LOC="P8" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR1
NET sram_addr(2) LOC="P9" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR2
NET sram_addr(3) LOC="P10" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR3
NET sram_addr(4) LOC="P11" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR4
NET sram_addr(5) LOC="P5" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR5
NET sram_addr(6) LOC="P2" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR6
NET sram_addr(7) LOC="P1" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR7
NET sram_addr(8) LOC="P143" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR8
NET sram_addr(9) LOC="P142" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR9
NET sram_addr(10) LOC="P43" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR10
NET sram_addr(11) LOC="P41" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR11
NET sram_addr(12) LOC="P40" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR12
NET sram_addr(13) LOC="P35" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR13
NET sram_addr(14) LOC="P34" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR14
NET sram_addr(15) LOC="P27" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR15
NET sram_addr(16) LOC="P29" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR16
NET sram_addr(17) LOC="P33" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR17
NET sram_addr(18) LOC="P32" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR18
NET sram_addr(19) LOC="P44" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR19
NET sram_addr(20) LOC="P30" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_ADDR20
NET sram_dq(0) LOC="P14" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA0
NET sram_dq(1) LOC="P15" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA1
NET sram_dq(2) LOC="P16" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA2
NET sram_dq(3) LOC="P17" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA3
NET sram_dq(4) LOC="P21" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA4
NET sram_dq(5) LOC="P22" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA5
NET sram_dq(6) LOC="P23" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST ; # SRAM_DATA6
NET sram_dq(7) LOC="P24" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_DATA7
NET sram_ce_n LOC="P12" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_CE
NET sram_we_n LOC="P6" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_WE
NET sram_oe_n LOC="P26" |IOSTANDARD=LVTTL | DRIVE=8 |IOB=TRUE |SLEW=FAST; # SRAM_OE
#NET JTAG_TMS LOC="P107" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # JTAG_TMS
#NET JTAG_TCK LOC="P109" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # JTAG_TCK
#NET JTAG_TDI LOC="P110" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # JTAG_TDI
#NET JTAG_TDO LOC="P106" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # JTAG_TDO
#NET FLASH_CS LOC="P38" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # FLASH_CS
#NET FLASH_CK LOC="P70" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # FLASH_CK
#NET FLASH_SI LOC="P64" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST; # FLASH_SI
#NET FLASH_SO LOC="P65" | IOSTANDARD=LVTTL | DRIVE=8 | SLEW=FAST | PULLUP; # FLASH_SO
NET "CLK" TNM_NET = CLK;
TIMESPEC TS_CLK = PERIOD "CLK" 31.25 ns HIGH 50%;

471
src/spi_controller.vhd Normal file
View File

@ -0,0 +1,471 @@
-------------------------------------------------------------------------------
--
-- SD/MMC interface (SPI-style) for the Apple ][ Emulator
--
-- Michel Stempin (michel.stempin@wanadoo.fr)
-- Working with MMC/SD/SDHC cards
--
-- From previous work by:
-- Stephen A. Edwards (sedwards@cs.columbia.edu)
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity spi_controller is
generic (
BLOCK_SIZE : natural := 512;
BLOCK_BITS : natural := 9;
TRACK_SIZE : natural := 16#1A00#
);
port (
-- Card Interface ---------------------------------------------------------
CS_N : out std_logic; -- MMC chip select
MOSI : out std_logic; -- Data to card (master out slave in)
MISO : in std_logic; -- Data from card (master in slave out)
SCLK : out std_logic; -- Card clock
-- Track buffer Interface -------------------------------------------------
ram_write_addr : out unsigned(13 downto 0);
ram_di : out unsigned(7 downto 0);
ram_we : out std_logic;
track : in unsigned(5 downto 0); -- Track number (0-34)
image : in unsigned(9 downto 0); -- Which disk image to read
-- System Interface -------------------------------------------------------
CLK_14M : in std_logic; -- System clock
reset : in std_logic
);
end spi_controller;
architecture rtl of spi_controller is
-----------------------------------------------------------------------------
-- States of the combined MMC/SD/SDHC reset, track and command FSM
--
type states is (
-- Reset FSM
POWER_UP,
RAMP_UP,
CHECK_CMD0,
CHECK_CMD8,
CHECK_CMD55,
CHECK_ACMD41,
CHECK_CMD1,
CHECK_CMD58,
CHECK_SET_BLOCKLEN,
ERROR,
-- Track read FSM
IDLE,
READ_TRACK,
READ_BLOCK_WAIT,
READ_BLOCK_DATA,
READ_BLOCK_CRC,
-- SD command embedded FSM
WAIT_NRC,
SEND_CMD,
RECEIVE_BYTE_WAIT,
RECEIVE_BYTE);
--
signal state, return_state : states;
--
-----------------------------------------------------------------------------
signal slow_clk : boolean := true;
signal spi_clk : std_logic;
signal sclk_sig : std_logic;
signal current_track : unsigned(5 downto 0);
signal current_image : unsigned(9 downto 0);
signal write_addr : unsigned(13 downto 0);
signal command : std_logic_vector(5 downto 0);
signal argument : std_logic_vector(31 downto 0);
signal crc7 : std_logic_vector(6 downto 0);
signal command_out : std_logic_vector(55 downto 0);
signal recv_bytes : unsigned(39 downto 0);
type versions is (MMC, SD1x, SD2x);
signal version : versions;
signal high_capacity : boolean;
begin
ram_write_addr <= write_addr;
-----------------------------------------------------------------------------
-- Process var_clkgen
--
-- Purpose:
-- Implements the variable speed clock for MMC compatibility.
-- If slow_clk is false, spi_clk == CLK_14M, thus SCLK = 7M
-- If slow_clk is true, spi_clk = CLK_14M / 32 and SCLK = 223.214kHz, which
-- is between 100kHz and 400kHz, as required for MMC compatibility.
--
var_clkgen : process (CLK_14M, slow_clk)
variable var_clk : unsigned(4 downto 0) := (others => '0');
begin
if slow_clk then
spi_clk <= var_clk(4);
if rising_edge(CLK_14M) then
var_clk := var_clk + 1;
end if;
else
spi_clk <= CLK_14M;
end if;
end process;
SCLK <= sclk_sig;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Process sd_fsm
--
-- Purpose:
-- Implements the combined "SD Card init", "track read" and "command" FSMs.
--
sd_fsm : process(spi_clk)
subtype cmd_t is std_logic_vector(5 downto 0);
constant CMD0 : cmd_t := std_logic_vector(to_unsigned(0, 6));
constant CMD1 : cmd_t := std_logic_vector(to_unsigned(1, 6));
constant CMD8 : cmd_t := std_logic_vector(to_unsigned(8, 6));
constant CMD16 : cmd_t := std_logic_vector(to_unsigned(16, 6));
constant CMD17 : cmd_t := std_logic_vector(to_unsigned(17, 6));
constant CMD55 : cmd_t := std_logic_vector(to_unsigned(55, 6));
constant CMD58 : cmd_t := std_logic_vector(to_unsigned(58, 6));
constant ACMD41 : cmd_t := std_logic_vector(to_unsigned(41, 6));
variable counter : unsigned(7 downto 0);
variable byte_counter : unsigned(BLOCK_BITS - 1 downto 0);
variable lba : unsigned(31 downto 0);
begin
if rising_edge(spi_clk) then
ram_we <= '0';
if reset = '1' then
state <= POWER_UP;
-- Deliberately out of range
current_track <= (others => '1');
current_image <= (others => '1');
sclk_sig <= '0';
slow_clk <= true;
CS_N <= '1';
command <= (others => '0');
argument <= (others => '0');
crc7 <= (others => '0');
command_out <= (others => '1');
counter := TO_UNSIGNED(0, 8);
byte_counter := TO_UNSIGNED(0, BLOCK_BITS);
write_addr <= (others => '0');
high_capacity <= false;
version <= MMC;
lba := (others => '0');
else
case state is
---------------------------------------------------------------------
-- SD Card init FSM
---------------------------------------------------------------------
when POWER_UP =>
counter := TO_UNSIGNED(224, 8);
state <= RAMP_UP;
-- Output a series of 74 clock signals (or 1ms delay, whichever is
-- greater) to wake up the card
when RAMP_UP =>
if counter = 0 then
CS_N <= '0';
command <= CMD0;
argument <= (others => '0');
crc7 <= "1001010";
return_state <= CHECK_CMD0;
state <= WAIT_NRC;
else
counter := counter - 1;
sclk_sig <= not sclk_sig;
end if;
-- CMD0: GO_IDLE_STATE ----------------------------------------------
when CHECK_CMD0 =>
if recv_bytes(7 downto 0) = x"01" then
command <= CMD8;
-- Propose 2.7-3.6V operating voltage and a "10101010" test pattern
argument <= x"000001aa";
crc7 <= "1000011";
return_state <= CHECK_CMD8;
state <= WAIT_NRC;
else
state <= ERROR;
end if;
-- CMD8: SEND_IF_COND -----------------------------------------------
when CHECK_CMD8 =>
argument <= (others => '0');
crc7 <= (others => '0');
if recv_bytes(39 downto 32) <= x"01" then
-- This is an SD 2.x/3.x Card
version <= SD2x;
if recv_bytes(11 downto 8) /= "0001" or recv_bytes(7 downto 0) /= x"aa" then
-- Operating voltage or pattern check failure
state <= ERROR;
else
command <= CMD55;
high_capacity <= true;
return_state <= CHECK_CMD55;
state <= WAIT_NRC;
end if;
else
-- This is an MMC Card or an SD 1.x Card
version <= SD1x;
high_capacity <= false;
command <= CMD55;
return_state <= CHECK_CMD55;
state <= WAIT_NRC;
end if;
-- CMD55: APP_CMD ---------------------------------------------------
when CHECK_CMD55 =>
if recv_bytes(7 downto 0) = x"01" then
-- This is an SD Card
command <= ACMD41;
if high_capacity then
-- Ask for HCS (High Capacity Support)
argument <= x"40000000";
end if;
return_state <= CHECK_ACMD41;
state <= WAIT_NRC;
else
-- This is an MMC Card
version <= MMC;
command <= CMD1;
return_state <= CHECK_CMD1;
state <= WAIT_NRC;
end if;
-- ACMD41: SEND_OP_CMD (SD Card) ------------------------------------
when CHECK_ACMD41 =>
if recv_bytes(7 downto 0) = x"00" then
if version = SD2x then
-- This is an SD 2.x/3.x Card, read OCR
command <= CMD58;
argument <= (others => '0');
return_state <= CHECK_CMD58;
state <= WAIT_NRC;
else
-- This is an SD 1.x Card, no HCS
command <= CMD16;
argument <= std_logic_vector(to_unsigned(BLOCK_SIZE, 32));
return_state <= CHECK_SET_BLOCKLEN;
state <= WAIT_NRC;
end if;
elsif recv_bytes(7 downto 0) = x"01" then
-- Wait until the card goes out of idle state
command <= CMD55;
argument <= (others => '0');
return_state <= CHECK_CMD55;
state <= WAIT_NRC;
else
-- Found an MMC card that understands CMD55, but not ACMD41
command <= CMD1;
return_state <= CHECK_CMD1;
state <= WAIT_NRC;
end if;
-- CMD1: SEND_OP_CMD (MMC Card) -------------------------------------
when CHECK_CMD1 =>
if recv_bytes(7 downto 0) <= x"01" then
command <= CMD16;
argument <= std_logic_vector(to_unsigned(BLOCK_SIZE, 32));
return_state <= CHECK_SET_BLOCKLEN;
state <= WAIT_NRC;
else
-- Wait until the card goes out of idle state
command <= CMD1;
return_state <= CHECK_CMD1;
state <= WAIT_NRC;
end if;
-- CMD58: READ_OCR --------------------------------------------------
when CHECK_CMD58 =>
if recv_bytes(7 downto 0) = x"00" then
if recv_bytes(30) = '1' then
high_capacity <= true;
else
high_capacity <= false;
end if;
command <= CMD16;
argument <= std_logic_vector(to_unsigned(BLOCK_SIZE, 32));
return_state <= CHECK_SET_BLOCKLEN;
state <= WAIT_NRC;
else
state <= ERROR;
end if;
-- CMD16: SET_BLOCKLEN (BLOCK_SIZE) ---------------------------------
when CHECK_SET_BLOCKLEN =>
if recv_bytes(7 downto 0) = x"00" then
slow_clk <= false;
state <= IDLE;
else
state <= ERROR;
end if;
-- Error state ------------------------------------------------------
when ERROR =>
sclk_sig <= '0';
slow_clk <= true;
CS_N <= '1';
---------------------------------------------------------------------
-- Embedded "read track" FSM
---------------------------------------------------------------------
-- Idle state where we sit waiting for user image/track requests ----
when IDLE =>
if track /= current_track or image /= current_image then
-- Compute the LBA (Logical Block Address) from the given
-- image/track numbers.
-- Each Apple ][ floppy image contains 35 tracks, each consisting of
-- 16 x 256-byte sectors.
-- However, because of inter-sector gaps and address fields in
-- raw mode, the actual length is set to 0x1A00, so each image is
-- actually $1A00 bytes * 0x23 tracks = 0x38E00 bytes.
-- So: lba = image * 0x38E00 + track * 0x1A00
-- In order to avoid multiplications by constants, we replace
-- them by direct add/sub of shifted image/track values:
-- 0x38E00 = 0011 1000 1110 0000 0000
-- = 0x40000 - 0x8000 + 0x1000 - 0x200
-- 0x01A00 = 0000 0001 1010 0000 0000
-- = 0x1000 + 0x800 + 0x200
lba := ("0000" & image & "000000000000000000") -
( image & "000000000000000") +
( image & "000000000000") -
( image & "000000000") +
( track & "000000000") +
( track & "00000000000") +
( track & "000000000000");
if high_capacity then
-- For SDHC, blocks are addressed by blocks, not bytes
lba := lba srl BLOCK_BITS;
end if;
write_addr <= (others => '0');
CS_N <= '0';
state <= READ_TRACK;
current_track <= track;
current_image <= image;
else
CS_N <= '1';
sclk_sig <= '1';
end if;
-- Read in a whole track into buffer memory -------------------------
when READ_TRACK =>
if write_addr = TRACK_SIZE then
state <= IDLE;
else
command <= CMD17;
argument <= std_logic_vector(lba);
return_state <= READ_BLOCK_WAIT;
state <= WAIT_NRC;
end if;
-- Wait for a 0 bit to signal the start of the block ----------------
when READ_BLOCK_WAIT =>
if sclk_sig = '1' and MISO = '0' then
state <= READ_BLOCK_DATA;
byte_counter := TO_UNSIGNED(BLOCK_SIZE - 1, BLOCK_BITS);
counter := TO_UNSIGNED(7, 8);
return_state <= READ_BLOCK_DATA;
state <= RECEIVE_BYTE;
end if;
sclk_sig <= not sclk_sig;
-- Read a block of data ---------------------------------------------
when READ_BLOCK_DATA =>
ram_we <= '1';
write_addr <= write_addr + 1;
if byte_counter = 0 then
counter := TO_UNSIGNED(7, 8);
return_state <= READ_BLOCK_CRC;
state <= RECEIVE_BYTE;
else
byte_counter := byte_counter - 1;
counter := TO_UNSIGNED(7, 8);
return_state <= READ_BLOCK_DATA;
state <= RECEIVE_BYTE;
end if;
-- Read the block CRC -----------------------------------------------
when READ_BLOCK_CRC =>
counter := TO_UNSIGNED(7, 8);
return_state <= READ_TRACK;
if high_capacity then
lba := lba + 1;
else
lba := lba + BLOCK_SIZE;
end if;
state <= RECEIVE_BYTE;
---------------------------------------------------------------------
-- Embedded "command" FSM
---------------------------------------------------------------------
-- Wait for card response in front of host command ------------------
when WAIT_NRC =>
counter := TO_UNSIGNED(63, 8);
command_out <= "11111111" & "01" & command & argument & crc7 & "1";
sclk_sig <= not sclk_sig;
state <= SEND_CMD;
-- Send a command to the card ---------------------------------------
when SEND_CMD =>
if sclk_sig = '1' then
if counter = 0 then
state <= RECEIVE_BYTE_WAIT;
else
counter := counter - 1;
command_out <= command_out(54 downto 0) & "1";
end if;
end if;
sclk_sig <= not sclk_sig;
-- Wait for a "0", indicating the first bit of a response -----------
when RECEIVE_BYTE_WAIT =>
if sclk_sig = '1' then
if MISO = '0' then
recv_bytes <= (others => '0');
if command = CMD8 or command = CMD58 then
-- This is an R7 response, but we already have read bit 39
counter := TO_UNSIGNED(38,8);
else
-- This is a data byte or an r1 response, but we already read
-- bit 7
counter := TO_UNSIGNED(6, 8);
end if;
state <= RECEIVE_BYTE;
end if;
end if;
sclk_sig <= not sclk_sig;
-- Receive a byte ---------------------------------------------------
when RECEIVE_BYTE =>
if sclk_sig = '1' then
recv_bytes <= recv_bytes(38 downto 0) & MISO;
if counter = 0 then
state <= return_state;
ram_di <= recv_bytes(6 downto 0) & MISO;
else
counter := counter - 1;
end if;
end if;
sclk_sig <= not sclk_sig;
when others => null;
end case;
end if;
end if;
end process sd_fsm;
MOSI <= command_out(55);
end rtl;

174
src/timing_generator.vhd Normal file
View File

@ -0,0 +1,174 @@
-------------------------------------------------------------------------------
--
-- Apple ][ Timing logic
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
--
-- Taken more-or-less verbatim from the schematics in the
-- Apple ][ reference manual
--
-- This takes a 14.31818 MHz master clock and divides it down to generate
-- the various lower-frequency signals (e.g., 7M, phase 0, colorburst)
-- as well as horizontal and vertical blanking and sync signals for the video
-- and the video addresses.
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity timing_generator is
port (
CLK_14M : in std_logic; -- 14.31818 MHz master clock
CLK_7M_out : out std_logic := '0';
Q3_out : out std_logic := '0'; -- 2 MHz signal in phase with PHI0
RAS_N_out : out std_logic := '0';
CAS_N_out : out std_logic := '0';
AX_out : out std_logic := '0';
PHI0_out : out std_logic := '0'; -- 1.0 MHz processor clock
PRE_PHI0_out : out std_logic := '0'; -- One 14M cycle before
COLOR_REF_out : out std_logic := '0'; -- 3.579545 MHz colorburst
TEXT_MODE : in std_logic;
PAGE2 : in std_logic;
HIRES : in std_logic;
VIDEO_ADDRESS : out unsigned(15 downto 0);
H0 : out std_logic;
VA : out std_logic; -- Character row address
VB : out std_logic;
VC : out std_logic;
V2 : out std_logic;
V4 : out std_logic;
HBL_out : out std_logic; -- Horizontal blanking
VBL_out : out std_logic; -- Vertical blanking
BLANK : out std_logic; -- Composite blanking
LDPS_N : out std_logic;
LD194 : out std_logic
);
end timing_generator;
architecture rtl of timing_generator is
signal H : unsigned(6 downto 0) := "0000000";
signal V : unsigned(8 downto 0) := "011111010";
signal COLOR_DELAY_N : std_logic;
signal CLK_7M : std_logic := '0';
signal Q3 : std_logic := '0'; -- 2 MHz signal in phase with PHI0
signal RAS_N : std_logic := '0';
signal CAS_N : std_logic := '0';
signal AX : std_logic := '0';
signal PHI0 : std_logic := '0'; -- 1.0 MHz processor clock
signal PRE_PHI0 : std_logic := '0'; -- One 14M cycle before
signal COLOR_REF : std_logic := '0'; -- 3.579545 MHz colorburst
signal HBL : std_logic; -- Horizontal blanking
signal VBL : std_logic; -- Vertical blanking
begin
-- To generate the once-a-line hiccup: D1 pin 6
COLOR_DELAY_N <=
not (not COLOR_REF and (not AX and not CAS_N) and PHI0 and not H(6));
CLK_7M_out <= CLK_7M;
Q3_out <= Q3;
RAS_N_out <= RAS_N;
CAS_N_out <= CAS_N;
AX_out <= AX;
PHI0_out <= PHI0; -- 1.0 MHz processor clock
PRE_PHI0_out <= PRE_PHI0; -- One 14M cycle before
COLOR_REF_out <= COLOR_REF; -- 3.579545 MHz colorburst
HBL_out <= HBL; -- Horizontal blanking
VBL_out <= VBL; -- Vertical blanking
-- The DRAM signal generator
C2_74S195: process (CLK_14M)
begin
if rising_edge(CLK_14M) then
if Q3 = '1' then -- shift
(Q3, CAS_N, AX, RAS_N) <=
unsigned'(CAS_N, AX, RAS_N, '0');
else -- load
(Q3, CAS_N, AX, RAS_N) <=
unsigned'(RAS_N, AX, COLOR_DELAY_N, AX);
end if;
end if;
end process;
-- The main clock signal generator
B1_74S175 : process (CLK_14M)
begin
if rising_edge(CLK_14M) then
COLOR_REF <= CLK_7M xor COLOR_REF;
CLK_7M <= not CLK_7M;
PHI0 <= PRE_PHI0;
if AX = '1' then
PRE_PHI0 <= not (Q3 xor PHI0); -- B1 pin 10
end if;
end if;
end process;
LDPS_N <= not (PHI0 and not AX and not CAS_N);
LD194 <= not (PHI0 and not AX and not CAS_N and not CLK_7M);
-- Four four-bit presettable binary counters
-- Seven-bit horizontal counter counts 0, 40, 41, ..., 7F (65 states)
-- Nine-bit vertical counter counts $FA .. $1FF (262 states)
D11D12D13D14_74LS161 : process (CLK_14M)
begin
if rising_edge(CLK_14M) then
-- True the cycle before the rising edge of LDPS_N: emulates
-- the effects of using LDPS_N as the clock for the video counters
if (PHI0 and not AX and ((Q3 and RAS_N) or
(not Q3 and COLOR_DELAY_N))) = '1' then
if H(6) = '0' then H <= "1000000";
else
H <= H + 1;
if H = "1111111" then
V <= V + 1;
if V = "111111111" then V <= "011111010"; end if;
end if;
end if;
end if;
end if;
end process;
H0 <= H(0);
VA <= V(0);
VB <= V(1);
VC <= V(2);
V2 <= V(5);
V4 <= V(7);
HBL <= not (H(5) or (H(3) and H(4)));
VBL <= V(6) and V(7);
BLANK <= HBL or VBL;
-- V_SYNC <= VBL and V(5) and not V(4) and not V(3) and
-- not V(2) and (H(4) or H(3) or H(5));
-- H_SYNC <= HBL and H(3) and not H(2);
-- SYNC <= not (V_SYNC or H_SYNC);
-- COLOR_BURST <= HBL and H(2) and H(3) and (COLOR_REF or TEXT_MODE);
-- Video address calculation
VIDEO_ADDRESS(2 downto 0) <= H(2 downto 0);
VIDEO_ADDRESS(6 downto 3) <= (not H(5) & V(6) & H(4) & H(3)) +
( V(7) & not H(5) & V(7) & '1') +
( "000" & V(6));
VIDEO_ADDRESS(9 downto 7) <= V(5 downto 3);
VIDEO_ADDRESS(14 downto 10) <=
( "00" & HBL & PAGE2 & not PAGE2) when HIRES = '0' else
(PAGE2 & not PAGE2 & V(2 downto 0));
VIDEO_ADDRESS(15) <= '0';
end rtl;

25
src/timing_testbench.vhd Normal file
View File

@ -0,0 +1,25 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity timing_testbench is
end timing_testbench;
architecture behavioral of timing_testbench is
signal CLK_14M : std_logic := '0';
begin
uut : entity work.timing_generator
port map (
CLK_14M => CLK_14M,
TEXT_MODE => '1',
PAGE2 => '0',
HIRES => '1'
);
CLK_14M <= not CLK_14M after 34.920639355 ns;
end behavioral;

326
src/vga_controller.vhd Normal file
View File

@ -0,0 +1,326 @@
-------------------------------------------------------------------------------
--
-- A VGA line-doubler for an Apple ][
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
--
--
-- FIXME: This is all wrong
--
-- The Apple ][ uses a 14.31818 MHz master clock. It outputs a new
-- horizontal line every 65 * 14 + 2 = 912 14M cycles. The extra two
-- are from the "extended cycle" used to keep the 3.579545 MHz
-- colorburst signal in sync. Of these, 40 * 14 = 560 are active video.
--
-- In graphics mode, the Apple effectively generates 140 four-bit pixels
-- output serially (i.e., with 3.579545 MHz pixel clock). In text mode,
-- it generates 280 one-bit pixels (i.e., with a 7.15909 MHz pixel clock).
--
-- We capture 140 four-bit nibbles for each line and interpret them in
-- one of the two modes. In graphics mode, each is displayed as a
-- single pixel of one of 16 colors. In text mode, each is displayed
-- as two black or white pixels.
--
-- The VGA display is nominally 640 X 480, but we use a 14.31818 MHz
-- dot clock. To stay in sync with the Apple, we generate a new line
-- every 912 / 2 = 456 14M cycles= 31.8 us, a 31.4 kHz horizontal
-- refresh rate. Of these, 280 will be active video.
--
-- One set of suggested VGA timings:
--
-- ______________________ ________
-- ________| VIDEO |________| VIDEO
-- |-C-|----------D-----------|-E-|
-- __ ______________________________ ___________
-- |_| |_|
-- |B|
-- |---------------A----------------|
--
-- A = 31.77 us Scanline time
-- B = 3.77 us Horizontal sync time
-- C = 1.89 us Back porch
-- D = 25.17 us Active video
-- E = 0.94 us Front porch
--
-- We use A = 456 / 14.31818 MHz = 31.84 us
-- B = 54 / 14.31818 MHz = 3.77 us
-- C = 106 / 14.31818 MHz = 7.40 us
-- D = 280 / 14.31818 MHz = 19.56 us
-- E = 16 / 14.31818 MHz = 1.12 us
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity vga_controller is
port (
CLK_28M : in std_logic; -- 14.31818 MHz master clock
VIDEO : in std_logic; -- from the Apple video generator
COLOR_LINE : in std_logic;
HBL : in std_logic;
VBL : in std_logic;
LD194 : in std_logic;
--VGA_CLK : out std_logic;
VGA_HS : out std_logic; -- Active low
VGA_VS : out std_logic; -- Active low
--VGA_BLANK : out std_logic;
VGA_R : out unsigned(9 downto 0);
VGA_G : out unsigned(9 downto 0);
VGA_B : out unsigned(9 downto 0)
);
end vga_controller;
architecture rtl of vga_controller is
-- Double-ported RAM (one read port, one write port)
-- that holds two lines of 560 pixels
type line_memory_t is array (0 to 2047) of std_logic;
signal line_memory : line_memory_t;
-- RGB values from Linards Ticmanis,
-- http://newsgroups.derkeiler.com/Archive/Comp/comp.sys.apple2/2005-09/msg00534.html
type basis_color is array(0 to 3) of unsigned(7 downto 0);
constant basis_r : basis_color := ( X"88", X"38", X"07", X"38" );
constant basis_g : basis_color := ( X"22", X"24", X"67", X"52" );
constant basis_b : basis_color := ( X"2C", X"A0", X"2C", X"07" );
signal ram_write_addr : unsigned(10 downto 0);
signal ram_we : std_logic;
signal ram_read_addr : unsigned(10 downto 0);
signal ram_data_out : std_logic;
signal shift_reg : unsigned(5 downto 0); -- Last six pixels
signal last_hbl : std_logic;
signal hcount : unsigned(10 downto 0);
signal hcount2 : unsigned(10 downto 0);
signal vcount : unsigned(5 downto 0);
signal even_line : std_logic;
signal hactive, hactive_early2, hactive_early1 : std_logic;
constant VGA_SCANLINE : integer := 456*2; -- Must be 456*2 (set by the Apple)
constant VGA_HSYNC : integer := 54 * 2;
constant VGA_BACK_PORCH : integer := 66 * 2;
constant VGA_ACTIVE : integer := 282 * 2;
constant VGA_FRONT_PORCH : integer := 54 * 2;
-- VGA_HSYNC + VGA_BACK_PORCH + VGA_ACTIVE + VGA_FRONT_PORCH = VGA_SCANLINE
constant VBL_TO_VSYNC : integer := 33;
constant VGA_VSYNC_LINES : integer := 3;
signal VGA_VS_I, VGA_HS_I : std_logic;
signal video_active : std_logic;
signal vbl_delayed, vbl_delayed2 : std_logic;
signal hbl_delayed : std_logic;
signal color_line_delayed_1, color_line_delayed_2 : std_logic;
signal VGA_R_mono : unsigned(9 downto 0);
signal VGA_G_mono : unsigned(9 downto 0);
signal VGA_B_mono : unsigned(9 downto 0);
signal VGA_R_col : unsigned(9 downto 0);
signal VGA_G_col : unsigned(9 downto 0);
signal VGA_B_col : unsigned(9 downto 0);
signal disp_color : std_logic := '0';
begin
VGA_R <= VGA_R_mono when (COLOR_LINE = '0') else VGA_R_col;
VGA_G <= VGA_G_mono when (COLOR_LINE = '0') else VGA_G_col;
VGA_B <= VGA_B_mono when (COLOR_LINE = '0') else VGA_B_col;
delay_hbl : process (CLK_28M)
begin
if rising_edge(CLK_28M) then
if LD194 = '0' then
hbl_delayed <= HBL;
end if;
end if;
end process;
hcount_vcount_control : process (CLK_28M)
begin
if rising_edge(CLK_28M) then
if last_hbl = '1' and hbl_delayed = '0' then -- Falling edge
color_line_delayed_2 <= color_line_delayed_1;
color_line_delayed_1 <= COLOR_LINE;
hcount <= (others => '0');
vbl_delayed2 <= vbl_delayed;
vbl_delayed <= VBL;
if vbl_delayed = '1' then
even_line <= '0';
vcount <= vcount + 1;
else
vcount <= (others => '0');
even_line <= not even_line;
end if;
else
hcount <= hcount + 1;
end if;
last_hbl <= hbl_delayed;
end if;
end process hcount_vcount_control;
hsync_gen : process (CLK_28M)
begin
if rising_edge(CLK_28M) then
if hcount = VGA_ACTIVE + VGA_FRONT_PORCH or
hcount = VGA_SCANLINE + VGA_ACTIVE + VGA_FRONT_PORCH then
VGA_HS_I <= '0';
elsif hcount = VGA_ACTIVE + VGA_FRONT_PORCH + VGA_HSYNC or
hcount = VGA_SCANLINE + VGA_ACTIVE + VGA_FRONT_PORCH + VGA_HSYNC then
VGA_HS_I <= '1';
end if;
hactive <= hactive_early1;
hactive_early1 <= hactive_early2;
if hcount = VGA_SCANLINE - 1 or
hcount = VGA_SCANLINE + VGA_SCANLINE - 1 then
hactive_early2 <= '1';
elsif hcount = VGA_ACTIVE or
hcount = VGA_ACTIVE + VGA_SCANLINE then
hactive_early2 <= '0';
end if;
end if;
end process hsync_gen;
VGA_HS <= VGA_HS_I;
vsync_gen : process (CLK_28M)
begin
if rising_edge(CLK_28M) then
if vcount = VBL_TO_VSYNC then
VGA_VS_I <= '0';
elsif vcount = VBL_TO_VSYNC + VGA_VSYNC_LINES then
VGA_VS_I <= '1';
end if;
end if;
end process vsync_gen;
VGA_VS <= VGA_VS_I;
hcount2 <= hcount - VGA_SCANLINE;
ram_read_addr <=
even_line & hcount(9 downto 0) when hcount < VGA_SCANLINE else
even_line & hcount2(9 downto 0);
shifter: process (CLK_28M)
begin
if rising_edge(CLK_28M) then
shift_reg <= ram_data_out & shift_reg(5 downto 1);
if shift_reg(0) = shift_reg(4) and shift_reg(5) = shift_reg(1) then
disp_color <= '1';
else
disp_color <= '0';
end if;
end if;
end process;
ram_write_addr <= (not even_line) & hcount(10 downto 1);
ram_we <= '1' when hcount(0) = '1' else '0';
video_active <= hactive and not vbl_delayed2;
pixel_generator: process (CLK_28M)
variable r, g, b : unsigned(7 downto 0);
begin
if rising_edge(CLK_28M) then
r := X"00";
g := X"00";
b := X"00";
if video_active = '1' then
--if color_line_delayed_2 = '0' then -- Monochrome mode
if shift_reg(2) = '1' then
r := X"FF"; g := X"FF"; b := X"FF";
end if;
--end if;
VGA_R_mono <= r & r(7 downto 6);
VGA_G_mono <= g & g(7 downto 6);
VGA_B_mono <= b & b(7 downto 6);
end if;
end if;
end process pixel_generator;
pixel_generator_color: process (CLK_28M)
variable r, g, b : unsigned(7 downto 0);
begin
if rising_edge(CLK_28M) then
r := X"00";
g := X"00";
b := X"00";
if video_active = '1' then
--if disp_color = '1' then
-- Tint of adjacent pixels is consistent : display the color
if shift_reg(1) = '1' then
r := r + basis_r(to_integer(hcount + 1));
g := g + basis_g(to_integer(hcount + 1));
b := b + basis_b(to_integer(hcount + 1));
end if;
if shift_reg(2) = '1' then
r := r + basis_r(to_integer(hcount + 2));
g := g + basis_g(to_integer(hcount + 2));
b := b + basis_b(to_integer(hcount + 2));
end if;
if shift_reg(3) = '1' then
r := r + basis_r(to_integer(hcount + 3));
g := g + basis_g(to_integer(hcount + 3));
b := b + basis_b(to_integer(hcount + 3));
end if;
if shift_reg(4) = '1' then
r := r + basis_r(to_integer(hcount));
g := g + basis_g(to_integer(hcount));
b := b + basis_b(to_integer(hcount));
end if;
-- else
--
-- Tint is changing: display only black, gray, or white
-- case shift_reg(3 downto 2) is
-- when "11" => r := X"FF"; g := X"FF"; b := X"FF";
-- when "01" => r := X"80"; g := X"80"; b := X"80";
-- when "10" => r := X"80"; g := X"80"; b := X"80";
-- when others => r := X"00"; g := X"00"; b := X"00";
-- end case;
--end if;
end if;
VGA_R_col <= r & r(7 downto 6);
VGA_G_col <= g & g(7 downto 6);
VGA_B_col <= b & b(7 downto 6);
end if;
end process pixel_generator_color;
-- The two-port RAM that stores the line data
line_storage : process (CLK_28M)
begin
if rising_edge(CLK_28M) then
if ram_we = '1' then
line_memory(to_integer(ram_write_addr)) <= VIDEO;
end if;
ram_data_out <= line_memory(to_integer(ram_read_addr));
end if;
end process line_storage;
end rtl;

3
tools/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
add_executable(dsk2nib dsk2nib.c)
install(TARGETS dsk2nib DESTINATION dsk2nib)

228
tools/dsk2nib.c Normal file
View File

@ -0,0 +1,228 @@
/***********************************************************************
*
* Apple ][ .dsk file to .nib file format converter
*
* Stephen A. Edwards, sedwards@cs.columbia.edu
*
* Adapted from the "dsk2pdb" program supplied with the PalmApple/Appalm ][
*
***********************************************************************
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned char BYTE;
#define VOLUME_NUMBER 254
#define TRACKS 35
#define SECTORS 16
#define SECTOR_SIZE 256
#define DOS_TRACK_BYTES (SECTORS * SECTOR_SIZE)
#define RAW_TRACK_BYTES 0x1A00
FILE *disk_file;
BYTE dos_track[SECTORS * SECTOR_SIZE];
BYTE raw_track[RAW_TRACK_BYTES];
BYTE *target; /* Where to write in the raw_track buffer */
#define write_byte(x) (*target++ = (x))
BYTE GCR_encoding_table[64] = {
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
int Swap_Bit[4] = { 0, 2, 1, 3 }; /* swap lower 2 bits */
BYTE GCR_buffer[256];
BYTE GCR_buffer2[86];
/* physical sector no. to DOS 3.3 logical sector no. table */
int Logical_Sector[16] = {
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
/*
* write an FM encoded value, used in writing address fields
*/
void FM_encode( BYTE data )
{
write_byte( (data >> 1) | 0xAA );
write_byte( data | 0xAA );
}
/*
* Write 0xFF sync bytes
*/
void write_sync( int length )
{
while( length-- ) write_byte( 0xFF );
}
void write_address_field( int volume, int track, int sector )
{
/*
* write address mark
*/
write_byte( 0xD5 );
write_byte( 0xAA );
write_byte( 0x96 );
/*
* write Volume, Track, Sector & Check-sum
*/
FM_encode( volume );
FM_encode( track );
FM_encode( sector );
FM_encode( volume ^ track ^ sector );
/*
* write epilogue
*/
write_byte( 0xDE );
write_byte( 0xAA );
write_byte( 0xEB );
}
/*
* 6-and-2 group encoding: the heart of the "nibblization" procedure
*/
void encode62( BYTE *page )
{
int i, j;
/* 86 * 3 = 258, so the first two byte are encoded twice */
GCR_buffer2[0] = Swap_Bit[page[1] & 0x03];
GCR_buffer2[1] = Swap_Bit[page[0] & 0x03];
/* save higher 6 bits in GCR_buffer and lower 2 bits in GCR_buffer2 */
for( i = 255, j = 2; i >= 0; i--, j = j == 85? 0: j + 1 ) {
GCR_buffer2[j] = (GCR_buffer2[j] << 2) | Swap_Bit[page[i] & 0x03];
GCR_buffer[i] = page[i] >> 2;
}
/* clear off higher 2 bits of GCR_buffer2 set in the last call */
for( i = 0; i < 86; i++ )
GCR_buffer2[i] &= 0x3f;
}
void write_data_field(BYTE *page)
{
int i;
BYTE last, checksum;
encode62(page);
/* write prologue */
write_byte( 0xD5 );
write_byte( 0xAA );
write_byte( 0xAD );
/* write GCR encoded data */
for ( i = 0x55, last = 0 ; i >= 0 ; --i ) {
checksum = last ^ GCR_buffer2[i];
write_byte( GCR_encoding_table[checksum] );
last = GCR_buffer2[i];
}
for ( i = 0 ; i < 256 ; ++i ) {
checksum = last ^ GCR_buffer[i];
write_byte( GCR_encoding_table[checksum] );
last = GCR_buffer[i];
}
/* write checksum and epilogue */
write_byte( GCR_encoding_table[last] );
write_byte( 0xDE );
write_byte( 0xAA );
write_byte( 0xEB );
}
int main(int argc, char **argv)
{
char nibname[256], *p;
FILE *nib_file;
int track;
if (argc < 2) {
fprintf(stderr, "Usage: %s <DSK file> [NIB file]\n", argv[0]);
exit(1);
}
if (!(disk_file = fopen(argv[1], "rb"))) {
fprintf(stderr, "Unable to mount disk file \"%s\"\n", argv[1]);
exit(1);
}
if (argc > 2) {
strcpy(nibname, argv[2]);
} else {
/* Strip leading pathname from DSK name */
for (p = argv[1]; *p; p++) {
if (*p == '/' || *p == '\\')
argv[1] = p + 1;
}
strcpy(nibname, argv[1]);
/* Strip trailing .dsk, if any, from DSK name */
p = nibname + strlen(nibname);
if (p[-4] == '.' &&
(p[-3] == 'd' || p[-3] == 'D') &&
(p[-2] == 's' || p[-2] == 'S') &&
(p[-1] == 'k' || p[-1] == 'K')) p[-4] = 0;
strcat(nibname, ".nib");
}
if (!(nib_file = fopen(nibname, "wb"))) {
fprintf(stderr, "Unable to write \"%s\"\n", nibname);
exit(1);
}
/* Read, convert, and write each track */
for (track = 0 ; track < TRACKS ; ++track ) {
int sector;
fseek( disk_file, track * DOS_TRACK_BYTES, 0L );
if ( fread(dos_track, 1, DOS_TRACK_BYTES, disk_file) != DOS_TRACK_BYTES ) {
fprintf(stderr, "Unexpected end of disk data\n");
exit(1);
}
target = raw_track;
for ( sector = 0 ; sector < SECTORS ; sector ++ ) {
write_sync( 38 ); /* Inter-sector gap */
write_address_field( VOLUME_NUMBER, track, sector );
write_sync( 8 );
write_data_field( dos_track + Logical_Sector[sector] * SECTOR_SIZE );
}
/* Pad rest of buffer with sync bytes */
while (target != &raw_track[RAW_TRACK_BYTES])
write_byte( 0xff );
if ( fwrite(raw_track, 1, RAW_TRACK_BYTES, nib_file) != RAW_TRACK_BYTES) {
fprintf(stderr, "Error writing .nib file\n");
exit(1);
}
}
fclose(disk_file);
fclose(nib_file);
return 0;
}
/* Local Variables: */
/* compile-command: "cc -O -Wall -pedantic -ansi -o dsk2nib dsk2nib.c" */
/* End: */