Merge pull request #20 from alanswx/master

Ported Gyurco's floppy code, added version of Gary's clock card
This commit is contained in:
Alexey Melnikov
2023-07-05 23:58:04 +08:00
committed by GitHub
14 changed files with 1236 additions and 277 deletions

View File

@@ -9,46 +9,69 @@
# --------------------------------------------------------------------------
set_global_assignment -name TOP_LEVEL_ENTITY sys_top
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Standard Edition"
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Lite Edition"
set_global_assignment -name GENERATE_RBF_FILE ON
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL
set_global_assignment -name SAVE_DISK_SPACE OFF
set_global_assignment -name SMART_RECOMPILE ON
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
set_global_assignment -name TIMEQUEST_MULTICORNER_ANALYSIS OFF
set_global_assignment -name OPTIMIZE_POWER_DURING_FITTING OFF
set_global_assignment -name FINAL_PLACEMENT_OPTIMIZATION ALWAYS
set_global_assignment -name FITTER_EFFORT "STANDARD FIT"
set_global_assignment -name OPTIMIZATION_MODE "HIGH PERFORMANCE EFFORT"
set_global_assignment -name ALLOW_POWER_UP_DONT_CARE ON
set_global_assignment -name QII_AUTO_PACKED_REGISTERS NORMAL
set_global_assignment -name ROUTER_LCELL_INSERTION_AND_LOGIC_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC ON
set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_RETIMING ON
set_global_assignment -name OPTIMIZATION_TECHNIQUE SPEED
set_global_assignment -name MUX_RESTRUCTURE ON
set_global_assignment -name REMOVE_REDUNDANT_LOGIC_CELLS ON
set_global_assignment -name AUTO_DELAY_CHAINS_FOR_HIGH_FANOUT_INPUT_PINS ON
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC_FOR_AREA ON
set_global_assignment -name ADV_NETLIST_OPT_SYNTH_WYSIWYG_REMAP ON
set_global_assignment -name SYNTH_GATED_CLOCK_CONVERSION ON
set_global_assignment -name PRE_MAPPING_RESYNTHESIS ON
set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON
set_global_assignment -name ECO_OPTIMIZE_TIMING ON
set_global_assignment -name PERIPHERY_TO_CORE_PLACEMENT_AND_ROUTING_OPTIMIZATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_ASYNCHRONOUS_SIGNAL_PIPELINING ON
set_global_assignment -name ALM_REGISTER_PACKING_EFFORT LOW
set_global_assignment -name SEED 1
set_global_assignment -name GENERATE_RBF_FILE ON
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
set_global_assignment -name NUM_PARALLEL_PROCESSORS ALL
set_global_assignment -name SAVE_DISK_SPACE OFF
set_global_assignment -name SMART_RECOMPILE ON
set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40"
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
set_global_assignment -name TIMEQUEST_MULTICORNER_ANALYSIS OFF
set_global_assignment -name OPTIMIZE_POWER_DURING_FITTING OFF
set_global_assignment -name FINAL_PLACEMENT_OPTIMIZATION ALWAYS
set_global_assignment -name FITTER_EFFORT "STANDARD FIT"
set_global_assignment -name OPTIMIZATION_MODE "HIGH PERFORMANCE EFFORT"
set_global_assignment -name ALLOW_POWER_UP_DONT_CARE ON
set_global_assignment -name QII_AUTO_PACKED_REGISTERS NORMAL
set_global_assignment -name ROUTER_LCELL_INSERTION_AND_LOGIC_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC ON
set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_RETIMING ON
set_global_assignment -name OPTIMIZATION_TECHNIQUE SPEED
set_global_assignment -name MUX_RESTRUCTURE ON
set_global_assignment -name REMOVE_REDUNDANT_LOGIC_CELLS ON
set_global_assignment -name AUTO_DELAY_CHAINS_FOR_HIGH_FANOUT_INPUT_PINS ON
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC_FOR_AREA ON
set_global_assignment -name ADV_NETLIST_OPT_SYNTH_WYSIWYG_REMAP ON
set_global_assignment -name SYNTH_GATED_CLOCK_CONVERSION ON
set_global_assignment -name PRE_MAPPING_RESYNTHESIS ON
set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON
set_global_assignment -name ECO_OPTIMIZE_TIMING ON
set_global_assignment -name PERIPHERY_TO_CORE_PLACEMENT_AND_ROUTING_OPTIMIZATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_ASYNCHRONOUS_SIGNAL_PIPELINING ON
set_global_assignment -name ALM_REGISTER_PACKING_EFFORT MEDIUM
set_global_assignment -name SEED 1
#set_global_assignment -name VERILOG_MACRO "MISTER_FB=1"
#enable it only if 8bit indexed mode is used in core
#set_global_assignment -name VERILOG_MACRO "MISTER_FB_PALETTE=1"
#do not enable DEBUG_NOHDMI in release!
#set_global_assignment -name VERILOG_MACRO "MISTER_DEBUG_NOHDMI=1"
# disable bilinear filtering when downscaling
#set_global_assignment -name VERILOG_MACRO "MISTER_DOWNSCALE_NN=1"
# disable adaptive scanline filtering
#set_global_assignment -name VERILOG_MACRO "MISTER_DISABLE_ADAPTIVE=1"
#use only 1MB per frame for scaler to free ~21MB DDR3 RAM
#set_global_assignment -name VERILOG_MACRO "MISTER_SMALL_VBUF=1"
# Disable YC / Composite output to save some resources
#set_global_assignment -name VERILOG_MACRO "MISTER_DISABLE_YC=1"
# Disable ALSA audio output to save some resources
#set_global_assignment -name VERILOG_MACRO "MISTER_DISABLE_ALSA=1"
source sys/sys.tcl
source sys/sys_analog.tcl

View File

