Files
CDi_MiSTer/rtl/audiodecoder.sv
Andre Zeps ca4216f956 FMV: Added support for window, screen, display offset and NIS events
- Fixes wrong offset with "Les Guignols de l’Info"
- Fixes graphical corruption with "Christmas Crisis"
- Fixes "skewed image corruption" with MPEG video
- Adds support for NIS video event due to update of sequence parameters
- Fixes "Philips Logo Intro" with "Brain Dead 13"

Dynamic behavior is probably not yet correct.
- "Christmas Crisis" is stuttering in the bonus rides.

It should be noted that "Brain Dead 13" is still not working
after the company logo.
2025-12-18 13:30:33 +01:00

359 lines
13 KiB
Systemverilog

`timescale 1 ns / 1 ns
`include "bus.svh"
`include "audiotypes.svh"
// Playback of Audio Sectors
// inspired by https://github.com/MiSTer-devel/PSX_MiSTer/blob/main/rtl/cd_xa.vhd
// inspired by https://github.com/dankan1890/mewui/blob/master/src/mame/machine/cdicdic.cpp
module audiodecoder (
input clk,
input reset,
input reset_filter_on_start,
input stop_playback,
output bit [12:0] mem_addr,
output bit mem_rd,
input [15:0] mem_data,
input mem_ack, // Flag indicates that the memory request was accepted
input mem_ack_q, // Flag indicates that mem_data is valid
audiostream.source out,
output bit sample_channel,
input cdda_mode, // don't read coding from memory, just go for CDDA
input start_playback,
output header_coding_s playback_coding_out,
input [12:0] playback_addr,
output idle,
output bit disable_audiomap // flag, when 0xff coding is detected
);
enum {
IDLE,
// XA ADPCM handling
DETECT_CODING,
DETECT_CODING2,
EVALHEADER,
EVALHEADER2,
READ_SAMPLE,
APPLY_FILTER1,
APPLY_FILTER2,
CALC1,
CALC2,
CALC3,
NEXT_SAMPLE,
// CDDA handling
CDDA_READ,
CDDA_PROCESS,
CDDA_WAIT
} decoder_state;
assign idle = decoder_state == IDLE;
bit [13:0] data_addr;
bit [13:0] block_addr;
bit [4:0] group_cnt; // 18 groups per sector
bit [3:0] block_cnt; // counts to 8 for 4BPS and to 4 for 8BPS
bit [5:0] data_cnt; // counts to 28
header_coding_s playback_coding;
// Those are byte addresses
localparam bit [13:0] HEADER_OFFSETS_4BIT[8] = '{4, 5, 6, 7, 12, 13, 14, 15};
localparam bit [13:0] DATA_OFFSETS_4BIT[8] = '{16, 16, 17, 17, 18, 18, 19, 19};
localparam bit [13:0] HEADER_OFFSETS_8BIT[4] = '{12, 13, 14, 15};
localparam bit [13:0] DATA_OFFSETS_8BIT[4] = '{16, 17, 18, 19};
localparam bit [4:0] LAST_GROUP_INDEX = 17;
localparam bit [5:0] SAMPLES_PER_BLOCK = 28;
localparam bit [13:0] BLOCK_SIZE = 128;
bit [13:0] header_offset;
bit [13:0] data_offset;
bit [ 3:0] gain_shift_offset;
bit [ 3:0] last_block_index;
always_comb begin
if (playback_coding.bps == k8Bps) begin
header_offset = HEADER_OFFSETS_8BIT[block_cnt[1:0]];
data_offset = DATA_OFFSETS_8BIT[block_cnt[1:0]];
gain_shift_offset = 8;
last_block_index = 3;
end else begin
header_offset = HEADER_OFFSETS_4BIT[block_cnt[2:0]];
data_offset = DATA_OFFSETS_4BIT[block_cnt[2:0]];
gain_shift_offset = 12;
last_block_index = 7;
end
end
// Stolen from MAME
localparam bit signed [11:0] s_xa_filter_coef[4][2] = '{
'{12'h000, 12'h000},
'{12'h0F0, 12'h000},
'{12'h1CC, -12'h0D0},
'{12'h188, -12'h0DC}
};
// TODO reduce width of register. 32 bit is rather large for 16 bit
bit signed [31:0] sample /*verilator public_flat_rd*/;
bit signed [15:0] sample16;
bit [7:0] mem_data_bytes[2];
bit [13:0] mem_addr_byte;
bit mem_addr_byte_selector;
bit mem_addr_byte_selector_q;
wire [7:0] mem_data_byte = mem_data_bytes[mem_addr_byte_selector_q];
bit [3:0] mem_data_byte_nibbles[2];
wire channel /*verilator public_flat_rd*/ = playback_coding.chan == kStereo ? block_cnt[0] : 0;
always_comb begin
out.sample = old_samples[sample_channel][0];
if (playback_coding.bps == k16Bps) out.sample = sample[15:0];
end
always_comb begin
// create nibble and byte views on memory result
{mem_data_byte_nibbles[0], mem_data_byte_nibbles[1]} = mem_data_byte;
{mem_data_bytes[0], mem_data_bytes[1]} = mem_data;
mem_rd = 0;
mem_addr_byte = 0;
mem_addr_byte_selector = 0;
sample16 = sample[15:0];
if (sample < -32768) sample16 = -32768;
else if (sample > 32767) sample16 = 32767;
case (decoder_state)
DETECT_CODING: begin
mem_addr_byte = block_addr + 11;
mem_rd = 1;
end
EVALHEADER: begin
mem_addr_byte = block_addr + header_offset + 12;
mem_rd = 1;
end
EVALHEADER2: begin
mem_addr_byte = data_addr + 12;
mem_rd = 1;
end
NEXT_SAMPLE: begin
mem_addr_byte = data_addr + 12;
mem_rd = 1;
end
CDDA_READ: begin
mem_addr_byte = data_addr;
mem_rd = 1;
end
endcase
{mem_addr, mem_addr_byte_selector} = mem_addr_byte;
end
bit [3:0] gain_shift;
bit [1:0] filter_index;
bit signed [15:0] old_samples[2][2];
bit signed [31:0] mac;
bit stop_playback_latch;
always_ff @(posedge clk) begin
disable_audiomap <= 0;
// catch overflow during simulation
if (start_playback) assert (decoder_state == IDLE);
if (out.strobe) begin
assert (out.write);
out.write <= 0;
end
if (mem_rd) mem_addr_byte_selector_q <= mem_addr_byte_selector;
if (reset) begin
decoder_state <= IDLE;
old_samples <= '{'{0, 0}, '{0, 0}};
group_cnt <= 0;
out.write <= 0;
stop_playback_latch <= 0;
end else begin
if (stop_playback) stop_playback_latch <= 1;
case (decoder_state)
IDLE: begin
stop_playback_latch <= 0;
if (start_playback) begin
// Coding can be read from memory or forced
if (cdda_mode) begin
// Don't read coding from memory.
// Use cd_audio_coding
block_cnt <= 0;
group_cnt <= 0;
playback_coding.rate <= k44Khz;
playback_coding.bps <= k16Bps;
playback_coding.chan <= kStereo;
decoder_state <= CDDA_READ;
end else begin
// Read coding from sector header
decoder_state <= DETECT_CODING;
end
block_addr <= {playback_addr, 1'b0};
data_addr <= {playback_addr, 1'b0}; // for CDDA
$display("Start Decoder at %x", {playback_addr, 1'b0});
if (reset_filter_on_start) begin
$display("Reset audio filters");
old_samples <= '{'{0, 0}, '{0, 0}};
end
end
end
DETECT_CODING: begin
if (mem_ack) begin
decoder_state <= DETECT_CODING2;
end
end
DETECT_CODING2: begin
if (mem_ack_q) begin
block_cnt <= 0;
group_cnt <= 0;
$display("DETECT_CODING2 %x", mem_data_byte);
assert (mem_data_byte != 0); // plausible?
if (mem_data_byte == 8'hff) begin
// special coding to stop the decoder?
decoder_state <= IDLE;
disable_audiomap <= 1;
end else begin
playback_coding <= mem_data_byte;
// Measurements of the CDIC IRQ are showing
// that the audiomap is not synchronized to CD Data IRQs
// No waiting ticks added here
decoder_state <= EVALHEADER;
assert (mem_data_byte != 0); // plausible?
end
end
end
EVALHEADER: begin
if (mem_ack) begin
data_addr <= block_addr + data_offset;
decoder_state <= EVALHEADER2;
data_cnt <= 0;
end
end
EVALHEADER2: begin
if (mem_ack_q) begin
$display("Coding param: %x", mem_data_byte);
gain_shift <= gain_shift_offset - mem_data_byte[3:0];
filter_index <= mem_data_byte[5:4];
end
if (mem_ack) begin
decoder_state <= READ_SAMPLE;
end
end
READ_SAMPLE: begin
if (mem_ack_q) begin
if (playback_coding.bps == k8Bps)
sample <= 32'(signed'(mem_data_byte)) <<< gain_shift;
else
sample <= 32'(signed'(mem_data_byte_nibbles[!block_cnt[0]])) <<< gain_shift;
data_addr <= data_addr + 4;
data_cnt <= data_cnt + 1;
decoder_state <= APPLY_FILTER1;
end
end
APPLY_FILTER1: begin
mac <= s_xa_filter_coef[filter_index][0] * old_samples[channel][0];
decoder_state <= APPLY_FILTER2;
end
APPLY_FILTER2: begin
mac <= mac + s_xa_filter_coef[filter_index][1] * old_samples[channel][1];
decoder_state <= CALC1;
end
CALC1: begin
if (!out.write) begin
sample <= sample + ((mac + 128) / 256);
decoder_state <= CALC2;
sample_channel <= channel;
end
if (stop_playback_latch) begin
decoder_state <= IDLE;
stop_playback_latch <= 0;
end
end
CALC2: begin
old_samples[sample_channel][1] <= old_samples[sample_channel][0];
old_samples[sample_channel][0] <= sample16;
out.write <= 1;
playback_coding_out <= playback_coding;
if (data_cnt == SAMPLES_PER_BLOCK) begin
if (block_cnt == last_block_index) begin
if ((group_cnt == LAST_GROUP_INDEX) || stop_playback_latch) begin
decoder_state <= IDLE;
stop_playback_latch <= 0;
end else begin
block_addr <= block_addr + BLOCK_SIZE; // group has 128 byte size
decoder_state <= EVALHEADER;
block_cnt <= 0;
group_cnt <= group_cnt + 1;
end
end else begin
block_cnt <= block_cnt + 1;
decoder_state <= EVALHEADER;
end
end else begin
decoder_state <= NEXT_SAMPLE;
end
end
NEXT_SAMPLE: begin
if (mem_ack) begin
decoder_state <= READ_SAMPLE;
end
end
CDDA_READ: begin
if (mem_ack) begin
data_addr <= data_addr + 2;
decoder_state <= CDDA_PROCESS;
end
end
CDDA_PROCESS: begin
if (mem_ack_q) begin
sample[15:0] <= {mem_data[7:0], mem_data[15:8]};
sample_channel <= !data_addr[1];
out.write <= 1;
playback_coding_out <= playback_coding;
decoder_state <= CDDA_WAIT;
end
end
CDDA_WAIT: begin
if (!out.write) begin
// TODO Remove magic numbers
if (block_addr == 14'h1400 && data_addr == (14'h930 + 14'h1400)) begin
decoder_state <= IDLE;
end else if (block_addr == 14'h1e00 && data_addr == (14'h930 + 14'h1e00))
decoder_state <= IDLE;
else begin
decoder_state <= CDDA_READ;
end
end
end
endcase
end
end
endmodule