mirror of https://github.com/zxdos/zxuno.git
563 lines
21 KiB
Verilog
563 lines
21 KiB
Verilog
`timescale 1ns / 1ps
|
||
`default_nettype none
|
||
|
||
// This file is part of the ZXUNO Spectrum core.
|
||
// Creation date is 11:43:22 2015-06-16 by Miguel Angel Rodriguez Jodar
|
||
// (c)2014-2020 ZXUNO association.
|
||
// ZXUNO official repository: http://svn.zxuno.com/svn/zxuno
|
||
// Username: guest Password: zxuno
|
||
// Github repository for this core: https://github.com/mcleod-ideafix/zxuno_spectrum_core
|
||
//
|
||
// ZXUNO Spectrum core 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.
|
||
//
|
||
// ZXUNO Spectrum core 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 the ZXUNO Spectrum core. If not, see <https://www.gnu.org/licenses/>.
|
||
//
|
||
// Any distributed copy of this file must keep this notice intact.
|
||
|
||
module pzx_player (
|
||
input wire clk,
|
||
input wire sram_access_allowed,
|
||
input wire rst_n,
|
||
//--------------------
|
||
input wire [7:0] zxuno_addr,
|
||
input wire zxuno_regrd,
|
||
input wire zxuno_regwr,
|
||
input wire [7:0] din,
|
||
output wire [7:0] dout,
|
||
output wire oe,
|
||
//--------------------
|
||
input wire in48kmode,
|
||
input wire [1:0] cpu_speed,
|
||
input wire [1:0] memory_register,
|
||
input wire play_in,
|
||
input wire stop_in,
|
||
input wire rewindTo0Counter_in,
|
||
input wire resetTo0Counter_in,
|
||
input wire jump_in,
|
||
output wire pulse_out,
|
||
output wire playing,
|
||
output reg speed_change_allowed,
|
||
//--------------------
|
||
output wire [20:0] sramaddr,
|
||
output wire sramwe,
|
||
input wire [7:0] sramdin,
|
||
output wire [7:0] sramdout
|
||
);
|
||
|
||
// parameter INITSRAM_ADDR = 21'h032000;
|
||
// parameter LENGTH_SRAM = 21'h04E000;
|
||
|
||
// Para placas con 1MB de SRAM o m<>s
|
||
// parameter INITSRAM_ADDR = 21'h080000;
|
||
// parameter ENDSRAM_ADDR = 21'h080000;
|
||
|
||
`include "config.vh"
|
||
|
||
parameter IDLE = 6'd0,
|
||
PROGRESS = 6'd1,
|
||
INCADD = 6'd2,
|
||
READTAG = 6'd3,
|
||
READLTAG1 = 6'd4,
|
||
READLTAG2 = 6'd5,
|
||
READLTAG3 = 6'd6,
|
||
READLTAG4 = 6'd7,
|
||
STOP1 = 6'd8,
|
||
PULSE1 = 6'd9,
|
||
PULSE2 = 6'd10,
|
||
PULSE3 = 6'd11,
|
||
PULSE4 = 6'd12,
|
||
PULSE5 = 6'd13,
|
||
PULSE6 = 6'd14,
|
||
DOPULSE = 6'd15,
|
||
FULLSTOP1 = 6'd16,
|
||
DATA1 = 6'd17,
|
||
DATA2 = 6'd18,
|
||
DATA3 = 6'd19,
|
||
DATA4 = 6'd20,
|
||
DATATAIL1 = 6'd21,
|
||
DATATAIL2 = 6'd22,
|
||
READNPULSE0 = 6'd23,
|
||
READNPULSE1 = 6'd24,
|
||
READDPULSE0_1 = 6'd25,
|
||
READDPULSE0_2 = 6'd26,
|
||
READDPULSE1_1 = 6'd27,
|
||
READDPULSE1_2 = 6'd28,
|
||
READDATA1 = 6'd29,
|
||
OUTPUTBIT1 = 6'd30,
|
||
OUTPUTBIT2 = 6'd31,
|
||
DATADOTAIL = 6'd32;
|
||
|
||
parameter FULLSTOP = 8'd0,
|
||
STOP = 8'd1,
|
||
PULSE = 8'd2,
|
||
DATA = 8'd3,
|
||
BROWSE = 8'd4;
|
||
|
||
wire [20:0] initsram_addr = (memory_register == 2'b00)? 21'h032000 : 21'h080000;
|
||
wire [20:0] length_sram = (memory_register == 2'b00)? 21'h04E000 :
|
||
(memory_register == 2'b01)? 21'h080000 :
|
||
21'h180000;
|
||
reg [20:0] a = 21'h000000;
|
||
reg [20:0] tag_address = 21'h000000;
|
||
assign sramaddr = initsram_addr + a + ((a > 21'hDFFF && memory_register == 2'b00) ? 21'h18000 : 0);
|
||
|
||
reg [20:0] counter0_address = 21'h000000;
|
||
reg flagResetCounter0 = 1'b0;
|
||
|
||
assign oe = (zxuno_addr == SRAMDATA && zxuno_regrd == 1'b1);
|
||
assign sramwe = (zxuno_addr == SRAMDATA && zxuno_regwr == 1'b1);
|
||
assign sramdout = din;
|
||
assign dout = sramdin;
|
||
|
||
reg [1:0] vdeckctrl = 2'b00;
|
||
reg [1:0] vdeckprev = 2'b00;
|
||
always @(posedge clk) begin
|
||
if (rst_n == 1'b0) begin
|
||
vdeckctrl <= 2'b00;
|
||
vdeckprev <= 2'b00;
|
||
end
|
||
else begin
|
||
if (zxuno_addr == VDECKCTRL && zxuno_regwr == 1'b1) begin
|
||
vdeckprev <= vdeckctrl;
|
||
vdeckctrl <= din[1:0];
|
||
end
|
||
end
|
||
end
|
||
wire soft_play_in = vdeckprev[0] & ~vdeckctrl[0];
|
||
wire soft_stop_in = vdeckprev[1] & ~vdeckctrl[1];
|
||
|
||
reg [1:0] edplay = 2'b00;
|
||
reg [1:0] edstop = 2'b00;
|
||
reg [1:0] edjump = 2'b00;
|
||
reg [1:0] edrewind = 2'b00;
|
||
reg [1:0] edresetcounter0 = 2'b00;
|
||
wire play = (edplay == 2'b01) | soft_play_in;
|
||
wire stop = (edstop == 2'b01) | soft_stop_in;
|
||
wire jump = (edjump == 2'b01);
|
||
wire rewindTo0Counter = (edrewind == 2'b01);
|
||
wire resetTo0Counter = (edresetcounter0 == 2'b01);
|
||
always @(posedge clk) begin
|
||
edplay <= {edplay[0], play_in};
|
||
edstop <= {edstop[0], stop_in};
|
||
edjump <= {edjump[0], jump_in};
|
||
edrewind <= {edrewind[0], rewindTo0Counter_in};
|
||
edresetcounter0 <= {edresetcounter0[0], resetTo0Counter_in};
|
||
end
|
||
|
||
// Variables para la reproducci<63>n de audio
|
||
reg play_enabled = 1'b0;
|
||
reg [7:0] tag = 8'h00;
|
||
reg [31:0] lblock = 32'h0; // longitud en bytes del bloque actual
|
||
reg [30:0] duration = 31'h0;
|
||
reg [33:0] cduration = 34'h0; // cuenta ciclos de 28MHz hasta alcanzar el valor de duration
|
||
reg [15:0] pulsecounter = 16'h0;
|
||
reg [15:0] pulse0[0:31];
|
||
reg [15:0] pulse1[0:31];
|
||
reg [4:0] indexpulse = 5'd0;
|
||
reg [4:0] numberpulse0 = 5'd0;
|
||
reg [4:0] numberpulse1 = 5'd0;
|
||
reg [30:0] numberofbits = 31'h0;
|
||
reg [15:0] durationextrapulse = 16'h0000;
|
||
reg [7:0] databyte = 8'h00;
|
||
reg [3:0] countbits = 4'd0;
|
||
reg pulse = 1'b0;
|
||
reg next_pulse = 1'b0;
|
||
assign pulse_out = pulse;
|
||
assign playing = play_enabled;
|
||
wire [33:0] adj_duration = (cpu_speed == 2'b00)? {duration, 3'b000} :
|
||
(cpu_speed == 2'b01)? {1'b0, duration, 2'b00} :
|
||
(cpu_speed == 2'b10)? {2'b00, duration, 1'b0} :
|
||
{3'b000, duration};
|
||
integer i;
|
||
initial begin
|
||
for (i=0;i<32;i=i+1) begin
|
||
pulse0[i] = 8'h00;
|
||
pulse1[i] = 8'h00;
|
||
end
|
||
end
|
||
|
||
reg [5:0] state = IDLE;
|
||
always @(posedge clk) begin
|
||
if (rst_n == 1'b0 || stop) begin
|
||
state <= IDLE;
|
||
play_enabled <= 1'b0;
|
||
a <= 21'h000000;
|
||
if (rst_n == 1'b0) begin
|
||
tag_address <= 21'h000000;
|
||
counter0_address <= 21'h000000;
|
||
end
|
||
end
|
||
else if (play) begin
|
||
play_enabled <= ~play_enabled;
|
||
end
|
||
else if (jump) begin
|
||
pulse <= 1'b0;
|
||
a <= tag_address;
|
||
state <= READTAG;
|
||
end
|
||
else if (rewindTo0Counter) begin
|
||
pulse <= 1'b0;
|
||
a <= counter0_address;
|
||
state <= READTAG;
|
||
end
|
||
else if (resetTo0Counter) begin
|
||
flagResetCounter0 <= 1'b1;
|
||
end
|
||
else if (a == length_sram) begin
|
||
a <= 21'h000000;
|
||
end
|
||
else begin
|
||
case (state)
|
||
IDLE:
|
||
begin
|
||
if ((zxuno_addr == SRAMDATA || zxuno_addr == SRAMADDRINC) && (zxuno_regrd == 1'b1 || zxuno_regwr == 1'b1))
|
||
state <= PROGRESS;
|
||
else if (zxuno_addr == SRAMADDR && zxuno_regwr == 1'b1) begin
|
||
a <= {a[12:0],din};
|
||
state <= PROGRESS;
|
||
end
|
||
else if (play_enabled == 1'b1) begin
|
||
pulse <= 1'b0;
|
||
a <= 21'h000000;
|
||
state <= READTAG;
|
||
end
|
||
end
|
||
PROGRESS:
|
||
begin
|
||
if (zxuno_regrd == 1'b0 && zxuno_regwr == 1'b0) begin
|
||
if (zxuno_addr == SRAMADDRINC)
|
||
state <= INCADD;
|
||
else
|
||
state <= IDLE;
|
||
end
|
||
end
|
||
INCADD:
|
||
begin
|
||
a <= a + 21'd1;
|
||
state <= IDLE;
|
||
end
|
||
//------------------------------------------
|
||
READTAG:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
if (flagResetCounter0) begin
|
||
counter0_address <= a;
|
||
flagResetCounter0 <= 1'b0;
|
||
end
|
||
tag <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READLTAG1;
|
||
end
|
||
READLTAG1:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
lblock[7:0] <= sramdin; // LSB
|
||
a <= a + 21'd1;
|
||
state <= READLTAG2;
|
||
end
|
||
READLTAG2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
lblock[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READLTAG3;
|
||
end
|
||
READLTAG3:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
lblock[23:16] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READLTAG4;
|
||
end
|
||
READLTAG4:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
lblock[31:24] <= sramdin; // Ya tenemos en lblock el tama<6D>o del bloque
|
||
a <= a + 21'd1;
|
||
if (tag == STOP) begin
|
||
if (lblock[7:0] != 8'd1 || in48kmode == 1'b1)
|
||
state <= STOP1;
|
||
else
|
||
state <= READTAG;
|
||
end
|
||
else if (tag == PULSE) begin
|
||
next_pulse <= 1'b0; // Valor inicial del pulso en este bloque
|
||
state <= PULSE1;
|
||
end
|
||
else if (tag == DATA)
|
||
state <= DATA1;
|
||
else if (tag == BROWSE) begin
|
||
state <= READTAG;
|
||
tag_address <= a + 21'd1;
|
||
end
|
||
else begin
|
||
state <= FULLSTOP1;
|
||
durationextrapulse <= 16'h6d60; // 1ms en ciclos de 28MHz
|
||
end
|
||
end
|
||
|
||
FULLSTOP1:
|
||
if (play_enabled == 1'b1) begin
|
||
if (durationextrapulse != 16'h0000)
|
||
durationextrapulse <= durationextrapulse - 1;
|
||
else begin
|
||
play_enabled <= 1'b0;
|
||
//a <= 21'h000000;
|
||
state <= IDLE;
|
||
end
|
||
end
|
||
|
||
STOP1:
|
||
if (play_enabled == 1'b1) begin
|
||
play_enabled <= 1'b0; // se para la maquina de estados
|
||
state <= READTAG;
|
||
end
|
||
PULSE1:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
if (lblock !=32'h00000000) begin
|
||
cduration <= 34'h000000000;
|
||
duration[7:0] <= sramdin;
|
||
pulsecounter <= 16'h0001;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= PULSE2;
|
||
end
|
||
else begin
|
||
state <= READTAG;
|
||
end
|
||
end
|
||
PULSE2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
duration[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= PULSE3;
|
||
end
|
||
PULSE3:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
if (duration[15:0]>16'h8000) begin
|
||
pulsecounter <= {1'b0,duration[14:0]};
|
||
duration[7:0] <= sramdin;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= PULSE4;
|
||
end
|
||
else begin
|
||
state <= PULSE5;
|
||
end
|
||
end
|
||
PULSE4:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
duration[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= PULSE5;
|
||
end
|
||
PULSE5:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
if (duration[15] == 1'b1) begin
|
||
duration[23:16] <= duration[7:0];
|
||
duration[30:24] <= duration[14:8];
|
||
duration[7:0] <= sramdin;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= PULSE6;
|
||
end
|
||
else begin
|
||
duration[30:16] <= 15'h0000;
|
||
state <= DOPULSE;
|
||
end
|
||
end
|
||
PULSE6:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
duration[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
lblock <= lblock - 1;
|
||
state <= DOPULSE;
|
||
end
|
||
DOPULSE:
|
||
if (play_enabled == 1'b1) begin
|
||
if (duration==32'h00000000) begin // caso especial de duration=0
|
||
next_pulse <= next_pulse ^ pulsecounter[0];
|
||
state <= PULSE1;
|
||
end
|
||
else if (cduration >= adj_duration) begin
|
||
next_pulse <= ~next_pulse;
|
||
cduration <= 34'h000000000;
|
||
if (pulsecounter == 16'h0001) begin // se ha acabado el tren de pulsos
|
||
state <= PULSE1;
|
||
end
|
||
else begin
|
||
pulsecounter <= pulsecounter - 1;
|
||
end
|
||
end
|
||
else begin
|
||
pulse <= next_pulse;
|
||
cduration <= cduration + 1;
|
||
end
|
||
end
|
||
DATA1: // DATA1 a 4 para leer numero de bits en bloque
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
numberofbits[7:0] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= DATA2;
|
||
end
|
||
DATA2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
numberofbits[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= DATA3;
|
||
end
|
||
DATA3:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
numberofbits[23:16] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= DATA4;
|
||
end
|
||
DATA4:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
{next_pulse,numberofbits[30:24]} <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= DATATAIL1;
|
||
end
|
||
DATATAIL1: // DATATAIL1 y 2 leen la duraci<63>n del pulso extra
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
durationextrapulse[7:0] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= DATATAIL2;
|
||
end
|
||
DATATAIL2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
durationextrapulse[15:8] <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READNPULSE0;
|
||
end
|
||
READNPULSE0: // Leer cantidad de pulsos para bit 0
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
numberpulse0 <= sramdin[4:0];
|
||
indexpulse <= 5'd0;
|
||
a <= a + 21'd1;
|
||
state <= READNPULSE1;
|
||
end
|
||
READNPULSE1: // Leer cantidad de pulsos para bit 1
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
numberpulse1 <= sramdin[4:0];
|
||
a <= a + 21'd1;
|
||
state <= READDPULSE0_1;
|
||
end
|
||
READDPULSE0_1: // Leer secuencia de pulsos para 0
|
||
if (play_enabled == 1'b1) begin
|
||
if (indexpulse == numberpulse0) begin
|
||
indexpulse <= 5'd0;
|
||
state <= READDPULSE1_1;
|
||
end
|
||
else begin
|
||
databyte <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READDPULSE0_2;
|
||
end
|
||
end
|
||
READDPULSE0_2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
pulse0[indexpulse] <= {sramdin, databyte};
|
||
indexpulse <= indexpulse + 1;
|
||
a <= a + 21'd1;
|
||
state <= READDPULSE0_1;
|
||
end
|
||
READDPULSE1_1: // Leer secuencia de pulsos para 1
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
if (indexpulse == numberpulse1) begin
|
||
state <= READDATA1;
|
||
end
|
||
else begin
|
||
databyte <= sramdin;
|
||
a <= a + 21'd1;
|
||
state <= READDPULSE1_2;
|
||
end
|
||
end
|
||
READDPULSE1_2:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
pulse1[indexpulse] <= {sramdin,databyte};
|
||
indexpulse <= indexpulse + 1;
|
||
a <= a + 21'd1;
|
||
state <= READDPULSE1_1;
|
||
end
|
||
READDATA1:
|
||
if (play_enabled == 1'b1 && sram_access_allowed == 1'b1) begin
|
||
databyte <= sramdin;
|
||
a <= a + 21'd1;
|
||
countbits <= 4'd8;
|
||
indexpulse <= 5'd0;
|
||
state <= OUTPUTBIT1;
|
||
end
|
||
OUTPUTBIT1:
|
||
if (play_enabled == 1'b1) begin
|
||
if (numberofbits == 31'h0) begin
|
||
duration <= {15'h0000,durationextrapulse};
|
||
cduration <= 34'h0;
|
||
state <= DATADOTAIL;
|
||
end
|
||
else if (countbits == 0) begin
|
||
state <= READDATA1;
|
||
end
|
||
else if ((databyte[7] == 1'b0 && indexpulse >= numberpulse0) ||
|
||
(databyte[7] == 1'b1 && indexpulse >= numberpulse1)) begin
|
||
databyte <= {databyte[6:0],1'b0};
|
||
numberofbits <= numberofbits - 1;
|
||
countbits <= countbits - 1;
|
||
indexpulse <= 5'd0;
|
||
end
|
||
else begin
|
||
if (databyte[7] == 1'b0)
|
||
duration <= {15'h0000,pulse0[indexpulse]};
|
||
else
|
||
duration <= {15'h0000,pulse1[indexpulse]};
|
||
cduration <= 34'h0;
|
||
indexpulse <= indexpulse + 1;
|
||
state <= OUTPUTBIT2;
|
||
end
|
||
end
|
||
OUTPUTBIT2:
|
||
if (play_enabled == 1'b1) begin
|
||
if (cduration >= adj_duration) begin
|
||
next_pulse <= ~next_pulse;
|
||
state <= OUTPUTBIT1;
|
||
end
|
||
else begin
|
||
pulse <= next_pulse;
|
||
cduration <= cduration + 1;
|
||
end
|
||
end
|
||
DATADOTAIL:
|
||
if (play_enabled == 1'b1) begin
|
||
pulse <= next_pulse;
|
||
if (cduration >= adj_duration) begin
|
||
//??next_pulse <= ~next_pulse;
|
||
state <= READTAG;
|
||
end
|
||
else begin
|
||
cduration <= cduration + 1;
|
||
end
|
||
end
|
||
default:
|
||
begin
|
||
state <= IDLE;
|
||
play_enabled <= 1'b0;
|
||
a <= 21'h000000;
|
||
end
|
||
endcase
|
||
end
|
||
end
|
||
|
||
always @* begin
|
||
if (play_enabled == 1'b0 || (state == DOPULSE && cduration >= adj_duration) || (state != DATADOTAIL && state != DOPULSE && state != OUTPUTBIT1 && state != OUTPUTBIT2))
|
||
speed_change_allowed = 1'b1;
|
||
else
|
||
speed_change_allowed = 1'b0;
|
||
end
|
||
endmodule
|