@@ -206,6 +206,7 @@ parameter CONF_STR = {
"Apple-II;UART19200:9600:4800:2400:1200:300;",
"-;",
"S0,NIBDSKDO PO ;",
"S2,NIBDSKDO PO ;",
"S1,HDV;",
"-;",
"OCD,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
@@ -251,20 +252,21 @@ wire [7:0] paddle_0;
wire [10:0] ps2_key;
wire [31:0] sd_lba[2];
reg [1:0] sd_rd;
reg [1:0] sd_wr;
wire [1:0] sd_ack;
wire [31:0] sd_lba[3];
reg [2:0] sd_rd;
reg [2:0] sd_wr;
wire [2:0] sd_ack;
wire [8:0] sd_buff_addr;
wire [7:0] sd_buff_dout;
wire [7:0] sd_buff_din[2];
wire [7:0] sd_buff_din[3];
wire sd_buff_wr;
wire [1:0] img_mounted;
wire [2:0] img_mounted;
wire img_readonly;
wire [63:0] img_size;
wire [64:0] RTC;
hps_io #(.CONF_STR(CONF_STR), .VDNUM(2)) hps_io
hps_io #(.CONF_STR(CONF_STR), .VDNUM(3)) hps_io
(
.clk_sys(clk_sys),
.HPS_BUS(HPS_BUS),
@@ -292,7 +294,10 @@ hps_io #(.CONF_STR(CONF_STR), .VDNUM(2)) hps_io
.joystick_0(joystick_0),
.joystick_l_analog_0(joystick_a0),
.paddle_0(paddle_0)
.paddle_0(paddle_0),
.RTC(RTC)
);
///////////////////////////////////////////////////
@@ -351,7 +356,7 @@ apple2_top apple2_top
.CLK_14M(clk_sys),
.CLK_50M(CLK_50M),
.CPU_WAIT(cpu_wait_hdd | cpu_wait_fdd),
.CPU_WAIT(cpu_wait_hdd /*| cpu_wait_fdd*/),
.cpu_type(status[5]),
.reset_cold(RESET | status[0]),
@@ -377,12 +382,25 @@ apple2_top apple2_top
.joy_an(joya),
.mb_enabled(~status[4]),
.TRACK1(TRACK1),
.TRACK1_ADDR(TRACK1_RAM_ADDR),
.TRACK1_DI(TRACK1_RAM_DI),
.TRACK1_DO (TRACK1_RAM_DO),
.TRACK1_WE (TRACK1_RAM_WE),
.TRACK1_BUSY (TRACK1_RAM_BUSY),
//-- Track buffer interface disk 2
.TRACK2(TRACK2),
.TRACK2_ADDR(TRACK2_RAM_ADDR),
.TRACK2_DI(TRACK2_RAM_DI),
.TRACK2_DO (TRACK2_RAM_DO),
.TRACK2_WE (TRACK2_RAM_WE),
.TRACK2_BUSY (TRACK2_RAM_BUSY),
.TRACK(track),
.DISK_RAM_ADDR({track_sec, sd_buff_addr}),
.DISK_RAM_DI(sd_buff_dout),
.DISK_RAM_DO(sd_buff_din[0]),
.DISK_RAM_WE(sd_buff_wr & sd_ack[0]),
.DISK_READY(DISK_READY),
.D1_ACTIVE(D1_ACTIVE),
.D2_ACTIVE(D2_ACTIVE),
.DISK_ACT(led),
.HDD_SECTOR(sd_lba[1]),
.HDD_READ(hdd_read),
@@ -400,14 +418,14 @@ apple2_top apple2_top
.ram_we(ram_we),
.ram_aux(ram_aux),
.DISK_ACT(led),
.UART_TXD(UART_TXD),
.UART_RXD(UART_RXD),
.UART_RTS(UART_RTS),
.UART_CTS(UART_CTS),
.UART_DTR(UART_DTR),
.UART_DSR(UART_DSR)
.UART_DSR(UART_DSR),
.RTC(RTC)
);
@@ -506,51 +524,100 @@ always @(posedge clk_sys) begin
end
end
assign sd_lba[0] = lba_fdd;
wire [5:0] track;
reg [3:0] track_sec;
reg cpu_wait_fdd = 0;
reg [31:0] lba_fdd;
always @(posedge clk_sys) begin
reg state = 0;
reg [5:0] cur_track;
reg fdd_mounted = 0;
reg old_ack = 0;
old_ack <= sd_ack[0];
fdd_mounted <= fdd_mounted | img_mounted[0];
sd_wr[0] <= 0;
if(dd_reset) begin
state <= 0;
cpu_wait_fdd <= 0;
sd_rd[0] <= 0;
end
else if(!state) begin
if((cur_track != track) || (fdd_mounted && ~img_mounted[0])) begin
cur_track <= track;
fdd_mounted <= 0;
if(img_size) begin
track_sec <= 0;
lba_fdd <= 13 * track;
state <= 1;
sd_rd[0] <= 1;
cpu_wait_fdd <= 1;
end
end
end
else begin
if(~old_ack & sd_ack[0]) begin
if(track_sec >= 12) sd_rd[0] <= 0;
lba_fdd <= lba_fdd + 1'd1;
end else if(old_ack & ~sd_ack[0]) begin
track_sec <= track_sec + 1'd1;
if(~sd_rd[0]) state <= 0;
cpu_wait_fdd <= 0;
end
if (img_mounted[0]) begin
disk_mount[0] <= img_size != 0;
DISK_CHANGE[0] <= ~DISK_CHANGE[0];
//disk_protect <= img_readonly;
end
end
always @(posedge clk_sys) begin
if (img_mounted[2]) begin
disk_mount[1] <= img_size != 0;
DISK_CHANGE[1] <= ~DISK_CHANGE[1];
//disk_protect <= img_readonly;
end
end
wire D1_ACTIVE,D2_ACTIVE;
wire TRACK1_RAM_BUSY;
wire [12:0] TRACK1_RAM_ADDR;
wire [7:0] TRACK1_RAM_DI;
wire [7:0] TRACK1_RAM_DO;
wire TRACK1_RAM_WE;
wire [5:0] TRACK1;
wire TRACK2_RAM_BUSY;
wire [12:0] TRACK2_RAM_ADDR;
wire [7:0] TRACK2_RAM_DI;
wire [7:0] TRACK2_RAM_DO;
wire TRACK2_RAM_WE;
wire [5:0] TRACK2;
wire [1:0] DISK_READY;
reg [1:0] DISK_CHANGE;
reg [1:0]disk_mount;
floppy_track floppy_track_1
(
.clk(clk_sys),
.reset(dd_reset),
.ram_addr(TRACK1_RAM_ADDR),
.ram_di(TRACK1_RAM_DI),
.ram_do(TRACK1_RAM_DO),
.ram_we(TRACK1_RAM_WE),
.track (TRACK1),
.busy (TRACK1_RAM_BUSY),
.change(DISK_CHANGE[0]),
.mount (disk_mount[0]),
.ready (DISK_READY[0]),
.active (D1_ACTIVE),
.sd_buff_addr (sd_buff_addr),
.sd_buff_dout (sd_buff_dout),
.sd_buff_din (sd_buff_din[0]),
.sd_buff_wr (sd_buff_wr),
.sd_lba (sd_lba[0] ),
.sd_rd (sd_rd[0]),
.sd_wr ( sd_wr[0]),
.sd_ack (sd_ack[0])
);
floppy_track floppy_track_2
(
.clk(clk_sys),
.reset(dd_reset),
.ram_addr(TRACK2_RAM_ADDR),
.ram_di(TRACK2_RAM_DI),
.ram_do(TRACK2_RAM_DO),
.ram_we(TRACK2_RAM_WE),
.track (TRACK2),
.busy (TRACK2_RAM_BUSY),
.change(DISK_CHANGE[1]),
.mount (disk_mount[1]),
.ready (DISK_READY[1]),
.active (D2_ACTIVE),
.sd_buff_addr (sd_buff_addr),
.sd_buff_dout (sd_buff_dout),
.sd_buff_din (sd_buff_din[2]),
.sd_buff_wr (sd_buff_wr),
.sd_lba (sd_lba[2] ),
.sd_rd (sd_rd[2]),
.sd_wr ( sd_wr[2]),
.sd_ack (sd_ack[2])
);
wire tape_adc, tape_adc_act;
ltc2308_tape ltc2308_tape

