CDIC: Rewritten audio state machine

- Changes based on findings of the CDIC_BlackBoxAnalyzer project
  - Removed side channel for audio coding
    - A real CDIC reads the audio coding always from RAM
  - Removed the concept of the audio tick found in MAME
    - A real CDIC performs audio playback asynchronous to the CD reading
- Seek time now 19 sectors
  - Fixes audio glitch in "Help cutscene" in "Zelda - Wand of Gamelon"
    "Remember, tools can only be used..."
- Fixes audio regression during "Hotel Mario" score screen
- Fixed spurious IRQ caused by sector data interrupt after reading stopped
  - Should fix hang on shopkeeper cutscene in "Zelda - Wand of Gamelon"
    (Cannot be reproduced or is very unlikely now)
This commit is contained in:
Andre Zeps
2025-03-17 18:22:39 +01:00
parent f286047100
commit d53c682fcc
15 changed files with 260 additions and 218 deletions

View File

@@ -68,6 +68,8 @@
"system_error": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"typeindex": "cpp"
"typeindex": "cpp",
"iterator": "cpp",
"random": "cpp"
}
}

View File

@@ -23,27 +23,22 @@ CD images can be stored as CHD or CUE/BIN format.
Core Utilization:
Logic utilization (in ALMs) 13,431 / 41,910 ( 32 % )
Total registers 15619
Logic utilization (in ALMs) 13,425 / 41,910 ( 32 % )
Total registers 15587
Total block memory bits 630,471 / 5,662,720 ( 11 % )
Total DSP Blocks 66 / 112 ( 59 % )
### TODOs in order of priority
* Fix audio regression during Hotel Mario Score Screen (was ok in 250214)
* Investigate hangup during Wand of Gamelon Shopkeeper cutscene
* Find a better solution for reducing CPU speed
* Fix regression: Audio hiccups during Philips Logo in Burn:Cycle
* A workaround is CPU overclocking
* Investigate mysterious non loading behavior
* Investigate "Zelda's Adventure" sound hiccups
* During stop of audiomap and switch to audio channel playback
the audio channel mask is set one sector too late.
* Investigate sound hiccups in both 2D Zelda games
* Occurs during "Help-Cutscene" when SFX is played
* Probably the same as with "Zelda's Adventure"
* Investigate input responsiveness (skipped events?)
* Investigate graphical glitch on the left edge in "Alice in Wonderland" and "Laser Lords"
* Investigate graphical glitch at the bottom of the screen in Kether during pause
* Investigate screeching sound effect in the menu of "Golf Tips"
* Fix hang on audio track stop or change
* Fix hang on audio track stop or change in media player
* Investigate "Gray border glitch" at the top of "Myst" gameplay (seems to be only one plane)
* Fix reset behaviour (Core is sometimes hanging after reset)
* Add auto start of titles using front panel "Play" button
@@ -103,9 +98,22 @@ by emulation errors but are also present on the real machine.
The video data is changed while it is displayed.
* When I load a save game in "Burn:Cycle" it plays a short and unclean loop of noise until I do something
* I thought that was a bug in the core at first too. However, it is actually like this on a real CD-i too.
* The music of Burn:Cycle sometimes has "pops" and "clicks"
* This game is special. It doesn't play music from CD like most games for this system would do.
* Small loops of sampled music are loaded from CD, stored in memory and randomly concatenated together
to create the background music. These samples are sometimes not very "loopable" creating a pop at looping points.
* This issue is reproducible on a real 210/05 as well
* The music during the Philips Logo animation of Burn:Cycle has broken audio
* This issue is reproducible on a real 210/05 as well
* The problem can be fixed by overclocking the CPU
* Burn:Cycle - Random flickering animated text in front of the "Psychic Roulette" credit card terminal
* This actually happens on the real machine. I also thought this might be a CPU speed issue, considering that
the flickering disappears if the CPU is slightly overclocked.
* Flashback: The audio and video during the intro are asynchronous
* This curiously happens on the real machine as well and doesn't depend on 50 or 60 Hz.
* This curiously happens on the real machine as well and doesn't depend on 50 or 60 Hz
* When dying in "Zelda's Adventure" the accompanying sound effect doesn't match the audio data on CD
* Good catch! This is a programming error which can be reproduced on a real CD-i 210/05 as well,
which causes the audio playback to start one sector too late.
* The same phenomenon exists in the "Help cutscene" of "Zelda - Wand of Gamelon"
* It is not audible in that game, because of silence in the beginning

View File

