From 941c65834e76bd8ebaea514b8a52e44d8915e893 Mon Sep 17 00:00:00 2001 From: sorgelig Date: Fri, 9 Apr 2021 02:20:08 +0800 Subject: [PATCH] Support for TZX. --- ZXNext.sv | 141 +++++++++- files.qip | 6 +- rtl/mister/MiSTer.qip | 7 + rtl/mister/ddram.sv | 119 +++++++++ rtl/mister/tzxplayer.vhd | 528 ++++++++++++++++++++++++++++++++++++++ rtl/mister/zxnext_top.vhd | 10 +- 6 files changed, 798 insertions(+), 13 deletions(-) create mode 100644 rtl/mister/MiSTer.qip create mode 100644 rtl/mister/ddram.sv create mode 100644 rtl/mister/tzxplayer.vhd diff --git a/ZXNext.sv b/ZXNext.sv index 736e4fe..053c54f 100644 --- a/ZXNext.sv +++ b/ZXNext.sv @@ -173,14 +173,13 @@ module emu ); assign USER_OUT = '1; -assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = '0; assign AUDIO_S = 0; // 1 - signed audio samples, 0 - unsigned assign AUDIO_MIX = status[4:3]; assign LED_DISK = 0; assign LED_POWER = 0; -assign LED_USER = sd_act; +assign LED_USER = sd_act | tape_led; assign BUTTONS = 0; assign UART_RTS = 0; @@ -203,6 +202,8 @@ localparam CONF_STR = { "S0,VHD,Mount C:;", "S1,VHD,Mount D:;", "O1,Hard Reset on C: mount,No,Yes;", + "-;", + "F1,TZX,Load Tape;", "-;", "O78,Aspect Ratio,Original,Full Screen,[ARC1],[ARC2];", "O56,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;", @@ -263,6 +264,13 @@ wire [21:0] gamma_bus; wire [64:0] RTC; +wire ioctl_wr; +wire [24:0] ioctl_addr; +wire [15:0] ioctl_dout; +wire ioctl_download; +wire [7:0] ioctl_index; +wire ioctl_wait; + hps_io #(.STRLEN($size(CONF_STR)>>3), .VDNUM(2), .WIDE(1)) hps_io ( .clk_sys(clk_sys), @@ -289,6 +297,13 @@ hps_io #(.STRLEN($size(CONF_STR)>>3), .VDNUM(2), .WIDE(1)) hps_io .img_mounted(img_mounted), .img_size(img_size), + .ioctl_wr(ioctl_wr), + .ioctl_addr(ioctl_addr), + .ioctl_dout(ioctl_dout), + .ioctl_download(ioctl_download), + .ioctl_index(ioctl_index), + .ioctl_wait(ioctl_wait), + .ps2_key(ps2_key), .ps2_mouse(ps2_mouse), .ps2_mouse_ext(ps2_mouse_ext), @@ -576,7 +591,7 @@ rtc #(28000000) rtc wire tape_in; wire tape_adc, tape_adc_act; -assign tape_in = tape_adc_act & tape_adc; +assign tape_in = tape_adc_act ? tape_adc : audio_out; ltc2308_tape #(.CLK_RATE(28000000)) ltc2308_tape ( @@ -586,4 +601,124 @@ ltc2308_tape #(.CLK_RATE(28000000)) ltc2308_tape .active(tape_adc_act) ); +///////////////////////////////////////////////////////////////////// + +assign DDRAM_CLK = CLK_112; +wire clk_tape = CLK_112; + +reg tape_ce; +always @(posedge clk_tape) begin + reg [4:0] div; + reg [1:0] speed; + + div <= div + 1'd1; + if(&div) speed <= cpu_speed; + case(speed) + 0: tape_ce <= !div[4:0]; + 1: tape_ce <= !div[3:0]; + 2: tape_ce <= !div[2:0] & ~RAM_A_WAIT; + 3: tape_ce <= !div[1:0] & ~RAM_A_WAIT; + endcase +end + +ddram ddram +( + .*, + + .wraddr(ioctl_addr[24:1]), + .din(ioctl_dout), + .we_req(ddram_we_req), + .we_ack(ddram_we_ack), + + .rdaddr(tape_addr), + .dout(tape_data), + .rom_req(tape_req), + .rom_ack(tape_ack) +); + +reg ddram_we_req; +wire ddram_we_ack; +always @(posedge clk_sys) if(ioctl_wr) ddram_we_req <= ~ddram_we_req; + +assign ioctl_wait = ddram_we_req ^ ddram_we_ack; +wire tape_download = ioctl_download && (ioctl_index == 1); + +wire tzx_stop, tzx_stop48k, tzx_loop_start, tzx_loop_next, tzx_audio, tzx_req; + +tzxplayer #(.TZX_MS(3500)) tzxplayer +( + .clk(clk_tape), + .ce(tape_ce), + .tzx_req(tzx_req), + .tzx_ack(tape_ack), + .loop_start(tzx_loop_start), + .loop_next(tzx_loop_next), + .stop(tzx_stop), + .stop48k(tzx_stop48k), + .restart_tape(~tape_ready | tape_restart), + .host_tap_in(tape_data), + .cass_read(tzx_audio), + .cass_motor(!play_pause) +); + +wire [7:0] tape_data; +reg tape_req; +wire tape_ack; + +reg key_restart, tape_restart, play_pause; +reg audio_out; +reg [24:0] tape_addr; +reg tape_ready; +always @(posedge clk_tape) begin + reg old_download; + reg [24:0] tape_len, tzx_loop_addr; + reg key_stb1, key_stb2; + reg [24:0] addr; + + tape_restart <= tape_download; + + key_stb1 <= ps2_key[10]; + key_stb2 <= key_stb1; + if(key_stb1 ^ key_stb2) begin + if(ps2_key[8:0] == 'h03 && ps2_key[9]) play_pause <= ~play_pause; // F5 + if(ps2_key[8:0] == 'h0B) tape_restart <= 1; // F6 + if(ps2_key[8:0] == 'h83) tape_ready <= 0; // F7 + end + + if(reset) tape_ready <= 0; + if(!tape_ready) play_pause <= 1; + + tape_req <= tzx_req; + if(tape_restart) begin + addr <= 0; + tape_addr <= 0; + play_pause <= 1; + end + else if(tape_req ^ tzx_req) begin + tape_addr <= addr; + addr <= addr + 1'd1; + if(addr >= tape_len) tape_ready <= 0; + end + + audio_out <= tzx_audio; + if(tzx_stop | tzx_stop48k) play_pause <= 1; + if(tzx_loop_start) tzx_loop_addr <= addr; + if(tzx_loop_next) begin + addr <= tzx_loop_addr + 1'd1; + tape_addr <= tzx_loop_addr; + end + + old_download <= tape_download; + if(old_download & ~tape_download) begin + tape_len <= ioctl_addr; + tape_ready <= 1; + play_pause <= 0; + end +end + +wire tape_led = act_cnt[22] ? act_cnt[21:14] > act_cnt[7:0] : act_cnt[21:14] <= act_cnt[7:0]; + +reg [22:0] act_cnt; +always @(posedge clk_sys) if(~play_pause || ~(tape_ready ^ act_cnt[22]) || act_cnt[21:0]) act_cnt <= act_cnt + 1'd1; + endmodule diff --git a/files.qip b/files.qip index 64d1867..56c88b1 100644 --- a/files.qip +++ b/files.qip @@ -1,3 +1,4 @@ +set_global_assignment -name QIP_FILE rtl/mister/MiSTer.qip set_global_assignment -name VHDL_FILE rtl/audio/ym2149.vhd set_global_assignment -name VHDL_FILE rtl/audio/turbosound.vhd set_global_assignment -name VHDL_FILE rtl/audio/soundrive.vhd @@ -39,10 +40,5 @@ set_global_assignment -name VHDL_FILE rtl/rom/bootrom.vhd set_global_assignment -name VHDL_FILE rtl/device/ctc_chan.vhd set_global_assignment -name VHDL_FILE rtl/device/ctc.vhd set_global_assignment -name VHDL_FILE rtl/zxnext.vhd -set_global_assignment -name VHDL_FILE rtl/mister/ps2_keyb.vhd -set_global_assignment -name VHDL_FILE rtl/mister/bram.vhd -set_global_assignment -name SYSTEMVERILOG_FILE rtl/mister/rtc.sv -set_global_assignment -name SYSTEMVERILOG_FILE rtl/mister/sdram.sv -set_global_assignment -name VHDL_FILE rtl/mister/zxnext_top.vhd set_global_assignment -name SYSTEMVERILOG_FILE ZXNext.sv set_global_assignment -name SDC_FILE ZXNext.sdc diff --git a/rtl/mister/MiSTer.qip b/rtl/mister/MiSTer.qip new file mode 100644 index 0000000..2e59648 --- /dev/null +++ b/rtl/mister/MiSTer.qip @@ -0,0 +1,7 @@ +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) ps2_keyb.vhd ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) bram.vhd ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) tzxplayer.vhd ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) rtc.sv ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) ddram.sv ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) sdram.sv ] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) zxnext_top.vhd ] diff --git a/rtl/mister/ddram.sv b/rtl/mister/ddram.sv new file mode 100644 index 0000000..72ec58d --- /dev/null +++ b/rtl/mister/ddram.sv @@ -0,0 +1,119 @@ +// +// ddram.v +// Copyright (c) 2021 Sorgelig +// +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the 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 . +// +// ------------------------------------------ +// + +// 16-bit version + +module ddram +( + input DDRAM_CLK, + + input DDRAM_BUSY, + output [7:0] DDRAM_BURSTCNT, + output [28:0] DDRAM_ADDR, + input [63:0] DDRAM_DOUT, + input DDRAM_DOUT_READY, + output DDRAM_RD, + output [63:0] DDRAM_DIN, + output [7:0] DDRAM_BE, + output DDRAM_WE, + + input [27:1] wraddr, + input [15:0] din, + input we_req, + output reg we_ack, + + input [27:0] rdaddr, + output [7:0] dout, + input rom_req, + output reg rom_ack +); + +assign DDRAM_BURSTCNT = ram_burst; +assign DDRAM_BE = ram_be | {8{ram_read}}; +assign DDRAM_ADDR = {4'b0011, ram_address}; // RAM at 0x30000000 +assign DDRAM_RD = ram_read; +assign DDRAM_DIN = ram_data; +assign DDRAM_WE = ram_write; + +assign dout = ram_q[{rdaddr[2:0], 3'b000} +:8]; + +reg [7:0] ram_burst; +reg [63:0] ram_q, next_q; +reg [63:0] ram_data; +reg [27:3] ram_address, cache_addr; +reg ram_read = 0; +reg ram_write = 0; +reg [7:0] ram_be = 0; + +always @(posedge DDRAM_CLK) begin + reg [1:0] state = 0; + + if(rom_req != rom_ack && state != 1 && cache_addr == rdaddr[27:3]) rom_ack <= rom_req; + + if(!DDRAM_BUSY) begin + ram_write <= 0; + ram_read <= 0; + + case(state) + 0: if(we_ack != we_req) begin + ram_be <= 8'd3<<{wraddr[2:1],1'b0}; + ram_data <= {4{din}}; + ram_address <= wraddr[27:3]; + ram_write <= 1; + ram_burst <= 1; + cache_addr <= '1; + cache_addr[3] <= 0; + we_ack <= we_req; + end + else if(rom_req != rom_ack) begin + if((cache_addr+1'd1) == rdaddr[27:3]) begin + rom_ack <= rom_req; + ram_q <= next_q; + cache_addr <= rdaddr[27:3]; + ram_address <= rdaddr[27:3]+1'd1; + ram_read <= 1; + ram_burst <= 1; + state <= 2; + end + else if(cache_addr != rdaddr[27:3]) begin + ram_address <= rdaddr[27:3]; + cache_addr <= rdaddr[27:3]; + ram_read <= 1; + ram_burst <= 2; + state <= 1; + end + end + + 1: if(DDRAM_DOUT_READY) begin + ram_q <= DDRAM_DOUT; + rom_ack <= rom_req; + state <= 2; + end + + 2: if(DDRAM_DOUT_READY) begin + next_q <= DDRAM_DOUT; + state <= 0; + end + endcase + end +end + +endmodule diff --git a/rtl/mister/tzxplayer.vhd b/rtl/mister/tzxplayer.vhd new file mode 100644 index 0000000..8a0e94b --- /dev/null +++ b/rtl/mister/tzxplayer.vhd @@ -0,0 +1,528 @@ +--------------------------------------------------------------------------------- +-- TZX player +-- by György Szombathelyi +-- basic idea for the structure based on c1530 tap player by darfpga +-- +--------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use IEEE.STD_LOGIC_ARITH.ALL; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; + +entity tzxplayer is +generic ( + TZX_MS : integer := 64000; -- CE periods for one milliseconds + -- Default: ZX Spectrum + NORMAL_PILOT_LEN : integer := 2168; + NORMAL_SYNC1_LEN : integer := 667; + NORMAL_SYNC2_LEN : integer := 735; + NORMAL_ZERO_LEN : integer := 855; + NORMAL_ONE_LEN : integer := 1710; + NORMAL_PILOT_PULSES : integer := 4031 + + -- Amstrad CPC + --NORMAL_PILOT_LEN : integer := 2000; + --NORMAL_SYNC1_LEN : integer := 855; + --NORMAL_SYNC2_LEN : integer := 855; + --NORMAL_ZERO_LEN : integer := 855; + --NORMAL_ONE_LEN : integer := 1710; + --NORMAL_PILOT_PULSES : integer := 4096; +); +port( + clk : in std_logic; + ce : in std_logic; + restart_tape : in std_logic; + + host_tap_in : in std_logic_vector(7 downto 0); -- 8bits fifo input + tzx_req : buffer std_logic; -- request for new byte (edge trigger) + tzx_ack : in std_logic; -- new data available + loop_start : out std_logic; -- active for one clock if a loop starts + loop_next : out std_logic; -- active for one clock at the next iteration + stop : out std_logic; -- tape should be stopped + stop48k : out std_logic; -- tape should be stopped in 48k mode + + cass_read : buffer std_logic; -- tape read signal + cass_motor : in std_logic -- 1 = tape motor is powered +); +end tzxplayer; + +architecture struct of tzxplayer is + + +signal tap_fifo_do : std_logic_vector(7 downto 0); +signal tick_cnt : std_logic_vector(16 downto 0); +signal wave_cnt : std_logic_vector(15 downto 0); +signal wave_period : std_logic; +signal skip_bytes : std_logic; +signal playing : std_logic; -- 1 = tap or wav file is playing +signal bit_cnt : std_logic_vector(2 downto 0); + +type tzx_state_t is ( + TZX_HEADER, + TZX_NEWBLOCK, + TZX_LOOP_START, + TZX_LOOP_END, + TZX_PAUSE, + TZX_PAUSE2, + TZX_STOP48K, + TZX_HWTYPE, + TZX_TEXT, + TZX_MESSAGE, + TZX_ARCHIVE_INFO, + TZX_CUSTOM_INFO, + TZX_GLUE, + TZX_TONE, + TZX_PULSES, + TZX_DATA, + TZX_NORMAL, + TZX_TURBO, + TZX_PLAY_TONE, + TZX_PLAY_SYNC1, + TZX_PLAY_SYNC2, + TZX_PLAY_TAPBLOCK, + TZX_PLAY_TAPBLOCK2, + TZX_PLAY_TAPBLOCK3, + TZX_PLAY_TAPBLOCK4, + TZX_DIRECT, + TZX_DIRECT2, + TZX_DIRECT3); + +signal tzx_state: tzx_state_t; + +signal tzx_offset : std_logic_vector( 7 downto 0); +signal pause_len : std_logic_vector(15 downto 0); +signal ms_counter : std_logic_vector(15 downto 0); +signal pilot_l : std_logic_vector(15 downto 0); +signal sync1_l : std_logic_vector(15 downto 0); +signal sync2_l : std_logic_vector(15 downto 0); +signal zero_l : std_logic_vector(15 downto 0); +signal one_l : std_logic_vector(15 downto 0); +signal pilot_pulses : std_logic_vector(15 downto 0); +signal last_byte_bits : std_logic_vector( 3 downto 0); +signal data_len : std_logic_vector(23 downto 0); +signal pulse_len : std_logic_vector(15 downto 0); +signal end_period : std_logic; +signal cass_motor_D : std_logic; +signal motor_counter : std_logic_vector(21 downto 0); +signal loop_iter : std_logic_vector(15 downto 0); +signal data_len_dword : std_logic_vector(31 downto 0); + +begin + +tap_fifo_do <= host_tap_in; +process(clk) +begin + if rising_edge(clk) then + if restart_tape = '1' then + + tzx_offset <= (others => '0'); + tzx_state <= TZX_HEADER; + pulse_len <= (others => '0'); + motor_counter <= (others => '0'); + wave_period <= '0'; + playing <= '0'; + tzx_req <= tzx_ack; + loop_start <= '0'; + loop_next <= '0'; + loop_iter <= (others => '0'); + + else + + -- simulate tape motor momentum + -- don't change the playing state if the motor is switched in 50 ms + -- Opera Soft K17 protection needs this! + cass_motor_D <= cass_motor; + if cass_motor_D /= cass_motor then + motor_counter <= CONV_STD_LOGIC_VECTOR(50*TZX_MS, motor_counter'length); + elsif motor_counter /= 0 then + if ce = '1' then motor_counter <= motor_counter - 1; end if; + else + playing <= cass_motor; + end if; + + if playing = '0' then + --cass_read <= '1'; + end if; + + if pulse_len /= 0 then + if ce = '1' then + tick_cnt <= tick_cnt + 3500; + if tick_cnt >= (TZX_MS - 3500) then + tick_cnt <= tick_cnt - (TZX_MS - 3500); + wave_cnt <= wave_cnt + 1; + if wave_cnt = pulse_len then + wave_cnt <= (others => '0'); + cass_read <= wave_period; + wave_period <= not wave_period; + if wave_period = end_period then + pulse_len <= (others => '0'); + end if; + end if; + end if; + end if; + else + tick_cnt <= (others => '0'); + wave_cnt <= (others => '0'); + end if; + + loop_start <= '0'; + loop_next <= '0'; + stop <= '0'; + stop48k <= '0'; + + if playing = '1' and pulse_len = 0 and tzx_req = tzx_ack then + + tzx_req <= not tzx_ack; -- default request for new data + + case tzx_state is + when TZX_HEADER => + cass_read <= '1'; + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"0A" then -- skip 9 bytes, offset lags 1 + tzx_state <= TZX_NEWBLOCK; + end if; + + when TZX_NEWBLOCK => + tzx_offset <= (others=>'0'); + ms_counter <= (others=>'0'); + case tap_fifo_do is + when x"10" => tzx_state <= TZX_NORMAL; + when x"11" => tzx_state <= TZX_TURBO; + when x"12" => tzx_state <= TZX_TONE; + when x"13" => tzx_state <= TZX_PULSES; + when x"14" => tzx_state <= TZX_DATA; + when x"15" => tzx_state <= TZX_DIRECT; + when x"18" => null; -- CSW recording (not implemented) + when x"19" => null; -- Generalized data block (not implemented) + when x"20" => tzx_state <= TZX_PAUSE; + when x"21" => tzx_state <= TZX_TEXT; -- Group start + when x"22" => null; -- Group end + when x"23" => null; -- Jump to block (not implemented) + when x"24" => tzx_state <= TZX_LOOP_START; + when x"25" => tzx_state <= TZX_LOOP_END; + when x"26" => null; -- Call sequence (not implemented) + when x"27" => null; -- Return from sequence (not implemented) + when x"28" => null; -- Select block (not implemented) + when x"2A" => tzx_state <= TZX_STOP48K; + when x"2B" => null; -- Set signal level (not implemented) + when x"30" => tzx_state <= TZX_TEXT; + when x"31" => tzx_state <= TZX_MESSAGE; + when x"32" => tzx_state <= TZX_ARCHIVE_INFO; + when x"33" => tzx_state <= TZX_HWTYPE; + when x"35" => tzx_state <= TZX_CUSTOM_INFO; + when x"5A" => tzx_state <= TZX_GLUE; + when others => null; + end case; + + when TZX_LOOP_START => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then loop_iter( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then + loop_iter(15 downto 8) <= tap_fifo_do; + tzx_state <= TZX_NEWBLOCK; + loop_start <= '1'; + end if; + + when TZX_LOOP_END => + if loop_iter > 1 then + loop_iter <= loop_iter - 1; + loop_next <= '1'; + else + tzx_req <= tzx_ack; -- don't request new byte + end if; + tzx_state <= TZX_NEWBLOCK; + + when TZX_PAUSE => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then + pause_len(7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then + pause_len(15 downto 8) <= tap_fifo_do; + tzx_state <= TZX_PAUSE2; + if pause_len(7 downto 0) = 0 and tap_fifo_do = 0 then + stop <= '1'; + end if; + end if; + + when TZX_PAUSE2 => + tzx_req <= tzx_ack; -- don't request new byte + if ms_counter /= 0 then + if ce = '1' then + ms_counter <= ms_counter - 1; + -- Set pulse level to low after 1 ms + if ms_counter = 1 then + wave_period <= '0'; + end_period <= '0'; + cass_read <= '0'; + end if; + end if; + elsif pause_len /= 0 then + pause_len <= pause_len - 1; + ms_counter <= conv_std_logic_vector(TZX_MS, 16); + else + tzx_state <= TZX_NEWBLOCK; + end if; + + when TZX_STOP48K => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"03" then + stop48k <= '1'; + tzx_state <= TZX_NEWBLOCK; + end if; + + when TZX_HWTYPE => + tzx_offset <= tzx_offset + 1; + -- 0, 1-3, 1-3, ... + if tzx_offset = x"00" then data_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then + if data_len(7 downto 0) = x"01" then + tzx_state <= TZX_NEWBLOCK; + else + data_len(7 downto 0) <= data_len(7 downto 0) - 1; + tzx_offset <= x"01"; + end if; + end if; + + when TZX_MESSAGE => + -- skip display time, then then same as TEXT DESRCRIPTION + tzx_state <= TZX_TEXT; + + when TZX_TEXT => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then data_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = data_len(7 downto 0) then + tzx_state <= TZX_NEWBLOCK; + end if; + + when TZX_ARCHIVE_INFO => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then data_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then data_len(15 downto 8) <= tap_fifo_do; + else + tzx_offset <= x"02"; + data_len <= data_len - 1; + if data_len = 1 then + tzx_state <= TZX_NEWBLOCK; + end if; + end if; + + when TZX_CUSTOM_INFO => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"10" then data_len_dword( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"11" then data_len_dword(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"12" then data_len_dword(23 downto 16) <= tap_fifo_do; + elsif tzx_offset = x"13" then data_len_dword(31 downto 24) <= tap_fifo_do; + elsif tzx_offset = x"14" then + tzx_offset <= x"14"; + if data_len_dword = 1 then + tzx_state <= TZX_NEWBLOCK; + else + data_len_dword <= data_len_dword - 1; + end if; + end if; + + when TZX_GLUE => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"08" then + tzx_state <= TZX_NEWBLOCK; + end if; + + when TZX_TONE => + tzx_offset <= tzx_offset + 1; + -- 0, 1, 2, 3, 4, 4, 4, ... + if tzx_offset = x"00" then pilot_l( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then pilot_l(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"02" then pilot_pulses( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then + tzx_req <= tzx_ack; -- don't request new byte + pilot_pulses(15 downto 8) <= tap_fifo_do; + else + tzx_offset <= x"04"; + tzx_req <= tzx_ack; -- don't request new byte + if pilot_pulses = 0 then + tzx_req <= not tzx_ack; -- default request for new data + tzx_state <= TZX_NEWBLOCK; + else + pilot_pulses <= pilot_pulses - 1; + end_period <= wave_period; + pulse_len <= pilot_l; + end if; + end if; + + when TZX_PULSES => + tzx_offset <= tzx_offset + 1; + -- 0, 1-2+3, 1-2+3, ... + if tzx_offset = x"00" then data_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then one_l( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"02" then + tzx_req <= tzx_ack; -- don't request new byte + end_period <= wave_period; + pulse_len <= tap_fifo_do & one_l( 7 downto 0); + elsif tzx_offset = x"03" then + if data_len(7 downto 0) = x"01" then + tzx_state <= TZX_NEWBLOCK; + else + data_len(7 downto 0) <= data_len(7 downto 0) - 1; + tzx_offset <= x"01"; + end if; + end if; + + when TZX_DATA => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then zero_l ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then zero_l (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"02" then one_l ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then one_l (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"04" then last_byte_bits <= tap_fifo_do(3 downto 0); + elsif tzx_offset = x"05" then pause_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"06" then pause_len(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"07" then data_len ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"08" then data_len (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"09" then + tzx_req <= tzx_ack; -- don't request new byte + data_len (23 downto 16) <= tap_fifo_do; + tzx_state <= TZX_PLAY_TAPBLOCK; + end if; + + when TZX_NORMAL => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then pause_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then pause_len(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"02" then data_len ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then + tzx_req <= tzx_ack; -- don't request new byte + data_len(15 downto 8) <= tap_fifo_do; + data_len(23 downto 16) <= (others => '0'); + pilot_l <= conv_std_logic_vector(NORMAL_PILOT_LEN, 16); + sync1_l <= conv_std_logic_vector(NORMAL_SYNC1_LEN, 16); + sync2_l <= conv_std_logic_vector(NORMAL_SYNC2_LEN, 16); + zero_l <= conv_std_logic_vector(NORMAL_ZERO_LEN, 16); + one_l <= conv_std_logic_vector(NORMAL_ONE_LEN, 16); + pilot_pulses <= conv_std_logic_vector(NORMAL_PILOT_PULSES, 16); + last_byte_bits <= "1000"; + tzx_state <= TZX_PLAY_TONE; + end if; + + when TZX_TURBO => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then pilot_l( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"01" then pilot_l(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"02" then sync1_l( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then sync1_l(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"04" then sync2_l( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"05" then sync2_l(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"06" then zero_l ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"07" then zero_l (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"08" then one_l ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"09" then one_l (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"0A" then pilot_pulses( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"0B" then pilot_pulses(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"0C" then last_byte_bits <= tap_fifo_do(3 downto 0); + elsif tzx_offset = x"0D" then pause_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"0E" then pause_len(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"0F" then data_len ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"10" then data_len (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"11" then + tzx_req <= tzx_ack; -- don't request new byte + data_len (23 downto 16) <= tap_fifo_do; + tzx_state <= TZX_PLAY_TONE; + end if; + + when TZX_PLAY_TONE => + tzx_req <= tzx_ack; -- don't request new byte + end_period <= not wave_period; + pulse_len <= pilot_l; + if pilot_pulses /= 0 then + pilot_pulses <= pilot_pulses - 1; + else + tzx_state <= TZX_PLAY_SYNC1; + end if; + + when TZX_PLAY_SYNC1 => + tzx_req <= tzx_ack; -- don't request new byte + end_period <= wave_period; + pulse_len <= sync1_l; + tzx_state <= TZX_PLAY_SYNC2; + + when TZX_PLAY_SYNC2 => + tzx_req <= tzx_ack; -- don't request new byte + end_period <= wave_period; + pulse_len <= sync2_l; + tzx_state <= TZX_PLAY_TAPBLOCK; + + when TZX_PLAY_TAPBLOCK => + bit_cnt <= "111"; + tzx_state <= TZX_PLAY_TAPBLOCK2; + + when TZX_PLAY_TAPBLOCK2 => + tzx_req <= tzx_ack; -- don't request new byte + bit_cnt <= bit_cnt - 1; + if bit_cnt = "000" or (data_len = 1 and ((bit_cnt = (8 - last_byte_bits)) or (last_byte_bits = 0))) then + data_len <= data_len - 1; + tzx_state <= TZX_PLAY_TAPBLOCK3; + end if; + end_period <= not wave_period; + if tap_fifo_do(CONV_INTEGER(bit_cnt)) = '0' then + pulse_len <= zero_l; + else + pulse_len <= one_l; + end if; + + when TZX_PLAY_TAPBLOCK3 => + if data_len = 0 then + tzx_state <= TZX_PAUSE2; + else + tzx_state <= TZX_PLAY_TAPBLOCK4; + end if; + + when TZX_PLAY_TAPBLOCK4 => + tzx_req <= tzx_ack; -- don't request new byte + tzx_state <= TZX_PLAY_TAPBLOCK2; + + when TZX_DIRECT => + tzx_offset <= tzx_offset + 1; + if tzx_offset = x"00" then zero_l ( 7 downto 0) <= tap_fifo_do; -- here this is used for one bit, too + elsif tzx_offset = x"01" then zero_l (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"02" then pause_len ( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"03" then pause_len (15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"04" then last_byte_bits <= tap_fifo_do(3 downto 0); + elsif tzx_offset = x"05" then data_len( 7 downto 0) <= tap_fifo_do; + elsif tzx_offset = x"06" then data_len(15 downto 8) <= tap_fifo_do; + elsif tzx_offset = x"07" then + data_len(23 downto 16) <= tap_fifo_do; + tzx_state <= TZX_DIRECT2; + bit_cnt <= "111"; + end if; + + when TZX_DIRECT2 => + tzx_req <= tzx_ack; -- don't request new byte + bit_cnt <= bit_cnt - 1; + if bit_cnt = "000" or (data_len = 1 and ((bit_cnt = (8 - last_byte_bits)) or (last_byte_bits = 0))) then + data_len <= data_len - 1; + tzx_state <= TZX_DIRECT3; + end if; + + pulse_len <= zero_l; + cass_read <= tap_fifo_do(CONV_INTEGER(bit_cnt)); + wave_period <= tap_fifo_do(CONV_INTEGER(bit_cnt)); + end_period <= tap_fifo_do(CONV_INTEGER(bit_cnt)); + + when TZX_DIRECT3 => + if data_len = 0 then + tzx_state <= TZX_PAUSE2; + else + tzx_state <= TZX_DIRECT2; + end if; + + when others => null; + end case; + + end if; -- play tzx + + end if; + end if; -- clk +end process; + +end struct; diff --git a/rtl/mister/zxnext_top.vhd b/rtl/mister/zxnext_top.vhd index 2f76f7a..4a49080 100644 --- a/rtl/mister/zxnext_top.vhd +++ b/rtl/mister/zxnext_top.vhd @@ -43,7 +43,7 @@ entity zxnext_top is SW_RESET : in std_logic; CPU_SPEED : out std_logic_vector(1 downto 0); - CPU_WAIT : in std_logic := '0'; + CPU_WAIT : in std_logic := '0'; RAM_A_ADDR : out std_logic_vector(20 downto 0); -- 2MB memory space RAM_A_REQ : out std_logic; -- '1' indicates memory request on next rising edge @@ -222,7 +222,7 @@ begin -- CLOCKS -------------------------------------------------- ------------------------------------------------------------ - CPU_SPEED <= zxn_cpu_speed; + CPU_SPEED <= zxn_cpu_speed2; -- cpu clock selection process (CLK_28) @@ -245,8 +245,8 @@ begin CLK_i0 <= clk_28_div(0); end case; - if clk_28_div(1 downto 0) = "11" and zxn_cpu_speed /= "11" then - zxn_cpu_speed2 <= zxn_cpu_speed; + if clk_28_div(1 downto 0) = "11" then + zxn_cpu_speed2 <= zxn_cpu_speed; end if; end if; end process; @@ -272,7 +272,7 @@ begin q0 <= not (zxn_cpu_speed(1) and zxn_cpu_speed(0)) and q0_enable when rising_edge(CLK_i0); q1 <= (zxn_cpu_speed(1) and zxn_cpu_speed(0)) and q1_enable when rising_edge(CLK_28); - CLK_CPU <= CLK_i0 when q0 = '1' else CLK_28 when q1 = '1' else '1'; + CLK_CPU <= CLK_i0 when q0 = '1' else CLK_28 when q1 = '1' else '1'; -- Clock Enables clk_28_div <= clk_28_div + 1 when rising_edge(CLK_28);