mirror of
https://github.com/mandl/Apple_II_vhdl.git
synced 2024-12-26 19:29:24 +00:00
add more source
This commit is contained in:
parent
5258fcf4d9
commit
b77730fcec
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
project (Tools)
|
||||
find_package(PkgConfig)
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
add_subdirectory(tools)
|
||||
|
||||
|
||||
|
||||
|
17
README.md
17
README.md
@ -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
38
make_roms.sh
Executable 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
45
src/Debouncer.vhd
Normal 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
71
src/dac.vhd
Normal 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
288
src/disk_ii.vhd
Normal 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
204
src/grp_debouncer.vhd
Normal 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
502
src/keyboard_apple.vhd
Normal 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
112
src/papilio_duo.ucf
Normal 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
471
src/spi_controller.vhd
Normal 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
174
src/timing_generator.vhd
Normal 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
25
src/timing_testbench.vhd
Normal 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
326
src/vga_controller.vhd
Normal 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
3
tools/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
add_executable(dsk2nib dsk2nib.c)
|
||||
install(TARGETS dsk2nib DESTINATION dsk2nib)
|
228
tools/dsk2nib.c
Normal file
228
tools/dsk2nib.c
Normal 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: */
|
Loading…
Reference in New Issue
Block a user