@@ -22,10 +22,8 @@ module audiodecoder (
audiostream.source out,
output bit sample_channel,
input use_external_coding, // don't read coding from memory, use parameter
input header_coding_s cd_audio_coding, // used when external_coding set
input cdda_mode, // don't read coding from memory, just go for CDDA
input start_playback,
input audio_tick, // CD sector tick rate 75 Hz
output header_coding_s playback_coding_out,
input [12:0] playback_addr,
@@ -38,7 +36,6 @@ module audiodecoder (
// XA ADPCM handling
DETECT_CODING,
DETECT_CODING2,
WAIT_AUDIO_TICKS,
EVALHEADER,
EVALHEADER2,
READ_SAMPLE,
@@ -195,16 +192,17 @@ module audiodecoder (
stop_playback_latch <= 0;
if (start_playback) begin
// Coding can be read from memory or forced
if (use_external_coding) begin
if (cdda_mode) begin
// Don't read coding from memory.
// Use cd_audio_coding
block_cnt <= 0;
group_cnt <= 0;
$display("EXTERNAL CODING %x", cd_audio_coding);
playback_coding.rate <= k44Khz;
playback_coding.bps <= k16Bps;
playback_coding.chan <= kStereo;
playback_coding <= cd_audio_coding;
decoder_state <= (cd_audio_coding.bps == k16Bps) ? CDDA_READ : WAIT_AUDIO_TICKS;
decoder_state <= CDDA_READ;
end else begin
// Read coding from sector header
decoder_state <= DETECT_CODING;
@@ -249,14 +247,6 @@ module audiodecoder (
end
end
WAIT_AUDIO_TICKS: begin
if (audio_tick) decoder_state <= EVALHEADER;
if (stop_playback_latch) begin
decoder_state <= IDLE;
stop_playback_latch <= 0;
end
end
EVALHEADER: begin
if (mem_ack) begin
data_addr <= block_addr + data_offset;

View File

@@ -5,7 +5,8 @@ module audiofifo (
input reset,
audiostream.sink in,
audiostream.source out,
output nearly_full
output nearly_full,
output nearly_empty
);
bit signed [15:0] mem[64];
@@ -20,12 +21,13 @@ module audiofifo (
bit indizes_equal_during_write_d;
bit indizes_equal_during_write_q;
assign out.write = count != 0 && !reset && !indizes_equal_during_write_q;
assign in.strobe = count < 60 && !reset && in.write;
assign out.write = count != 0 && !reset && !indizes_equal_during_write_q;
assign in.strobe = count < 60 && !reset && in.write;
// Always a minimum of 28 XA samples per block
// We go for 48 just to be safe
assign nearly_full = count >= 48;
assign nearly_empty = count <= 3;
always_comb begin
read_index_d = read_index_q;

View File

@@ -6,7 +6,6 @@ module audioplayer (
input clk,
input sample_tick37,
input sample_tick44,
input audio_tick,
input reset,
output bit [12:0] mem_addr,
@@ -15,18 +14,15 @@ module audioplayer (
input mem_ack,
input mem_ack_q,
input header_coding_s cd_audio_coding,
input start_playback, // flag starts CD sector playback at playback_addr
input abort_playback_request,
input enable_audiomap,
input disable_audiomap,
input [12:0] playback_addr,
input start_playback,
input stop_playback,
input cdda_mode,
output bit playback_active,
output bit finished_buffer_playback,
output decoder_disable_audiomap,
output bit signed [15:0] audio_left,
output bit signed [15:0] audio_right,
output bit audiomap_active,
output bit audiomap_finished_playback,
output decoder_disable_audiomap
output bit signed [15:0] audio_right
);
audiostream xa_out ();
@@ -42,7 +38,6 @@ module audioplayer (
header_coding_s current_active_coding;
wire decoder_idle;
bit playback_request;
bit [12:0] playback_request_addr /*verilator public_flat_rd*/;
bit decoder_start /*verilator public_flat_rd*/ = 0;
@@ -52,8 +47,6 @@ module audioplayer (
.clk(clk),
.reset(reset),
.reset_filter_on_start(reset_filter_on_start),
.stop_playback(disable_audiomap),
.audio_tick(audio_tick),
.mem_addr(mem_addr),
.mem_data(mem_data),
.mem_rd(mem_rd),
@@ -63,55 +56,57 @@ module audioplayer (
.out(xa_out),
.sample_channel(xa_channel),
.use_external_coding(!audiomap_active),
.cd_audio_coding(cd_audio_coding),
.start_playback(decoder_start),
.stop_playback(stop_playback),
.cdda_mode,
.playback_coding_out(current_active_coding),
.playback_addr(playback_request_addr),
.idle(decoder_idle),
.disable_audiomap(decoder_disable_audiomap)
);
wire [1:0] fifo_nearly_full;
wire [1:0] fifo_nearly_empty;
bit fifo_nearly_empty_q;
always_ff @(posedge clk) begin
decoder_start <= 0;
fifo_nearly_empty_q <= fifo_nearly_empty[0];
finished_buffer_playback <= 0;
if (reset) begin
playback_request <= 0;
audiomap_active <= 0;
playback_active <= 0;
playback_request_addr <= 0;
end else begin
// Only start playback when decoder is idle
if (decoder_idle && !decoder_start) begin
playback_request <= 0;
decoder_start <= playback_request || (audiomap_active && !decoder_disable_audiomap);
end
// CD Sector playback
if (start_playback) begin
playback_request <= 1;
playback_request_addr <= playback_addr;
if (decoder_idle && !decoder_start && fifo_nearly_empty[0]) begin
decoder_start <= playback_active && !decoder_disable_audiomap;
finished_buffer_playback <= finished_buffer_playback_latched;
end
// To react on audiomap activation change
if (enable_audiomap) audiomap_active <= 1;
if (disable_audiomap || decoder_disable_audiomap) audiomap_active <= 0;
if (stop_playback || decoder_disable_audiomap) begin
playback_active <= 0;
end
if (disable_audiomap || abort_playback_request) playback_request <= 0;
// Buffer playback
if (start_playback && !playback_active) begin
playback_active <= 1;
// ADPCM playback always starts at 0x2800
// For CDDA this might not be accurate
playback_request_addr <= cdda_mode ? 13'h0f00 : 13'h1400;
end
// after the decoder was started, change address to the next buffer
if (audiomap_active && decoder_start) begin
playback_request_addr <= playback_request_addr == 13'h1400 ? 13'h1900 : 13'h1400;
if (decoder_start && playback_active) begin
if (cdda_mode)
playback_request_addr <= playback_request_addr == 13'h0a00 ? 13'h0f00 : 13'h0a00;
else
playback_request_addr <= playback_request_addr == 13'h1400 ? 13'h1900 : 13'h1400;
end
// Store playback_request_addr from extern only at start
if (enable_audiomap && !audiomap_active) begin
playback_request_addr <= playback_addr;
end
end
end
wire [1:0] fifo_nearly_full;
always_comb begin
// Mono is default. Write to both FIFOs and ignore the channel
@@ -139,26 +134,28 @@ module audioplayer (
.reset,
.in(xa_fifo_in[0]),
.out(xa_fifo_out[0]),
.nearly_full(fifo_nearly_full[0])
.nearly_full(fifo_nearly_full[0]),
.nearly_empty(fifo_nearly_empty[0])
);
audiofifo fifo_right (
.clk,
.reset,
.in(xa_fifo_in[1]),
.out(xa_fifo_out[1]),
.nearly_full(fifo_nearly_full[1])
.nearly_full(fifo_nearly_full[1]),
.nearly_empty(fifo_nearly_empty[1])
);
bit playback_active = 0;
bit audio_fifo_output_enabled = 0;
bit sample_toggle18 = 0;
bit decoder_idle_q;
assign reset_filter_on_start = !playback_active;
assign reset_filter_on_start = !audio_fifo_output_enabled;
bit strobe_fifo;
always_comb begin
strobe_fifo = 0;
if (playback_active) begin
if (audio_fifo_output_enabled) begin
if (current_active_coding.rate == k44Khz && sample_tick44) strobe_fifo = 1;
if (current_active_coding.rate == k37Khz && sample_tick37) strobe_fifo = 1;
if (current_active_coding.rate == k18Khz && sample_tick37 && sample_toggle18)
@@ -174,20 +171,21 @@ module audioplayer (
// One would have been enough, but let's be sure.
// Not having this results into pops especially percievible
// with Tetris
bit playback_active_next_sample_tick;
bit audio_fifo_output_enabled_next_sample_tick;
bit finished_buffer_playback_latched;
always_ff @(posedge clk) begin
dc_bias_cnt <= !dc_bias_cnt;
audiomap_finished_playback <= 0;
decoder_idle_q <= decoder_idle;
xa_fifo_out[0].strobe <= 0;
xa_fifo_out[1].strobe <= 0;
if (reset) begin
playback_active <= 0;
playback_active_next_sample_tick <= 0;
audio_fifo_output_enabled <= 0;
audio_fifo_output_enabled_next_sample_tick <= 0;
xa_fifo_out[0].strobe <= 0;
xa_fifo_out[1].strobe <= 0;
end else begin
@@ -195,20 +193,24 @@ module audioplayer (
if (fifo_nearly_full == 2'b11 &&
((current_active_coding.rate != k44Khz && sample_tick37) ||
(current_active_coding.rate == k44Khz && sample_tick44))) begin
playback_active_next_sample_tick <= 1;
playback_active <= playback_active_next_sample_tick;
audio_fifo_output_enabled_next_sample_tick <= 1;
audio_fifo_output_enabled <= audio_fifo_output_enabled_next_sample_tick;
end
if (playback_active && decoder_idle && !decoder_idle_q && audiomap_active) begin
audiomap_finished_playback <= 1;
if (audio_fifo_output_enabled && decoder_idle && !decoder_idle_q) begin
finished_buffer_playback_latched <= 1;
end
if (finished_buffer_playback || stop_playback) begin
finished_buffer_playback_latched <= 0;
end
if (xa_fifo_out[0].write == 0 && xa_fifo_out[0].write == 0) begin
playback_active <= 0;
playback_active_next_sample_tick <= 0;
audio_fifo_output_enabled <= 0;
audio_fifo_output_enabled_next_sample_tick <= 0;
end
if (playback_active) begin
if (audio_fifo_output_enabled) begin
if (strobe_fifo) begin
xa_fifo_out[0].strobe <= 1;
xa_fifo_out[1].strobe <= 1;

View File

@@ -61,18 +61,16 @@ module cdic (
bit [31:0] channel_register = 0;
// MODE2 Channel Audio Mask
// If matching, disables audiomap and plays sector
// directly without waiting for CPU?
// The data is delivered to one of the ADPCM buffers?
// If matching, the data is delivered to one of the ADPCM buffers
bit [15:0] audio_channel_register = 0;
bit [15:0] command_register = 0;
// DBUF @ 303FFE
// Bit 0 is toggled on every received sector
// Bit 0 is toggled on every received sector and
// points to the just refreshed buffer
// Bit 2 is reset on data sector
// Bit 2 is set on audio sector
// Bit 14 is set on every received sector
// Bit 15 starts the CD reading and parses command register
// Resetting Bit 14 stops CD reading
// Buffer of the stored sector is visible here
@@ -94,6 +92,7 @@ module cdic (
// ABUF @ 303FF4
// Bit 15 causes IRQ
// Bit 15 is set when an audio buffer was played
// when bit 13 of AUDCTL is set
// Reading register clears bit 15 after the read
// The CPU still gets the set bit
bit [15:0] audio_buffer_register = 0;
@@ -108,17 +107,12 @@ module cdic (
bit [15:0] dma_control_register = 0;
// AUDCTL @ 303FFA (called Z buffer in MAME)
// Bit 0 toggles on every read when no audio is played?
// The toggled result is the one returned
// to the CPU?
// Resetting bit 13 stops audio map playback? Or is it bit 11?
// Bit 0 is set when the audiomap has finished playback
// with 0xff coding
// Resetting bit 11 stops audio map playback directly.
// CPU does write 0x0000 to do so, ignoring the previous content
// Setting bit 13 starts audio map playback? Or is it bit 11?
// CPU does write 0x2800 to do so, ignoring the previous content
// CPU writes 0x0800 to this register when unmuting
// It does so only if bit 11 is not set but bit 0 is set
// after a CDIC IRQ
// This is also the address for playback of audio
// Setting bit 11 starts audio map playback at ADPCM buffer 0
// Bit 13 activates IRQs when a single ADPCM buffer was played
bit [15:0] audio_control_register = 0;
// Matching value for File in MODE2 header
@@ -153,6 +147,11 @@ module cdic (
wire sector_tick_audio = sector75counter == 0;
wire sample_tick37 /*verilator public_flat_rd*/;
wire sample_tick44 /*verilator public_flat_rd*/;
`ifdef VERILATOR
wire sample_tick /*verilator public_flat_rd*/ = read_cdda ? sample_tick44 : sample_tick37;
`endif
wire sector_tick;
flag_cross_domain cross1 (
.clk_a(clk_audio),
@@ -237,16 +236,6 @@ module cdic (
.q_b(mem_cdic_readout)
);
// Flag is set after a full sector is received
// which had audio data and matching audio channel and
// needs to be played back now
bit cd_audio_start_playback;
bit cd_audio_abort_playback_request;
// Address of CD sector to play back
// Should be 13'h1900 or 13'h1400
// Can be either an audiomap or coming directly from CD
bit [12:0] audio_playback_addr;
bit [12:0] mem_cd_audio_addr; // Address for CDIC memory
bit mem_cd_audio_rd;
@@ -272,18 +261,16 @@ module cdic (
assign audio_left = adpcm_left;
assign audio_right = adpcm_right;
wire audiomap_active;
bit adpcm_enable_audiomap; // Flag when audio_control_register[13] is set
bit adpcm_disable_audiomap; // Flag when audio_control_register[13] is reset
wire audiomap_finished_playback;
bit audio_start_playback /*verilator public_flat_rd*/;
bit audio_stop_playback;
wire audio_playback_active /*verilator public_flat_rd*/;
wire finished_audio_buffer_playback;
wire decoder_disable_audiomap;
header_coding_s cd_audio_coding;
audioplayer adpcm (
.clk(clk),
.sample_tick37,
.sample_tick44,
.audio_tick(sector_tick),
.reset(reset),
.mem_addr(mem_cd_audio_addr),
.mem_data(mem_cdic_readout),
@@ -291,14 +278,11 @@ module cdic (
.mem_ack(mem_cd_audio_ack),
.mem_ack_q(mem_cd_audio_ack_q),
.cd_audio_coding(cd_audio_coding),
.start_playback(cd_audio_start_playback),
.abort_playback_request(cd_audio_abort_playback_request),
.enable_audiomap(adpcm_enable_audiomap),
.disable_audiomap(adpcm_disable_audiomap),
.playback_addr(audio_playback_addr),
.audiomap_active(audiomap_active),
.audiomap_finished_playback(audiomap_finished_playback),
.start_playback(audio_start_playback),
.stop_playback(audio_stop_playback),
.cdda_mode(read_cdda),
.playback_active(audio_playback_active),
.finished_buffer_playback(finished_audio_buffer_playback),
.decoder_disable_audiomap(decoder_disable_audiomap),
.audio_left (adpcm_left),
@@ -310,28 +294,6 @@ module cdic (
bit data_target_buffer;
bit audio_target_buffer;
always_comb begin
`ifdef VERILATOR
audio_playback_addr = 0; // better visibility in waveform
`else
audio_playback_addr = audio_control_register[13:1]; // optimize LUTs
`endif
if (adpcm_enable_audiomap) audio_playback_addr = audio_control_register[13:1];
// CD Audio Playback
if (cd_audio_start_playback) begin
if (header_mode2) begin
audio_playback_addr = !audio_target_buffer ? 13'h1900 : 13'h1400;
$display("ADPCM at %x", {audio_playback_addr, 1'b0});
end else begin
assert (read_cdda);
audio_playback_addr = data_target_buffer ? 13'h0f00 : 13'h0a00;
$display("CDDA at %x", {audio_playback_addr, 1'b0});
end
end
end
bit reset_write_timecode1;
bit reset_write_timecode2;
bit write_timecode1 = 0;
@@ -424,7 +386,8 @@ module cdic (
localparam bit [5:0] kSeekTime = 1;
`else
// Seeking on a real 210/05 takes about 200ms
localparam bit [5:0] kSeekTime = 14;
// But 19 (250ms) seems to be more stable
localparam bit [5:0] kSeekTime = 19;
`endif
// Simulates reading time. Remaining sectors to wait.
bit [5:0] start_cd_reading_cnt = 0;
@@ -445,7 +408,7 @@ module cdic (
end
`endif
assign intreq = x_buffer_register[15] | audio_buffer_register[15];
assign intreq = (x_buffer_register[15] & data_buffer_register[14]) | audio_buffer_register[15];
assign req = dma_control_register[15];
localparam kSectorHeader_Mode = 15 / 2; // Low Byte
@@ -459,14 +422,12 @@ module cdic (
always_ff @(posedge clk) begin
bus_ack <= 0;
rdy <= 0;
cd_audio_start_playback <= 0;
cd_audio_abort_playback_request <= 0;
cd_hps_data_valid_q2 <= cd_hps_data_valid_q;
cd_hps_data_valid_q <= cd_hps_data_valid;
cd_hps_ack_q <= cd_hps_ack;
adpcm_enable_audiomap <= 0;
adpcm_disable_audiomap <= 0;
audio_start_playback <= 0;
audio_stop_playback <= 0;
if (reset_write_timecode1) write_timecode1 <= 0;
if (reset_write_timecode2) write_timecode2 <= 0;
@@ -615,7 +576,6 @@ module cdic (
header_timecode1[7:0], header_timecode2[15:8]);
cd_data_target_adr <= audio_target_buffer ? 13'h1904 : 13'h1404;
cd_audio_coding <= header_coding;
end else if (use_sector_data && sector_word_index == 10 && !read_raw) begin
$display("Use sector %x %x %x for data in buffer %x", header_timecode1[15:8],
header_timecode1[7:0], header_timecode2[15:8], {
@@ -636,7 +596,7 @@ module cdic (
end
// Audio map finished? Cause an IRQ to inform the CPU
if (audiomap_finished_playback) begin
if (finished_audio_buffer_playback && audio_control_register[13]) begin
audio_buffer_register[15] <= 1;
end
@@ -700,9 +660,6 @@ module cdic (
// Bit 2 is set on audio sector
data_buffer_register[2] <= header_submode.audio && audio_channel_match && header_mode2;
// Bit 14 is set on every received sector
data_buffer_register[14] <= 1'b1;
// Bit 15 is set when a sector is stored
// This causes an IRQ to occur
x_buffer_register[15] <= 1'b1;
@@ -713,19 +670,9 @@ module cdic (
cd_reading_active <= 0;
if (header_submode.audio && audio_channel_match && header_mode2) begin
cd_audio_start_playback <= 1;
// Resetting this bit, causes the CDIC driver to unmute the mixer
audio_control_register[11] <= 0;
data_buffer_register[0] <= audio_target_buffer;
audio_target_buffer <= !audio_target_buffer;
end
if (read_cdda) begin
cd_audio_start_playback <= 1;
cd_audio_coding.rate <= k44Khz;
cd_audio_coding.bps <= k16Bps;
cd_audio_coding.chan <= kStereo;
end
end
// Request the next sector from HPS if the last one
@@ -754,8 +701,8 @@ module cdic (
read_mode2 <= 1;
end
16'h2b: begin
$display("CDIC Command: Stop CDDA");
cd_reading_active <= 0;
// Unknown purpose
$display("CDIC Command: Stop CDDA?");
end
16'h2e: begin
$display("CDIC Command: Update");
@@ -884,9 +831,8 @@ module cdic (
$display("CDIC Write Z Buffer Register / Audio Control Register %x %x",
address[13:1], din);
audio_control_register <= din;
adpcm_enable_audiomap <= din[13];
adpcm_disable_audiomap <= din == 0;
audio_start_playback <= din[11];
audio_stop_playback <= !din[11];
end
13'h1FFE: begin // 0x3FFC IVEC Interrupt Vector register
$display("CDIC Write Interrupt Vector Register %x %x", address[13:1],
@@ -912,7 +858,6 @@ module cdic (
audio_control_register[0] <= 0;
sector_word_index <= 0;
start_cd_reading_cnt <= 0;
cd_audio_abort_playback_request <= 1;
end
end
default: begin
@@ -1013,6 +958,7 @@ module cdic (
end
13'h1FFD: begin // 0x3FFA AUDCTL Audio control register
dout = audio_control_register;
dout[11] = audio_playback_active;
end
13'h1FFE: begin // 0x3FFC IVEC Interrupt Vector register
dout = interrupt_vector_register;

View File

@@ -268,10 +268,10 @@ module cditop (
.uds(uds),
.bus_ack(bus_ack),
.bus_err,
.int1(vdsc_int),
.int1(vdsc_int), // Video IRQ
.int2(1'b0), // unconnected in CDi MONO1
.in2(!in2in), // TODO fix polarity
.in4(in4in),
.in2(in2in), // Slave IRQ
.in4(in4in), // CDIC IRQ
.in5(1'b0),
.iack2(iack2),
.iack4(iack4),
@@ -345,7 +345,9 @@ module cditop (
wire dtackslaven = ddrb[6] ? portb_out[6] : 1'b1;
assign slave_rts = ddrb[4] ? portb_out[4] : 1'b1;
assign in2in = ddrb[5] ? portb_out[5] : 1'b1;
// The polarity must be altered as a real SCC68070 is low active
assign in2in = !(ddrb[5] ? portb_out[5] : 1'b1);
wire datadac = ddrb[0] ? portb_out[0] : 1'b0;
wire clkdac = ddrb[1] ? portb_out[1] : 1'b0;
@@ -353,7 +355,7 @@ module cditop (
wire csdac2n = ddrb[3] ? portb_out[3] : 1'b1;
bit dtackslaven_q = 0;
bit in2in_q = 1;
bit in2in_q = 0;
bit slave_irq;
@@ -431,14 +433,14 @@ module cditop (
if (reset) begin
attex_cs_slave_q <= 0;
dtackslaven_q <= 0;
in2in_q <= 1;
in2in_q <= 0;
end else begin
attex_cs_slave_q <= attex_cs_slave;
dtackslaven_q <= dtackslaven;
in2in_q <= in2in;
if (!in2in && in2in_q) $display("SLAVE IRQ2 1");
if (in2in && !in2in_q) $display("SLAVE IRQ2 0");
if (!in2in && in2in_q) $display("SLAVE IRQ2 0");
if (in2in && !in2in_q) $display("SLAVE IRQ2 1");
end
end

2
sim2/.gitignore vendored
View File

@@ -5,3 +5,5 @@ log*
*.BIN
*.rom
*.wav
*.vcd

View File

@@ -34,14 +34,14 @@ play_audiomap:
jsr waitforirq
move.b #'A',$80002019
move.w #$8a00,d0
move.w #$b200,d1
move.w #$a800,d1 ; 0x2800
jsr movedata
move.b #'1',$80002019
jsr waitforirq
move.b #'B',$80002019
move.w #$8000,d0
move.w #$a800,d1
move.w #$b200,d1; 0x3200
jsr movedata
jsr waitforirq
@@ -79,10 +79,15 @@ play_some_cd_sectors:
; Wait for the music to play back some sectors
jsr waitforirq
move.w #$0800,$303FFA ; Start playback
jsr waitforirq
jsr waitforirq
move.w #$0000,$303FFE ; Stop CD reading
move.w #$0000,$303FFA ; Stop playback
rts
main:
@@ -176,7 +181,7 @@ movedata:
move.w $303200,d7
move.w $303202,d7
move.w #$3200,$303FFA ; Start Audiomap at 0x3200
move.w #$2800,$303FFA ; Start Audiomap at 0x2800
rts
wait:

View File

@@ -17,6 +17,8 @@ main:
move.w #$2480,$303FFC ; Interrupt Vector
move.w #$0028,$303C00 ; Play CDDA
;move.w #$0027,$303C00 ; Fetch TOC
; Apprentice - Level 1
move.l #$19468000,$303C02 ; Timer Register
@@ -26,6 +28,9 @@ main:
move.w #$C000,$303FFE ; Start the Read by setting bit 15 of the data buffer
jsr waitforirq
move.w #$0800,$303FFA ; Start playback
jsr waitforirq
jsr waitforirq
jsr waitforirq

View File

@@ -34,6 +34,12 @@ main:
move.w #$4000,$303C0C ; Audio Channel Register
move.l #$07494800,$303C02 ; Timer Register
; Apprentice USA Philips Logo
move.w #$0000,$303C06 ; File Register
move.l #$8000,$303C08 ; Channel Register
move.w #$8000,$303C0C ; Audio Channel Register
move.l #$12215300,$303C02 ; Timer Register
; Tetris 00 35 68 00356800 Philips Logo Coding 01, 2 channels, 4 bits, 000093a8 frequency
; Tetris 01 42 67 01426700 Main Menu Coding 05, 2 channels, 4 bits, 000049d4 frequency
; Tetris 55 50 33 55503300 Intro Coding 05, 2 channels, 4 bits, 000049d4 frequency
@@ -44,11 +50,17 @@ main:
move.w #$C000,$303FFE ; Start the Read by setting bit 15 of the data buffer
jsr waitforirq
move.w #$0800,$303FFA ; Start playback
jsr waitforirq
jsr waitforirq
jsr waitforirq
jsr waitforirq
move.w #$0000,$303FFA ; Stop playback
;move.w #$0000,$303FFE ; Deactivate cd reading
endless:

4
sim2/create_wav.sh Executable file
View File

@@ -0,0 +1,4 @@
sox -M \
-r 37800 -e signed-integer -b 16 -c 1 -t raw $1/audio_left.bin \
-r 37800 -e signed-integer -b 16 -c 1 -t raw $1/audio_right.bin \
$1_audio.wav

View File

@@ -9,12 +9,13 @@ vasmm68k_mot -Fbin -m68000 byte_word_test.asm -o byte_word_test.rom
vasmm68k_mot -Fbin -m68000 slavetest.asm -o slavetest.rom
vasmm68k_mot -Fbin -m68000 cdic_audiomap.asm -o cdic_audiomap.rom
vasmm68k_mot -Fbin -m68000 cdic_cdda_play.asm -o cdic_cdda_play.rom
vasmm68k_mot -Fbin -m68000 cdic_xa_play.asm -o cdic_xa_play.rom
vasmm68k_mot -Fbin -m68000 cdic_data.asm -o cdic_data.rom
vasmm68k_mot -Fbin -m68000 uarttest.asm -o uarttest.rom
vasmm68k_mot -Fbin -m68000 nvram_backup_restore.asm -o nvram_backup_restore.rom
vasmm68k_mot -Fbin -m68000 nvramsender.asm -o nvramsender.rom
vasmm68k_mot -Fbin -m68000 cdic_sector_send.asm -o cdic_sector_send.rom
xxd -p -c2 cdic_cdda_play.rom cdi200.mem
xxd -p -c2 cdic_xa_play.rom cdi200.mem
xxd -p -c1 ../sim/cdimono1/zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206 slave.mem
#dd if=/dev/urandom of=save.bin count=16

View File

@@ -130,8 +130,10 @@ void subcode_data(int lba, struct subcode &out) {
s = fake_lba / 75;
f = fake_lba % 75;
if (lba < toc_entry_count) {
auto &toc_entry = toc_buffer[lba];
int toc_entry_index = lba + 0x10000;
if (lba < 0 && toc_entry_index < toc_entry_count) {
auto &toc_entry = toc_buffer[toc_entry_index];
out.control = htons(toc_entry.control);
out.track = 0; // Track 0 for TOC
@@ -146,8 +148,8 @@ void subcode_data(int lba, struct subcode &out) {
out.mode1_crc0 = htons(0xff);
out.mode1_crc1 = htons(0xff);
// printf("toc lba=%d %02x %02x %02x %02x %02x\n", lba, out.control, out.index, out.mode1_amins,
// out.mode1_asecs, out.mode1_afrac);
// printf("toc lba=%d %02x %02x %02x %02x %02x\n", toc_entry_index, out.control, out.index, out.mode1_amins,
// out.mode1_asecs, out.mode1_afrac);
} else {
int track = 1;
out.control = htons(0x01);
@@ -266,7 +268,6 @@ class CDi {
}
void clock() {
for (int i = 0; i < 2; i++) {
dut.rootp->emu__DOT__clk_sys = (sim_time & 1);
dut.rootp->emu__DOT__clk_audio = (sim_time & 1);
@@ -584,7 +585,7 @@ class CDi {
}
// Simulate Audio
if (dut.rootp->emu__DOT__cditop__DOT__cdic_inst__DOT__sample_tick44) {
if (dut.rootp->emu__DOT__cditop__DOT__cdic_inst__DOT__sample_tick) {
int16_t sample_l = dut.rootp->emu__DOT__cditop__DOT__cdic_inst__DOT__adpcm__DOT__fifo_out_left;
int16_t sample_r = dut.rootp->emu__DOT__cditop__DOT__cdic_inst__DOT__adpcm__DOT__fifo_out_right;
fwrite(&sample_l, 2, 1, f_audio_left);
@@ -629,7 +630,7 @@ class CDi {
}
dut.eval();
// do_trace = false;
do_trace = false;
dut.rootp->emu__DOT__debug_uart_fake_space = false;
dut.rootp->emu__DOT__img_size = 4096;
@@ -688,6 +689,86 @@ class CDi {
}
};
void prepare_apprentice_usa_toc() {
toc_buffer[0] = {1, 1, 21, 34, 1};
toc_buffer[1] = {1, 1, 21, 34, 0};
toc_buffer[2] = {1, 1, 21, 34, 34};
toc_buffer[3] = {1, 2, 25, 68, 21};
toc_buffer[4] = {1, 2, 25, 68, 2};
toc_buffer[5] = {1, 2, 25, 68, 1};
toc_buffer[6] = {1, 3, 35, 85, 0};
toc_buffer[7] = {1, 3, 35, 85, 68};
toc_buffer[8] = {1, 3, 35, 85, 35};
toc_buffer[9] = {1, 4, 36, 86, 3};
toc_buffer[10] = {1, 4, 36, 86, 1};
toc_buffer[11] = {1, 4, 36, 86, 0};
toc_buffer[12] = {1, 5, 41, 66, 86};
toc_buffer[13] = {1, 5, 41, 66, 36};
toc_buffer[14] = {1, 5, 41, 66, 4};
toc_buffer[15] = {1, 6, 48, 67, 1};
toc_buffer[16] = {1, 6, 48, 67, 0};
toc_buffer[17] = {1, 6, 48, 67, 66};
toc_buffer[18] = {1, 7, 53, 49, 41};
toc_buffer[19] = {1, 7, 53, 49, 6};
toc_buffer[20] = {1, 7, 53, 49, 1};
toc_buffer[21] = {1, 8, 54, 55, 0};
toc_buffer[22] = {1, 8, 54, 55, 67};
toc_buffer[23] = {1, 8, 54, 55, 53};
toc_buffer[24] = {1, 9, 65, 18, 7};
toc_buffer[25] = {1, 9, 65, 18, 1};
toc_buffer[26] = {1, 9, 65, 18, 0};
toc_buffer[27] = {1, 16, 66, 21, 55};
toc_buffer[28] = {1, 16, 66, 21, 54};
toc_buffer[29] = {1, 16, 66, 21, 8};
toc_buffer[30] = {1, 17, 70, 37, 1};
toc_buffer[31] = {1, 17, 70, 37, 0};
toc_buffer[32] = {1, 17, 70, 37, 18};
toc_buffer[33] = {1, 18, 71, 32, 65};
toc_buffer[34] = {1, 18, 71, 32, 16};
toc_buffer[35] = {1, 18, 71, 32, 1};
toc_buffer[36] = {1, 19, 81, 54, 0};
toc_buffer[37] = {1, 19, 81, 54, 21};
toc_buffer[38] = {1, 19, 81, 54, 70};
toc_buffer[39] = {1, 20, 82, 54, 17};
toc_buffer[40] = {1, 20, 82, 54, 1};
toc_buffer[41] = {1, 20, 82, 54, 0};
toc_buffer[42] = {1, 21, 83, 69, 32};
toc_buffer[43] = {1, 21, 83, 69, 71};
toc_buffer[44] = {1, 21, 83, 69, 18};
toc_buffer[45] = {1, 22, 86, 85, 1};
toc_buffer[46] = {1, 22, 86, 85, 0};
toc_buffer[47] = {1, 22, 86, 85, 54};
toc_buffer[48] = {1, 23, 87, 5, 81};
toc_buffer[49] = {1, 23, 87, 5, 20};
toc_buffer[50] = {1, 23, 87, 5, 1};
toc_buffer[51] = {1, 24, 89, 2, 0};
toc_buffer[52] = {1, 24, 89, 2, 54};
toc_buffer[53] = {1, 24, 89, 2, 83};
toc_buffer[54] = {1, 25, 89, 37, 21};
toc_buffer[55] = {1, 25, 89, 37, 1};
toc_buffer[56] = {1, 25, 89, 37, 0};
toc_buffer[57] = {1, 32, 96, 0, 85};
toc_buffer[58] = {1, 32, 96, 0, 86};
toc_buffer[59] = {1, 32, 96, 0, 22};
toc_buffer[60] = {1, 33, 96, 34, 1};
toc_buffer[61] = {1, 33, 96, 34, 0};
toc_buffer[62] = {1, 33, 96, 34, 5};
toc_buffer[63] = {1, 34, 96, 87, 87};
toc_buffer[64] = {1, 34, 96, 87, 24};
toc_buffer[65] = {1, 34, 96, 87, 1};
toc_buffer[66] = {1, 160, 1, 0, 0};
toc_buffer[67] = {1, 160, 1, 0, 2};
toc_buffer[68] = {1, 160, 1, 0, 89};
toc_buffer[69] = {1, 161, 34, 0, 25};
toc_buffer[70] = {1, 161, 34, 0, 1};
toc_buffer[71] = {1, 161, 34, 0, 0};
toc_buffer[72] = {1, 162, 21, 34, 0};
toc_buffer[73] = {1, 162, 21, 34, 96};
toc_buffer[74] = {1, 162, 21, 34, 32};
toc_entry_count = 75;
}
int main(int argc, char **argv) {
// Initialize Verilators variables
Verilated::commandArgs(argc, argv);
@@ -727,39 +808,19 @@ int main(int argc, char **argv) {
f_cd_bin = fopen("images/Zelda's Adventure (Europe).bin", "rb");
break;
case 6:
f_cd_bin = fopen("images/tetris.bin", "rb");
f_cd_bin = fopen("images/audiocd.bin", "rb");
break;
case 7:
f_cd_bin = fopen("images/Flashback (Europe).bin", "rb");
break;
case 8:
f_cd_bin = fopen("images/Earth Command (Germany).bin", "rb");
f_cd_bin = fopen("images/Apprentice_USA_single.bin", "rb");
prepare_apprentice_usa_toc();
break;
}
assert(f_cd_bin);
toc_buffer[0] = {1, 1, 0, 0, 0};
toc_buffer[1] = {1, 1, 0, 0, 0};
toc_buffer[2] = {1, 1, 0, 0, 0};
toc_buffer[3] = {1, 2, 3, 38, 69};
toc_buffer[4] = {1, 2, 3, 38, 69};
toc_buffer[5] = {1, 2, 3, 38, 69};
toc_buffer[6] = {1, 3, 7, 1, 96};
toc_buffer[7] = {1, 3, 7, 1, 96};
toc_buffer[8] = {1, 3, 7, 1, 96};
toc_buffer[9] = {1, 4, 18, 16, 71};
toc_buffer[10] = {1, 4, 18, 16, 71};
toc_buffer[11] = {1, 4, 18, 16, 71};
toc_buffer[12] = {1, 160, 1, 0, 0};
toc_buffer[13] = {1, 160, 1, 0, 0};
toc_buffer[14] = {1, 160, 1, 0, 0};
toc_buffer[15] = {1, 161, 4, 0, 0};
toc_buffer[16] = {1, 161, 4, 0, 0};
toc_buffer[17] = {1, 161, 4, 0, 0};
toc_buffer[18] = {1, 162, 0, 0, 0};
toc_entry_count=19;
CDi machine(machineindex);
while (status == 0) {

View File

@@ -1,5 +1,5 @@
verilator --top-module emu \
--trace --trace-fst --trace-structs --cc --assert --exe --timing --build \
--trace --trace-fst --trace-structs --cc --assert --exe --build \
--build-jobs 8 sim_top.cpp -I../rtl \
../rtl/*.sv ../CDi.sv ../rtl/*.v \
tg68kdotc_verilog_wrapper.v ur6805.v \