diff --git a/Apple-II.qsf b/Apple-II.qsf index 56d2347..8130b71 100644 --- a/Apple-II.qsf +++ b/Apple-II.qsf @@ -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 diff --git a/Apple-II.sv b/Apple-II.sv index 61a8d7c..3ed5674 100644 --- a/Apple-II.sv +++ b/Apple-II.sv @@ -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 diff --git a/README.md b/README.md index 4b7168e..726f1de 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/files.qip b/files.qip index 9907510..e3d0b25 100644 --- a/files.qip +++ b/files.qip @@ -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 diff --git a/rtl/apple2_top.vhd b/rtl/apple2_top.vhd index e4a1fa4..ef6f5ac 100644 --- a/rtl/apple2_top.vhd +++ b/rtl/apple2_top.vhd @@ -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'); diff --git a/rtl/clock_card.v b/rtl/clock_card.v new file mode 100644 index 0000000..dbe988c --- /dev/null +++ b/rtl/clock_card.v @@ -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 diff --git a/rtl/disk_ii.vhd b/rtl/disk_ii.vhd index 4eb2644..7142345 100644 --- a/rtl/disk_ii.vhd +++ b/rtl/disk_ii.vhd @@ -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; diff --git a/rtl/dpram.vhd b/rtl/dpram.vhd new file mode 100644 index 0000000..0a7feaa --- /dev/null +++ b/rtl/dpram.vhd @@ -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; diff --git a/rtl/drive_ii.vhd b/rtl/drive_ii.vhd new file mode 100644 index 0000000..31cc38a --- /dev/null +++ b/rtl/drive_ii.vhd @@ -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; diff --git a/rtl/floppy_track.sv b/rtl/floppy_track.sv new file mode 100644 index 0000000..5e5b453 --- /dev/null +++ b/rtl/floppy_track.sv @@ -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 . +// +// +///////////////////////////////////////////////////////////////////////// + +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 diff --git a/rtl/rom.v b/rtl/rom.v new file mode 100644 index 0000000..47ecff1 --- /dev/null +++ b/rtl/rom.v @@ -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 +//------------------------------------------------------------------------------------------------- diff --git a/rtl/roms/clock.a65 b/rtl/roms/clock.a65 new file mode 100644 index 0000000..124f444 --- /dev/null +++ b/rtl/roms/clock.a65 @@ -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 diff --git a/rtl/roms/clock.hex b/rtl/roms/clock.hex new file mode 100644 index 0000000..3dbe747 --- /dev/null +++ b/rtl/roms/clock.hex @@ -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 diff --git a/sys/sys_top.v b/sys/sys_top.v index 6de0011..d87cc54 100644 --- a/sys/sys_top.v +++ b/sys/sys_top.v @@ -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;