View File

@@ -10,7 +10,8 @@ Port for the MiST: http://ws0.org/tag/apple2/
## Features
* disk loading via osd (no write support yet) supported formats: .nib, .dsk, .do, .po
* disk loading via osd. supported formats: .nib, .dsk, .do, .po
NOTE: only .nib will persist saves to disk
* HDD loading via osd
* Tape loading via the ADC-in
* Selectable 6502 or 65C02 CPU
@@ -18,6 +19,7 @@ Port for the MiST: http://ws0.org/tag/apple2/
* scanlines
* color, amber, green and black&white monitor
* language card in slot 0
* prodos compatible clock card in slot 1
* 64K base + 64K auxilary RAM with 80 column and double hi-res support (256KB total with Saturn 128K)
* Saturn 128k RAM expansion in slot 5 (get the utility disks from here: http://apple2online.com/?page_id=3447 , under "Saturn RAMSoft")
* Mockingboard model A (two AY-3-8913 chips for six audio channels) in slot 4

View File

@@ -11,9 +11,14 @@ set_global_assignment -name VERILOG_FILE rtl/ssc/uart_6551.v
set_global_assignment -name VERILOG_FILE rtl/ssc/6551rx.v
set_global_assignment -name VERILOG_FILE rtl/ssc/6551tx.v
set_global_assignment -name VHDL_FILE rtl/ssc/ssc_rom.vhd
set_global_assignment -name VERILOG_FILE rtl/clock_card.v
set_global_assignment -name VERILOG_FILE rtl/rom.v
set_global_assignment -name VHDL_FILE rtl/spram.vhd
set_global_assignment -name VHDL_FILE rtl/disk_ii.vhd
set_global_assignment -name VHDL_FILE rtl/drive_ii.vhd
set_global_assignment -name VHDL_FILE rtl/disk_ii_rom.vhd
set_global_assignment -name VHDL_FILE rtl/dpram.vhd
set_global_assignment -name SYSTEMVERILOG_FILE rtl/floppy_track.sv
set_global_assignment -name VHDL_FILE rtl/keyboard.vhd
set_global_assignment -name VHDL_FILE rtl/timing_generator.vhd
set_global_assignment -name VHDL_FILE rtl/video_generator.vhd

View File

@@ -58,14 +58,32 @@ port (
-- mocking board
mb_enabled : in std_logic;
-- disk control
TRACK : out unsigned(5 downto 0);
DISK_RAM_ADDR : in unsigned(12 downto 0);
DISK_RAM_DI : in unsigned(7 downto 0);
DISK_RAM_DO : out unsigned(7 downto 0);
DISK_RAM_WE : in std_logic;
DISK_ACT : out std_logic;
-- disk control
TRACK1 : out unsigned( 5 downto 0); -- Current track (0-34)
TRACK1_ADDR : out unsigned(12 downto 0);
TRACK1_DI : out unsigned( 7 downto 0);
TRACK1_DO : in unsigned( 7 downto 0);
TRACK1_WE : out std_logic;
TRACK1_BUSY : in std_logic;
-- Track buffer interface disk 2
TRACK2 : out unsigned( 5 downto 0); -- Current track (0-34)
TRACK2_ADDR : out unsigned(12 downto 0);
TRACK2_DI : out unsigned( 7 downto 0);
TRACK2_DO : in unsigned( 7 downto 0);
TRACK2_WE : out std_logic;
TRACK2_BUSY : in std_logic;
D1_ACTIVE : buffer std_logic; -- Disk 1 motor on
D2_ACTIVE : buffer std_logic; -- Disk 2 motor on
DISK_ACT : out std_logic;
DISK_READY : in std_logic_vector(1 downto 0);
-- HDD control
HDD_SECTOR : out unsigned(15 downto 0);
HDD_READ : out std_logic;
@@ -86,7 +104,8 @@ port (
UART_RTS :out std_logic;
UART_CTS :in std_logic;
UART_DTR :out std_logic;
UART_DSR :in std_logic
UART_DSR :in std_logic;
RTC :in std_logic_vector(64 downto 0)
);
end apple2_top;
@@ -117,6 +136,22 @@ architecture arch of apple2_top is
end component;
component clock_card is
port (
CLK_14M : in std_logic;
CLK_2M : in std_logic;
PH_2 : in std_logic;
IO_SELECT_N : in std_logic;
DEVICE_SELECT_N : in std_logic;
IO_STROBE_N : in std_logic;
ADDRESS : std_logic_vector(15 downto 0);
RW_N : in std_logic;
RESET : in std_logic;
DATA_IN : in std_logic_vector(7 downto 0);
DATA_OUT : out std_logic_vector(7 downto 0);
RTC : in std_logic_vector(64 downto 0));
end component;
signal CLK_2M, CLK_2M_D, PHASE_ZERO : std_logic;
@@ -133,6 +168,7 @@ architecture arch of apple2_top is
signal SSC_ROM_EN : std_logic;
signal SSC_DO : unsigned(7 downto 0);
signal CLOCK_DO : unsigned(7 downto 0);
signal we_ram : std_logic;
signal VIDEO, HBL, VBL : std_logic;
@@ -149,7 +185,6 @@ architecture arch of apple2_top is
signal power_on_reset : std_logic := '1';
signal reset : std_logic;
signal D1_ACTIVE, D2_ACTIVE : std_logic;
signal a_ram: unsigned(17 downto 0);
@@ -235,6 +270,7 @@ begin
ram_di <= std_logic_vector(D) when reset_cold = '0' else "00000000";
PD <= PSG_DO when IO_SELECT(4) = '1' and mb_enabled = '1' else
CLOCK_DO when IO_SELECT(1) = '1' or DEVICE_SELECT(1) = '1' else
HDD_DO when IO_SELECT(7) = '1' or DEVICE_SELECT(7) = '1' else
SSC_DO when IO_SELECT(2) = '1' or DEVICE_SELECT(2) = '1' or SSC_ROM_EN ='1' else
DISK_DO;
@@ -302,6 +338,9 @@ begin
closed_apple => closed_apple
);
DISK_ACT <= not (D1_ACTIVE or D2_ACTIVE);
disk : entity work.disk_ii port map (
CLK_14M => CLK_14M,
CLK_2M => CLK_2M,
@@ -309,21 +348,28 @@ begin
IO_SELECT => IO_SELECT(6),
DEVICE_SELECT => DEVICE_SELECT(6),
RESET => reset,
DISK_READY => DISK_READY, -- TODO
A => ADDR,
D_IN => D,
D_OUT => DISK_DO,
TRACK => TRACK,
TRACK_ADDR => open,
D1_ACTIVE => D1_ACTIVE,
D1_ACTIVE => D1_ACTIVE,
D2_ACTIVE => D2_ACTIVE,
ram_write_addr => DISK_RAM_ADDR,
ram_di => DISK_RAM_DI,
ram_we => DISK_RAM_WE
-- track buffer interface for disk 1 -- TODO
TRACK1 => TRACK1,
TRACK1_ADDR => TRACK1_ADDR,
TRACK1_DO => TRACK1_DO,
TRACK1_DI => TRACK1_DI,
TRACK1_WE => TRACK1_WE,
TRACK1_BUSY => TRACK1_BUSY,
-- track buffer interface for disk 2 -- TODO
TRACK2 => TRACK2,
TRACK2_ADDR => TRACK2_ADDR,
TRACK2_DO => TRACK2_DO,
TRACK2_DI => TRACK2_DI,
TRACK2_WE => TRACK2_WE,
TRACK2_BUSY => TRACK2_BUSY
);
DISK_ACT <= D1_ACTIVE or D2_ACTIVE;
DISK_RAM_DO <= (others => '0');
hdd : entity work.hdd port map (
CLK_14M => CLK_14M,
IO_SELECT => IO_SELECT(7),
@@ -386,6 +432,23 @@ begin
IRQ_N => ssc_irq_n
);
clock : component clock_card
port map (
CLK_14M => CLK_14M,
CLK_2M => CLK_2M,
PH_2 => PHASE_ZERO,
IO_SELECT_N => not IO_SELECT(1),
DEVICE_SELECT_N => not DEVICE_SELECT(1),
IO_STROBE_N => NOT IO_STROBE,
ADDRESS => std_logic_vector(ADDR),
RW_N => not cpu_we,
RESET => reset,
DATA_IN => std_logic_vector(D),
unsigned(DATA_OUT) => CLOCK_DO,
RTC => RTC
);
audio(6 downto 0) <= (others => '0');
audio(9 downto 8) <= (others => '0');

265
rtl/clock_card.v Normal file
View File

@@ -0,0 +1,265 @@
`timescale 1ns / 1ps
module clock_card(
input IO_SELECT_N,
input [15:0] ADDRESS,
input RW_N,
// input SYNC -- only slot 7
input IO_STROBE_N,
//output RDY,
//input DMA,
//output IRQ_N,
//output NMI_N,
input RESET,
//input INH_N,
input CLK_14M,
// input CLK_7M,
input CLK_2M,
input PH_2,
input DEVICE_SELECT_N,
input [7:0] DATA_IN,
output [7:0] DATA_OUT,
//output ROM_EN,
input [64:0] RTC
);
reg [20:0] second_div;
reg TICK;
reg V_SYNC;
always @(posedge CLK_2M)
begin
second_div<=second_div+1'b1;
V_SYNC<=1'b0;
//$display("second_div %d",second_div);
if (second_div==('d1000000/'d60))
begin
second_div<=1'b0;
V_SYNC<=1'b1;
$display("V_SYNC is now 1");
end
end
reg [3:0] YEARS_TENS;
reg [3:0] YEARS_ONES;
reg MONTHS_TENS;
reg [3:0] MONTHS_ONES;
reg [2:0] DAY_WEEK;
reg [1:0] DAYS_TENS;
reg [3:0] DAYS_ONES;
reg [1:0] HOURS_TENS;
reg [3:0] HOURS_ONES;
reg [2:0] MINUTES_TENS;
reg [3:0] MINUTES_ONES;
reg [2:0] SECONDS_TENS;
reg [3:0] SECONDS_ONES;
reg [5:0] DEB_COUNTER;
//wire SLOT_IO = (ADDRESS[15:4] == 12'hC0C) ? 1'b1: 1'b0;
wire SLOT_IO = ~DEVICE_SELECT_N;
/*****************************************************************************
* Hardware Clock
******************************************************************************/
wire [7:0] CLK_IN =
({SLOT_IO, ADDRESS[3:0]}== 5'b10000)? 8'h32: // Year thousand
({SLOT_IO, ADDRESS[3:0]}== 5'b10001)? 8'h30: // Year hundreds
({SLOT_IO, ADDRESS[3:0]}== 5'b10010)? {4'h3, YEARS_TENS}: // Year tens
({SLOT_IO, ADDRESS[3:0]}== 5'b10011)? {4'h3, YEARS_ONES}: // Year ones
({SLOT_IO, ADDRESS[3:0]}== 5'b10100)? {7'h18, MONTHS_TENS}: // Month tens
({SLOT_IO, ADDRESS[3:0]}== 5'b10101)? {4'h3, MONTHS_ONES}: // Month ones
({SLOT_IO, ADDRESS[3:0]}== 5'b10110)? {5'h06, DAY_WEEK}: // Day of week
({SLOT_IO, ADDRESS[3:0]}== 5'b10111)? {6'h0C, DAYS_TENS}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11000)? {4'h3, DAYS_ONES}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11001)? {6'h0C, HOURS_TENS}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11010)? {4'h3, HOURS_ONES}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11011)? {5'h06, MINUTES_TENS}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11100)? {4'h3, MINUTES_ONES}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11101)? {5'h06, SECONDS_TENS}:
({SLOT_IO, ADDRESS[3:0]}== 5'b11110)? {4'h3, SECONDS_ONES}:
{2'b00, DEB_COUNTER}; /*Not needed for Apple*/
reg flg;
always @(negedge PH_2)
begin
flg <= RTC[64];
if (flg!=RTC[64])
begin
$display("setting time");
SECONDS_TENS<= RTC[6:4];
SECONDS_ONES<= RTC[3:0];
MINUTES_TENS<= RTC[14:12];
MINUTES_ONES<= RTC[11:8];
HOURS_TENS<= RTC[21:20];
HOURS_ONES<= RTC[19:16];
DAY_WEEK<= RTC[50:48];
DAYS_TENS<= RTC[29:28];
DAYS_ONES<= RTC[27:24];
MONTHS_TENS<= RTC[36];
MONTHS_ONES<= RTC[35:32];
YEARS_TENS <= RTC[47:44];
YEARS_ONES<= RTC[43:40];
end
else
case({RW_N, SLOT_IO, ADDRESS[3:0]})
6'b010010: // Write C0C2
begin
YEARS_TENS <= DATA_IN[3:0]; // 0-9
end
6'b010011: // Write C0C3
begin
YEARS_ONES <= DATA_IN[3:0]; // 0-9
end
6'b010100: // Write C0C4
begin
MONTHS_TENS <= DATA_IN[0]; // 0-1
end
6'b010101: // Write C0C5
begin
MONTHS_ONES <= DATA_IN[3:0]; // 0-9
end
6'b010110: // Write C0C6
begin
DAY_WEEK <= DATA_IN[2:0]; // 0-6
end
6'b010111: // Write C0C7
begin
DAYS_TENS <= DATA_IN[1:0]; // 0-3
end
6'b011000: // Write C0C8
begin
DAYS_ONES <= DATA_IN[3:0]; // 0-9
end
6'b011001: // Write C0C9
begin
HOURS_TENS <= DATA_IN[1:0]; // 0-2
end
6'b011010: // Write C0CA
begin
HOURS_ONES <= DATA_IN[3:0]; // 0-9
end
6'b011011: // Write C0CB
begin
MINUTES_TENS <= DATA_IN[2:0]; // 0-5
end
6'b011100: // Write C0CC
begin
MINUTES_ONES <= DATA_IN[3:0]; // 0-9
end
6'b011101: // Write C0CD
begin
SECONDS_TENS <= DATA_IN[2:0]; // 0-5
end
6'b011110: // Write C0CE
begin
SECONDS_ONES <= DATA_IN[3:0]; // 0-9
end
default:
begin
TICK <= V_SYNC;
if(TICK & ~V_SYNC) // EDGE DETECT
begin
// 1/60 timer
if(DEB_COUNTER == 6'd59)
begin
DEB_COUNTER <= 6'd0;
if(SECONDS_ONES == 4'd9)
begin
SECONDS_ONES <= 4'd0;
if(SECONDS_TENS == 3'd5)
begin
SECONDS_TENS <= 3'd0;
if(MINUTES_ONES == 4'd9)
begin
MINUTES_ONES <= 4'd0;
if(MINUTES_TENS == 3'd5)
begin
MINUTES_TENS <= 3'd0;
if((HOURS_ONES == 4'd9) || ((HOURS_ONES == 4'd3)&&(HOURS_TENS == 3'd2)))
begin
HOURS_ONES <= 3'd0;
if(HOURS_TENS == 3'd2)
begin
HOURS_TENS <= 2'd0;
if(DAYS_ONES == 4'd9)
begin
DAYS_ONES <= 4'd0;
DAYS_TENS <= DAYS_TENS + 1'b1;
end
else
begin
DAYS_ONES <= DAYS_ONES + 1'b1;
end
if(DAY_WEEK == 3'd6)
begin
DAY_WEEK <= 3'd0;
end
else
begin
DAY_WEEK <= DAY_WEEK + 1'b1;
end
end
else
begin
HOURS_TENS <= HOURS_TENS + 1'b1;
end
end
else
begin
HOURS_ONES <= HOURS_ONES + 1'b1;
end
end
else
begin
MINUTES_TENS <= MINUTES_TENS + 1'b1;
end
end
else
begin
MINUTES_ONES <= MINUTES_ONES + 1'b1;
end
end
else
begin
SECONDS_TENS <= SECONDS_TENS + 1'b1;
end
end
else
begin
SECONDS_ONES <= SECONDS_ONES + 1'b1;
end
end
else
begin
$display("increment deb_counter seconds %d SECONDS_ONES %d",DEB_COUNTER,SECONDS_ONES);
DEB_COUNTER <= DEB_COUNTER + 1'b1;
end
end
end
endcase
end
wire [7:0] DOA_CS;
wire [7:0] ROM_ADDR = ADDRESS[7:0];
assign DATA_OUT = ~IO_SELECT_N ? DOA_CS : CLK_IN;
rom #(8,8,"rtl/roms/clock.hex") roms (
.clock(CLK_14M),
.ce(1'b1),
.a(ROM_ADDR),
.data_out(DOA_CS)
);
endmodule

View File

@@ -2,10 +2,10 @@
--
-- 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).
-- This feeds "pre-nibblized" data to the processor.
--
-- Stephen A. Edwards, sedwards@cs.columbia.edu
-- Original by Stephen A. Edwards, sedwards@cs.columbia.edu
-- Write and 2 disks support by (c)2022 Gyorgy Szombathelyi
--
-------------------------------------------------------------------------------
--
@@ -44,10 +44,11 @@
--
-- Writing
-- STA $C08F,X set write mode
-- CMP $C08C,X shift byte to disk
-- ..
-- LDA DATA
-- STA $C08D,X load byte to write
-- STA $C08C,X write byte to disk
-- CMP $C08C,X shift byte to disk
--
-- Data bytes must be written in 32 cycle loops.
--
@@ -68,22 +69,32 @@ use ieee.numeric_std.all;
entity disk_ii is
port (
CLK_14M : in std_logic;
CLK_2M : in std_logic;
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(12 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
CLK_14M : in std_logic;
CLK_2M : in std_logic;
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;
DISK_READY : in std_logic_vector(1 downto 0);
A : in unsigned(15 downto 0);
D_IN : in unsigned( 7 downto 0); -- From 6502
D_OUT : out unsigned( 7 downto 0); -- To 6502
D1_ACTIVE : buffer std_logic; -- Disk 1 motor on
D2_ACTIVE : buffer std_logic; -- Disk 2 motor on
-- Track buffer interface disk 1
TRACK1 : out unsigned( 5 downto 0); -- Current track (0-34)
TRACK1_ADDR : out unsigned(12 downto 0);
TRACK1_DI : out unsigned( 7 downto 0);
TRACK1_DO : in unsigned( 7 downto 0);
TRACK1_WE : out std_logic;
TRACK1_BUSY : in std_logic;
-- Track buffer interface disk 2
TRACK2 : out unsigned( 5 downto 0); -- Current track (0-34)
TRACK2_ADDR : out unsigned(12 downto 0);
TRACK2_DI : out unsigned( 7 downto 0);
TRACK2_DO : in unsigned( 7 downto 0);
TRACK2_WE : out std_logic;
TRACK2_BUSY : in std_logic
);
end disk_ii;
@@ -91,30 +102,26 @@ architecture rtl of disk_ii is
signal motor_phase : std_logic_vector(3 downto 0);
signal drive_on : std_logic;
signal drive_real_on : std_logic; -- 1 sec delay for turning off the drive
signal drive2_select : std_logic;
signal q6, q7 : std_logic;
signal CLK_2M_D: std_logic;
signal rom_dout : unsigned(7 downto 0);
signal d_out1 : unsigned(7 downto 0);
signal d_out2 : 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
signal track_byte_addr : unsigned(12 downto 0);
signal read_disk : std_logic; -- When C08C accessed
signal write_reg : std_logic;
signal data_reg : unsigned(7 downto 0);
signal reset_data_reg : std_logic;
signal write_mode : std_logic; -- When C08E/F accessed
begin
@@ -144,147 +151,96 @@ begin
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_14M, reset)
variable byte_delay : unsigned(5 downto 0); -- Accounts for disk spin rate
drive_on_delay: process (CLK_14M, reset)
variable spindown_delay : unsigned(23 downto 0); -- Accounts for disk spin rate
variable drive_on_old : std_logic;
begin
if reset = '1' then
track_byte_addr <= (others => '0');
byte_delay := (others => '0');
spindown_delay := (others => '0');
drive_real_on <= '0';
elsif rising_edge(CLK_14M) then
CLK_2M_D <= CLK_2M;
if CLK_2M = '1' and CLK_2M_D = '0' then
byte_delay := byte_delay - 1;
if (read_disk = '1' and 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;
if spindown_delay /= 0 then
spindown_delay := spindown_delay - 1;
if spindown_delay = 0 then
drive_real_on <= '0';
end if;
end if;
if drive_on = '1' then
spindown_delay := (others => '0');
drive_real_on <= '1';
elsif drive_on_old = '1' then
spindown_delay := to_unsigned(14000000, 24); -- 1 sec delay
end if;
drive_on_old := drive_on;
end if;
end process;
D1_ACTIVE <= drive_real_on and not drive2_select;
D2_ACTIVE <= drive_real_on and drive2_select;
write_mode <= q7;
read_disk <= '1' when DEVICE_SELECT = '1' and A(3 downto 0) = x"C" else
'0'; -- C08C
write_reg <= '1' when DEVICE_SELECT = '1' and A(3 downto 2) = "11" and A(0) = '1' else
'0'; -- C08F/D
D_OUT <= rom_dout when IO_SELECT = '1' else data_reg when q6 = '0' else x"00";
data_reg <= d_out1 when drive2_select = '0' else d_out2;
drive_1 : entity work.drive_ii
port map (
CLK_14M => CLK_14M,
CLK_2M => CLK_2M,
PHASE_ZERO => PHASE_ZERO,
RESET => RESET,
DISK_READY => DISK_READY(0),
D_IN => D_IN, -- From 6502
D_OUT => d_out1, -- To 6502
DISK_ACTIVE => D1_ACTIVE, -- Disk motor on
MOTOR_PHASE => motor_phase,
WRITE_MODE => write_mode,
READ_DISK => read_disk, -- C08C
WRITE_REG => write_reg, -- C08F/D
-- Track buffer interface
TRACK => TRACK1, -- Current track (0-34)
TRACK_ADDR => TRACK1_ADDR,
TRACK_DI => TRACK1_DI,
TRACK_DO => TRACK1_DO,
TRACK_WE => TRACK1_WE,
TRACK_BUSY => TRACK1_BUSY
);
drive_2 : entity work.drive_ii
port map (
CLK_14M => CLK_14M,
CLK_2M => CLK_2M,
PHASE_ZERO => PHASE_ZERO,
RESET => RESET,
DISK_READY => DISK_READY(1),
D_IN => D_IN, -- From 6502
D_OUT => d_out2, -- To 6502
DISK_ACTIVE => D2_ACTIVE, -- Disk motor on
MOTOR_PHASE => motor_phase,
WRITE_MODE => write_mode,
READ_DISK => read_disk, -- C08C
WRITE_REG => write_reg, -- C08F/D
-- Track buffer interface
TRACK => TRACK2, -- Current track (0-34)
TRACK_ADDR => TRACK2_ADDR,
TRACK_DI => TRACK2_DI,
TRACK_DO => TRACK2_DO,
TRACK_WE => TRACK2_WE,
TRACK_BUSY => TRACK2_BUSY
);
-- ROM
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;

75
rtl/dpram.vhd Normal file
View File

@@ -0,0 +1,75 @@
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
entity dpram is
generic (
addr_width_g : integer := 8;
data_width_g : integer := 8
);
PORT
(
address_a : IN STD_LOGIC_VECTOR (addr_width_g-1 DOWNTO 0);
address_b : IN STD_LOGIC_VECTOR (addr_width_g-1 DOWNTO 0) := (others => '0');
clock_a : IN STD_LOGIC := '1';
clock_b : IN STD_LOGIC := '1';
data_a : IN STD_LOGIC_VECTOR (data_width_g-1 DOWNTO 0);
data_b : IN STD_LOGIC_VECTOR (data_width_g-1 DOWNTO 0) := (others => '0');
enable_a : IN STD_LOGIC := '1';
enable_b : IN STD_LOGIC := '1';
wren_a : IN STD_LOGIC := '0';
wren_b : IN STD_LOGIC := '0';
q_a : OUT STD_LOGIC_VECTOR (data_width_g-1 DOWNTO 0);
q_b : OUT STD_LOGIC_VECTOR (data_width_g-1 DOWNTO 0)
);
END dpram;
ARCHITECTURE SYN OF dpram IS
BEGIN
altsyncram_component : altsyncram
GENERIC MAP (
address_reg_b => "CLOCK1",
clock_enable_input_a => "NORMAL",
clock_enable_input_b => "NORMAL",
clock_enable_output_a => "BYPASS",
clock_enable_output_b => "BYPASS",
indata_reg_b => "CLOCK1",
intended_device_family => "Cyclone V",
lpm_type => "altsyncram",
numwords_a => 2**addr_width_g,
numwords_b => 2**addr_width_g,
operation_mode => "BIDIR_DUAL_PORT",
outdata_aclr_a => "NONE",
outdata_aclr_b => "NONE",
outdata_reg_a => "UNREGISTERED",
outdata_reg_b => "UNREGISTERED",
power_up_uninitialized => "FALSE",
read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ",
read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ",
widthad_a => addr_width_g,
widthad_b => addr_width_g,
width_a => data_width_g,
width_b => data_width_g,
width_byteena_a => 1,
width_byteena_b => 1,
wrcontrol_wraddress_reg_b => "CLOCK1"
)
PORT MAP (
address_a => address_a,
address_b => address_b,
clock0 => clock_a,
clock1 => clock_b,
clocken0 => enable_a,
clocken1 => enable_b,
data_a => data_a,
data_b => data_b,
wren_a => wren_a,
wren_b => wren_b,
q_a => q_a,
q_b => q_b
);
END SYN;

180
rtl/drive_ii.vhd Normal file
View File

@@ -0,0 +1,180 @@
-------------------------------------------------------------------------------
--
-- Disk II emulator - drive part
--
-- This feeds "pre-nibblized" data to the processor.
--
-- Original by Stephen A. Edwards, sedwards@cs.columbia.edu
-- Write support by (c)2022 Gyorgy Szombathelyi
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity drive_ii is
port (
CLK_14M : in std_logic;
CLK_2M : in std_logic;
PHASE_ZERO : in std_logic;
RESET : in std_logic;
DISK_READY : in std_logic;
D_IN : in unsigned( 7 downto 0); -- From 6502
D_OUT : out unsigned( 7 downto 0); -- To 6502
DISK_ACTIVE : in std_logic; -- Disk motor on
MOTOR_PHASE : in std_logic_vector(3 downto 0);
WRITE_MODE : in std_logic;
READ_DISK : in std_logic; -- C08C
WRITE_REG : in std_logic; -- C08F/D
-- Track buffer interface
TRACK : out unsigned( 5 downto 0); -- Current track (0-34)
TRACK_ADDR : out unsigned(12 downto 0);
TRACK_DI : out unsigned( 7 downto 0);
TRACK_DO : in unsigned( 7 downto 0);
TRACK_WE : out std_logic;
TRACK_BUSY : in std_logic
);
end drive_ii;
architecture rtl of drive_ii is
signal CLK_2M_D: std_logic;
-- 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
signal track_byte_addr : unsigned(12 downto 0);
signal data_reg : unsigned(7 downto 0);
signal reset_data_reg : std_logic;
begin
update_phase : process (CLK_14M, reset)
variable phase_change : integer;
variable new_phase : integer;
variable rel_phase : std_logic_vector(3 downto 0);
begin
if reset = '1' then
phase <= TO_UNSIGNED(70, 8); -- Deliberately odd to test reset
elsif rising_edge(CLK_14M) then
if DISK_ACTIVE = '1' then
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);
-- Go to the next byte if the counter times out (read) or when a new byte is written
read_head : process (CLK_14M, reset)
variable byte_delay : unsigned(5 downto 0); -- Accounts for disk spin rate
begin
if reset = '1' then
track_byte_addr <= (others => '0');
byte_delay := (others => '0');
reset_data_reg <= '0';
elsif rising_edge(CLK_14M) then
TRACK_WE <= '0';
CLK_2M_D <= CLK_2M;
if CLK_2M = '1' and CLK_2M_D = '0' and DISK_READY = '1' and DISK_ACTIVE = '1' then
byte_delay := byte_delay - 1;
if WRITE_MODE = '0' then
-- read mode
if reset_data_reg = '1' then
data_reg <= (others => '0');
reset_data_reg <= '0';
end if;
if byte_delay = 0 then
data_reg <= TRACK_DO;
if track_byte_addr = X"19FF" then
track_byte_addr <= (others => '0');
else
track_byte_addr <= track_byte_addr + 1;
end if;
end if;
if READ_DISK = '1' and PHASE_ZERO = '1' then
reset_data_reg <= '1';
end if;
else
-- write mode
if WRITE_REG = '1' then data_reg <= D_IN; end if;
if READ_DISK = '1' and PHASE_ZERO = '1' then
TRACK_WE <= not TRACK_BUSY;
if track_byte_addr = X"19FF" then
track_byte_addr <= (others => '0');
else
track_byte_addr <= track_byte_addr + 1;
end if;
end if;
end if;
end if;
end if;
end process;
D_OUT <= data_reg;
TRACK_ADDR <= track_byte_addr;
TRACK_DI <= data_reg;
end rtl;

167
rtl/floppy_track.sv Normal file
View File

@@ -0,0 +1,167 @@
//
// Apple ][ track read/write interface to MiST
//
// Based on the work of
// Copyright (c) 2016 Sorgelig
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the Lesser GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
/////////////////////////////////////////////////////////////////////////
module floppy_track
(
input clk,
input reset,
output [31:0] sd_lba,
output reg sd_rd,
output reg sd_wr,
input sd_ack,
input [8:0] sd_buff_addr,
input [7:0] sd_buff_dout,
output [7:0] sd_buff_din,
input sd_buff_wr,
input change,
input mount,
input [5:0] track,
output reg ready = 0,
input active,
input [12:0] ram_addr,
output [7:0] ram_do,
input [7:0] ram_di,
input ram_we,
output reg busy
);
assign sd_lba = lba;
reg [31:0] lba;
reg [3:0] rel_lba;
always @(posedge clk) begin
reg old_ack;
reg [5:0] cur_track = 0;
reg old_change;
reg saving = 0;
reg dirty = 0;
old_change <= change;
old_ack <= sd_ack;
if(sd_ack) {sd_rd,sd_wr} <= 0;
if(ready && ram_we) dirty <= 1;
if(~old_change & change) begin
ready <= mount;
cur_track <= 'b111111;
busy <= 0;
sd_rd <= 0;
sd_wr <= 0;
saving<= 0;
dirty <= 0;
end
else
if(reset) begin
cur_track <= 'b111111;
busy <= 0;
sd_rd <= 0;
sd_wr <= 0;
saving<= 0;
dirty <= 0;
end
else
if(busy) begin
if(old_ack && ~sd_ack) begin
if(rel_lba != 4'd12) begin
lba <= lba + 1'd1;
rel_lba <= rel_lba + 1'd1;
if(saving) sd_wr <= 1;
else sd_rd <= 1;
end
else
if(saving && (cur_track != track)) begin
saving <= 0;
cur_track <= track;
rel_lba <= 0;
lba <= track * 8'd13; //track size = 1a00h = 13*512
sd_rd <= 1;
end
else
begin
busy <= 0;
dirty <= 0;
end
end
end
else
if(ready && ((cur_track != track) || (old_change && ~change) || (dirty && ~active)))
if (dirty && cur_track != 'b111111) begin
saving <= 1;
lba <= cur_track * 8'd13;
rel_lba <= 0;
sd_wr <= 1;
busy <= 1;
end
else
begin
saving <= 0;
cur_track <= track;
rel_lba <= 0;
lba <= track * 8'd13; //track size = 1a00h
sd_rd <= 1;
busy <= 1;
dirty <= 0;
end
end
dpram #(13,8) floppy_dpram
(
.clock_a(clk),
.address_a({rel_lba, sd_buff_addr}),
.wren_a(sd_buff_wr & sd_ack),
.data_a(sd_buff_dout),
.q_a(sd_buff_din),
.clock_b(clk),
.address_b(ram_addr),
.wren_b(ram_we),
.data_b(ram_di),
.q_b(ram_do)
);
/*
// Dual port track buffer
reg [7:0] track_ram[13*512];
// IO controller side
always @(posedge clk) begin
sd_buff_din <= track_ram[{rel_lba, sd_buff_addr}];
if (sd_buff_wr & sd_ack) track_ram[{rel_lba, sd_buff_addr}] <= sd_buff_dout;
end
// Disk controller side
always @(posedge clk) begin
ram_do <= track_ram[ram_addr];
if (ram_we) track_ram[ram_addr] <= ram_di;
end
*/
endmodule

27
rtl/rom.v Normal file
View File

@@ -0,0 +1,27 @@
`timescale 1ns / 1ps
//-------------------------------------------------------------------------------------------------
module rom
//-------------------------------------------------------------------------------------------------
#
(
parameter DW = 8,
parameter AW = 14,
parameter FN = "rom8x16K.hex"
)
(
input wire clock,
input wire ce,
output reg [DW-1:0] data_out,
input wire[AW-1:0] a
);
//-------------------------------------------------------------------------------------------------
reg[DW-1:0] d[(2**AW)-1:0];
initial $readmemh(FN, d, 0);
always @(posedge clock) if(ce) data_out<= d[a];
//-------------------------------------------------------------------------------------------------
endmodule
//-------------------------------------------------------------------------------------------------

112
rtl/roms/clock.a65 Normal file
View File

@@ -0,0 +1,112 @@
;This is 6502 assembler code for a ProDOS compatible
;Clock Card
;
; Slot x IO locations
YEAR_TENS = $C082
YEAR_ONES = $C083
MONTH_TENS = $C084
MONTH_ONES = $C085
DAY_WEEK= $C086
DAY_TENS= $C087
DAY_ONES= $C088
HOUR_TENS = $C089
HOUR_ONES = $C08A
MIN_TENS = $C08B
MIN_ONES = $C08C
SEC_TENS = $C08D
SEC_ONES = $C08E
ROMSOFF = $CFFF
; Entry for the clock card
*= $C400 ; do we want this? the card shouldn't be anchored to C400
PHP
SEI
PLP
BIT $FF58
BVS DOS
READ_ENTRY_4
CLC
BCC READ_TIME
WRITE_ENTRY_4
BNE LBL3
DOS
LBL3
rts
READ_TIME
pha
; find slot number
php
sei
STA ROMSOFF
lda $C089 ; for some reason on prodos 1.0.1 ROM was swapped out
lda $C089
jsr $FF58 ; JSR to the ROM, and we will get the stack back
tsx
lda $0100,X ; load the slot prefix into the A
plp
and #$07
asl ; rotate slot prefix into $S0 (left 4 times)
asl
asl
asl
tax ;X will be $S0 for memory locations
; create a comma delimited string for prodos at 200 - first we put the commas in
; format: mo,da,dt,hr,mn
; it looks like we can have ,sec at the end
;mo is the month (01 = January...12 = December) da is the day of the week (00 = Sunday...06 = Saturday) dt is the date (00 through 31) hr is the hour (00 through 23) mn is the minute (00 through 59)
; from: https://prodos8.com/docs/techref/adding-routines-to-prodos/
lda #','+$80
sta $0202
sta $0205
sta $0208
sta $020B
sta $020E
lda MONTH_TENS,X
ora #$80
sta $0200
lda MONTH_ONES,X
ora #$80
sta $0201
lda #'0'+$80 ; Day of week tens is always 0
sta $0203
lda DAY_WEEK,X
ora #$80
sta $0204
lda DAY_TENS,X
ora #$80
sta $0206
lda DAY_ONES,X
ora #$80
sta $0207
lda HOUR_TENS,X
ora #$80
sta $0209
lda HOUR_ONES,X
ora #$80
sta $020A
lda MIN_TENS,X
ora #$80
sta $020C
lda MIN_ONES,X
ora #$80
sta $020D
lda SEC_TENS,X
ora #$80
sta $020F
lda SEC_ONES,X
ora #$80
sta $0210
lda #$8D ; carrage return
sta $0211
ldx #$0E
lda $C08B ; turn ROM back off for prodos 1.0.1 ?
lda $C08B
pla
rts

11
rtl/roms/clock.hex Normal file
View File

@@ -0,0 +1,11 @@
08 78 28 2C 58 FF 70 05 18 90 03 D0 00 60 48 08
78 8D FF CF AD 89 C0 AD 89 C0 20 58 FF BA BD 00
01 28 29 07 0A 0A 0A 0A AA A9 AC 8D 02 02 8D 05
02 8D 08 02 8D 0B 02 8D 0E 02 BD 84 C0 09 80 8D
00 02 BD 85 C0 09 80 8D 01 02 A9 B0 8D 03 02 BD
86 C0 09 80 8D 04 02 BD 87 C0 09 80 8D 06 02 BD
88 C0 09 80 8D 07 02 BD 89 C0 09 80 8D 09 02 BD
8A C0 09 80 8D 0A 02 BD 8B C0 09 80 8D 0C 02 BD
8C C0 09 80 8D 0D 02 BD 8D C0 09 80 8D 0F 02 BD
8E C0 09 80 8D 10 02 A9 8D 8D 11 02 A2 0E AD 8B
C0 AD 8B C0 68 60

View File

@@ -124,6 +124,12 @@ module sys_top
inout [6:0] USER_IO
);
`ifdef MISTER_DUAL_SDRAM
`ifndef MISTER_DISABLE_YC
`define MISTER_DISABLE_YC
`endif
`endif
////////////////////// Secondary SD ///////////////////////////////////
wire SD_CS, SD_CLK, SD_MOSI;