diff --git a/Jaguar.sv b/Jaguar.sv index 8cd4a88..e914f51 100644 --- a/Jaguar.sv +++ b/Jaguar.sv @@ -232,8 +232,10 @@ wire clk_ram = clk_106m; wire [1:0] scale = status[10:9]; wire [1:0] ar = status[8:7]; +wire ntsc = ~status[4]; +wire video_center = status[84]; -assign VIDEO_ARX = (!ar) ? 12'd2776 : (ar - 1'd1); +assign VIDEO_ARX = (!ar) ? 12'd2896 : (ar - 1'd1); assign VIDEO_ARY = (!ar) ? 12'd2040 : 12'd0; // Status Bit Map: @@ -241,9 +243,9 @@ assign VIDEO_ARY = (!ar) ? 12'd2040 : 12'd0; // 000-015 ..X.XXXXXXX..XX. 000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 // 016-031 ..XXXX.XXXXXXXXX 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 // 032-047 XXXXXXXXXXXX.... 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 -// 048-063 ....XXXXXXX...XX 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 +// 048-063 ....XXXXX.....XX 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 // 064-079 ................ 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 -// 080-095 XX.............. 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 +// 080-095 XXXXX........... 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 // 096-111 ................ 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 // 112-127 ................ 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 @@ -252,9 +254,7 @@ localparam CONF_STR = { "Jaguar;;", "-;", "FS1,JAGJ64ROMBIN;", - //"F7,BIN,Load CUE Bin;", - "S1,CDI,Stream CD(Fast);", - //"F8,CDI,Load CD(Stable);", + "S1,CDIJCD,Stream CD;", "-;", "C,Cheats;", "H1O[23],Cheats Enabled,Yes,No;", @@ -266,6 +266,7 @@ localparam CONF_STR = { "P1O[8:7],Aspect ratio,Original,Full Screen,[ARC1],[ARC2];", "P1O[10:9],Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%,CRT 75%;", "P1O[19:18],Crop,None,Small,Primal;", + "P1O[84],Vertical Center,Off,On;", // Input Options "P2,Input Options;", "P2-;", @@ -290,33 +291,17 @@ localparam CONF_STR = { "D0P4O[13],Autosave,On,Off;", "P4-;", "P4O[30],Homebrew Support,On,Off;", + "P4-;", "P4O[52],Force CD Enabled,Off,On;", "P4O[56],Force MemoryTrack,Off,On;", "P4O[55],Force Music CD,Off,On;", + "P4O[82],CD Timing,Accurate,Fast;", + "P4O[83],Debug Overlay,Off,On;", + "P4-;", `ifndef MISTER_DUAL_SDRAM "P4O[36:34],FastRAM1,0,1,2,3,4,5,6,7;", "P4O[39:37],FastRAM2,7,6,5,4,3,2,1,0;", `endif - // "P2,CD Options;", - // "P2-;", - //"P2O[27:24],Bin Offset 0x3,0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F;", - //"P2O[53],CD Inserted,No,Yes;", - //"P2O[58:57],CDI Source,Auto,Stream,Auto,DDRAM;", - //"P2O[31],Quick CD,No,Yes;", - //"P3O[2],Cart Checksum Patch,Off,On;", - //"P3O[29],Auto EEPROM,No,Yes;", - //"P3O[81],Vint Fix,Yes,No;", - //"P3O[80],Gamedrive,On,Off;", - // "-;", - // "P6,Debug Options;", - // "P6-Options may crash;", - // "P6RF,Reset RAM(debug);", - // "P6D2OG,SDRAM,2,1(debug);", - // "P6O[62],Debug Save,No,Yes;", - // "P6O[63],Compare BIN,No,Yes;", - // "P6O[54],Disable DSP,No,Yes;", - // "P6O[14],VSync,vvs,hvs(debug);", - // "P6O[28],Tap Clock,1,4;", "-;", "R0,Reset;", "J1,A,B,C,Option,Pause,1,2,3,4,5,6,7,8,9,0,Star,Hash;", @@ -380,11 +365,11 @@ wire [8:0] spinner_1; wire [1:0] lightgun_mode = status[41:40]; wire crossmenu_disable = (lightgun_mode == 2'd0); -reg [31:0] cd_hps_lba; -reg cd_hps_req; +wire [31:0] cd_hps_lba; +wire cd_hps_req; wire cd_hps_ack; wire cd_media_change; -reg cd_img_mounted; +wire cd_img_mounted; wire nvram_hps_ack; bit nvram_hps_wr; @@ -439,7 +424,7 @@ hps_io #(.CONF_STR(CONF_STR), .PS2DIV(1000), .WIDE(1), .VDNUM(2)) hps_io .sd_lba('{sd_lba, cd_hps_lba}), .sd_blk_cnt('{0, 0}), - .sd_rd({cd_hps_req && cd_img_mounted, sd_rd}), + .sd_rd({cd_hps_req, sd_rd}), .sd_wr({1'b0, sd_wr}), .sd_ack({cd_hps_ack, sd_ack}), .sd_buff_addr(sd_buff_addr), @@ -485,43 +470,112 @@ wire os_index = (boot_index && (ioctl_index[7:6] == 0)) || ioctl_index[5:0] == 2 wire cart_index = ioctl_index[5:0] == 1; wire cdos_index = (boot_index && (ioctl_index[7:6] == 1)) || ioctl_index[5:0] == 3; wire nvme_index = (boot_index && (ioctl_index[7:6] == 2)) || ioctl_index[5:0] == 4; -wire cue_index = ioctl_index[5:0] == 7; wire os_download = ioctl_download && os_index; wire cart_download = ioctl_download & cart_index; wire code_download = ioctl_download & &ioctl_index; wire cdos_download = ioctl_download && cdos_index; wire nvme_download = ioctl_download && nvme_index; -wire cue_download = ioctl_download && cue_index; wire override; -assign ioctl_wait = cd_index ? !cd_wrack : !cart_wrack; -wire cd_wrack = !cd_wait; // TESTING!! -reg cd_wait; -wire cd_index = ioctl_index[5:0] == 6'd8; // 8-15 -// wire [1:0] file_type = ioctl_index[7:6]; -//wire cd_download = ioctl_download && cd_index; -wire [9:0] toc_addr = cd_init ? loader_addr[10:1] : cd_toc_addr; -wire [15:0] toc_data = cd_init ? loader_data_bs : cd_toc_data; -wire toc_wr = cd_init ? cue_download && loader_wr : cd_toc_wr; -wire [9:0] cd_toc_addr = {cd_track[6:0],cd_toc_type[2:0]}; -reg [2:0] cd_toc_type; -reg [15:0] cd_toc_data; -reg cd_toc_wr; +assign ioctl_wait = !cart_wrack; +wire cd_state_idle; +wire [7:0] cd_session_count; +wire cd_toc_wr; +wire [9:0] cd_toc_addr; +wire [15:0] cd_toc_data; +wire cd_valid; +wire audbus_busy; +wire aud_rd_trig; +wire lcnt; +wire clcnt; +wire [1:0] dbg_cd_fmt; +wire [4:0] dbg_cd_state; +wire [7:0] dbg_cd_track; +wire [7:0] dbg_cd_session; +wire [7:0] dbg_cd_desc_index; +wire [6:0] dbg_butch_cue_tracks; +wire [6:0] dbg_butch_aud_tracks; +wire [6:0] dbg_butch_dat_track; +wire [7:0] dbg_butch_dsa_sessions; +wire dbg_butch_sess1_valid; +wire [15:0] dbg_butch_last_ds; +wire [7:0] dbg_butch_last_err; +wire [6:0] dbg_butch_track_idx; +wire [6:0] dbg_butch_cues_addr; +wire [6:0] dbg_butch_cuet_addr; +wire [15:0] dbg_butch_resp_54; +wire [39:0] dbg_butch_toc0; +wire [39:0] dbg_butch_toc1; +wire [15:0] dbg_butch_spin; +wire [15:0] dbg_butch_ltoc0; +wire [15:0] dbg_butch_ltoc1; +wire dbg_butch_toc_ready; +wire [29:0] dbg_first_aud_addr0; +wire [29:0] dbg_first_aud_addr1; +wire dbg_first_grant_seen; +wire [29:0] dbg_first_grant_addr; +wire [29:0] dbg_first_grant_file_addr; +wire [63:0] dbg_first_grant_word; +wire [29:0] dbg_cur_file_addr; reg [22:20] cart_mask; reg found_cd = 0; reg bios_overwrote = 0; reg cd_loaded = 0; -reg old_cmc; +reg old_cmc = 1'b0; +reg dbg_first_boot_read_seen = 1'b0; +reg [6:0] dbg_first_boot_track_idx = 7'h0; +reg [6:0] dbg_first_boot_cuet = 7'h0; +reg dbg_data_probe_pending = 1'b0; +reg [29:0] dbg_data_probe_addr = 30'h0; +reg [29:0] dbg_data_probe_file_addr = 30'h0; +reg [63:0] dbg_data_probe_word = 64'h0; + +wire cd_mount_prestart = !old_cmc && cd_media_change; +wire cd_mount_start = old_cmc && !cd_media_change; +reg cd_mount_start_pending = 0; -wire cd_stream_start = !old_cmc && cd_media_change; always @(posedge clk_sys) begin old_cmc <= cd_media_change; - if (cd_stream_start) begin + + if (!xresetlp) begin + dbg_first_boot_read_seen <= 1'b0; + dbg_first_boot_track_idx <= 7'h0; + dbg_first_boot_cuet <= 7'h0; + dbg_data_probe_pending <= 1'b0; + dbg_data_probe_addr <= 30'h0; + dbg_data_probe_file_addr <= 30'h0; + dbg_data_probe_word <= 64'h0; + end else if (!dbg_first_boot_read_seen && cd_img_mounted && aud_rd_trig && + (dbg_butch_dat_track != 7'h0) && (dbg_butch_cuet_addr == dbg_butch_dat_track)) begin + dbg_first_boot_track_idx <= dbg_butch_track_idx; + dbg_first_boot_cuet <= dbg_butch_cuet_addr; + dbg_data_probe_addr <= audbus_out; + if (cd_valid) begin + dbg_first_boot_read_seen <= 1'b1; + dbg_data_probe_pending <= 1'b0; + dbg_data_probe_file_addr <= dbg_cur_file_addr; + dbg_data_probe_word <= cd_stream_q; + end else begin + dbg_data_probe_pending <= 1'b1; + end + end else if (dbg_data_probe_pending && cd_valid) begin + dbg_first_boot_read_seen <= 1'b1; + dbg_data_probe_pending <= 1'b0; + dbg_data_probe_file_addr <= dbg_cur_file_addr; + dbg_data_probe_word <= cd_stream_q; + end + + if (cd_mount_prestart) begin cd_loaded <= 1; + cd_mount_start_pending <= 1; cart_mask <= '0; loader_addr <= 32'h0000_0000; end + if (cd_mount_start) begin + cd_mount_start_pending <= 0; + end + if (reset && !ioctl_download) begin // ioctl_wait <= 0; // status_reg <= 0; @@ -537,7 +591,7 @@ always @(posedge clk_sys) begin loader_wr <= 0; // Default! old_ramreset <= status[15]; - if (~old_download && ioctl_download && (cart_index || os_index || cdos_index || cd_index || cue_index || nvme_index)) begin + if (~old_download && ioctl_download && (cart_index || os_index || cdos_index || nvme_index)) begin loader_addr <= 32'h0000_0000; // Force the cart ROM to load at 0x00800000 in DDR for Jag core. (byte address!) // (The ROM actually gets written at 0x30800000 in DDR, which is done when load_addr gets assigned to DDRAM_ADDR below). loader_en <= 1; @@ -567,15 +621,11 @@ always @(posedge clk_sys) begin if (loader_wr) loader_addr <= loader_addr + 2'd2; // Writing a 16-bit WORD at a time! // if (ioctl_wr && (cart_index || aud_index)) begin - if (ioctl_wr && (cart_index || os_index || cdos_index || cd_index || cue_index || nvme_index)) begin + if (ioctl_wr && (cart_index || os_index || cdos_index || nvme_index)) begin loader_wr <= 1; - // cd_wait <= 1; end // else if (cart_wrack) ioctl_wait <= 1'b0; - if (loader_en && DDRAM_BUSY) cd_wait <= 1; - else cd_wait <= 0; - /* if(ioctl_wait && !loader_wr) begin if(cnt) begin @@ -609,18 +659,13 @@ always @(posedge clk_sys) begin loader_save <= loader_data_bs; be_save <= loader_be; end - if (~old_download && ioctl_download && (cd_index)) begin - cart_mask <= '0; - mismatch <= 0; - cd_loaded <= 1; - end end end -wire reset = RESET | status[0] | buttons[1] | status[15]; +wire reset = RESET | status[0] | buttons[1] | status[15] | cd_stream_reset; -wire xresetlp = !(reset | os_download | cart_download | cue_download | cdos_download| nvme_download| !cd_init); // Forces reset on BIOS (boot.rom) load (ioctl_index==0), AND cart ROM. -wire xresetl = xresetlp && !(|bootcopy) && !(|timed_reset); +wire xresetlp = !(reset | os_download | cart_download | cdos_download | nvme_download /*| cd_boot*/); // Forces reset on BIOS (boot.rom) load (ioctl_index==0), cart ROM, and while CD stream housekeeping is still in progress. +wire xresetl = xresetlp && !(|bootcopy); reg [18:0] bootcopy; // 128k_bios+256k_cdbios+128k_nvbios == 512k wire [9:0] dram_a; wire dram_ras_n; @@ -665,7 +710,7 @@ wire cart_ce_n; wire [31:0] cart_q; // wire cart_ce_n_falling = (cart_ce_n_1 && !cart_ce_n); -reg xwaitl; +wire xwaitl; wire startcas; wire [15:0] aud_16_l; @@ -685,8 +730,13 @@ wire gamedrive_enable = max_compat; wire patch_checksums = status[2] || max_compat || status[31]; -wire cd_drive_en = cd_loaded || status[52] || cd_stream_start; -wire cd_inserted = cd_drive_en || status[53] || status[31]; +wire cd_drive_en = cd_loaded || status[52]; +wire cd_inserted = (cd_drive_en || status[53] || status[31]); +wire cd_latency_en = !status[82]; +// Temporary bring-up forcing: keep overlay visible to diagnose CDI boot hang. +wire debug_overlay_en = status[83]; + +wire cd_stream_reset = cd_drive_en && cd_media_change; jaguar jaguar_inst ( @@ -739,7 +789,6 @@ jaguar jaguar_inst .aud_16_r( aud_16_r ) , // output [15:0] aud_16_r .xwaitl( 1'b1 ) , -// .xwaitl( xwaitl ) , .vid_ce( vid_ce ) , @@ -766,20 +815,40 @@ jaguar jaguar_inst .vintbugfix( ~status[81] | max_compat ), .cd_en( cd_drive_en ), .cd_ex( cd_inserted ), + .cd_latency_en( cd_latency_en ), .b_override(override), .maxc(max_compat), .auto_eeprom(status[29] | max_compat), .addr_ch3(addr_ch3[23:0]), - .toc_addr(toc_addr), - .toc_data(toc_data), - .toc_wr(toc_wr), + .toc_addr(cd_toc_addr), + .toc_data(cd_toc_data), + .toc_wr(cd_toc_wr), .audbus_out( audbus_out ) , .aud_in( cart_q1 ) , .aud_cmp( cart_cmp ) , .audwaitl( xwaitl ) , .aud_ce(aud_ce), .aud_busy(audbus_busy), + // aud_sess: menu-driven audio-session override into Butch. .aud_sess(~status[55] ^ status[31]), + .force_music_cd(status[55]), + .dbg_butch_cue_tracks(dbg_butch_cue_tracks), + .dbg_butch_aud_tracks(dbg_butch_aud_tracks), + .dbg_butch_dat_track(dbg_butch_dat_track), + .dbg_butch_dsa_sessions(dbg_butch_dsa_sessions), + .dbg_butch_sess1_valid(dbg_butch_sess1_valid), + .dbg_butch_last_ds(dbg_butch_last_ds), + .dbg_butch_last_err(dbg_butch_last_err), + .dbg_butch_track_idx(dbg_butch_track_idx), + .dbg_butch_cues_addr(dbg_butch_cues_addr), + .dbg_butch_cuet_addr(dbg_butch_cuet_addr), + .dbg_butch_resp_54(dbg_butch_resp_54), + .dbg_butch_toc0(dbg_butch_toc0), + .dbg_butch_toc1(dbg_butch_toc1), + .dbg_butch_spin(dbg_butch_spin), + .dbg_butch_ltoc0(dbg_butch_ltoc0), + .dbg_butch_ltoc1(dbg_butch_ltoc1), + .dbg_butch_toc_ready(dbg_butch_toc_ready), .dohacks(patch_checksums), .xvclk_o(xvclk_o), .overflow (overflow), @@ -787,7 +856,8 @@ jaguar jaguar_inst .errflow (errflow), .unhandled (unhandled), .cd_valid(cd_valid), - .ntsc( ~status[4] ) , + .ntsc( ntsc ) , + .video_center(video_center), .ps2_mouse( ps2_mouse ) , @@ -836,6 +906,114 @@ assign CLK_VIDEO = clk_sys; //assign VGA_SL = {~interlace,~interlace} & sl[1:0]; +reg crop; +reg [13:0] hcount; + +wire [7:0] base_video_r = crop ? 8'h00 : vga_r; +wire [7:0] base_video_g = crop ? 8'h00 : vga_g; +wire [7:0] base_video_b = crop ? 8'h00 : vga_b; +wire [7:0] mix_video_r; +wire [7:0] mix_video_g; +wire [7:0] mix_video_b; +wire [31:0] audbus_out_dbg = {2'b00, audbus_out}; +(* keep = 1 *) wire [125:0] stp_jcd_ctrl = { + cd_hps_lba[19:0], + dbg_cur_file_addr, + audbus_out, + dbg_butch_cues_addr, + dbg_butch_cuet_addr, + dbg_butch_track_idx, + dbg_butch_dat_track, + dbg_cd_state, + dbg_cd_fmt, + cd_state_idle, + cd_stream_boot_pending, + dbg_butch_toc_ready, + dbg_first_boot_read_seen, + dbg_data_probe_pending, + cd_hps_ack, + cd_hps_req, + xwaitl, + cd_valid, + cd_img_mounted, + aud_rd_trig +}; +(* keep = 1 *) wire [63:0] stp_jcd_first_word = dbg_data_probe_word; +(* keep = 1 *) wire [143:0] stp_butch_dsa = { + dbg_butch_resp_54, + dbg_butch_toc0, + dbg_butch_toc1, + dbg_butch_spin, + dbg_butch_ltoc0, + dbg_butch_ltoc1 +}; +(* keep = 1 *) wire [63:0] stp_jcd_live_word = cd_stream_q; + +jaguar_debug_overlay debug_overlay_inst +( + .clk_sys(clk_sys), + .ce_pix(vid_ce), + .reset(reset), + .enable(debug_overlay_en), + .hblank(hblank), + .vblank(vblank), + .in_r(base_video_r), + .in_g(base_video_g), + .in_b(base_video_b), + .cd_drive_en(cd_drive_en), + .cd_img_mounted(cd_img_mounted), + .cd_inserted(cd_inserted), + .cd_valid(cd_valid), + .cd_fmt(dbg_cd_fmt), + .cd_state(dbg_cd_state), + .cd_track(dbg_cd_track), + .cd_session(dbg_cd_session), + .cd_desc_index(dbg_cd_desc_index), + .cd_session_count(cd_session_count), + .cd_toc_wr(cd_toc_wr), + .cd_toc_addr(cd_toc_addr), + .cd_toc_data(cd_toc_data), + .cd_hps_req(cd_hps_req), + .cd_hps_ack(cd_hps_ack), + .xwaitl(xwaitl), + .cd_stream_boot_pending(cd_stream_boot_pending), + .cd_state_idle_dbg(cd_state_idle), + .xresetlp_dbg(xresetlp), + .xresetl_dbg(xresetl), + .bootcopy_active(|bootcopy), + .cd_hps_lba(cd_hps_lba), + .audbus_out_dbg(audbus_out_dbg), + .butch_aud_sess(~status[55] ^ status[31]), + .butch_cue_tracks(dbg_butch_cue_tracks), + .butch_aud_tracks(dbg_butch_aud_tracks), + .butch_dat_track(dbg_butch_dat_track), + .butch_dsa_sessions(dbg_butch_dsa_sessions), + .butch_sess1_valid(dbg_butch_sess1_valid), + .butch_last_ds(dbg_butch_last_ds), + .butch_last_err(dbg_butch_last_err), + .butch_track_idx(dbg_butch_track_idx), + .butch_cues_addr(dbg_butch_cues_addr), + .butch_cuet_addr(dbg_butch_cuet_addr), + .butch_resp_54(dbg_butch_resp_54), + .butch_toc0(dbg_butch_toc0), + .butch_toc1(dbg_butch_toc1), + .butch_spin(dbg_butch_spin), + .butch_ltoc0(dbg_butch_ltoc0), + .butch_ltoc1(dbg_butch_ltoc1), + .first_aud_addr0(dbg_first_aud_addr0), + .first_aud_addr1(dbg_first_aud_addr1), + .first_grant_seen(dbg_first_boot_read_seen), + .first_grant_track(dbg_first_boot_track_idx), + .first_grant_cuet(dbg_first_boot_cuet), + .first_grant_addr(dbg_data_probe_addr), + .first_grant_file_addr(dbg_data_probe_file_addr), + .first_grant_word(dbg_data_probe_word), + .stream_q_dbg(cd_stream_q), + .out_r(mix_video_r), + .out_g(mix_video_g), + .out_b(mix_video_b) +); + video_mixer #(.LINE_LENGTH(700), .HALF_DEPTH(0), .GAMMA(1)) video_mixer ( .CLK_VIDEO(CLK_VIDEO), // input clk_sys @@ -850,9 +1028,9 @@ video_mixer #(.LINE_LENGTH(700), .HALF_DEPTH(0), .GAMMA(1)) video_mixer .gamma_bus(gamma_bus), - .R(crop ? 0 : vga_r), // Input [DW:0] R (set by HALF_DEPTH. is [7:0] here). - .G(crop ? 0 : vga_g), // Input [DW:0] G (set by HALF_DEPTH. is [7:0] here). - .B(crop ? 0 : vga_b), // Input [DW:0] B (set by HALF_DEPTH. is [7:0] here). + .R(mix_video_r), // Input [DW:0] R (set by HALF_DEPTH. is [7:0] here). + .G(mix_video_g), // Input [DW:0] G (set by HALF_DEPTH. is [7:0] here). + .B(mix_video_b), // Input [DW:0] B (set by HALF_DEPTH. is [7:0] here). // Positive pulses. .HSync(vga_hs_n), // input HSync @@ -869,8 +1047,6 @@ video_mixer #(.LINE_LENGTH(700), .HALF_DEPTH(0), .GAMMA(1)) video_mixer .CE_PIXEL(CE_PIXEL) ); -reg crop; -reg [13:0] hcount; always @(posedge clk_sys) if (reset) begin hcount <= 0; @@ -911,25 +1087,14 @@ assign DDRAM_CLK = clk_sys; assign DDRAM_BURSTCNT = 1; wire compare = status[63]; -// Jag CD bin data is now mapped at 0x30000000 in DDR on MiSTer, hence the setting of the upper bits here. -// Setting an offset from OSD will place the offset in this range in 16MB chunks. ie If offset 0xC is chosen it will start at 0x3C000000. -// If the bin is larger than 16MB it will continue into the next chunks as necessary. The cue needs to account for this. -// Note it appears 0x3C000000-3FFFFFFF is overwritten when a core is programmed over USB Blaster. The lower 196MB appears to be unchanged. -// DRAM address is using "abus_out" here (byte address, so three LSB bits are ignored!) -//assign DDRAM_ADDR = (loader_en) ? {4'h3,status[25:24], (status[26] | loader_addr[25]), (status[27] | loader_addr[24]), loader_addr[23:3]} : {4'h3,aud_idx[1:0], (aud_idx[2] | audbus_out[25]), (aud_idx[3] | -//assign DDRAM_ADDR = (loader_en) ? {4'h3,(status[27:24] | loader_addr[27:24]), loader_addr[23:3]} : {4'h3,audbus_out[27:3]}; -wire [28:3] sloader_addr; -assign sloader_addr[28:24] = loader_addr[28] ? loader_addr[28:24] : {1'b0, status[27:24] | loader_addr[27:24]}; -assign sloader_addr[23:3] = loader_addr[23:3]; -wire [28:3] premixed_addr = (loader_en) ? cd_index ? sloader_addr[28:3] : boot_addr[28:3] : audbus_out[28:3]; +wire [28:3] premixed_addr = loader_en ? boot_addr[28:3] : audbus_out[28:3]; wire [28:3] boot_addr; assign boot_addr[28:20] = (os_index) ? 9'h1FF : (cdos_index) ? 9'h1FE : 9'h1FC; //nvme_index = default //assign boot_addr[28:20] = (os_index) ? 9'h01F : (cdos_index) ? 9'h01E : 9'h01D; //nvme_index = default assign boot_addr[19:3] = audbus_out[19:3]; -assign sloader_addr[19:3] = loader_addr[19:3]; assign DDRAM_ADDR = {3'h1,~premixed_addr[28:23],premixed_addr[22:3]}; assign DDRAM_RD = (loader_en) ? compare && loader_wr : aud_rd_trig; -assign DDRAM_WE = (loader_en) ? loader_wr && (cd_index || os_index || os_index || cdos_index || nvme_index) && !compare: 1'b0; +assign DDRAM_WE = (loader_en) ? loader_wr && (os_index || cdos_index || nvme_index) && !compare : 1'b0; // Byteswap... // @@ -943,496 +1108,76 @@ assign DDRAM_BE = (loader_en) ? loader_be : 8'b11111111; // IIRC, the DDR contro reg [15:0] loader_save; reg mismatch; reg [7:0] be_save; - reg [23:0] old_abus_out; -reg [29:0] old_audbus_out; -reg old_aud_ce; wire overflow; wire underflow; wire errflow; wire unhandled; - -// wire os_rd_trigp = !os_ce_n && (os_ce_n_falling || (abus_out != old_abus_out)); -// wire cart_rd_trigp = !cart_ce_n && (cart_ce_n_falling || (abus_out != old_abus_out)); -wire aud_rd_trig = aud_ce && ((audbus_out != old_audbus_out) || (!old_aud_ce)); -wire cd_rd_trig = cd_ce && ((cd_bus_out != old_audbus_out) || (!old_aud_ce)); -wire img_rd_trig = cd_init ? aud_rd_trig : cd_rd_trig; -wire img_ce = cd_init ? aud_ce : cd_ce; -reg xwaitl_latch; -assign xwaitl = DDRAM_DOUT_READY | xwaitl_latch; -always @(posedge clk_sys) -if (reset) begin - xwaitl_latch <= 1'b1; // De-assert on reset! - old_abus_out <= 24'h112233; - old_audbus_out <= 30'h112233; - old_aud_ce <= 1'b1; -end else begin -// os_ce_n_1 <= os_ce_n; -// cart_ce_n_1 <= cart_ce_n; - old_abus_out <= abus_out; - old_audbus_out <= cd_init ? audbus_out : cd_bus_out; - old_aud_ce <= cd_init ? aud_ce : cd_ce; -// cart_diff <= cart_q1 != cart_q; -// cart_diff <= xwaitl && && (cdram_dout != DDRAM_DOUT); - - - if (img_rd_trig) begin - xwaitl_latch <= 1'b0; // Assert this (low) until the Cart data is ready. - end else if (DDRAM_DOUT_READY) - xwaitl_latch <= 1'b1; // De-assert, to let the core know. -end - -localparam [5:0] CD_RING_DEPTH = 6'd32; - -wire [29:0] imgbus_out; -wire [10:0] cd_ring_rd_addr = {imgbus_out[13:9], imgbus_out[8:3]}; -wire [10:0] cd_ring_wr_addr = {cd_hps_lba[4:0], sd_buff_addr[7:2]}; - -dpram #(11,16) cdram_inst0 -( - .clock(clk_sys), - .address_a(cd_ring_rd_addr), - .q_a({cdram_dout[55:48],cdram_dout[63:56]}), - - .address_b(cd_ring_wr_addr), - .data_b(sd_buff_dout), - .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b00)) -); - -dpram #(11,16) cdram_inst1 -( - .clock(clk_sys), - .address_a(cd_ring_rd_addr), - .q_a({cdram_dout[39:32],cdram_dout[47:40]}), - - .address_b(cd_ring_wr_addr), - .data_b(sd_buff_dout), - .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b01)) -); - -dpram #(11,16) cdram_inst2 -( - .clock(clk_sys), - .address_a(cd_ring_rd_addr), - .q_a({cdram_dout[23:16],cdram_dout[31:24]}), - - .address_b(cd_ring_wr_addr), - .data_b(sd_buff_dout), - .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b10)) -); - -dpram #(11,16) cdram_inst3 -( - .clock(clk_sys), - .address_a(cd_ring_rd_addr), - .q_a({cdram_dout[7:0],cdram_dout[15:8]}), - - .address_b(cd_ring_wr_addr), - .data_b(sd_buff_dout), - .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b11)) -); - -wire [63:0] cdram_dout; -wire audbus_busy = img_ce || img_rd_trig || load_state; -reg load_state; -reg cd_ring_armed; -reg [20:0] cd_ring_base_lba; -reg [5:0] cd_ring_count; -wire [20:0] cd_ring_end_lba = cd_ring_base_lba + {15'h0, cd_ring_count}; -reg [31:0] load_cnt; -reg [31:0] max_load_cnt; -wire lcnt = max_load_cnt == load_cnt; -reg [31:0] cload_cnt; -reg [31:0] max_cload_cnt; -wire clcnt = max_cload_cnt == cload_cnt; -wire cd_init = (cd_state == 0) || cd_stream_start; -wire [5:0] cd_ring_target_depth = cd_init ? CD_RING_DEPTH : 6'd1; -reg [3:0] cd_state; -reg [29:0] cd_size; -reg [29:0] cd_bus_out; -reg cd_ce; -assign imgbus_out = cd_init ? audbus_out : cd_bus_out; -reg [2:0] cd_cnt; -reg [31:0] cd_header; -reg [7:0] cd_sessions; -reg cd_session0; -reg [7:0] cd_tracks; -reg [7:0] cd_track; -wire [7:0] cd_data = cdram_dout[8*(7-cd_bus_out[2:0]) +:8]; -reg [23:0] cd_pregap; -reg [29:4] cd_pregap_pos; -reg [31:0] cd_length; -reg [31:0] cd_startlba; -reg [31:0] cd_totlength; -reg [31:0] cd_start; -reg [19:0] cd_add1; // max lba fits in 19bits -reg [29:8] cd_add2; -reg [23:4] cd_add3; -reg [23:0] cd_tomsf; -reg do_tomsf; -reg [6:0] cd_min; -reg [5:0] cd_sec; -wire [6:0] cd_frame = cd_tomsf[6:0]; -wire [23:0] cd_msf = {1'b0, cd_min, 2'b0, cd_sec, 1'b0, cd_frame}; -wire [23:0] min_to_frames = 24'd4500; // 60*75 -wire [23:0] sec_to_frames = 24'd75; // 75 -reg [7:0] cd_tmp; -reg [29:0] cd_bus_add; -reg cd_bus_size; -reg cd_bus_header; -reg old_msf; -reg djv2; -reg djv3; -wire [20:0] cd_img_lba = imgbus_out[29:9]; -wire cd_lba_in_ring = (cd_ring_count != 6'd0) && (cd_img_lba >= cd_ring_base_lba) && (cd_img_lba < cd_ring_end_lba); -wire cd_lba_loading = load_state && (cd_hps_lba[20:0] == cd_img_lba); -wire cd_fresh = !(cd_lba_loading && (imgbus_out[8:1] >= sd_buff_addr[7:0])); -wire cd_valid = cd_img_mounted && cd_lba_in_ring && cd_fresh; - -reg [23:0] timed_reset = 0; +wire [63:0] cd_stream_q; always @(posedge clk_sys) begin - reg old_ack; - reg [20:0] lba_delta; - reg miss_request_now; - miss_request_now = 1'b0; if (reset) begin - load_state <= 1'b0; - cd_ring_armed <= 1'b0; - cd_ring_base_lba <= 21'h0; - cd_ring_count <= 6'h0; - //cd_img_mounted <= 1'b0; - cd_hps_req <= 0; - cd_hps_lba[31:0] <= 32'h0; - load_cnt[31:0] <= 32'h0; - max_load_cnt[31:0] <= 32'h0; - cload_cnt[31:0] <= 32'h0; - max_cload_cnt[31:0] <= 32'h0; - cd_state <= 4'h0; - cd_size[29:0] <= 30'h0; - cd_toc_type[2:0] <= 3'h0; - cd_toc_data[15:0] <= 16'h0; - cd_toc_wr <= 0; + old_abus_out <= 24'h112233; + end else begin + old_abus_out <= abus_out; end - - if (|timed_reset) - timed_reset <= timed_reset - 1'b1; - - if (cd_stream_start) begin - cd_img_mounted <= 1; - cd_state <= 4'hF; - timed_reset <= 24'hFFFFFF; - cd_size[29:0] <= img_size[29:0]; - load_state <= 1'b0; - cd_ring_armed <= 1'b0; - cd_ring_base_lba <= 21'h0; - cd_ring_count <= 6'h0; - cd_hps_req <= 1'b0; - end - - cd_ce <= 0; - cd_toc_wr <= 0; - old_msf <= do_tomsf; - if (do_tomsf) begin - if (!old_msf) begin - cd_min <= 'h0; - cd_sec <= 'h0; - end else if (cd_tomsf >= min_to_frames) begin - cd_tomsf <= cd_tomsf - min_to_frames; - cd_min <= cd_min + 7'd1; - end else if (cd_tomsf >= sec_to_frames) begin - cd_tomsf <= cd_tomsf - sec_to_frames; - cd_sec <= cd_sec + 6'd1; - end else begin - do_tomsf <= 0; - end - end - if (!audbus_busy && !do_tomsf) begin - cd_cnt <= cd_cnt + 3'h1; - cd_bus_header = 0; // intentionally not <= - cd_bus_size = 0; // intentionally not <= - cd_bus_add = 30'h0; // intentionally not <= - if (cd_state == 4'hF) begin // cache (header size - 1) - if (|cd_size[29:10]) begin// must be at least one buffer + header - cd_bus_size = 1; // intentionally not <= - cd_bus_add = 30'h8; // intentionally not <= - //cd_bus_out[29:0] <= cd_size[29:0] - 30'h8; // get file type version and header size - cd_ce <= 1; - cd_cnt <= 3'h0; - cd_state <= 4'hE; - end else begin - cd_state <= 4'h0; // too small - end - end else if (cd_state == 4'hE) begin // get file type version - if (cd_cnt == 3'h0) begin - if (cd_data == 8'h6) begin // 80000006=v3.5,80000005=v3,80000004=v2 - djv2 <= 1'b0; - djv3 <= 1'b0; - end else if (cd_data == 8'h5) begin // 80000006=v3.5,80000005=v3,80000004=v2 - djv2 <= 1'b0; - djv3 <= 1'b1; - end else if (cd_data == 8'h4) begin // 80000006=v3.5,80000005=v3,80000004=v2 - djv2 <= 1'b1; - djv3 <= 1'b0; - end else begin // 80000006=v3.5,80000005=v3,80000004=v2 - cd_state <= 4'h0; // unsupported - end - end - if ((cd_cnt == 3'h1) || (cd_cnt == 3'h2)) begin - if (cd_data != 8'h0) begin // 80000006=v3.5,80000005=v3,80000004=v2 - cd_state <= 4'h0; // unsupported - end - end - if (cd_cnt == 3'h3) begin - if (cd_data != 8'h80) begin // 80000006=v3.5,80000005=v3,80000004=v2 - cd_state <= 4'h0; // unsupported - end - end - if (cd_cnt[2] == 1'b1) begin - cd_header[8*cd_cnt[1:0] +:8] <= cd_data; - end - cd_bus_add = 30'h1; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h1; - cd_ce <= 1; - if (cd_cnt == 3'h7) begin - cd_state <= 4'hD; - //cd_cnt <= 3'h0; // handled by cnt+1 - cd_bus_add = 30'h0; // intentionally not <= - end - end else if (cd_state == 4'hD) begin // get sessions - if (cd_cnt[0] == 1'b0) begin - cd_bus_size = 1; // intentionally not <= - cd_bus_header = djv2 || djv3; // intentionally not <= - cd_bus_add = cd_header[29:0]; // intentionally not <= - //cd_bus_out[29:0] <= cd_size[29:0] - cd_header[29:0]; // get sessions (start of header - assuming v3.5) - end else begin - cd_sessions <= cd_data; - cd_state <= 4'hC; - cd_bus_add = 30'h2; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h2; // get tracks - end - cd_ce <= 1; - cd_session0 <= 1; - cd_track <= 8'h1; - cd_tracks <= 8'h0; - cd_add1 <= 'h0; - end else if (cd_state == 4'hC) begin // get tracks - cd_tracks <= cd_tracks + cd_data; - cd_state <= 4'hB; - cd_bus_add = 30'h1E; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h1E; // get filenamelength 2+28 (assuming no 8 length extra info) - cd_ce <= 1; - cd_cnt <= 3'h0; - end else if (cd_state == 4'hB) begin // get filenamelength - if (cd_cnt[0] == 1'b0) begin - cd_bus_add = cd_data; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + cd_data; // get pregap +1+11+4+4+4+8+2 (assuming 8 DJ4) - end else begin - cd_bus_add = djv2 ? 30'h1A : 30'h22; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h22; // get pregap +1+11+4+4+4+8+2 (assuming 8 DJ4) - cd_state <= 4'hA; - cd_ce <= 1; - cd_cnt <= 3'h0; - end - end else if (cd_state == 4'hA) begin // get pregap and length - cd_bus_add = 30'h1; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h1; - cd_ce <= 1; - if (cd_cnt[2] == 1'b0) begin - cd_pregap[8*cd_cnt[1:0] +:8] <= cd_data; - end else begin - cd_length[8*cd_cnt[1:0] +:8] <= cd_data; - end - if (cd_cnt == 3'h7) begin - cd_state <= 4'h9; - cd_cnt <= 3'h0; - cd_bus_add = 30'h17; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h17; // get startlba +1+6+4+12 (assuming mode is 0) - end - end else if (cd_state == 4'h9) begin // get startlba and totlength - cd_bus_add = 30'h1; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h1; - cd_ce <= 1; - if (cd_cnt[2] == 1'b0) begin - cd_startlba[8*cd_cnt[1:0] +:8] <= cd_data; - end else begin - cd_totlength[8*cd_cnt[1:0] +:8] <= cd_data; - end - if (cd_cnt == 3'h7) begin - cd_state <= 4'h8; - cd_cnt <= 3'h0; - cd_bus_add = djv2 ? 30'h32 : 30'h89; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h89; // get startlba +1+16+4+29+5+4+78 (assuming sectorsize is 2352 and extra) - end - end else if (cd_state == 4'h8) begin // write settings - cd_state <= 4'h7; - cd_start <= cd_startlba + cd_pregap; - cd_tomsf <= cd_startlba[23:0] + cd_pregap; - do_tomsf <= 1; - end else if (cd_state == 4'h7) begin // write settings - cd_toc_type[2:0] <= 3'h0; - cd_toc_data[15:0] <= cd_msf[23:8]; - cd_tmp[7:0] <= cd_msf[7:0]; - cd_toc_wr <= 1; - cd_state <= 4'h6; - cd_tomsf <= cd_totlength[23:0]; - do_tomsf <= 1; - cd_cnt <= 3'h0; - end else if (cd_state == 4'h6) begin // write settings - // *2352=<<11 + <<8 + <<5 + <<4 - if (cd_cnt[1:0] == 2'b00) begin - cd_pregap_pos[29:4] <= {7'h0,cd_add1[18:0]}; - end else if (cd_cnt[1:0] == 2'b01) begin - cd_pregap_pos[29:5] <= cd_pregap_pos[29:5] + {6'h0,cd_add1[18:0]}; - end else if (cd_cnt[1:0] == 2'b10) begin - cd_pregap_pos[29:8] <= cd_pregap_pos[29:8] + {3'h0,cd_add1[18:0]}; - end else if (cd_cnt[1:0] == 2'b11) begin - cd_pregap_pos[29:11] <= cd_pregap_pos[29:11] + {cd_add1[18:0]}; - cd_toc_type[2:0] <= 3'h1; - cd_toc_data[15:8] <= cd_tmp[7:0]; - cd_toc_data[7:0] <= cd_msf[23:16]; - cd_toc_wr <= 1; - cd_state <= 4'h5; - end - end else if (cd_state == 4'h5) begin // write settings - cd_toc_type[2:0] <= 3'h2; - cd_toc_data[15:0] <= cd_msf[15:0]; - cd_toc_wr <= 1; - cd_state <= 4'h4; - cd_tomsf <= cd_pregap; - do_tomsf <= 1; - cd_add1[18:0] <= cd_add1[18:0] + cd_pregap[18:0]; - end else if (cd_state == 4'h4) begin // write settings - cd_toc_type[2:0] <= 3'h3; -// cd_toc_data[15:0] <= 16'h0; // cd_msf[15:0]; // assumes pregap < 1 min - cd_toc_data[15:0] <= cd_msf[15:0]; // assumes pregap < 1 min - cd_toc_wr <= 1; - cd_state <= 4'h3; - cd_add1[18:0] <= cd_add1[18:0] + cd_length[18:0]; - cd_cnt <= 3'h0; - end else if (cd_state == 4'h3) begin // write settings - if (cd_cnt[0] == 1'b0) begin - cd_toc_type[2:0] <= 3'h4; - cd_toc_data[15:8] <= cd_session0 ? 8'h1 : 8'h2; - cd_toc_data[7:0] <= {2'b00,cd_pregap_pos[29:24]}; - end else begin - cd_toc_type[2:0] <= 3'h5; - cd_toc_data[15:0] <= {cd_pregap_pos[23:8]}; - cd_state <= 4'h2; - cd_cnt <= 3'h0; - end - cd_toc_wr <= 1; - cd_tomsf <= cd_start[23:0] + cd_length[23:0]; - do_tomsf <= 1; - end else if (cd_state == 4'h2) begin // write settings - if (cd_cnt[0] == 1'b0) begin - cd_toc_type[2:0] <= 3'h6; - cd_toc_data[15:8] <= {cd_pregap_pos[7:4],4'h0}; - cd_toc_data[7:0] <= {cd_msf[23:16]}; - end else begin - cd_toc_type[2:0] <= 3'h7; - cd_toc_data[15:0] <= {cd_msf[15:0]}; - cd_state <= 4'h1; - end - cd_toc_wr <= 1; - end else if (cd_state == 4'h1) begin // track done - cd_state <= 4'hB; - cd_cnt <= 3'h0; - cd_track <= cd_track + 8'h1; - cd_bus_add = 30'h1C; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'h1C; // get filenamelength +28 (assuming no extra) - cd_ce <= 1; - if (cd_track == cd_tracks) begin - if (cd_session0 && (cd_sessions[7:1] != 7'h0)) begin - cd_session0 <= 0; - cd_state <= 4'hC; - cd_bus_add = djv2 ? 30'hC : 30'hD; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0] + 30'hD; // get tracks +4+8+1 (assuming not v2) - end else begin - cd_state <= 4'h0; // done - cd_bus_add = 30'h0; // intentionally not <= - //cd_bus_out[29:0] <= cd_bus_out[29:0]; // get tracks +0 - end - end - end - cd_bus_out[29:0] <= cd_bus_header ? cd_bus_add[29:0] : cd_bus_size ? (cd_size[29:0] - cd_bus_add[29:0]) : (cd_bus_out[29:0] + cd_bus_add[29:0]); - end -// 3 lbatomsf(startlba+pregap) -// 3 lbatomsf(length) -// 2 0 (pregap) -// 1 session (1 or 2) -// 4 troffset = position + pregap*2352 -// 3 lbatomsf(sessend = startlba+pregap+length) - if (cd_index) begin - cd_img_mounted <= 0; - cd_ring_armed <= 1'b0; - cd_ring_count <= 6'h0; - load_state <= 1'b0; - cd_hps_req <= 1'b0; - end - - old_ack <= cd_hps_ack; - - if (~old_ack && cd_hps_ack) begin - cd_hps_req <= 1'b0; - end - - if (load_state && old_ack && ~cd_hps_ack) begin - load_state <= 1'b0; - if ((cd_ring_count < CD_RING_DEPTH) && (cd_hps_lba[20:0] == cd_ring_end_lba)) begin - cd_ring_count <= cd_ring_count + 6'd1; - end - end - - if (img_rd_trig && cd_img_mounted) begin - load_cnt[31:0] <= 32'h0; - cload_cnt[31:0] <= 32'h0; - cd_ring_armed <= 1'b1; - if (!cd_lba_in_ring) begin - cd_ring_base_lba <= cd_img_lba; - cd_ring_count <= 6'd0; - if (!load_state) begin - cd_hps_lba <= {11'h000, cd_img_lba}; - cd_hps_req <= 1'b1; - load_state <= 1'b1; - miss_request_now = 1'b1; - end - end else if (cd_img_lba != cd_ring_base_lba) begin - lba_delta = cd_img_lba - cd_ring_base_lba; - cd_ring_base_lba <= cd_img_lba; - cd_ring_count <= cd_ring_count - lba_delta[5:0]; - end - end - - if (cd_ring_armed && !cd_lba_in_ring) begin - load_cnt <= load_cnt + 1'd1; - if (load_cnt > max_load_cnt) begin - max_load_cnt <= load_cnt; - end - end - - if (load_state || cd_hps_req) begin - cload_cnt <= cload_cnt + 1'd1; - if (cload_cnt > max_cload_cnt) begin - max_cload_cnt <= cload_cnt; - end - end - - if (cd_img_mounted && !cd_index && cd_ring_armed && !load_state && !miss_request_now && (cd_ring_count < cd_ring_target_depth)) begin - cd_hps_lba <= {11'h000, cd_ring_end_lba}; - cd_hps_req <= 1'b1; - load_state <= 1'b1; - end - end -wire [1:0] cart_oe; +// Keep the top level focused on bus selection and reset policy. All CD image +// caching, parsing, TOC synthesis, and stream refill logic now lives in +// rtl/cd_stream.sv. +jaguar_cd_stream cd_stream_inst +( + .clk_sys(clk_sys), + // Keep the mounted CD image/cache alive across menu soft resets. A menu reset + // should reset the emulated console, not force the streamed disc to vanish + // until cd_media_change toggles again. Reserve the stream reset for the + // framework-level reset input only. + .reset(RESET || cd_mount_prestart), + .bk_int(bk_int), + .DDRAM_DOUT_READY(DDRAM_DOUT_READY), + .audbus_out(audbus_out), + .aud_ce(aud_ce), + .cd_stream_start(cd_mount_start), + .img_size(img_size), + .cd_hps_ack(cd_hps_ack), + .sd_buff_addr(sd_buff_addr), + .sd_buff_dout(sd_buff_dout), + .sd_buff_wr(sd_buff_wr), + .cd_hps_lba(cd_hps_lba), + .cd_hps_req(cd_hps_req), + .cd_img_mounted(cd_img_mounted), + .cd_state_idle(cd_state_idle), + .cd_session_count(cd_session_count), + .cd_toc_wr(cd_toc_wr), + .cd_toc_addr(cd_toc_addr), + .cd_toc_data(cd_toc_data), + .cd_valid(cd_valid), + .audbus_busy(audbus_busy), + .xwaitl(xwaitl), + .aud_rd_trig(aud_rd_trig), + .lcnt(lcnt), + .clcnt(clcnt), + .dbg_cd_fmt(dbg_cd_fmt), + .dbg_cd_state(dbg_cd_state), + .dbg_cd_track(dbg_cd_track), + .dbg_cd_session(dbg_cd_session), + .dbg_cd_desc_index(dbg_cd_desc_index), + .dbg_first_aud_addr0(dbg_first_aud_addr0), + .dbg_first_aud_addr1(dbg_first_aud_addr1), + .dbg_first_grant_seen(dbg_first_grant_seen), + .dbg_first_grant_addr(dbg_first_grant_addr), + .dbg_first_grant_file_addr(dbg_first_grant_file_addr), + .dbg_first_grant_word(dbg_first_grant_word), + .dbg_cur_file_addr(dbg_cur_file_addr), + .stream_q(cd_stream_q), + .cd_boot(cd_boot) +); // 32-bit cart mode... // //assign cart_q1 = (!abus_out[2]) ? DDRAM_DOUT[63:32] : DDRAM_DOUT[31:00]; -assign cart_q1 = ((!status[57] && !cd_img_mounted) || (&status[58:57])) ? {DDRAM_DOUT[31:00],DDRAM_DOUT[63:32]} : {cdram_dout[31:00],cdram_dout[63:32]}; -//assign cart_q1 = (!(|imgbus_out[29:24])) ? {DDRAM_DOUT[31:00],DDRAM_DOUT[63:32]} : {cdram_dout[31:00],cdram_dout[63:32]}; +assign cart_q1 = cd_img_mounted ? cd_stream_q : {DDRAM_DOUT[31:00],DDRAM_DOUT[63:32]}; wire [63:0] cart_cmp = {DDRAM_DOUT[31:00],DDRAM_DOUT[63:32]}; wire [3:0] dram_oe = (~dram_cas_n) ? ~dram_oe_n[3:0] : 4'b0000; diff --git a/rtl/Rework/butch.v b/rtl/Rework/butch.v index 25abbf5..f562d5d 100644 --- a/rtl/Rework/butch.v +++ b/rtl/Rework/butch.v @@ -9,6 +9,7 @@ module _butch input cd_en, input cd_ex, input aud_sess, + input force_music_cd, input eoe0l, input eoe1l, input ewe0l, @@ -47,6 +48,24 @@ module _butch output errflowo, output unhandledo, input cd_valid, + input cd_latency_en, + output [6:0] dbg_cue_tracks, + output [6:0] dbg_aud_tracks, + output [6:0] dbg_dat_track, + output [7:0] dbg_dsa_sessions, + output dbg_sess1_valid, + output [15:0] dbg_last_ds, + output [7:0] dbg_last_err, + output [6:0] dbg_track_idx, + output [6:0] dbg_cues_addr, + output [6:0] dbg_cuet_addr, + output [15:0] dbg_resp_54, + output [39:0] dbg_toc0, + output [39:0] dbg_toc1, + output [15:0] dbg_spin, + output [15:0] dbg_ltoc0, + output [15:0] dbg_ltoc1, + output dbg_toc_ready, input sys_clk ); @@ -67,10 +86,11 @@ wire oet = !cart_ce_n && !(eoe0l && eoe1l); reg [31:0] butch_reg [0:11]; //BUTCH equ $DFFF00 ; base of Butch=interrupt control register, R/W //assign eint = (!butch_reg[0][0]) || (!fifo_int && !frame_int &&!sub_int && !tbuf_int && !rbuf_int); -assign eint = (butch_reg[0][0]) && (fifo_int || frame_int || sub_int || tbuf_int || rbuf_int); +// External interrupt line from Butch is active-high in this implementation. +assign eint = cd_en && (butch_reg[0][0]) && (fifo_int || frame_int || sub_int || tbuf_int || rbuf_int); wire fifo_int = butch_reg[0][9] && butch_reg[0][1]; -wire frame_int = butch_reg[0][10] && butch_reg[0][2]; -wire sub_int = butch_reg[0][11] && butch_reg[0][3]; +wire sub_int = butch_reg[0][10] && butch_reg[0][3]; +wire frame_int = butch_reg[0][11] && butch_reg[0][2]; wire tbuf_int = butch_reg[0][12] && butch_reg[0][4]; wire rbuf_int = butch_reg[0][13] && butch_reg[0][5]; wire cd_crcerror = butch_reg[0][6]; @@ -217,7 +237,7 @@ wire i2s_jerry = butch_reg[4][1]; wire i2s_fifo_enabled = butch_reg[4][2]; // guess. turned on in read handler (gas/das) wire i2s_16bit = butch_reg[4][3]; // ? only affects i2s format? wire i2s_fifonempty = i2s_rfifopos != i2s_wfifopos;//butch_reg[4][4]; -reg [31:0] ds_resp [0:4]; +reg [31:0] ds_resp [0:5]; reg [2:0] ds_resp_idx; reg [2:0] ds_resp_size; // max = 5 reg [6:0] ds_resp_loop; // max = numtracks=99 @@ -236,9 +256,12 @@ reg [6:0] aseconds; // 0-59 // (msf / 75) % 60 reg [6:0] aminutes; // 0-99 // (msf / 75) / 60 reg [6:0] atrack; // 1-99 wire [7:0] subcode [0:11]; -assign subcode[0] = 8'h1; // 2 channel audio no preemphasis, address 1 -assign subcode[1] = bcd[atrack]; // trackno bcd -assign subcode[2] = 8'h0; // trackidx bcd +wire [6:0] atrack_safe = (atrack > 7'd99) ? 7'd99 : atrack; +wire [7:0] subq_tno; +wire [7:0] subq_index; +assign subcode[0] = 8'h1; // ctrl/addr; keep stable for compatibility with current subcode path +assign subcode[1] = subq_tno; // track no: 00 lead-in/pregap, 01..99 program, AA lead-out +assign subcode[2] = subq_index; // index: 00 pregap, 01 program assign subcode[3] = bcd[rminutes]; // rel min bcd assign subcode[4] = bcd[rseconds]; // rel sec bcd assign subcode[5] = bcd[rframes]; // rel frames bcd @@ -253,7 +276,11 @@ reg [7:0] crc0; reg [15:0] crc; reg recrc; reg [3:0] subidx; -wire [15:0] subresp = {subcode[subidx],4'h1,subidx}; +reg [7:0] sub_chunk_count; +reg subcode_irq_pending; +reg frame_irq_pending; +wire [15:0] subresp = {subcode[subidx],sub_chunk_count}; +wire [15:0] subresp_b = {8'h00,sub_chunk_count}; wire subbit = subcode[crcidx[6:3]][~crcidx[2:0]]; wire [15:0] crcs = nextcrcb ? crc ^ {subcode[crcidx[6:3]],8'h00} : crc; wire [15:0] nextcrc = {crcs[14:0],1'b0}; @@ -303,7 +330,11 @@ assign hackbus2 = 1'b0;//cd_en && aud_sess && (({ain[23:1],1'b0}==24'h050EC0)) & assign override = cdbios && cd_en; assign doe = cd_en && oet && (breg);// || (!cdbios && caddr)); // not sure how mirroring applies or if reading is sometimes disabled - probably disabled when cdbios is disabled to allow cart pass through for >=4MB assign dout[31:0] = (aeven) ? dout_t[31:0] : {dout_t[15:0],dout_t[15:0]}; -wire [31:0] dout_t = doe_ds ? ds_resp[ds_resp_idx] : doe_sub ? {subresp,subresp} : doe_fif ? cur_i2s_fifo : butch_reg[ain[5:2]]; +wire [31:0] dout_t = doe_ds ? ds_resp[ds_resp_idx] : + doe_sub ? {subresp,subresp} : + doe_subb ? {subresp_b,subresp_b} : + doe_fif ? cur_i2s_fifo : + (ain[5:2] < 4'd12) ? butch_reg[ain[5:2]] : 32'hFFFFFFFF; wire aeven = (ain[1]==1'b0); //even is high [31:16] wire breg = ain[23:8]==24'hdfff; wire caddr = ain[23:22]==2'b10; @@ -311,10 +342,15 @@ wire dsc_a = ain[5:2]==4'h1; wire doe_dsc = doe && dsc_a; wire ds_a = ain[5:2]==4'h2; // should be 0xA not just 0x8? wire doe_ds = doe && ds_a; +wire dsa_cmd_stop_wr = cd_en && wet && (ain[23:8] == 24'hdfff) && ds_a && (din[15:8] == 8'h02); wire ictl_a = ain[5:2]==4'h4; // 0x10 wire doe_ictl = doe && ictl_a; +wire sbcntrl_a = ain[5:2]==4'h5; // 0x14 (SBCNTRL) +wire doe_sbcntrl = doe && sbcntrl_a; wire sub_a = ain[5:2]==4'h6; // should be 0x1A not just 0x18? wire doe_sub = doe && sub_a; +wire subb_a = ain[5:2]==4'h7; // 0x1C (SUBDATB) +wire doe_subb = doe && subb_a; wire fif_a1 = ain[5:2]==4'h9; // 0x24 wire fif_a2 = ain[5:2]==4'hA; // 0x28 wire fif_a = fif_a1 || fif_a2; // 0x24 or 0x28 @@ -326,12 +362,17 @@ reg [23:0] max_ch3; reg old_doe_ds; reg old_doe_dsc; reg old_doe_sub; +reg old_doe_sbcntrl; reg old_doe_fif; reg old_fif_a1; reg old_ws; //wire [6:0] num_tracks = 7'd6; wire [6:0] num_tracks = cue_tracks[6:0]; +wire [6:0] num_tracks_safe = (num_tracks != 7'h0) ? num_tracks : 7'h1; +wire [6:0] cues_addr_clamped = + (cues_addr < 7'h1) ? 7'h1 : + ((cues_addr > num_tracks_safe) ? num_tracks_safe : cues_addr); // 1 frame = 588 longs (samples) = 2352 bytes // 75 frames = 1 second // 60 frames = 1 minute @@ -383,24 +424,47 @@ wire speed2x = mode[1]; wire cdrommd = mode[3];//audiomd==0 wire attiabs = mode[4]; wire attirel = mode[5]; -wire pausetr = mode[6]; +wire pause_data_junk = pause && cdrommd; // 5 - 4 = Actual Title, Time, Index (ATTI) setting // 00 = no title, index or time send during play modes // 01 = sending title, index and absolute time (min/sec) // 10 = sending title, index and relative time (min/sec) // 11 = reserved +// ATTI runtime reporting state. +reg atti_report_valid; +reg [7:0] atti_last_title; +reg [7:0] atti_last_index; +reg [6:0] atti_last_rel_minutes; +reg [5:0] atti_last_rel_seconds; +reg [6:0] atti_last_abs_minutes; +reg [5:0] atti_last_abs_seconds; +reg atti_evt_title_pending; +reg atti_evt_index_pending; +reg atti_evt_rel_minutes_pending; +reg atti_evt_rel_seconds_pending; +reg atti_evt_abs_minutes_pending; +reg atti_evt_abs_seconds_pending; +reg play_title_pending_rsp; +reg [6:0] play_title_pending_track; +reg leadout_title_pending; +reg leadout_seen; + reg updabs; +reg updabs_req; reg [7:0] seek; reg [6:0] sframes; // 0-74 // (msf % 75) reg [5:0] sseconds; // 0-59 // (msf / 75) % 60 reg [6:0] sminutes; // 0-99 // (msf / 75) / 60 +reg [6:0] goto_minutes; // latched by 0x10 +reg [5:0] goto_seconds; // latched by 0x11 reg [2:0] gframes; // 0-6 gap frames reg [15:0] fdata; reg [63:0] fd; reg [7:0] seek_count; +reg [7:0] dsa_volume; wire aud_busy = (old_aud_rd3) || (old_aud_rd2) || (old_aud_rd) || (aud_rd) || (!audwaitl); reg [18:0] taud_add; reg [29:8] taud2_add; @@ -410,10 +474,38 @@ reg [5:0] subtrseconds; // 0-59 reg [15:0] last_ds; reg [31:0] seek_delay; reg [31:0] seek_delay_set; +reg seek_skip_cbusy_wait; +reg seek_found_pending; reg [19:0] seek_src_abs; reg [19:0] seek_dst_abs; reg [20:0] seek_delta_abs; reg [19:0] seek_mid_abs; +reg [7:0] dsa_last_error; +reg title_len_pending; +reg dsa_delay_pending; +reg [31:0] dsa_delay_ctr; +reg dsa_spinup_wait_toc; +reg [7:0] dsa_spinup_session; +reg dsa_long_toc_active; +reg [6:0] dsa_long_toc_first_track; +reg [6:0] dsa_long_toc_last_track; +reg toc_ready; + +localparam [31:0] DSA_DELAY_PLAY_TITLE = 32'd21272000; // 200 ms +localparam [31:0] DSA_DELAY_STOP_INNER = 32'd79770000; // 750 ms +localparam [31:0] DSA_DELAY_STOP_MID = 32'd159540000; // 1500 ms +localparam [31:0] DSA_DELAY_STOP_OUTER = 32'd239310000; // 2250 ms +localparam [31:0] DSA_DELAY_PAUSE = 32'd7977000; // 75 ms +localparam [31:0] DSA_DELAY_UNPAUSE = 32'd13295000; // 125 ms +localparam [31:0] DSA_DELAY_SPIN_UP = 32'd319080000; // 3000 ms +localparam [31:0] DSA_DELAY_SCAN_GOTO = 32'd3190800; // 30 ms (interactive VLM scan) +localparam integer DSA_MAX_SESSIONS = 100; +localparam [7:0] DSA_SERVO_VERSION = 8'h01; +localparam [7:0] DSA_ERR_NONE = 8'h00; +localparam [7:0] DSA_ERR_FOCUS_NO_DISC= 8'h02; +localparam [7:0] DSA_ERR_TOC_STALE = 8'h08; +localparam [7:0] DSA_ERR_ILLEGAL_CMD = 8'h22; +localparam [7:0] DSA_ERR_ILLEGAL_VALUE= 8'h29; // CD-DA/CD-ROM frame time is mm:ss:ff with 75 frames/sec. function [19:0] msf_to_frames; @@ -431,13 +523,59 @@ begin end endfunction +function [15:0] msf_to_seconds; + input [6:0] mins; + input [5:0] secs; + input [6:0] frms; + reg [15:0] mins_secs; +begin + // mins * 60 = mins * (64 - 4) + mins_secs = {mins,6'h00} - {mins,2'h0}; + // DSA title length is integer seconds. Round frame component to nearest second. + msf_to_seconds = mins_secs + {10'h000,secs} + {15'h0000,(frms >= 7'd38)}; +end +endfunction + +function [23:0] msf_sub_small_gap; + input [23:0] start_msf; + input [15:0] gap_msf; + reg [7:0] mins; + reg [7:0] secs; + reg [7:0] frms; +begin + mins = start_msf[23:16]; + secs = start_msf[15:8]; + frms = start_msf[7:0]; + + if (frms < gap_msf[7:0]) begin + frms = frms + 8'h4B - gap_msf[7:0]; + if (secs == 8'h00) begin + secs = 8'h3B; + mins = mins - 8'h1; + end else begin + secs = secs - 8'h1; + end + end else begin + frms = frms - gap_msf[7:0]; + end + + if (secs < gap_msf[15:8]) begin + secs = secs + 8'h3C - gap_msf[15:8]; + mins = mins - 8'h1; + end else begin + secs = secs - gap_msf[15:8]; + end + + msf_sub_small_gap = {mins, secs, frms}; +end +endfunction + function [31:0] seek_delay_cycles; input [20:0] delta_frames; input [19:0] mid_frames; input speed_2x; reg [20:0] eff_delta; - reg [31:0] dist_cycles; - reg [31:0] rot_cycles; + reg [31:0] seek_cycles; begin // Position-aware distance scaling: // near inner radius, same LBA delta is a larger radial move than outer radius. @@ -448,49 +586,111 @@ begin eff_delta = delta_frames >> 1; // /2 end - // Distance component (approx seek sled motion + settle), in sys_clk cycles. - if (eff_delta < 21'd75) begin - dist_cycles = 32'd638160; // 6 ms - end else if (eff_delta < 21'd750) begin - dist_cycles = 32'd1595400; // 15 ms - end else if (eff_delta < 21'd3750) begin - dist_cycles = 32'd3190800; // 30 ms - end else if (eff_delta < 21'd15000) begin - dist_cycles = 32'd6381600; // 60 ms - end else if (eff_delta < 21'd45000) begin - dist_cycles = 32'd10104200; // 95 ms - end else if (eff_delta < 21'd120000) begin - dist_cycles = 32'd14890400; // 140 ms + // Seek timing model derived from Atari CD-ROM latency table. + // Values are tuned to a realistic profile (~25% under worst-case). + if (eff_delta < 21'd4500) begin + // Short seek within 1-minute span: + // 1st 37 minutes: 250 ms -> 188 ms + // 2nd 37 minutes: 375 ms -> 281 ms + seek_cycles = (mid_frames < 20'd166500) ? 32'd19995680 : 32'd29887160; + end else if (eff_delta < 21'd90000) begin + seek_cycles = 32'd39885000; // 375 ms (0..20 min) + end else if (eff_delta < 21'd180000) begin + seek_cycles = 32'd49989200; // 470 ms (21..40 min) + end else if (eff_delta < 21'd270000) begin + seek_cycles = 32'd59774320; // 562 ms (41..60 min) + end else if (eff_delta < 21'd315000) begin + seek_cycles = 32'd79770000; // 750 ms (61..74 min) end else begin - dist_cycles = 32'd20208400; // 190 ms + seek_cycles = 32'd119655000; // 1125 ms (long 0..74 min) end - // Rotational latency component (half-turn average), radius/speed dependent. - if (speed_2x) begin - if (mid_frames < 20'd120000) begin - rot_cycles = 32'd3722600; // 35 ms @ inner - end else if (mid_frames < 20'd240000) begin - rot_cycles = 32'd4998920; // 47 ms @ middle - end else begin - rot_cycles = 32'd6381600; // 60 ms @ outer - end - end else begin - if (mid_frames < 20'd120000) begin - rot_cycles = 32'd7445200; // 70 ms @ inner - end else if (mid_frames < 20'd240000) begin - rot_cycles = 32'd9997840; // 94 ms @ middle - end else begin - rot_cycles = 32'd12763200; //120 ms @ outer - end + // 1x mode has higher rotational latency; add a fixed penalty. + if (!speed_2x) begin + seek_cycles = seek_cycles + 32'd8508800; // +80 ms end + seek_delay_cycles = seek_cycles; +end +endfunction - // Base command/servo overhead: 8 ms. - seek_delay_cycles = 32'd850880 + dist_cycles + rot_cycles; +function [31:0] stop_delay_cycles; + input [19:0] mid_frames; +begin + // Stop + park head: + // base 1s with radial adders (middle +1s, outer +2s), scaled by ~0.75. + if (mid_frames < 20'd120000) begin + stop_delay_cycles = DSA_DELAY_STOP_INNER; + end else if (mid_frames < 20'd240000) begin + stop_delay_cycles = DSA_DELAY_STOP_MID; + end else begin + stop_delay_cycles = DSA_DELAY_STOP_OUTER; + end end endfunction wire [19:0] cur_abs_frames = msf_to_frames(cur_aminutes, cur_aseconds, cur_aframes); -wire [19:0] seek_cmd_abs_frames = msf_to_frames(sminutes, sseconds, din[6:0]); +wire [19:0] cur_rel_frames_abs = msf_to_frames(cur_minutes, cur_seconds, cur_frames); +wire [19:0] seek_cmd_abs_frames = msf_to_frames(sminutes, sseconds, sframes); +wire [19:0] seek_cmd_abs_frames_next = msf_to_frames(sminutes, sseconds, din[6:0]); +wire [19:0] goto_cmd_abs_frames_next = msf_to_frames(goto_minutes, goto_seconds, din[6:0]); +wire [19:0] ab_stop_abs_frames = msf_to_frames(abbminutes, abbseconds, abbframes); +wire [19:0] subq_track_start_frames = msf_to_frames(cues_dout[23:16], cues_dout[15:8], cues_dout[6:0]); +wire [19:0] subq_track_pregap_frames = msf_to_frames(cuep_dout[23:16], cuep_dout[15:8], cuep_dout[6:0]); +wire [19:0] subq_disc_leadout_frames = msf_to_frames( + dsa_sess_leadout[dsa_last_sess_idx][23:16], + dsa_sess_leadout[dsa_last_sess_idx][15:8], + dsa_sess_leadout[dsa_last_sess_idx][6:0] +); +wire subq_leadout = dsa_disc_ready && + (subq_disc_leadout_frames != 20'h0) && + (cur_abs_frames >= subq_disc_leadout_frames); +wire subq_program = dsa_disc_ready && !subq_leadout && (cur_abs_frames >= subq_track_start_frames); +wire subq_pregap = dsa_disc_ready && !subq_leadout && !subq_program && + (cur_abs_frames >= subq_track_pregap_frames); +assign subq_tno = subq_leadout ? 8'hAA : + ((subq_program || subq_pregap) ? bcd[atrack_safe] : 8'h00); +assign subq_index = subq_program ? 8'h01 : 8'h00; +wire [7:0] atti_cur_index = subq_index; +wire [7:0] atti_cur_title = subq_leadout ? 8'hAA : {1'b0,track_idx}; +wire [19:0] track_len_frames = msf_to_frames(cuel_dout[23:16], cuel_dout[15:8], cuel_dout[6:0]); +wire [19:0] search_border_stop_frames = (track_len_frames > 20'd375) ? (track_len_frames - 20'd375) : 20'h0; +wire [19:0] search_step_frames = search_fast ? 20'd600 : 20'd150; +wire [19:0] dsa_cmd_search_step_frames = din[0] ? 20'd600 : 20'd150; +wire [6:0] seek_scan_start_track = (num_tracks != 7'h0) ? num_tracks : 7'h1; +wire goto_target_past_leadout = + (subq_disc_leadout_frames != 20'h0) && + (goto_cmd_abs_frames_next >= subq_disc_leadout_frames); +wire track_is_data = cdrommd && (dat_track != 7'h0) && (track_idx >= dat_track); +wire search_forward_border_hit = + search_borderflag && + ((cur_rel_frames_abs + search_step_frames) >= search_border_stop_frames); +wire search_backward_border_hit = + search_borderflag && + (cur_rel_frames_abs <= search_step_frames); +wire dsa_cmd_search_forward_border_hit = + din[1] && + ((cur_rel_frames_abs + dsa_cmd_search_step_frames) >= search_border_stop_frames); +wire dsa_cmd_search_backward_border_hit = + din[1] && + (cur_rel_frames_abs <= dsa_cmd_search_step_frames); +wire [7:0] dsa_presence_error = (!cd_ex) ? DSA_ERR_FOCUS_NO_DISC : DSA_ERR_TOC_STALE; +wire dsa_disc_present = cd_ex; +wire dsa_disc_ready = dsa_disc_present && toc_ready; +// Emulated image media defaults: +// bit4=1 finalized, bit3=0 normal reflectance (not RW), bit2=1 12cm disc. +// bit0 reports TOC-read readiness. +wire [4:0] dsa_disc_status = dsa_disc_present ? (5'b10100 | {4'b0000, dsa_disc_ready}) : 5'b00000; +wire [7:0] dsa_last_sess_idx = ((dsa_session_count != 8'h00) && (dsa_session_count <= 8'd100)) ? (dsa_session_count - 8'h1) : 8'h00; +wire [7:0] dsa_disc_id0 = {1'b0, num_tracks[6:0]}; +wire [7:0] dsa_disc_id1 = {1'b0, aud_tracks[6:0]}; +wire [7:0] dsa_disc_id2 = {1'b0, dat_track[6:0]}; +wire [7:0] dsa_disc_id3 = {1'b0, dsa_session_count[6:0]}; +wire [7:0] dsa_disc_id4 = dsa_sess_leadout[dsa_last_sess_idx][23:16] ^ dsa_sess_leadout[dsa_last_sess_idx][15:8] ^ dsa_sess_leadout[dsa_last_sess_idx][7:0] ^ {1'b0, num_tracks[6:0]}; +wire [7:0] dsa_long_toc_ctrl_addr = ((dat_track != 7'h0) && (cues_add >= dat_track)) ? 8'h41 : 8'h01; +wire dsa_dac_mode_valid = + (din[7:0] <= 8'h09) || + (din[7:0] == 8'h81) || + (din[7:0] == 8'h82); reg overflow; reg underflow; @@ -504,6 +704,16 @@ reg search_forward; reg search_backward; reg search_fast; reg search_borderflag; +reg [4:0] search_div; +reg dsa_service_mode; +reg dsa_sledge_out; +reg dsa_focus_on; +reg dsa_spindle_on; +reg dsa_radial_on; +reg dsa_laser_on; +reg dsa_high_gain; +reg [7:0] dsa_diag_last; +reg [15:0] dsa_jump_grooves; reg abplay; reg [7:0] abseek; reg [6:0] abaframes; // 0-74 // (msf % 75) @@ -512,6 +722,9 @@ reg [6:0] abaminutes; // 0-99 // (msf / 75) / 60 reg [6:0] abbframes; // 0-74 // (msf % 75) reg [5:0] abbseconds; // 0-59 // (msf / 75) % 60 reg [6:0] abbminutes; // 0-99 // (msf / 75) / 60 +reg [23:0] cueptemp; +reg [23:16] cuestoptemp; +reg tocsess1; reg [6:0] cues_addr; reg [6:0] cuet_addr; @@ -561,17 +774,130 @@ reg [6:0] cue_tracks; reg [6:0] aud_tracks; reg [6:0] dat_tracks; reg [6:0] dat_track; +reg [29:0] cueb [0:127]; +// Disc metadata is now delivered as one direct per-track record. The cue BRAMs +// remain because the playback/search logic already consumes them well, but the +// mount-time path no longer relies on the legacy packed TOC word stream. +reg [7:0] dsa_sess_count_toc; +reg [6:0] dsa_sess_first_track [0:DSA_MAX_SESSIONS-1]; +reg [6:0] dsa_sess_last_track [0:DSA_MAX_SESSIONS-1]; +reg [23:0] dsa_sess_leadout [0:DSA_MAX_SESSIONS-1]; +reg dsa_sess_valid [0:DSA_MAX_SESSIONS-1]; +integer dsa_sess_i; +// Once metadata has been reconstructed, the session map built from the per-track +// records is authoritative. Before that, report no sessions and force callers to +// observe the TOC-not-ready path instead of silently falling back to "1 session". +wire [7:0] dsa_session_count = dsa_sess_count_toc; + initial begin - cue_tracks <= 7'd6; // - aud_tracks <= 7'd2; - dat_tracks <= 7'd4; - dat_track <= 7'd3; + cue_tracks <= 7'd0; + aud_tracks <= 7'd0; + dat_tracks <= 7'd0; + dat_track <= 7'd0; + dsa_volume <= 8'hFF; + dsa_service_mode <= 1'b0; + dsa_sledge_out <= 1'b0; + dsa_focus_on <= 1'b0; + dsa_spindle_on <= 1'b0; + dsa_radial_on <= 1'b0; + dsa_laser_on <= 1'b0; + dsa_high_gain <= 1'b0; + dsa_diag_last <= 8'h0; + dsa_jump_grooves <= 16'h0000; + dsa_sess_count_toc <= 8'h0; + dsa_spinup_wait_toc <= 1'b0; + dsa_spinup_session <= 8'h00; + for (dsa_sess_i = 0; dsa_sess_i < DSA_MAX_SESSIONS; dsa_sess_i = dsa_sess_i + 1) begin + dsa_sess_first_track[dsa_sess_i] <= 7'h0; + dsa_sess_last_track[dsa_sess_i] <= 7'h0; + dsa_sess_leadout[dsa_sess_i] <= 24'h0; + dsa_sess_valid[dsa_sess_i] <= 1'b0; + end + toc_ready <= 1'b0; + dsa_long_toc_active <= 1'b0; + dsa_long_toc_first_track <= 7'h0; + dsa_long_toc_last_track <= 7'h0; end reg [23:0] cuestop [0:1]; initial begin - cuestop[1'h0] <= 24'h003A28; // - cuestop[1'h1] <= 24'h04022C; // + cuestop[1'h0] <= 24'h0; + cuestop[1'h1] <= 24'h0; end +// aud_sess is a user menu override for "force audio session": +// 0 = normal Jaguar behavior (commands are constrained to session 0), +// 1 = allow explicit session argument from DSA command byte (din[7:0]). +wire [7:0] dsa_req_session = aud_sess ? din[7:0] : 8'h00; +wire [7:0] dsa_spin_req_session = aud_sess ? din[7:0] : 8'h00; +wire dsa_req_sess_valid = dsa_sess_valid[dsa_req_session]; +wire [6:0] dsa_req_first_track = dsa_sess_valid[dsa_req_session] ? dsa_sess_first_track[dsa_req_session] : 7'h0; +wire [6:0] dsa_req_last_track = dsa_sess_valid[dsa_req_session] ? dsa_sess_last_track[dsa_req_session] : 7'h0; +wire [23:0] dsa_req_leadout = dsa_sess_valid[dsa_req_session] ? dsa_sess_leadout[dsa_req_session] : 24'h0; +wire [7:0] dsa_max_session_rsp = force_music_cd ? 8'h01 : + ((dsa_session_count != 8'h00) ? dsa_session_count : 8'h01); +wire dsa_cmd_known = + (din[15:8] == 8'h01) || + (din[15:8] == 8'h02) || + (din[15:8] == 8'h03) || + (din[15:8] == 8'h04) || + (din[15:8] == 8'h05) || + (din[15:8] == 8'h06) || + (din[15:8] == 8'h07) || + (din[15:8] == 8'h08) || + (din[15:8] == 8'h09) || + (din[15:8] == 8'h0A) || + (din[15:8] == 8'h0B) || + (din[15:8] == 8'h0C) || + (din[15:8] == 8'h0D) || + (din[15:8] == 8'h10) || + (din[15:8] == 8'h11) || + (din[15:8] == 8'h12) || + (din[15:8] == 8'h14) || + (din[15:8] == 8'h15) || + (din[15:8] == 8'h16) || + (din[15:8] == 8'h17) || + (din[15:8] == 8'h18) || + (din[15:8] == 8'h20) || + (din[15:8] == 8'h21) || + (din[15:8] == 8'h22) || + (din[15:8] == 8'h23) || + (din[15:8] == 8'h24) || + (din[15:8] == 8'h25) || + (din[15:8] == 8'h26) || + (din[15:8] == 8'h30) || + ((din[15:8] >= 8'h40) && (din[15:8] <= 8'h44)) || + (din[15:8] == 8'h50) || + (din[15:8] == 8'h51) || + (din[15:8] == 8'h52) || + (din[15:8] == 8'h54) || + (din[15:8] == 8'h6A) || + (din[15:8] == 8'h70) || + (din[15:8] == 8'hF0) || + ((din[15:8] >= 8'hF1) && (din[15:8] <= 8'hF9)); +assign dbg_cue_tracks = cue_tracks; +assign dbg_aud_tracks = aud_tracks; +assign dbg_dat_track = dat_track; +assign dbg_dsa_sessions = dsa_session_count; +assign dbg_sess1_valid = dsa_sess_valid[8'h01]; +assign dbg_last_ds = last_ds; +assign dbg_last_err = dsa_last_error; +assign dbg_track_idx = track_idx; +assign dbg_cues_addr = cues_addr; +assign dbg_cuet_addr = cuet_addr; + +reg [15:0] dbg_resp_54_r; +reg [39:0] dbg_toc0_r; +reg [39:0] dbg_toc1_r; +reg [15:0] dbg_spin_r; +reg [15:0] dbg_ltoc0_r; +reg [15:0] dbg_ltoc1_r; + +assign dbg_resp_54 = dbg_resp_54_r; +assign dbg_toc0 = dbg_toc0_r; +assign dbg_toc1 = dbg_toc1_r; +assign dbg_spin = dbg_spin_r; +assign dbg_ltoc0 = dbg_ltoc0_r; +assign dbg_ltoc1 = dbg_ltoc1_r; +assign dbg_toc_ready = toc_ready; // These are redundant with RAMs. Was implemented this way first then, intended to move to ram blocks. No longer needed - convert defaults to mif for BRAMs? /* reg [31:0] cuett [0:63]; @@ -647,7 +973,6 @@ begin aframes <= cur_aframes; aseconds <= {1'b0, cur_aseconds}; aminutes <= cur_aminutes; - atrack <= track_idx; end if (clk && ~old_clk) begin if (crcidx != 7'h50) begin @@ -661,15 +986,13 @@ begin end end -reg [23:0] cueptemp; -reg [23:16] cuestoptemp; -reg tocsess1; reg pastcdbios; always @(posedge sys_clk) begin aud_adds[29:0] <= aud_add[29:0] + cuet_dout[29:0]; // old_aud_rd will delay one cycle to match aud_adds delay cuelast[23:0] <= {carrys?cuel_dout[23:16]-8'h1:cuel_dout[23:16],carrys?8'h3B:carryf?cuel_dout[15:8]-8'h1:cuel_dout[15:8],carryf?8'h4A:cuel_dout[7:0]-8'h1}; + atrack <= track_idx; recrc <= 1'b0; updresp <= 1'b0; updrespa <= 1'b0; @@ -677,6 +1000,7 @@ begin old_doe_ds <= doe_ds; old_doe_dsc <= doe_dsc; old_doe_sub <= doe_sub; + old_doe_sbcntrl <= doe_sbcntrl; old_doe_fif <= doe_fif; old_fif_a1 <= fif_a1; old_clk <= clk; @@ -687,6 +1011,104 @@ begin old_aud_rd3 <= old_aud_rd2; old_upd_frames <= upd_frames; butch_reg[11][3] <= eeprom_din; + if (dsa_delay_pending) begin + if (!cd_latency_en) begin + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + // Some commands stage completion after an additional state transition + // (e.g. PLAY TITLE/GOTO FOUND). Don't assert RBUF-ready early. + if (!play_title_pending_rsp && !seek_found_pending && !dsa_spinup_wait_toc) begin + butch_reg[0][13] <= 1'b1; + end + end else if (dsa_delay_ctr > 32'h1) begin + dsa_delay_ctr <= dsa_delay_ctr - 32'h1; + end else begin + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + // Some commands stage completion after an additional state transition + // (e.g. PLAY TITLE/GOTO FOUND). Don't assert RBUF-ready early. + if (!play_title_pending_rsp && !seek_found_pending && !dsa_spinup_wait_toc) begin + butch_reg[0][13] <= 1'b1; + end + end + end + + // If Spin Up is requested before TOC/session metadata is fully ingested, + // keep the drive busy and only complete after toc_ready is asserted. + if (dsa_spinup_wait_toc) begin + butch_reg[0][13] <= 1'b0; + if (!cd_ex) begin + dsa_spinup_wait_toc <= 1'b0; + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + dsa_last_error <= DSA_ERR_FOCUS_NO_DISC; + ds_resp[0] <= 32'h0400 | DSA_ERR_FOCUS_NO_DISC; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + butch_reg[0][13] <= 1'b1; + end else if (toc_ready) begin + dsa_spinup_wait_toc <= 1'b0; + dsa_last_error <= DSA_ERR_NONE; + ds_resp[0] <= 32'h0100 | 32'h0043; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + mounted <= 1'b1; + spinpause <= 1'b1; + if (mounted) begin + spinpause <= 1'b0; + end + splay <= 5'h15; + stop <= 1'b0; + aud_add <= 30'h0; + if (dsa_sess_valid[dsa_spinup_session] && (dsa_sess_first_track[dsa_spinup_session] != 7'h00)) begin + track_idx <= dsa_sess_first_track[dsa_spinup_session]; + dbg_spin_r <= { + 1'b1, + dsa_sess_first_track[dsa_spinup_session], + dsa_spinup_session + }; + end else begin + track_idx <= 7'h1; + dbg_spin_r <= { + 1'b1, + 7'h01, + dsa_spinup_session + }; + end + cur_samples <= 10'h0; + cur_rframes <= 7'h0; + cur_rseconds <= 6'h2; + cur_rminutes <= 7'h0; + gframes <= 3'h0; + if (dsa_sess_valid[dsa_spinup_session] && (dsa_sess_first_track[dsa_spinup_session] != 7'h00)) begin + cues_addr <= dsa_sess_first_track[dsa_spinup_session]; + cuet_addr <= dsa_sess_first_track[dsa_spinup_session]; + end else begin + cues_addr <= 7'h1; + cuet_addr <= 7'h1; + end + updabs_req <= 1'b1; + if (cd_latency_en) begin + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= DSA_DELAY_SPIN_UP; + end else begin + butch_reg[0][13] <= 1'b1; + end + end + end + + if (title_len_pending) begin + title_len_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0900 | (msf_to_seconds(cuel_dout[23:16], cuel_dout[15:8], cuel_dout[6:0]) & 16'h00FF); + ds_resp[1] <= 32'h0A00 | ((msf_to_seconds(cuel_dout[23:16], cuel_dout[15:8], cuel_dout[6:0]) >> 8) & 16'h00FF); + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h2; + ds_resp_loop <= 7'h0; + end cues_wrr_next <= 0; cuep_wrr_next <= 0; @@ -696,6 +1118,56 @@ begin cuep_wrr <= cuep_wrr_next; cuel_wrr <= cuel_wrr_next; cuet_wrr <= cuet_wrr_next; + if (toc_wr && (toc_addr == 10'h008)) begin + toc_ready <= 1'b0; + dsa_long_toc_active <= 1'b0; + dsa_sess_count_toc <= 8'h0; + cue_tracks <= 7'h0; + aud_tracks <= 7'h0; + dat_tracks <= 7'h0; + dat_track <= 7'h0; + dbg_toc0_r <= 40'h0; + dbg_toc1_r <= 40'h0; + dbg_spin_r <= 16'h0000; + dbg_ltoc0_r <= 16'h0000; + dbg_ltoc1_r <= 16'h0000; + track_idx <= 7'h1; + atrack <= 7'h1; + cues_addr <= 7'h1; + cuet_addr <= 7'h1; + play <= 1'b0; + stop <= 1'b0; + pause <= 1'b0; + spinpause <= 1'b0; + splay <= 5'h0; + seek <= 8'h0; + seek_found_pending <= 1'b0; + updabs_req <= 1'b0; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + play_title_pending_rsp <= 1'b0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + frame_irq_pending <= 1'b0; + subcode_irq_pending <= 1'b0; + // Present first emitted chunk as 0x10 to match legacy GPU/VLM handlers. + sub_chunk_count <= 8'h0F; + for (dsa_sess_i = 0; dsa_sess_i < DSA_MAX_SESSIONS; dsa_sess_i = dsa_sess_i + 1) begin + dsa_sess_first_track[dsa_sess_i] <= 7'h0; + dsa_sess_last_track[dsa_sess_i] <= 7'h0; + dsa_sess_leadout[dsa_sess_i] <= 24'h0; + dsa_sess_valid[dsa_sess_i] <= 1'b0; + end + end if (toc_wr) begin cues_addr <= {toc_addr[9:3]}; cuet_addr <= {toc_addr[9:3]}; @@ -707,67 +1179,134 @@ begin cues_dinv[7:0] <= toc_data[15:8]; cueptemp[7:0] <= toc_data[15:8]; cuel_dinv[23:16] <= toc_data[7:0]; - cues_wrr_next <= 1; + cues_wrr_next <= 1'b1; end if (toc_addr[2:0] == 3'h2) begin cuel_dinv[15:0] <= toc_data[15:0]; - cuel_wrr_next <= 1; + cuel_wrr_next <= 1'b1; end if (toc_addr[2:0] == 3'h3) begin - //cue_gap[15:0] <= toc_data[15:0]; // logic below assumes gap is not larger than 2 seconds cueptemp[7:0] <= cueptemp[7:0] - toc_data[7:0]; cueptemp[14:8] <= cueptemp[14:8] - toc_data[14:8]; end if (toc_addr[2:0] == 3'h4) begin cuet_dinv[31:24] <= toc_data[7:0]; - tocsess1 <= 1; - if (toc_data[15:9] == 0) begin //session 1 == (0 or 1) + tocsess1 <= 1'b1; + if (toc_data[15:9] == 0) begin aud_tracks <= toc_addr[9:3]; - tocsess1 <= 0; + tocsess1 <= 1'b0; end cue_tracks <= toc_addr[9:3]; + if (toc_data[15:9] < DSA_MAX_SESSIONS) begin + if (!dsa_sess_valid[toc_data[15:9]]) begin + dsa_sess_first_track[toc_data[15:9]] <= toc_addr[9:3]; + end + dsa_sess_valid[toc_data[15:9]] <= 1'b1; + if ((toc_data[15:9] + 8'h1) > dsa_sess_count_toc) begin + dsa_sess_count_toc <= toc_data[15:9] + 8'h1; + end + end if (cueptemp[7]) begin cueptemp[7:0] <= cueptemp[7:0] + 8'h4B; - cueptemp[14:8] <= cueptemp[14:8] - (7'h1); + cueptemp[14:8] <= cueptemp[14:8] - 7'h1; end end if (toc_addr[2:0] == 3'h5) begin cuet_dinv[23:8] <= toc_data[15:0]; dat_tracks <= cue_tracks - aud_tracks; - dat_track <= aud_tracks + 4'h1; + dat_track <= aud_tracks + 7'h1; if (cueptemp[14]) begin cueptemp[14:8] <= cueptemp[14:8] + 7'h3C; - cueptemp[23:16] <= cueptemp[23:16] - (1'b1); + cueptemp[23:16] <= cueptemp[23:16] - 8'h1; end end if (toc_addr[2:0] == 3'h6) begin cuestoptemp[23:16] <= toc_data[7:0]; cuet_dinv[7:0] <= toc_data[15:8]; cuep_dinv <= cueptemp; - cuet_wrr_next <= 1; - cuep_wrr_next <= 1; + cuet_wrr_next <= 1'b1; + cuep_wrr_next <= 1'b1; end if (toc_addr[2:0] == 3'h7) begin - if (cuestoptemp[23:16] != 0 || toc_data[15:0] != 0) begin + if (cuestoptemp[23:16] != 8'h00 || toc_data[15:0] != 16'h0000) begin cuestop[tocsess1][23:16] <= cuestoptemp[23:16]; cuestop[tocsess1][15:0] <= toc_data[15:0]; + toc_ready <= 1'b1; + dsa_sess_last_track[tocsess1] <= toc_addr[9:3]; + dsa_sess_leadout[tocsess1] <= {cuestoptemp[23:16], toc_data[15:0]}; end end end + // `cues_dout` is synchronous RAM output; stage absolute-time updates one cycle + // after changing `cues_addr` to avoid latching stale track starts. + if (updabs_req) begin + updabs_req <= 1'b0; + updabs <= 1'b1; + end if (updabs) begin // Everything below here should be reset when resetl is low updabs <= 1'b0; cur_aframes <= cues_dout[6:0]; cur_aseconds <= cues_dout[13:8]; cur_aminutes <= cues_dout[22:16]; end + if (play_title_pending_rsp && + !updabs && + (ds_resp_size == 3'h0) && + !dsa_delay_pending && + !dsa_spinup_wait_toc && + !ds_a) begin + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + if (attiabs) begin + ds_resp[0] <= 32'h1000 | play_title_pending_track; + ds_resp[1] <= 32'h1100 | 8'h01; + ds_resp[2] <= 32'h1400 | cues_dout[22:16]; + ds_resp[3] <= 32'h1500 | cues_dout[13:8]; + ds_resp[4] <= 32'h0100; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h5; + ds_resp_loop <= 7'h0; + atti_report_valid <= 1'b1; + atti_last_title <= {1'b0,play_title_pending_track}; + atti_last_index <= 8'h01; + atti_last_abs_minutes <= cues_dout[22:16]; + atti_last_abs_seconds <= cues_dout[13:8]; + atti_last_rel_minutes <= cur_rminutes[6:0]; + atti_last_rel_seconds <= cur_rseconds[5:0]; + end else if (attirel) begin + ds_resp[0] <= 32'h1000 | play_title_pending_track; + ds_resp[1] <= 32'h1100 | 8'h01; + ds_resp[2] <= 32'h1200 | cur_rminutes[6:0]; + ds_resp[3] <= 32'h1300 | cur_rseconds[5:0]; + ds_resp[4] <= 32'h0100; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h5; + ds_resp_loop <= 7'h0; + atti_report_valid <= 1'b1; + atti_last_title <= {1'b0,play_title_pending_track}; + atti_last_index <= 8'h01; + atti_last_abs_minutes <= cues_dout[22:16]; + atti_last_abs_seconds <= cues_dout[13:8]; + atti_last_rel_minutes <= cur_rminutes[6:0]; + atti_last_rel_seconds <= cur_rseconds[5:0]; + end else begin + // No-ATTI mode expects a plain FOUND completion without title payload. + ds_resp[0] <= 32'h0100; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + atti_report_valid <= 1'b0; + end + play_title_pending_rsp <= 1'b0; + end if (seek != 8'h0) begin if (seek[7]) begin // Loop looking for cues_addr starting at last one seek[0] <= !seek[0]; // These two settings do alternate between updating cues_addr and using it seek[1] <= seek[0]; if (!seek[1]) begin // Check if cues_addr is before/after seek time - if ((cues_addr == 7'h0) || ({sminutes,2'b00,sseconds,1'b0,sframes} >= (cuep_dout[22:0]))) begin // fix this + if ((cues_addr <= 7'h1) || ({sminutes,2'b00,sseconds,1'b0,sframes} >= (cuep_dout[22:0]))) begin // fix this seek <= 8'h7F; - track_idx <= cues_addr; + track_idx <= cues_addr_clamped; cur_aframes <= sframes; cur_aseconds <= sseconds; cur_aminutes <= sminutes; @@ -795,18 +1334,30 @@ begin end end end else begin - cues_addr <= cues_addr - 7'h1; - cuet_addr <= cues_addr - 7'h1; + // Never step seek scan into track slot 0. + cues_addr <= (cues_addr > 7'h1) ? (cues_addr - 7'h1) : 7'h1; + cuet_addr <= (cues_addr > 7'h1) ? (cues_addr - 7'h1) : 7'h1; seek[1:0] <= 2'b11; end end end else if (seek[6]) begin if (seek[0]) begin // Using seek0 to delay one cycle. necessary? seek[0] <= 1'b0; - cur_seconds <= sseconds - subtseconds + ((sseconds >= subtseconds) ? 6'h0 : 6'h3C); - cur_minutes <= sminutes - cuep_dout[22:16] - ((sseconds >= subtseconds) ? 6'h0 : 6'h1); - cur_rseconds <= sseconds - subtrseconds + ((sseconds >= subtrseconds) ? 6'h0 : 6'h3C); - cur_rminutes <= sminutes - cues_dout[22:16] - ((sseconds >= subtrseconds) ? 6'h0 : 6'h1); + // Clamp boundary math to avoid transient boundary artifacts + if ({sminutes,2'b00,sseconds,1'b0,sframes} <= cuep_dout[22:0]) begin + cur_seconds <= 6'h0; + cur_minutes <= 7'h0; + end else begin + cur_seconds <= sseconds - subtseconds + ((sseconds >= subtseconds) ? 6'h0 : 6'h3C); + cur_minutes <= sminutes - cuep_dout[22:16] - ((sseconds >= subtseconds) ? 6'h0 : 6'h1); + end + if ({sminutes,2'b00,sseconds,1'b0,sframes} <= cues_dout[22:0]) begin + cur_rseconds <= 6'h0; + cur_rminutes <= 7'h0; + end else begin + cur_rseconds <= sseconds - subtrseconds + ((sseconds >= subtrseconds) ? 6'h0 : 6'h3C); + cur_rminutes <= sminutes - cues_dout[22:16] - ((sseconds >= subtrseconds) ? 6'h0 : 6'h1); + end end else begin seek <= 8'h3F; // cur_seconds <= cur_seconds + ((cues_gap) ? ((cur_seconds == 6'h3B) || (cur_seconds == 6'h3A)) ? 6'h6 : 6'h2 : 6'h0); //6=wrap 2+ 4=64-60 @@ -839,19 +1390,69 @@ begin if (seek_delay == seek_delay_set) begin aud_add[29:0] <= {{taud2_add[29:8],4'h0} + {taud3_add[23:4]},4'h0};//[31:30] are always 0 aud_rd <= 1'b1; - end else if ((seek_delay == 31'h1) && (aud_cbusy)) begin + end else if ((seek_delay == 31'h1) && (aud_cbusy) && !seek_skip_cbusy_wait) begin seek_delay <= 31'h1; end else if (seek_delay == 31'h1) begin seek[0] <= 1'b0; + seek_skip_cbusy_wait <= 1'b0; // *2352=<<11 + <<8 + <<5 + <<4 cur_samples <= 10'h0; - splay <= 5'h5; - splay[4] <= i2s_jerry || i2s_fifo_enabled; - stop <= 1'b0; + // Seek landed at a new logical position. Re-prime subcode stream + // from chunk 0x10 and recompute CRC from the new Q payload now. + sub_chunk_count <= 8'h0F; + subidx <= 4'h0; + recrc <= 1'b1; + // Only arm transport start when seek completes from an active + // playback context. Positioning seeks (e.g. VLM init/STOP-time + // GOTO updates) must not restart audio. + if (((play || search_forward || search_backward || abplay) && !pause && !spinpause) || + play_title_pending_rsp) begin + splay <= 5'h5; + splay[4] <= i2s_jerry || i2s_fifo_enabled; + stop <= 1'b0; + end else begin + splay <= 5'h0; + play <= 1'b0; + end upd_frames <= 1'b1; // ds_resp[0] <= 32'h0140; // dsa says 0x140; code is looking for 0x100 - ds_resp[0] <= 32'h0100; - butch_reg[0][13] <= 1'b1; // |= 0x2000 + if (!dsa_cmd_stop_wr && (abseek != 8'h0)) begin + ds_resp[0] <= 32'h0144; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + butch_reg[0][13] <= 1'b1; // response word ready + atti_report_valid <= 1'b0; + abseek <= 8'h0; + end else if (!dsa_cmd_stop_wr && seek_found_pending && + (play || search_forward || search_backward || abplay || play_title_pending_rsp)) begin + ds_resp[0] <= 32'h0100; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + butch_reg[0][13] <= 1'b1; // response word ready + seek_found_pending <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + if (attirel || attiabs) begin + atti_report_valid <= 1'b1; + atti_last_title <= atti_cur_title; + atti_last_index <= atti_cur_index; + atti_last_abs_minutes <= cur_aminutes[6:0]; + atti_last_abs_seconds <= cur_aseconds[5:0]; + atti_last_rel_minutes <= cur_rminutes[6:0]; + atti_last_rel_seconds <= cur_rseconds[5:0]; + end else begin + atti_report_valid <= 1'b0; + end + end else begin + // SEARCH fwd/back internal seeks don't emit FOUND responses. + butch_reg[0][13] <= 1'b0; + end i2s_wfifopos <= 5'h0; i2s_rfifopos <= 5'h0; overflow <= 1'b0; @@ -915,83 +1516,137 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end end end - if (play && !pause && !spinpause) begin + if (play && !spinpause && (!pause || cdrommd)) begin + if (abplay && (abseek == 8'h0) && (seek == 8'h0) && (cur_abs_frames >= ab_stop_abs_frames)) begin + abplay <= 1'b0; + pause <= 1'b1; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0145; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end old_ws <= wsout; if (old_ws != wsout) begin if (stop != 1'b0) begin play <= 1'b0; i2s4w <= 1'b1; sdin4[15:0] <= 16'h0; - butch_reg[0][13] <= 1'b1; // |= 0x2000 + if (!dsa_delay_pending) begin + butch_reg[0][13] <= 1'b1; // |= 0x2000 + end end else if (seek != 8'h0) begin sdin[15:0] <= 16'h0; end else begin i2s1w <= !wsout; i2s2w <= wsout; - fdata[15:0] = fd[15:0]; - fd <= {16'h0,fd[63:16]}; - sdin[15:0] <= (gframes[2:1] != 2'h0) ? 16'h0 : fdata[15:0]; - if (i2s_fifo_enabled && faddr[0] == 1'b0) begin - i2s_fifo[i2s_wfifopos[3:0]][15:0] <= (gframes != 3'h0) ? 16'h0 : fdata[15:0]; - end - if (i2s_fifo_enabled && faddr[0] == 1'b1) begin - i2s_fifo[i2s_wfifopos[3:0]][31:16] <= (gframes != 3'h0) ? 16'h0 : fdata[15:0]; - i2s_wfifopos <= i2s_wfifopos + 5'h1; - if (i2s_wfifopos == (i2s_rfifopos ^ 5'h10)) begin // fifo overflow - i2s_rfifopos <= i2s_rfifopos + 4'h1; - overflow <= 1'b1; + if (pause_data_junk) begin + // In data mode, pause keeps transfer active but returns nonsensical data. + fdata[15:0] = 16'h0; + sdin[15:0] <= 16'h0; + if (i2s_fifo_enabled && faddr[0] == 1'b0) begin + i2s_fifo[i2s_wfifopos[3:0]][15:0] <= 16'h0; end - end - if (gframes != 3'h0) begin - fd <= 64'h0; - valid <= 1'b0; - end - if ((faddr[1:0] == 2'b01) && (gframes[2:1] == 2'h0)) begin // handles throwing away first 16 bit value and using fifth in its place (plus endian/ordering nonsense) - fd[15:0] <= {fifo[1][23:16],fifo[1][31:24]}; // use next fifo; replaces current set below -// fd[15:0] <= {fifo[0][23:16],fifo[0][31:24]}; // use next fifo; replaces current set below - end - if ((faddr[1:0] == 2'b11) && (gframes == 3'h0)) begin //Assumes fifo filled before first entrance and next fifo data already pointed at. - fd <= {fifo[1][39:32],fifo[1][47:40], fifo[1][23:16],fifo[1][31:24], fifo[1][07:00],fifo[1][15:8], fifo[1][55:48],fifo[1][63:56]}; // endian/ordering nonsense -// fd <= {fifo[0][39:32],fifo[0][47:40], fifo[0][23:16],fifo[0][31:24], fifo[0][07:00],fifo[0][15:8], fifo[0][55:48],fifo[0][63:56]}; // endian/ordering nonsense - fifo[1] <= fifo[0]; // is this cache necessary or can directly use 0? - fifo[0] <= aud_in; -// if (aud_in != aud_cmp) begin -// underflow <= 1'b1; -// end - if ({cur_aminutes,2'b00,cur_aseconds,1'b0,cur_aframes} < cuep_dout[22:0]) begin - fifo[1] <= 64'h0; - fifo[0] <= 64'h0; - end else if ({cur_minutes,2'b00,cur_seconds,1'b0,cur_frames} >= cuelast[22:0]) begin - aud_add <= aud_add + 4'h8; - if ({cur_minutes,2'b00,cur_seconds,1'b0,cur_frames} > cuelast[22:0]) begin - fifo[0] <= 64'h0; - aud_add[29:0] <= 30'h0; - cuet_addr <= track_idx + 7'h1; -// end else if (aud_in != aud_cmp) begin - end else if (!cd_valid) begin - underflow <= 1'b1; + if (i2s_fifo_enabled && faddr[0] == 1'b1) begin + i2s_fifo[i2s_wfifopos[3:0]][31:16] <= 16'h0; + i2s_wfifopos <= i2s_wfifopos + 5'h1; + if (i2s_wfifopos == (i2s_rfifopos ^ 5'h10)) begin // fifo overflow + i2s_rfifopos <= i2s_rfifopos + 4'h1; + overflow <= 1'b1; end - if ({cur_samples[9:1],1'b0} == 10'd586) begin - aud_add[29:0] <= 30'h0; - cuet_addr <= track_idx + 7'h1; - end - end else begin - aud_add <= aud_add + 4'h8; -// if (aud_in != aud_cmp) begin - if (!cd_valid) begin - underflow <= 1'b1; - end end - aud_rd <= 1'b1; - if (aud_busy) begin + end else begin + fdata[15:0] = fd[15:0]; + fd <= {16'h0,fd[63:16]}; + sdin[15:0] <= (gframes[2:1] != 2'h0) ? 16'h0 : fdata[15:0]; + if (i2s_fifo_enabled && faddr[0] == 1'b0) begin + i2s_fifo[i2s_wfifopos[3:0]][15:0] <= (gframes != 3'h0) ? 16'h0 : fdata[15:0]; + end + if (i2s_fifo_enabled && faddr[0] == 1'b1) begin + i2s_fifo[i2s_wfifopos[3:0]][31:16] <= (gframes != 3'h0) ? 16'h0 : fdata[15:0]; + i2s_wfifopos <= i2s_wfifopos + 5'h1; + if (i2s_wfifopos == (i2s_rfifopos ^ 5'h10)) begin // fifo overflow + i2s_rfifopos <= i2s_rfifopos + 4'h1; + overflow <= 1'b1; + end + end + if (gframes != 3'h0) begin + fd <= 64'h0; + valid <= 1'b0; + end + if ((faddr[1:0] == 2'b01) && (gframes[2:1] == 2'h0)) begin // handles throwing away first 16 bit value and using fifth in its place (plus endian/ordering nonsense) + fd[15:0] <= {fifo[1][23:16],fifo[1][31:24]}; // use next fifo; replaces current set below +// fd[15:0] <= {fifo[0][23:16],fifo[0][31:24]}; // use next fifo; replaces current set below + end + if ((faddr[1:0] == 2'b11) && (gframes == 3'h0)) begin //Assumes fifo filled before first entrance and next fifo data already pointed at. + fd <= {fifo[1][39:32],fifo[1][47:40], fifo[1][23:16],fifo[1][31:24], fifo[1][07:00],fifo[1][15:8], fifo[1][55:48],fifo[1][63:56]}; // endian/ordering nonsense +// fd <= {fifo[0][39:32],fifo[0][47:40], fifo[0][23:16],fifo[0][31:24], fifo[0][07:00],fifo[0][15:8], fifo[0][55:48],fifo[0][63:56]}; // endian/ordering nonsense + fifo[1] <= fifo[0]; // is this cache necessary or can directly use 0? + fifo[0] <= aud_in; +// if (aud_in != aud_cmp) begin // underflow <= 1'b1; +// end + if ({cur_aminutes,2'b00,cur_aseconds,1'b0,cur_aframes} < cuep_dout[22:0]) begin + fifo[1] <= 64'h0; + fifo[0] <= 64'h0; + end else if ((track_is_data && (cueb_dout != 30'h0) && ((aud_add + 30'h8) >= cueb_dout)) || + ({cur_minutes,2'b00,cur_seconds,1'b0,cur_frames} >= cuelast[22:0])) begin + aud_add <= aud_add + 4'h8; + if ((track_is_data && (cueb_dout != 30'h0) && ((aud_add + 30'h8) >= cueb_dout)) || + ({cur_minutes,2'b00,cur_seconds,1'b0,cur_frames} > cuelast[22:0])) begin + fifo[0] <= 64'h0; + aud_add[29:0] <= 30'h0; + cuet_addr <= track_idx + 7'h1; +// end else if (aud_in != aud_cmp) begin + end else if (!cd_valid) begin + underflow <= 1'b1; + end + if ({cur_samples[9:1],1'b0} == 10'd586) begin + aud_add[29:0] <= 30'h0; + cuet_addr <= track_idx + 7'h1; + end + end else begin + aud_add <= aud_add + 4'h8; +// if (aud_in != aud_cmp) begin + if (!cd_valid) begin + underflow <= 1'b1; + end + end + aud_rd <= 1'b1; + if (aud_busy) begin +// underflow <= 1'b1; + end end end - if (wsout) begin + if (wsout && !pause_data_junk && (seek == 8'h0)) begin cur_samples <= cur_samples + 10'h1; + // Subcode chunk interrupt cadence: 12 chunk interrupts per 588-sample CD frame. + if ((cur_samples == 10'd48) || (cur_samples == 10'd97) || + (cur_samples == 10'd146) || (cur_samples == 10'd195) || + (cur_samples == 10'd244) || (cur_samples == 10'd293) || + (cur_samples == 10'd342) || (cur_samples == 10'd391) || + (cur_samples == 10'd440) || (cur_samples == 10'd489) || + (cur_samples == 10'd538) || (cur_samples == 10'd587)) begin + subcode_irq_pending <= 1'b1; + // Keep chunk counter in canonical 0x10..0x1B range. + if (sub_chunk_count == 8'h1B) begin + sub_chunk_count <= 8'h10; + subidx <= 4'h0; + end else begin + sub_chunk_count <= sub_chunk_count + 8'h1; + // Counter starts at 0x0F so first emitted chunk (0x10) maps to subidx 0. + if (sub_chunk_count == 8'h0F) begin + subidx <= 4'h0; + end else begin + subidx <= subidx + 4'h1; + end + end + end if (cur_samples == 10'd587) begin // recrc <= 1'b1; upd_frames <= 1'b1; + frame_irq_pending <= 1'b1; cur_samples <= 10'h0; if ({cur_aminutes,2'b00,cur_aseconds,1'b0,cur_aframes} >= cuep_dout[22:0]) begin cur_frames <= cur_frames + 7'h1; @@ -1041,6 +1696,88 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); cur_aminutes <= cur_aminutes + 7'h1; end end + if ((search_forward || search_backward) && (seek == 8'h0) && (splay == 5'h0)) begin + if ((search_forward && search_forward_border_hit) || (search_backward && search_backward_border_hit)) begin + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end else if (search_div == (search_fast ? 5'd1 : 5'd5)) begin + search_div <= 5'h0; + sframes <= cur_aframes; + if (search_forward) begin + if (search_fast) begin + if (cur_aseconds >= 6'd52) begin + if (cur_aminutes == 7'd99) begin + sminutes <= 7'd99; + sseconds <= 6'd59; + sframes <= 7'd74; + end else begin + sminutes <= cur_aminutes + 7'd1; + sseconds <= cur_aseconds - 6'd52; + end + end else begin + sminutes <= cur_aminutes; + sseconds <= cur_aseconds + 6'd8; + end + end else begin + if (cur_aseconds >= 6'd58) begin + if (cur_aminutes == 7'd99) begin + sminutes <= 7'd99; + sseconds <= 6'd59; + sframes <= 7'd74; + end else begin + sminutes <= cur_aminutes + 7'd1; + sseconds <= cur_aseconds - 6'd58; + end + end else begin + sminutes <= cur_aminutes; + sseconds <= cur_aseconds + 6'd2; + end + end + end else begin + if (search_fast) begin + if ((cur_aminutes == 7'd0) && (cur_aseconds < 6'd8)) begin + sminutes <= 7'd0; + sseconds <= 6'd0; + sframes <= 7'd0; + end else if (cur_aseconds < 6'd8) begin + sminutes <= cur_aminutes - 7'd1; + sseconds <= cur_aseconds + 6'd52; + end else begin + sminutes <= cur_aminutes; + sseconds <= cur_aseconds - 6'd8; + end + end else begin + if ((cur_aminutes == 7'd0) && (cur_aseconds < 6'd2)) begin + sminutes <= 7'd0; + sseconds <= 6'd0; + sframes <= 7'd0; + end else if (cur_aseconds < 6'd2) begin + sminutes <= cur_aminutes - 7'd1; + sseconds <= cur_aseconds + 6'd58; + end else begin + sminutes <= cur_aminutes; + sseconds <= cur_aseconds - 6'd2; + end + end + end + cues_addr <= seek_scan_start_track; + cuet_addr <= seek_scan_start_track; + seek_skip_cbusy_wait <= 1'b1; + seek_found_pending <= 1'b0; + seek <= 8'hFF; + abseek <= 8'h0; + stop <= 1'b0; + spinpause <= 1'b0; + seek_delay_set <= cd_latency_en ? 32'd500000 : 32'd100000; + end else begin + search_div <= search_div + 5'h1; + end + end else begin + search_div <= 5'h0; + end end end end @@ -1048,7 +1785,23 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end end - if (wet && ain[23:8]==24'hdfff) begin // restrict to lower 0-3f? + if (!play || !subq_leadout) begin + leadout_seen <= 1'b0; + end + if (play && !spinpause && subq_leadout && !leadout_seen) begin + leadout_seen <= 1'b1; + pause <= 1'b1; // Enter pause mode automatically on lead-out entry. + leadout_title_pending <= 1'b1; // Emit explicit ACTUAL TITLE=AAh notification. + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + end + + if (cd_en && wet && ain[23:8]==24'hdfff) begin // restrict to lower 0-3f? if (ain[5:2]==4'h0) begin // BUTCH ICR if (!ewe2l) begin butch_reg[4'h0][31:16] <= din[31:16]; @@ -1056,11 +1809,13 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); if (!ewe0l) begin butch_reg[4'h0][15:8] <= butch_reg[4'h0][15:8] & ~{din[15:14],2'b00,din[11:8]}; butch_reg[4'h0][7:0] <= din[7:0]; + if (din[10]) subcode_irq_pending <= 1'b0; + if (din[11]) frame_irq_pending <= 1'b0; // interrupt control end - end else if (aeven) begin + end else if (aeven && ain[5:2] < 4'd12) begin butch_reg[ain[5:2]][31:0] <= din[31:0]; - end else begin + end else if (ain[5:2] < 4'd12) begin butch_reg[ain[5:2]][15:0] <= din[15:0]; end if (ain[5:2]==4'h4) begin // I2SCTRL @@ -1072,82 +1827,175 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); // DSA info came from later spec. Some of these may be wrong/missing/unsupported for the Jag. last_ds <= din[15:0]; unhandled <= 1'b1; + dsa_spinup_wait_toc <= 1'b0; + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + if (din[15:8] != 8'h14) begin + dsa_long_toc_active <= 1'b0; + end if (din[15:8]==8'h01) begin // Play Title unhandled <= 1'b0; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0100 | din[7:0]; - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - spinpause <= 1'b0; - splay <= 5'h15; - stop <= 1'b0; - aud_add <= 30'h0; - track_idx <= din[6:0]; - cur_samples <= 10'h0; - cur_rframes <= 7'h0; - cur_rseconds <= 6'h2; - cur_rminutes <= 7'h0; - gframes <= 3'h0; - cues_addr <= din[6:0]; - cuet_addr <= din[6:0]; - updabs <= 1'b1; + play_title_pending_rsp <= 1'b0; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (((din[6:0] == 7'h00) ? 7'h01 : din[6:0]) > num_tracks) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + abplay <= 1'b0; + abseek <= 8'h0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + // Report completion once the play-title position has been latched. + butch_reg[0][13] <= 1'b0; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + play_title_pending_rsp <= 1'b1; + play_title_pending_track <= (din[6:0] == 7'h00) ? 7'h01 : din[6:0]; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + if (cd_latency_en) begin + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= DSA_DELAY_PLAY_TITLE; + end + spinpause <= 1'b0; + splay <= 5'h15; + stop <= 1'b0; + aud_add <= 30'h0; + track_idx <= (din[6:0] == 7'h00) ? 7'h01 : din[6:0]; + cur_samples <= 10'h0; + cur_rframes <= 7'h0; + cur_rseconds <= 6'h0; + cur_rminutes <= 7'h0; + gframes <= 3'h0; + cues_addr <= (din[6:0] == 7'h00) ? 7'h01 : din[6:0]; + cuet_addr <= (din[6:0] == 7'h00) ? 7'h01 : din[6:0]; + updabs_req <= 1'b1; + end end if (din[15:8]==8'h02) begin // Stop unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= !play; // |= 0x2000 - ds_resp[0] <= 32'h0200; + if (!cd_ex) begin + dsa_last_error <= DSA_ERR_FOCUS_NO_DISC; + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h0400 | DSA_ERR_FOCUS_NO_DISC; + end else begin + dsa_last_error <= DSA_ERR_NONE; + if (cd_latency_en) begin + butch_reg[0][13] <= 1'b0; + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= stop_delay_cycles(cur_abs_frames); + end else begin + butch_reg[0][13] <= 1'b1; // immediate STOP completion when latency is disabled + end + // STOP completion is "stopped" class in existing BIOS/VLM paths. + ds_resp[0] <= 32'h0200; + end ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; - stop <= play; + // Quiesce transport immediately so software-visible STOP state is + // stable without waiting for the next audio word-select edge. + play <= 1'b0; + stop <= 1'b0; + splay <= 5'h0; + seek <= 8'h0; + seek_skip_cbusy_wait <= 1'b0; + seek_found_pending <= 1'b0; + abplay <= 1'b0; + abseek <= 8'h0; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + play_title_pending_rsp <= 1'b0; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; pause <= 1'b0; spinpause <= 1'b0; + subcode_irq_pending <= 1'b0; + frame_irq_pending <= 1'b0; + sub_chunk_count <= 8'h0F; + subidx <= 4'h0; + cur_samples <= 10'h0; + recrc <= 1'b1; + fd <= 64'h0; + fifo[1] <= 64'h0; + fifo[0] <= 64'h0; + i2s_wfifopos <= 5'h0; + i2s_rfifopos <= 5'h0; + i2s1w <= 1'b1; + i2s2w <= 1'b1; + sdin[15:0] <= 16'h0; + i2s4w <= 1'b1; + sdin4[15:0] <= 16'h0; end if (din[15:8]==8'h03) begin // Read TOC unhandled <= 1'b0; - if (!cd_ex) begin // No CD - ds_resp[0] <= 32'h400; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + ds_resp[0] <= 32'h0400 | dsa_presence_error; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; - end else if ((din[7:0]==8'h01) && (!aud_sess)) begin // Multi Session CD, TODO - ds_resp[0] <= 32'h400; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - end else if (din[7:0]==8'h02) begin // Multi Session CD, TODO - ds_resp[0] <= 32'h400; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - end else if (din[7:0]==8'hFF) begin // Multi Session CD, TODO - ds_resp[0] <= 32'h29; + end else if ((!aud_sess && (din[7:0] != 8'h00)) || (din[7:0] >= dsa_session_count) || (din[7:0] >= DSA_MAX_SESSIONS) || !dsa_req_sess_valid) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end else begin - /* first track number */ - ds_resp[0] <= 32'h2000 | ((din[7:0]==8'h00) ? 32'h1 : (dat_track)); - /* last track number */ - ds_resp[1] <= 32'h2100 | (((din[7:0]==8'h00) && (aud_sess)) ? aud_tracks : num_tracks); - - /* end of last track minutes */ - ds_resp[2] <= 32'h2200 | (((din[7:0]==8'h00) && (aud_sess)) ? cuestop[1'h0][22:16] : cuestop[1'h1][22:16]); - /* end of last track seconds */ - ds_resp[3] <= 32'h2300 | (((din[7:0]==8'h00) && (aud_sess)) ? cuestop[1'h0][13:8] : cuestop[1'h1][13:8]); - /* end of last track frame */ - ds_resp[4] <= 32'h2400 | (((din[7:0]==8'h00) && (aud_sess)) ? cuestop[1'h0][6:0] : cuestop[1'h1][6:0]); + dsa_last_error <= DSA_ERR_NONE; + ds_resp[0] <= 32'h2000 | dsa_req_first_track; + ds_resp[1] <= 32'h2100 | dsa_req_last_track; + ds_resp[2] <= 32'h2200 | dsa_req_leadout[22:16]; + ds_resp[3] <= 32'h2300 | dsa_req_leadout[13:8]; + ds_resp[4] <= 32'h2400 | dsa_req_leadout[6:0]; + if (dsa_req_session == 8'h00) begin + dbg_toc0_r <= { + dsa_req_first_track, + dsa_req_last_track, + dsa_req_leadout + }; + end else if (dsa_req_session == 8'h01) begin + dbg_toc1_r <= { + dsa_req_first_track, + dsa_req_last_track, + dsa_req_leadout + }; + end butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp_idx <= 3'h0; @@ -1158,7 +2006,13 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); if (din[15:8]==8'h04) begin // Pause unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 + if (cd_latency_en) begin + butch_reg[0][13] <= 1'b0; + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= DSA_DELAY_PAUSE; + end else begin + butch_reg[0][13] <= 1'b1; // |= 0x2000 + end ds_resp[0] <= 32'h0141; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; @@ -1168,7 +2022,13 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); if (din[15:8]==8'h05) begin // Pause Release unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 + if (cd_latency_en) begin + butch_reg[0][13] <= 1'b0; + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= DSA_DELAY_UNPAUSE; + end else begin + butch_reg[0][13] <= 1'b1; // |= 0x2000 + end ds_resp[0] <= 32'h0142; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; @@ -1176,148 +2036,344 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); pause <= 1'b0; spinpause <= 1'b0; end - if (din[15:8]==8'h06) begin // Search Forward - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h06) begin // Search Forward + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b0; // no immediate response // No response - //butch_reg[0][13] <= 1'b1; // |= 0x2000 - search_forward <= 1'b1; - search_fast <= din[0]; - search_borderflag <= din[1]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + if (play && !pause && !spinpause && (seek == 8'h0) && (splay == 5'h0) && !dsa_delay_pending) begin + if (dsa_cmd_search_forward_border_hit) begin + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end else begin + search_forward <= 1'b1; + search_backward <= 1'b0; + search_fast <= din[0]; + search_borderflag <= din[1]; + search_div <= 5'h0; + end + end else begin + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end + end end - if (din[15:8]==8'h07) begin // Search Backward - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h07) begin // Search Backward + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b0; // no immediate response // No response - //butch_reg[0][13] <= 1'b1; // |= 0x2000 - search_backward <= 1'b1; - search_fast <= din[0]; - search_borderflag <= din[1]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + if (play && !pause && !spinpause && (seek == 8'h0) && (splay == 5'h0) && !dsa_delay_pending) begin + if (dsa_cmd_search_backward_border_hit) begin + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end else begin + search_forward <= 1'b0; + search_backward <= 1'b1; + search_fast <= din[0]; + search_borderflag <= din[1]; + search_div <= 5'h0; + end + end else begin + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + end + end end - if (din[15:8]==8'h08) begin // Search Release - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h08) begin // Search Release + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b0; // no immediate response // No response - //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + dsa_last_error <= DSA_ERR_NONE; search_forward <= 1'b0; search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; end - if (din[15:8]==8'h09) begin // Get Title Length - not implemented (only for data CDs?) - unhandled <= 1'b1; + if (din[15:8]==8'h09) begin // Get Title Length + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + seek_found_pending <= 1'b0; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if ((din[6:0] == 7'h00) || (din[6:0] > num_tracks)) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + butch_reg[0][13] <= 1'b0; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + cues_addr <= din[6:0]; + title_len_pending <= 1'b1; + end + end + if (din[15:8]==8'h0A) begin // Reserved + unhandled <= 1'b0; + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0900 | cuel_dout[7:0]; // needs to wait for data and convert to lba from time format - ds_resp[1] <= 32'h0A00 | cuel_dout[15:8]; // needs to wait for data and convert to lba from time format - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h2; - ds_resp_loop <= 7'h0; -// cuel_add <= din[6:0]; - cues_addr <= din[6:0]; - end - if (din[15:8]==8'h0A) begin // Open Tray - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0100; //?? + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h0B) begin // Close Tray - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h0B) begin // Reserved + unhandled <= 1'b0; + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0100; //?? + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h0D) begin // Get Complete Time - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h1400 | cur_minutes[6:0]; // needs to wait for next change - ds_resp[1] <= 32'h1500 | cur_seconds[5:0]; // needs to wait for next change - ds_resp[2] <= 32'h1600 | cur_aframes[6:0]; // needs to wait for next change + if (din[15:8]==8'h0C) begin // Reserved + unhandled <= 1'b0; + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h3; + ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end + if (din[15:8]==8'h0D) begin // Get Complete Time + unhandled <= 1'b0; + if (!cd_ex) begin + dsa_last_error <= dsa_presence_error; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h1400 | cur_aminutes[6:0]; + ds_resp[1] <= 32'h1500 | cur_aseconds[5:0]; + ds_resp[2] <= 32'h1600 | cur_aframes[6:0]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h3; + ds_resp_loop <= 7'h0; + end + end if (din[15:8]==8'h10) begin // 0x10 Goto ABS Min unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 -// butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0140; - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - sminutes <= din[6:0]; + if (din[6:0] > 7'd99) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + dsa_last_error <= DSA_ERR_NONE; + goto_minutes <= din[6:0]; + sminutes <= din[6:0]; + end end if (din[15:8]==8'h11) begin // 0x10 Goto ABS Sec unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 -// butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0140; - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - sseconds <= din[5:0]; + if (din[5:0] > 6'd59) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + dsa_last_error <= DSA_ERR_NONE; + goto_seconds <= din[5:0]; + sseconds <= din[5:0]; + end end if (din[15:8]==8'h12) begin // 0x10 Goto ABS Frame unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 -// butch_reg[0][13] <= 1'b1; // |= 0x2000 // too fast - wait for seek time -// ds_resp[0] <= 32'h0140; // dsa says 0x140; code is looking for 0x100 -// ds_resp[0] <= 32'h0100; // too fast - wait for seek time - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - sframes <= din[6:0]; - seek_src_abs <= cur_abs_frames; - seek_dst_abs <= seek_cmd_abs_frames; - seek_mid_abs <= (cur_abs_frames + seek_cmd_abs_frames) >> 1; - if (seek_cmd_abs_frames >= cur_abs_frames) begin - seek_delta_abs <= seek_cmd_abs_frames - cur_abs_frames; - seek_delay_set <= seek_delay_cycles(seek_cmd_abs_frames - cur_abs_frames, (cur_abs_frames + seek_cmd_abs_frames) >> 1, speed2x); + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + seek_found_pending <= 1'b0; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if ((goto_minutes > 7'd99) || (goto_seconds > 6'd59) || (din[6:0] > 7'd74) || goto_target_past_leadout) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + seek_found_pending <= 1'b0; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; end else begin - seek_delta_abs <= cur_abs_frames - seek_cmd_abs_frames; - seek_delay_set <= seek_delay_cycles(cur_abs_frames - seek_cmd_abs_frames, (cur_abs_frames + seek_cmd_abs_frames) >> 1, speed2x); + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][13] <= 1'b0; // response is posted after seek delay completes + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + // Drop stale IRQ pending from the prior position and restart + // subcode chunk stream at canonical first chunk (0x10). + subcode_irq_pending <= 1'b0; + frame_irq_pending <= 1'b0; + sub_chunk_count <= 8'h0F; + subidx <= 4'h0; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + sminutes <= goto_minutes; + sseconds <= goto_seconds; + sframes <= din[6:0]; + seek_skip_cbusy_wait <= play && !pause && !spinpause; + seek_src_abs <= cur_abs_frames; + seek_dst_abs <= goto_cmd_abs_frames_next; + seek_mid_abs <= (cur_abs_frames + goto_cmd_abs_frames_next) >> 1; + if (goto_cmd_abs_frames_next >= cur_abs_frames) begin + seek_delta_abs <= goto_cmd_abs_frames_next - cur_abs_frames; + seek_delay_set <= seek_delay_cycles(goto_cmd_abs_frames_next - cur_abs_frames, (cur_abs_frames + goto_cmd_abs_frames_next) >> 1, speed2x); + end else begin + seek_delta_abs <= cur_abs_frames - goto_cmd_abs_frames_next; + seek_delay_set <= seek_delay_cycles(cur_abs_frames - goto_cmd_abs_frames_next, (cur_abs_frames + goto_cmd_abs_frames_next) >> 1, speed2x); + end + + if (!cd_latency_en) begin + seek_delay_set <= 32'h1_000_000; + end + if (play && !pause && !spinpause) begin + seek_delay_set <= cd_latency_en ? DSA_DELAY_SCAN_GOTO : 32'h0008_0000; + end + + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; + cues_addr <= seek_scan_start_track; + cuet_addr <= seek_scan_start_track; + seek_found_pending <= 1'b1; + seek <= 8'hFF; + abseek <= 8'h0; + stop <= 1'b0; + spinpause <= 1'b0; end - cues_addr <= num_tracks + 7'h1; - cuet_addr <= num_tracks + 7'h1; - seek <= 8'hFF; - stop <= 1'b0; - spinpause <= 1'b0; end if (din[15:8]==8'h14) begin // Read Long TOC unhandled <= 1'b0; - if (!cd_ex) begin // No CD - ds_resp[0] <= 32'h400; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + dsa_long_toc_active <= 1'b0; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if ((!aud_sess && (din[7:0] != 8'h00)) || (din[7:0] >= dsa_session_count) || (din[7:0] >= DSA_MAX_SESSIONS) || !dsa_req_sess_valid) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + dsa_long_toc_active <= 1'b0; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end else begin -// for(int i=0;i= dat_track)) ? 8'h41 : 8'h01); + ds_resp[2] <= 32'h6200; + ds_resp[3] <= 32'h6300; + ds_resp[4] <= 32'h6400; + if (dsa_req_session == 8'h00) begin + dbg_ltoc0_r <= { + 1'b1, + dsa_req_first_track, + (((dat_track != 7'h0) && (dsa_req_first_track >= dat_track)) ? 8'h41 : 8'h01) + }; + end else if (dsa_req_session == 8'h01) begin + dbg_ltoc1_r <= { + 1'b1, + dsa_req_first_track, + (((dat_track != 7'h0) && (dsa_req_first_track >= dat_track)) ? 8'h41 : 8'h01) + }; + end butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp_idx <= 3'h0; ds_resp_size <= 3'h5; -// ds_resp_loop <= num_tracks[6:0]; - ds_resp_loop <= ((din[7:0]==8'h01) ? dat_tracks : ((aud_sess) ? aud_tracks : num_tracks)); - cues_addr <= ((din[7:0]==8'h00) ? 7'h1 : dat_track); + ds_resp_loop <= 7'h0; + cues_addr <= dsa_req_first_track; updresp <= 1'b1; end end @@ -1330,6 +2386,13 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; mode <= din[7:0]; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; if (din[1]) begin // bit1=speed2x sdin3[15:0] <= 16'h3; // 2*(3+1)=8 min for 9.279 (- currently setting 3 will alternate 3 and 4) end else begin // bit0=speed1x @@ -1337,19 +2400,20 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end i2s3w <= 1'b1; end - if (din[15:8]==8'h16) begin // Get Last Error - No errors - Untested + if (din[15:8]==8'h16) begin // Get Last Error unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h400; // | error[7:0]; + ds_resp[0] <= 32'h0400 | dsa_last_error; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h17) begin // Clear Error - No errors - Untested + if (din[15:8]==8'h17) begin // Clear Error unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 + dsa_last_error <= DSA_ERR_NONE; ds_resp[0] <= 32'h400; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; @@ -1357,83 +2421,184 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end if (din[15:8]==8'h18) begin // Spin Up unhandled <= 1'b0; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0100 | 32'h0043; - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - mounted <= 1'b0; - spinpause <= 1'b1; -// if (splay != 0 || play) begin - if (mounted) begin - spinpause <= 1'b0; - end + if (!cd_ex) begin + dsa_last_error <= DSA_ERR_FOCUS_NO_DISC; + ds_resp[0] <= 32'h0400 | DSA_ERR_FOCUS_NO_DISC; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (!toc_ready) begin + // Accept command but keep drive busy until TOC/session metadata arrives. + dsa_last_error <= DSA_ERR_NONE; + abplay <= 1'b0; + abseek <= 8'h0; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b0; + ds_resp[0] <= 32'h0100 | 32'h0043; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + dsa_spinup_wait_toc <= 1'b1; + dsa_spinup_session <= dsa_spin_req_session; + end else begin + abplay <= 1'b0; + abseek <= 8'h0; + search_forward <= 1'b0; + search_backward <= 1'b0; + search_fast <= 1'b0; + search_borderflag <= 1'b0; + search_div <= 5'h0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + if (cd_latency_en) begin + butch_reg[0][13] <= 1'b0; + dsa_delay_pending <= 1'b1; + dsa_delay_ctr <= DSA_DELAY_SPIN_UP; + end else begin + butch_reg[0][13] <= 1'b1; // |= 0x2000 + end + ds_resp[0] <= 32'h0100 | 32'h0043; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + mounted <= 1'b1; + spinpause <= 1'b1; + if (mounted) begin + spinpause <= 1'b0; + end splay <= 5'h15; stop <= 1'b0; - aud_add <= 30'h0; - track_idx <= (din[6:0] == 0) ? 7'h1 : dat_track; - cur_samples <= 10'h0; - cur_rframes <= 7'h0; - cur_rseconds <= 6'h2; - cur_rminutes <= 7'h0; - gframes <= 3'h0; - cues_addr <= (din[6:0] == 0) ? 7'h1 : dat_track; - cuet_addr <= (din[6:0] == 0) ? 7'h1 : dat_track; - updabs <= 1'b1; + aud_add <= 30'h0; + if (dsa_sess_valid[dsa_spin_req_session] && (dsa_sess_first_track[dsa_spin_req_session] != 7'h00)) begin + track_idx <= dsa_sess_first_track[dsa_spin_req_session]; + dbg_spin_r <= { + 1'b1, + dsa_sess_first_track[dsa_spin_req_session], + dsa_spin_req_session + }; + end else begin + track_idx <= 7'h1; + dbg_spin_r <= { + 1'b1, + 7'h01, + dsa_spin_req_session + }; + end + cur_samples <= 10'h0; + cur_rframes <= 7'h0; + cur_rseconds <= 6'h2; + cur_rminutes <= 7'h0; + gframes <= 3'h0; + if (dsa_sess_valid[dsa_spin_req_session] && (dsa_sess_first_track[dsa_spin_req_session] != 7'h00)) begin + cues_addr <= dsa_sess_first_track[dsa_spin_req_session]; + cuet_addr <= dsa_sess_first_track[dsa_spin_req_session]; + end else begin + cues_addr <= 7'h1; + cuet_addr <= 7'h1; + end + updabs_req <= 1'b1; + end end - if (din[15:8]==8'h20) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h20) begin // Play A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 // No response //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; abaminutes <= din[6:0]; end - if (din[15:8]==8'h21) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h21) begin // Play A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 // No response //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; abaseconds <= din[5:0]; end - if (din[15:8]==8'h22) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h22) begin // Play A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 // No response //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; abaframes <= din[6:0]; end - if (din[15:8]==8'h23) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h23) begin // Play A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 // No response //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; abbminutes <= din[6:0]; end - if (din[15:8]==8'h24) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h24) begin // Play A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 // No response //butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; abbseconds <= din[5:0]; end - if (din[15:8]==8'h25) begin // Play A Time To B Time - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 // too fast - wait for seek time - ds_resp[0] <= 32'h0144; // should be done on seek found - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; + if (din[15:8]==8'h25) begin // Play A Time To B Time (start) + unhandled <= 1'b0; abbframes <= din[6:0]; - cues_addr <= num_tracks + 7'h1; - cuet_addr <= num_tracks + 7'h1; - abseek <= 8'hFF; - stop <= 1'b0; - spinpause <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + if (!cd_ex) begin + dsa_last_error <= dsa_presence_error; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + abplay <= 1'b0; + abseek <= 8'h0; + end else if ({abaminutes,2'b00,abaseconds,1'b0,abaframes} > {abbminutes,2'b00,abbseconds,1'b0,din[6:0]}) begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + abplay <= 1'b0; + abseek <= 8'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][13] <= 1'b0; // response after A-time seek completes + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + sminutes <= abaminutes; + sseconds <= abaseconds; + sframes <= abaframes; + seek_skip_cbusy_wait <= 1'b0; + cues_addr <= seek_scan_start_track; + cuet_addr <= seek_scan_start_track; + seek_found_pending <= 1'b0; + seek <= 8'hFF; + abseek <= 8'hFF; + abplay <= 1'b1; + stop <= 1'b0; + spinpause <= 1'b0; + end end - if (din[15:8]==8'h26) begin // Release A Time To B Time - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h26) begin // Release A Time To B Time + unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 // too fast - wait for seek time ds_resp[0] <= 32'h2600; @@ -1441,31 +2606,54 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; abplay <= 1'b0; + abseek <= 8'h0; end - if (din[15:8]==8'h30) begin // Get Disc identifiers - not implemented - unhandled <= 1'b1; - ds_resp[0] <= 32'h3000; //| disc_identifier[0][7:0]; - ds_resp[1] <= 32'h3100; //| disc_identifier[1][7:0]; - ds_resp[2] <= 32'h3200; //| disc_identifier[2][7:0]; - ds_resp[3] <= 32'h3300; //| disc_identifier[3][7:0]; - ds_resp[4] <= 32'h3400; //| disc_identifier[4][7:0]; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h5; - ds_resp_loop <= 7'h0; + if (din[15:8]==8'h30) begin // Get Disc identifiers + unhandled <= 1'b0; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + ds_resp[0] <= 32'h3000 | dsa_disc_id0; + ds_resp[1] <= 32'h3100 | dsa_disc_id1; + ds_resp[2] <= 32'h3200 | dsa_disc_id2; + ds_resp[3] <= 32'h3300 | dsa_disc_id3; + ds_resp[4] <= 32'h3400 | dsa_disc_id4; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h5; + ds_resp_loop <= 7'h0; + end end - if (din[15:8]==8'h50) begin // Get Disc Status - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h0300; // |= {finalized,low_reflectance,disc_12cm,njukebox,disc_present}; // low_ref=CDRW,high_ref=CDR/CDDA 8cm=0 + if ((din[15:8] >= 8'h40) && (din[15:8] <= 8'h44)) begin // Reserved + unhandled <= 1'b0; + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h51) begin // Set Volume - not implemented - unhandled <= 1'b1; + if (din[15:8]==8'h50) begin // Get Disc Status + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h0300 | dsa_disc_status; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end + if (din[15:8]==8'h51) begin // Set Volume + unhandled <= 1'b0; + dsa_volume <= din[7:0]; butch_reg[0][12] <= 1'b1; // |= 0x1000 butch_reg[0][13] <= 1'b1; // |= 0x2000 ds_resp[0] <= 32'h5100 | din[7:0]; // 0=mute 1-254=fade 255=full @@ -1473,44 +2661,191 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h54) begin // Get Max Session - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h5400 | ((aud_sess) ? 8'h1 : 8'h2); // ?? // if correct only 1 or 2 sessions supported + if (din[15:8]==8'h52) begin // Get Mode Status (compatibility alias) + unhandled <= 1'b0; + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1700 | mode; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; end - if (din[15:8]==8'h6A) begin // Clear TOC - not implemented - unhandled <= 1'b1; - butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h6A00; - ds_resp_idx <= 3'h0; - ds_resp_size <= 3'h1; - ds_resp_loop <= 7'h0; - end - if (din[15:8]==8'h70) begin // Set DAC Mode - only 1 and 81 expected (which do nothing) + if (din[15:8]==8'h54) begin // Get Max Session unhandled <= 1'b0; butch_reg[0][12] <= 1'b1; // |= 0x1000 - butch_reg[0][13] <= 1'b1; // |= 0x2000 - ds_resp[0] <= 32'h7000 | din[7:0]; + if (!cd_ex || !toc_ready) begin + dsa_last_error <= dsa_presence_error; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | dsa_presence_error; + dbg_resp_54_r <= 16'h0400 | dsa_presence_error; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h5400 | dsa_max_session_rsp; + dbg_resp_54_r <= 16'h5400 | dsa_max_session_rsp; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end + end + if (din[15:8]==8'h6A) begin // Clear TOC + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + if (play || (seek != 8'h0) || (splay != 5'h0)) begin + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else begin + dsa_last_error <= DSA_ERR_NONE; + dsa_long_toc_active <= 1'b0; + dsa_long_toc_first_track <= 7'h0; + dsa_long_toc_last_track <= 7'h0; + toc_ready <= 1'b0; + dsa_sess_count_toc <= 8'h0; + cue_tracks <= 7'h0; + aud_tracks <= 7'h0; + dat_tracks <= 7'h0; + dat_track <= 7'h0; + for (dsa_sess_i = 0; dsa_sess_i < DSA_MAX_SESSIONS; dsa_sess_i = dsa_sess_i + 1) begin + dsa_sess_first_track[dsa_sess_i] <= 7'h0; + dsa_sess_last_track[dsa_sess_i] <= 7'h0; + dsa_sess_leadout[dsa_sess_i] <= 24'h0; + dsa_sess_valid[dsa_sess_i] <= 1'b0; + end + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h6A00; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end + end + if (din[15:8]==8'h70) begin // Set DAC Mode (validate against documented DAC mode set) + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; // |= 0x1000 + ds_resp_idx <= 3'h0; + ds_resp_loop <= 7'h0; + if (dsa_dac_mode_valid) begin + dsa_last_error <= DSA_ERR_NONE; + butch_reg[0][13] <= 1'b1; // |= 0x2000 + ds_resp[0] <= 32'h7000 | din[7:0]; + ds_resp_size <= 3'h1; + end else begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_size <= 3'h1; + end + end + if (din[15:8]==8'hF0) begin // Service mode control + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_loop <= 7'h0; + if (din[7:0] == 8'h01) begin // request servo version + enter service mode + dsa_last_error <= DSA_ERR_NONE; + dsa_service_mode <= 1'b1; + dsa_sledge_out <= 1'b0; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'hF000 | DSA_SERVO_VERSION; + ds_resp_size <= 3'h1; + end else if (din[7:0] == 8'h00) begin // service mode off + dsa_last_error <= DSA_ERR_NONE; + dsa_service_mode <= 1'b0; + dsa_sledge_out <= 1'b0; + dsa_focus_on <= 1'b0; + dsa_spindle_on <= 1'b0; + dsa_radial_on <= 1'b0; + dsa_laser_on <= 1'b0; + butch_reg[0][13] <= 1'b0; + ds_resp_size <= 3'h0; // no response + end else begin + dsa_last_error <= DSA_ERR_ILLEGAL_VALUE; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_VALUE; + ds_resp_size <= 3'h1; + end + end + if ((din[15:8] >= 8'hF1) && (din[15:8] <= 8'hF9)) begin // Service commands + unhandled <= 1'b0; + butch_reg[0][12] <= 1'b1; + ds_resp_idx <= 3'h0; + ds_resp_loop <= 7'h0; + if (dsa_service_mode) begin + ds_resp_size <= 3'h0; // no response + butch_reg[0][13] <= 1'b0; + dsa_last_error <= DSA_ERR_NONE; + if (din[15:8]==8'hF1) begin + dsa_sledge_out <= din[0]; + end + if (din[15:8]==8'hF2) begin + dsa_focus_on <= din[0]; + if (!din[0]) begin + dsa_radial_on <= 1'b0; + dsa_spindle_on <= 1'b0; + dsa_laser_on <= 1'b0; + end else begin + dsa_laser_on <= 1'b1; + end + end + if (din[15:8]==8'hF3) begin + dsa_spindle_on <= din[0]; + if (!din[0]) begin + dsa_radial_on <= 1'b0; + end else begin + dsa_laser_on <= 1'b1; + dsa_focus_on <= 1'b1; + end + end + if (din[15:8]==8'hF4) begin + dsa_radial_on <= din[0]; + if (din[0]) begin + dsa_laser_on <= 1'b1; + dsa_focus_on <= 1'b1; + dsa_spindle_on <= 1'b1; + end + end + if (din[15:8]==8'hF5) begin + dsa_laser_on <= din[0]; + if (!din[0]) begin + dsa_radial_on <= 1'b0; + dsa_spindle_on <= 1'b0; + dsa_focus_on <= 1'b0; + end + end + if (din[15:8]==8'hF6) begin + dsa_diag_last <= din[7:0]; + end + if (din[15:8]==8'hF7) begin + dsa_high_gain <= din[0]; + end + if (din[15:8]==8'hF8) begin + dsa_jump_grooves[15:8] <= din[7:0]; + end + if (din[15:8]==8'hF9) begin + dsa_jump_grooves[7:0] <= din[7:0]; + end + end else begin + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; + ds_resp_size <= 3'h1; + end + end + if (!dsa_cmd_known) begin + dsa_last_error <= DSA_ERR_ILLEGAL_CMD; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h0400 | DSA_ERR_ILLEGAL_CMD; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h1; ds_resp_loop <= 7'h0; - //0 reserved - //1 I2S - FS mode (default) DAC mode - //2 I2S - 2 FS mode DAC mode - //3 I2S - 4 FS mode DAC mode - //4 Sony 16 bit FS DAC mode - //5 Sony 16 bit 2 FS DAC mode - //6 Sony 16 bit 4 FS DAC mode - //7 Sony 18 bit FS DAC mode - //8 Sony 18 bit 2 FS DAC mode - //9 Sony 18 bit 4 FS DAC mode - //81 I2S - CD-ROM mode CD-ROM mode - //82 EIAJ CD-ROM mode CD-ROM mode end // 0xa0-0xaf User Define (???) // 0xf0 Service @@ -1542,6 +2877,17 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); ds_resp_loop <= ds_resp_loop - 7'h1; cues_addr <= cues_addr + 7'h1; updrespa <= 1'b1; + end else if (dsa_long_toc_active && (ds_resp_size == 3'h5)) begin + if (dsa_long_toc_last_track > dsa_long_toc_first_track) begin + if (cues_addr >= dsa_long_toc_last_track) begin + cues_addr <= dsa_long_toc_first_track; + end else begin + cues_addr <= cues_addr + 7'h1; + end + end else begin + cues_addr <= dsa_long_toc_first_track; + end + updrespa <= 1'b1; end else begin ds_resp_size <= 3'h0; // butch_reg[0][13] <= 1'b0; // &= ~0x2000 @@ -1554,25 +2900,143 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end if (updresp) begin ds_resp[0][7:0] <= cues_add; + ds_resp[1][15:8] <= 8'h61; + ds_resp[1][7:0] <= dsa_long_toc_ctrl_addr; + ds_resp[2][15:8] <= 8'h62; ds_resp[2][7:0] <= {1'b0,cues_dout[22:16]}; + ds_resp[3][15:8] <= 8'h63; ds_resp[3][7:0] <= {2'b0,cues_dout[13:8]}; + ds_resp[4][15:8] <= 8'h64; ds_resp[4][7:0] <= {1'b0,cues_dout[6:0]}; end + // ATTI runtime notifications: emit only when title/index/time fields actually change. + if (!(attirel || attiabs)) begin + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + end else if (play && !stop && !spinpause && !pause && (seek == 8'h0) && (splay == 5'h0) && !dsa_delay_pending) begin + if (!atti_report_valid) begin + atti_report_valid <= 1'b1; + atti_last_title <= atti_cur_title; + atti_last_index <= atti_cur_index; + atti_last_rel_minutes <= cur_rminutes[6:0]; + atti_last_rel_seconds <= cur_rseconds[5:0]; + atti_last_abs_minutes <= cur_aminutes[6:0]; + atti_last_abs_seconds <= cur_aseconds[5:0]; + end else begin + if (atti_cur_title != atti_last_title) begin + atti_evt_title_pending <= 1'b1; + atti_last_title <= atti_cur_title; + end + if (atti_cur_index != atti_last_index) begin + atti_evt_index_pending <= 1'b1; + atti_last_index <= atti_cur_index; + end + if (attirel) begin + if (cur_rminutes[6:0] != atti_last_rel_minutes) begin + atti_evt_rel_minutes_pending <= 1'b1; + atti_last_rel_minutes <= cur_rminutes[6:0]; + end + if (cur_rseconds[5:0] != atti_last_rel_seconds) begin + atti_evt_rel_seconds_pending <= 1'b1; + atti_last_rel_seconds <= cur_rseconds[5:0]; + end + end + if (attiabs) begin + if (cur_aminutes[6:0] != atti_last_abs_minutes) begin + atti_evt_abs_minutes_pending <= 1'b1; + atti_last_abs_minutes <= cur_aminutes[6:0]; + end + if (cur_aseconds[5:0] != atti_last_abs_seconds) begin + atti_evt_abs_seconds_pending <= 1'b1; + atti_last_abs_seconds <= cur_aseconds[5:0]; + end + end + end + end + // Drain one ATTI event at a time so responses match hardware-style discrete updates. + if ((ds_resp_size == 3'h0) && + !ds_a && + !updresp && + !updrespa && + !dsa_delay_pending && + !dsa_spinup_wait_toc && + !play_title_pending_rsp) begin + if (leadout_title_pending) begin + leadout_title_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1000 | 8'hAA; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + atti_report_valid <= 1'b1; + atti_last_title <= 8'hAA; + atti_last_index <= 8'h01; + end else if (atti_evt_title_pending) begin + atti_evt_title_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1000 | atti_cur_title; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (atti_evt_index_pending) begin + atti_evt_index_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1100 | atti_cur_index; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (attirel && atti_evt_rel_minutes_pending) begin + atti_evt_rel_minutes_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1200 | cur_rminutes[6:0]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (attirel && atti_evt_rel_seconds_pending) begin + atti_evt_rel_seconds_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1300 | cur_rseconds[5:0]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (attiabs && atti_evt_abs_minutes_pending) begin + atti_evt_abs_minutes_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1400 | cur_aminutes[6:0]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end else if (attiabs && atti_evt_abs_seconds_pending) begin + atti_evt_abs_seconds_pending <= 1'b0; + butch_reg[0][12] <= 1'b1; + butch_reg[0][13] <= 1'b1; + ds_resp[0] <= 32'h1500 | cur_aseconds[5:0]; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h1; + ds_resp_loop <= 7'h0; + end + end if (old_doe_dsc && !doe_dsc) begin butch_reg[0][12] <= 1'b0; // butch_reg[0][13] <= ((ds_resp[0][13:8] == 6'h20) || (ds_resp[0][15:8] == 6'h04)) ? 1'b1 : 1'b0; // gettoc==0x20 or 0x60 butch_reg[0][13] <= ((ds_resp_size == 3'h0) || (ds_resp_size == 3'h1)) ? 1'b0 : 1'b1; // gettoc==0x20 or 0x60 end - if (old_doe_sub && !doe_sub) begin - subidx <= subidx + 4'h1; - if (4'hC == subidx + 4'h1) begin - subidx <= 4'h0; -// butch_reg[0][13] <= 1'b0; // &= ~0x2000 - end - if (4'h1 == subidx + 4'h1) begin - upd_frames <= 1'b0; - recrc <= 1'b1; - end + if (old_doe_sbcntrl && !doe_sbcntrl) begin + // Real software (e.g. VLM GPU ISR) reads SBCNTRL ($DFFF14) to clear + // pending subcode/frame interrupt status bits. + subcode_irq_pending <= 1'b0; + frame_irq_pending <= 1'b0; end if (!old_doe_sub && !doe_sub) begin if ((4'h0 == subidx) && upd_frames) begin @@ -1580,8 +3044,8 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); recrc <= 1'b1; end end - if (recrc && (attiabs || attirel)) begin - butch_reg[0][13] <= 1'b1; // |= 0x2000 + if (dsa_delay_pending && cd_latency_en && (dsa_delay_ctr > 32'h1)) begin + butch_reg[0][13] <= 1'b0; end if (fifo_inc && (!doe_fif || eoe0l || (old_fif_a1 != fif_a1))) begin // if a1!= then swapping 24/28 @@ -1597,6 +3061,49 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); end butch_reg[4][4] <= i2s_rfifopos != i2s_wfifopos;//0x10; butch_reg[0][9] <= fifo_half; // 0x200 + butch_reg[0][10] <= subcode_irq_pending; + butch_reg[0][11] <= frame_irq_pending; + + // When CD hardware is disabled, force all drive-generated interrupt and + // deferred-response state quiescent so Butch cannot perturb cart/system boot. + if (!cd_en) begin + subcode_irq_pending <= 1'b0; + frame_irq_pending <= 1'b0; + butch_reg[0][9] <= 1'b0; + butch_reg[0][10] <= 1'b0; + butch_reg[0][11] <= 1'b0; + butch_reg[0][12] <= 1'b0; + butch_reg[0][13] <= 1'b0; + play <= 1'b0; + stop <= 1'b0; + pause <= 1'b0; + spinpause <= 1'b0; + seek <= 8'h0; + seek_skip_cbusy_wait <= 1'b0; + seek_found_pending <= 1'b0; + splay <= 5'h0; + abplay <= 1'b0; + abseek <= 8'h0; + search_forward <= 1'b0; + search_backward <= 1'b0; + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + dsa_spinup_wait_toc <= 1'b0; + dsa_long_toc_active <= 1'b0; + play_title_pending_rsp <= 1'b0; + ds_resp_idx <= 3'h0; + ds_resp_size <= 3'h0; + ds_resp_loop <= 7'h0; + atti_report_valid <= 1'b0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; + end if (!resetl) begin hackwait <= 1'b0; @@ -1604,6 +3111,9 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); pastcdbios <= 1'b0; recrc <= 1'b0; subidx <= 4'h0; + sub_chunk_count <= 8'h0F; + frame_irq_pending <= 1'b0; + subcode_irq_pending <= 1'b0; mounted <= 1'b0; splay <= 5'h0; play <= 1'b0; @@ -1617,7 +3127,8 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); aud_rd <= 1'b0; aud_add <= 30'h000000; unhandled <= 1'b0; - track_idx <= 7'h0; + track_idx <= 7'h1; + atrack <= 7'h1; cur_samples <= 10'h0; cur_frames <= 7'h0; cur_seconds <= 6'h0; @@ -1632,21 +3143,32 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); upd_seconds <= 1'b0; upd_minutes <= 1'b0; updabs <= 1'b0; + updabs_req <= 1'b0; ds_resp[0] <= 32'h0; ds_resp[1] <= 32'h0; ds_resp[2] <= 32'h0; ds_resp[3] <= 32'h0; ds_resp[4] <= 32'h0; + ds_resp[5] <= 32'h0; ds_resp_idx <= 3'h0; ds_resp_size <= 3'h0; ds_resp_loop <= 7'h0; updresp <= 1'b0; updrespa <= 1'b0; - mode <= 1'b0; + mode <= 8'h01; // default: 1x audio mode until BIOS/program sets explicit mode sdin[15:0] <= 0; sdin3[15:0] <= 0; sdin4[15:0] <= 0; last_ds <= 16'h0; + dbg_resp_54_r <= 16'h0000; + dbg_toc0_r <= 40'h0; + dbg_toc1_r <= 40'h0; + dbg_spin_r <= 16'h0000; + dbg_ltoc0_r <= 16'h0000; + dbg_ltoc1_r <= 16'h0000; + dsa_last_error <= DSA_ERR_NONE; + dsa_volume <= 8'hFF; + title_len_pending <= 1'b0; seek_delay_set <= 32'h2000000; seek_src_abs <= 20'h0; seek_dst_abs <= 20'h0; @@ -1670,6 +3192,8 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); sframes <= 7'h0; sseconds <= 6'h0; sminutes <= 7'h0; + goto_minutes <= 7'h0; + goto_seconds <= 6'h0; gframes <= 3'h0; subtseconds <= 6'h0; subtrseconds <= 6'h0; @@ -1677,6 +3201,15 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); taud2_add <= 22'h0; taud3_add <= 20'h0; seek_delay <= 32'h0; + seek_skip_cbusy_wait <= 1'b0; + seek_found_pending <= 1'b0; + dsa_delay_pending <= 1'b0; + dsa_delay_ctr <= 32'h0; + dsa_spinup_wait_toc <= 1'b0; + dsa_spinup_session <= 8'h00; + dsa_long_toc_active <= 1'b0; + dsa_long_toc_first_track <= 7'h0; + dsa_long_toc_last_track <= 7'h0; i2s_rfifopos <= 5'h0; i2s_wfifopos <= 5'h0; fifo_inc <= 1'b0; @@ -1700,6 +3233,33 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); search_backward <= 1'b0; search_fast <= 1'b0; search_borderflag <= 1'b0; + search_div <= 5'h0; + dsa_service_mode <= 1'b0; + dsa_sledge_out <= 1'b0; + dsa_focus_on <= 1'b0; + dsa_spindle_on <= 1'b0; + dsa_radial_on <= 1'b0; + dsa_laser_on <= 1'b0; + dsa_high_gain <= 1'b0; + dsa_diag_last <= 8'h0; + dsa_jump_grooves <= 16'h0000; + atti_report_valid <= 1'b0; + atti_last_title <= 8'h0; + atti_last_index <= 8'h0; + atti_last_rel_minutes <= 7'h0; + atti_last_rel_seconds <= 6'h0; + atti_last_abs_minutes <= 7'h0; + atti_last_abs_seconds <= 6'h0; + atti_evt_title_pending <= 1'b0; + atti_evt_index_pending <= 1'b0; + atti_evt_rel_minutes_pending <= 1'b0; + atti_evt_rel_seconds_pending <= 1'b0; + atti_evt_abs_minutes_pending <= 1'b0; + atti_evt_abs_seconds_pending <= 1'b0; + play_title_pending_rsp <= 1'b0; + play_title_pending_track <= 7'h0; + leadout_title_pending <= 1'b0; + leadout_seen <= 1'b0; abplay <= 1'b0; abseek <= 8'h0; abaframes <= 7'h0; @@ -1711,6 +3271,7 @@ hackwait <= (seek_count==4'h1) || (seek_count==4'h4); overflow <= 1'b0; underflow <= 1'b0; errflow <= 1'b0; + old_doe_sbcntrl <= 1'b0; end if (!cdbios) pastcdbios <= 1'b1; @@ -1721,6 +3282,7 @@ wire [31:0] cuet_din; wire [31:0] cuet_doutt; wire cuet_wr; wire [31:0] cuet_dout = (cuet_add > cue_tracks) ? 32'h0 : cuet_doutt; +wire [29:0] cueb_dout = 30'h0; spram #(.addr_width(7), .data_width(32)) cuet_bram_inst ( .clock ( sys_clk ), @@ -1993,3 +3555,4 @@ end endmodule + diff --git a/rtl/Rework/butch_i2s.v b/rtl/Rework/butch_i2s.v index 81e1fb7..d644a1e 100644 --- a/rtl/Rework/butch_i2s.v +++ b/rtl/Rework/butch_i2s.v @@ -142,6 +142,7 @@ wire [7:0] p__ = fractional[17] ? cd2x ? 8'h4 : 8'h9 : p_[7:0]; // 4 or 9 less l reg [17:0] fractional; reg old_wsout; // Goal = 9.279 for 2x +// Goal = 9.421 for 2x // 2*(3+1)=8 // 2*(4+1)=10 // To increase accuracy using fractional counter instead of only alternating (alternating=(8+10)/2=9) @@ -154,16 +155,20 @@ reg old_wsout; // 90.703us @ x2 = 32 bytes // 9647.5 cycles @ 106.36MHz = 90.703us // 358200 bytes/sec at double rate +// 352800 bytes/sec at double rate // 265909/(358.2*8) = 9.279 cycles/bit +// 265909/(352.8*8) = 9.421 cycles/bit // 1.279 = 18'h1476D (lower 16 bits=fraction) for 2x // 0.558 = 18'h08ED9 (lower 16 bits=fraction) for 1x +// 1.421 = 18'h16BDF (lower 16 bits=fraction) for 2x +// 0.843 = 18'h0D7BE (lower 16 bits=fraction) for 1x // When adds to 2, overflow to p[] which has resolution of 2 always @(posedge sys_clk) begin old_wsout <= wsout; if (~old_clk && clk) begin - if (~wsout != old_wsout) begin - fractional[17:0] <= fractional[16:0] + cd2x ? 18'h1476D : 18'h8ED9; // default cd speed = 1x + if (wsout != old_wsout) begin + fractional[17:0] <= fractional[16:0] + (cd2x ? 18'h16BC7 : 18'h0D7CF); // default cd speed = 1x end end end diff --git a/rtl/Rework/jaguar.v b/rtl/Rework/jaguar.v index 38778f9..ad1e826 100644 --- a/rtl/Rework/jaguar.v +++ b/rtl/Rework/jaguar.v @@ -83,8 +83,8 @@ module jaguar input maxc, input auto_eeprom, output [23:0] addr_ch3, - input [9:0] toc_addr, - input [15:0] toc_data, + input [9:0] toc_addr, + input [15:0] toc_data, input toc_wr, output [29:0] audbus_out, @@ -93,10 +93,29 @@ module jaguar input audwaitl, output aud_ce, input aud_busy, - input aud_sess, + input aud_sess, + input force_music_cd, + output [6:0] dbg_butch_cue_tracks, + output [6:0] dbg_butch_aud_tracks, + output [6:0] dbg_butch_dat_track, + output [7:0] dbg_butch_dsa_sessions, + output dbg_butch_sess1_valid, + output [15:0] dbg_butch_last_ds, + output [7:0] dbg_butch_last_err, + output [6:0] dbg_butch_track_idx, + output [6:0] dbg_butch_cues_addr, + output [6:0] dbg_butch_cuet_addr, + output [15:0] dbg_butch_resp_54, + output [39:0] dbg_butch_toc0, + output [39:0] dbg_butch_toc1, + output [15:0] dbg_butch_spin, + output [15:0] dbg_butch_ltoc0, + output [15:0] dbg_butch_ltoc1, + output dbg_butch_toc_ready, input cd_en, input cd_ex, + input cd_latency_en, output b_override, input dohacks, output xvclk_o, @@ -117,7 +136,8 @@ module jaguar input [15:0] m68k_di, input gamedrive_enable, - input ntsc + input ntsc, + input video_center ); assign xvclk_o = xvclk; @@ -419,7 +439,8 @@ assign j_xwel_0 = xwel[0]; // Write Enable assign j_xdtackl = xdtackl; // Data Acknowledge from Tom (also goes to the 68K) //assign j_xi2srxd = 1'b1; // (Async?) I2S receive -//assign j_xeint[0] = 1'b1; // External Interrupt +// External interrupt input follows Butch directly. +assign j_xeint[0] = b_eint; assign j_xeint[1] = 1'b1; // External Interrupt assign j_xtest = 1'b0; // "test" pins on both Tom and Jerry are tied to GND on the Jag assign j_xchrin = 1'b1; // Not used @@ -1305,7 +1326,9 @@ _tom tom_inst .startwe (startwe), .atp (dram_addrp[10:3]), .vintbugfix (vintbugfix), - .turbo (turbo) + .turbo (turbo), + .ntsc (ntsc), + .video_center (video_center) ); assign vga_hs_n = ~xhs_out; @@ -1401,6 +1424,7 @@ wire b_ee_cs; wire b_ee_sk; wire b_ee_dout; wire b_ee_din; +wire b_eint; wire [31:0] cart_qt = b_doe ? b_dout : cart_q; _butch butch_inst ( @@ -1409,6 +1433,7 @@ _butch butch_inst .cart_ce_n (cart_ce_n), .cd_en (cd_en), .cd_ex (cd_ex), + .cd_latency_en (cd_latency_en), .eoe0l (b_eoe0l), .eoe1l (b_eoe1l), .ewe0l (b_ewe0l), @@ -1423,22 +1448,23 @@ _butch butch_inst .audwaitl (audwaitl), .aud_cbusy (aud_busy), .i2srxd (j_xi2srxd), - .eint (j_xeint[0]), + .eint (b_eint), .sen (b_xsck_oe), .sck (b_xsck_out), .ws (b_xws_out), .override (b_override), .aud_ce (aud_ce), .aud_sess (aud_sess), + .force_music_cd (force_music_cd), + .toc_addr (toc_addr), + .toc_data (toc_data), + .toc_wr (toc_wr), .eeprom_cs (b_ee_cs), .eeprom_sk (b_ee_sk), .eeprom_dout (b_ee_din), .eeprom_din (b_ee_dout), .maxc (maxc), .addr_ch3 (addr_ch3[23:0]), - .toc_addr (toc_addr), - .toc_data (toc_data), - .toc_wr (toc_wr), .dohacks (dohacks), .hackbus (hackbus), .hackbus1 (hackbus1), @@ -1448,6 +1474,23 @@ _butch butch_inst .errflowo (errflow), .unhandledo (unhandled), .cd_valid(cd_valid), + .dbg_cue_tracks(dbg_butch_cue_tracks), + .dbg_aud_tracks(dbg_butch_aud_tracks), + .dbg_dat_track(dbg_butch_dat_track), + .dbg_dsa_sessions(dbg_butch_dsa_sessions), + .dbg_sess1_valid(dbg_butch_sess1_valid), + .dbg_last_ds(dbg_butch_last_ds), + .dbg_last_err(dbg_butch_last_err), + .dbg_track_idx(dbg_butch_track_idx), + .dbg_cues_addr(dbg_butch_cues_addr), + .dbg_cuet_addr(dbg_butch_cuet_addr), + .dbg_resp_54(dbg_butch_resp_54), + .dbg_toc0(dbg_butch_toc0), + .dbg_toc1(dbg_butch_toc1), + .dbg_spin(dbg_butch_spin), + .dbg_ltoc0(dbg_butch_ltoc0), + .dbg_ltoc1(dbg_butch_ltoc1), + .dbg_toc_ready(dbg_butch_toc_ready), .sys_clk (sys_clk) ); wire b_xsck_oe; @@ -1565,7 +1608,7 @@ fx68k fx68k_inst .IPL0n (fx68k_ipl_n[0]), // input .IPL1n (fx68k_ipl_n[1]), // input .IPL2n (fx68k_ipl_n[2]), // input - .iEdb (fx68k_din), // input + .iEdb (m68k_di), // input .oEdb (fx68k_dout), // output .eab (fx68k_address) // output ); diff --git a/rtl/Rework/jaguar_Rework.qip b/rtl/Rework/jaguar_Rework.qip index e200027..4c6900a 100644 --- a/rtl/Rework/jaguar_Rework.qip +++ b/rtl/Rework/jaguar_Rework.qip @@ -1,6 +1,8 @@ set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) defs.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) ../Rework/jaguar.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) ../gamedrive.v ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) ../cd_stream.sv ] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) ../debug_overlay.sv ] #set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) s2_hq_dac.v ] #set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) tb_old.v ] set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) ../Rework/ab8016a.v ] diff --git a/rtl/Rework/tom.v b/rtl/Rework/tom.v index 9a110f1..eb8f56a 100644 --- a/rtl/Rework/tom.v +++ b/rtl/Rework/tom.v @@ -89,6 +89,8 @@ module _tom output [10:3] atp, input turbo, input vintbugfix, + input ntsc, + input video_center, output wire hsl, output wire vsl ); @@ -850,6 +852,8 @@ _vid vid_inst .vey /* IN */ (vey), .vly /* IN */ (vly), .lock /* IN */ (lock), + .ntsc /* IN */ (ntsc), + .video_center /* IN */ (video_center), .start /* OUT */ (start), .dd /* OUT */ (dd), .lbufa /* OUT */ (lbufa), @@ -1402,4 +1406,3 @@ assign dr_15_12_oe = dr_gpu_oe | dr_vid_15_12_oe | dr_abus_oe | dr_ob_oe | dr_ob assign justify_out = (justify_gpu_oe ? justify_gpu_out : 1'b0) | (justify_mem_oe ? justify_mem_out : 1'b0) | (justify_ob_oe ? justify_ob_out : 1'b0); assign justify_oe = justify_gpu_oe | justify_mem_oe | justify_ob_oe; endmodule - diff --git a/rtl/Rework/vid.v b/rtl/Rework/vid.v index da3e590..9c2407b 100644 --- a/rtl/Rework/vid.v +++ b/rtl/Rework/vid.v @@ -42,6 +42,8 @@ module _vid input vey, input vly, input lock, + input ntsc, + input video_center, output start, output dd, output lbufa, @@ -628,8 +630,16 @@ begin end assign vbbeq = vbb[10:0] == vc[10:0]; assign vbeeq = vbe[10:0] == vc[10:0]; -assign vdbeq = vdb[10:0] == vc[10:0]; -assign vdeeq = vde[10:0] == vc[10:0]; +// Optional vertical centering tweak: +// move active video start earlier so top-only black is reduced and bottom headroom improves. +// keep VDE sentinel (all ones) untouched so "no explicit data end" behavior is preserved. +wire [10:0] vcenter_shift = ntsc ? 11'd16 : 11'd8; // NTSC: 8 lines, PAL: 4 lines (half-line units) +wire [10:0] vdb_shifted = (vdb[10:0] > vcenter_shift) ? (vdb[10:0] - vcenter_shift) : 11'd0; +wire [10:0] vde_shifted = (vde[10:0] > vcenter_shift) ? (vde[10:0] - vcenter_shift) : 11'd0; +wire [10:0] vdb_eff = video_center ? vdb_shifted : vdb[10:0]; +wire [10:0] vde_eff = (video_center && (vde[10:0] != 11'h7FF)) ? vde_shifted : vde[10:0]; +assign vdbeq = vdb_eff == vc[10:0]; +assign vdeeq = vde_eff == vc[10:0]; assign vebeq = veb[10:0] == vc[10:0]; assign veeeq = vee[10:0] == vc[10:0]; assign vseq = vs[10:0] == vc[10:0]; @@ -974,4 +984,3 @@ assign dr_out[11] = (dr_vc_oe & dr_vc_out[11]) | (dr_lph_oe & dr_lph_out[11]) | assign dr_11_0_oe = dr_15_12_oe | dr_test3r_oe; endmodule - diff --git a/rtl/cd_stream.sv b/rtl/cd_stream.sv new file mode 100644 index 0000000..08793d7 --- /dev/null +++ b/rtl/cd_stream.sv @@ -0,0 +1,1081 @@ +module jaguar_cd_stream +( + clk_sys, + reset, + bk_int, + DDRAM_DOUT_READY, + audbus_out, + aud_ce, + cd_stream_start, + img_size, + cd_hps_ack, + sd_buff_addr, + sd_buff_dout, + sd_buff_wr, + cd_hps_lba, + cd_hps_req, + cd_img_mounted, + cd_state_idle, + cd_session_count, + cd_toc_addr, + cd_toc_data, + cd_toc_wr, + cd_valid, + audbus_busy, + xwaitl, + aud_rd_trig, + lcnt, + clcnt, + dbg_cd_fmt, + dbg_cd_state, + dbg_cd_track, + dbg_cd_session, + dbg_cd_desc_index, + dbg_first_aud_addr0, + dbg_first_aud_addr1, + dbg_first_grant_seen, + dbg_first_grant_addr, + dbg_first_grant_file_addr, + dbg_first_grant_word, + dbg_cur_file_addr, + stream_q, + cd_boot +); + +input clk_sys; +input reset; +input bk_int; +input DDRAM_DOUT_READY; +input [29:0] audbus_out; +input aud_ce; +input cd_stream_start; +input [63:0] img_size; +input cd_hps_ack; +input [7:0] sd_buff_addr; +input [15:0] sd_buff_dout; +input sd_buff_wr; + +output [31:0] cd_hps_lba; +output cd_hps_req; +output cd_img_mounted; +output cd_state_idle; +output [7:0] cd_session_count; +output [9:0] cd_toc_addr; +output [15:0] cd_toc_data; +output cd_toc_wr; +output cd_valid; +output audbus_busy; +output xwaitl; +output aud_rd_trig; +output lcnt; +output clcnt; +output [1:0] dbg_cd_fmt; +output [4:0] dbg_cd_state; +output [7:0] dbg_cd_track; +output [7:0] dbg_cd_session; +output [7:0] dbg_cd_desc_index; +output [29:0] dbg_first_aud_addr0; +output [29:0] dbg_first_aud_addr1; +output dbg_first_grant_seen; +output [29:0] dbg_first_grant_addr; +output [29:0] dbg_first_grant_file_addr; +output [63:0] dbg_first_grant_word; +output [29:0] dbg_cur_file_addr; +output [63:0] stream_q; +output cd_boot; + +// This module owns the entire CD streaming path that used to live inside +// Jaguar.sv. It has three tightly related jobs: +// +// 1. Accept 16-bit sector data arriving from HPS and store it into the local +// dual-port ring buffer while preserving the byte order expected by the +// Jaguar CD path. +// 2. Stage static image metadata once per cd_stream_start, then parse TOC data +// from stable registers so mount-time bookkeeping never pollutes the live +// streaming cache. +// 3. Serve 64-bit words back to the Jaguar core using the same addressing, +// freshness checks, and format-specific byte-lane ordering as the original +// inlined implementation. +// +// The intent of this refactor is structural only: keep the established timing +// and state-machine behavior intact, but isolate the streaming/cache/parser code +// from the already crowded top-level module. +reg [31:0] cd_hps_lba; +reg cd_hps_req; +reg cd_img_mounted; +reg [2:0] cd_toc_type; +reg [15:0] cd_toc_data; +reg cd_toc_wr; + +reg [29:0] old_audbus_out; +reg old_aud_ce; +reg meta_active; +reg meta_is_jcd; +reg [1:0] meta_sector; +reg [31:0] meta_magic; +reg [1:0] dbg_first_aud_count; +reg [29:0] dbg_first_aud_addr0; +reg [29:0] dbg_first_aud_addr1; +reg dbg_first_grant_seen; +reg [29:0] dbg_first_grant_addr; +reg [29:0] dbg_first_grant_file_addr; +reg [63:0] dbg_first_grant_word; + +// xwaitl is the cart-side wait-state output when the CD stream is acting as the +// cartridge image. On a cache hit the read can complete immediately. On a miss, +// hold wait low until the requested 64-bit window becomes valid in the ring. +// +// This is especially important for JCD because the fixed metadata lives at the +// front of the file while the first data track can be far away. Without a real +// wait, the BIOS can sample stale metadata/cache data instead of the boot +// sector and incorrectly fall back to the audio player. +wire aud_rd_trig = aud_ce && ((audbus_out != old_audbus_out) || (!old_aud_ce)); +wire cd_rd_trig = cd_ce && ((cd_bus_out != old_audbus_out) || (!old_aud_ce)); +wire stream_idle = (cd_state == CD_STATE_IDLE) || cd_stream_start; +wire img_rd_trig = stream_idle ? aud_rd_trig : cd_rd_trig; +wire img_ce = stream_idle ? aud_ce : cd_ce; +reg xwaitl_latch; +assign xwaitl = xwaitl_latch; +always @(posedge clk_sys) +if (reset) begin + xwaitl_latch <= 1'b1; // De-assert on reset! + old_audbus_out <= 30'h112233; + old_aud_ce <= 1'b1; +end else begin + old_audbus_out <= stream_idle ? audbus_out : cd_bus_out; + old_aud_ce <= stream_idle ? aud_ce : cd_ce; + + + if (!cd_img_mounted) begin + xwaitl_latch <= 1'b1; + end else if (img_rd_trig) begin + xwaitl_latch <= cd_valid; + end else if (!xwaitl_latch && cd_valid) begin + xwaitl_latch <= 1'b1; + end +end + +localparam [5:0] CD_RING_DEPTH = 6'd32; +localparam [1:0] CD_FMT_UNKNOWN = 2'd0; +localparam [1:0] CD_FMT_CDI = 2'd1; +localparam [1:0] CD_FMT_JCD = 2'd2; +localparam [4:0] CD_STATE_IDLE = 5'd0; +localparam [4:0] CD_STATE_META_PREFETCH = 5'd1; +localparam [4:0] CD_STATE_CDI_TAIL_REQ = 5'd3; +localparam [4:0] CD_STATE_CDI_TAIL_READ = 5'd4; +localparam [4:0] CD_STATE_CDI_SESSIONS = 5'd5; +localparam [4:0] CD_STATE_CDI_TRACKS = 5'd6; +localparam [4:0] CD_STATE_CDI_FILENAME = 5'd7; +localparam [4:0] CD_STATE_CDI_PREGAP_LEN = 5'd8; +localparam [4:0] CD_STATE_CDI_START_TOTLEN = 5'd9; +localparam [4:0] CD_STATE_CDI_PREP_START = 5'd10; +localparam [4:0] CD_STATE_CDI_WRITE_START = 5'd11; +localparam [4:0] CD_STATE_CDI_WRITE_OFFSET = 5'd12; +localparam [4:0] CD_STATE_CDI_WRITE_LENGTH = 5'd13; +localparam [4:0] CD_STATE_CDI_WRITE_PREGAP = 5'd14; +localparam [4:0] CD_STATE_CDI_WRITE_SESSION = 5'd15; +localparam [4:0] CD_STATE_CDI_WRITE_END = 5'd16; +localparam [4:0] CD_STATE_CDI_TRACK_DONE = 5'd17; +localparam [4:0] CD_STATE_JCD_HEADER = 5'd18; +localparam [4:0] CD_STATE_JCD_DESC_HEAD = 5'd19; +localparam [4:0] CD_STATE_JCD_DESC_TAIL = 5'd20; +localparam [4:0] CD_STATE_JCD_PREP_START = 5'd21; +localparam [4:0] CD_STATE_JCD_WRITE_START = 5'd22; +localparam [4:0] CD_STATE_JCD_WRITE_OFFSET = 5'd23; +localparam [4:0] CD_STATE_JCD_WRITE_LENGTH = 5'd24; +localparam [4:0] CD_STATE_JCD_WRITE_PREGAP = 5'd25; +localparam [4:0] CD_STATE_JCD_WRITE_SESSION = 5'd26; +localparam [4:0] CD_STATE_JCD_WRITE_END = 5'd27; +localparam [4:0] CD_STATE_JCD_TRACK_DONE = 5'd28; +localparam [29:0] JCD_PAYLOAD_BASE = 30'h000600; +localparam [7:0] JCD_MAX_TRACKS = 8'd120; + +localparam [4:0] CD_STATE_EMIT_START = CD_STATE_CDI_WRITE_START; +localparam [4:0] CD_STATE_EMIT_OFFSET = CD_STATE_CDI_WRITE_OFFSET; +localparam [4:0] CD_STATE_EMIT_LENGTH = CD_STATE_CDI_WRITE_LENGTH; +localparam [4:0] CD_STATE_EMIT_PREGAP = CD_STATE_CDI_WRITE_PREGAP; +localparam [4:0] CD_STATE_EMIT_SESSION = CD_STATE_CDI_WRITE_SESSION; +localparam [4:0] CD_STATE_EMIT_END = CD_STATE_CDI_WRITE_END; +localparam [4:0] CD_STATE_EMIT_TRACK_DONE = CD_STATE_CDI_TRACK_DONE; + +// The ring buffer stores sectors in 64-bit read granules because Butch consumes +// streamed CD data in that width. HPS still arrives as 16-bit words, so the +// four RAMs preserve the existing lane packing and let the parser and stream +// reader share the same storage. +wire [29:0] imgbus_out; +wire jcd_stream_read = stream_idle && cd_img_mounted && (cd_fmt == CD_FMT_JCD); +wire [29:0] ringbus_out = jcd_stream_read ? (imgbus_out + JCD_PAYLOAD_BASE) : imgbus_out; +wire [10:0] cd_ring_rd_addr = {ringbus_out[13:9], ringbus_out[8:3]}; +wire [10:0] cd_ring_wr_addr = {cd_hps_lba[4:0], sd_buff_addr[7:2]}; + +dpram #(11,16) cdram_inst0 +( + .clock(clk_sys), + .address_a(cd_ring_rd_addr), + .q_a({cdram_dout[55:48],cdram_dout[63:56]}), + + .address_b(cd_ring_wr_addr), + .data_b(sd_buff_dout), + .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b00)) +); + +dpram #(11,16) cdram_inst1 +( + .clock(clk_sys), + .address_a(cd_ring_rd_addr), + .q_a({cdram_dout[39:32],cdram_dout[47:40]}), + + .address_b(cd_ring_wr_addr), + .data_b(sd_buff_dout), + .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b01)) +); + +dpram #(11,16) cdram_inst2 +( + .clock(clk_sys), + .address_a(cd_ring_rd_addr), + .q_a({cdram_dout[23:16],cdram_dout[31:24]}), + + .address_b(cd_ring_wr_addr), + .data_b(sd_buff_dout), + .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b10)) +); + +dpram #(11,16) cdram_inst3 +( + .clock(clk_sys), + .address_a(cd_ring_rd_addr), + .q_a({cdram_dout[7:0],cdram_dout[15:8]}), + + .address_b(cd_ring_wr_addr), + .data_b(sd_buff_dout), + .wren_b(bk_int & sd_buff_wr & cd_hps_ack & (sd_buff_addr[1:0] == 2'b11)) +); + +wire [63:0] cdram_dout; +wire audbus_busy = img_ce || img_rd_trig || load_state || meta_active; +reg load_state; +reg cd_ring_armed; +reg [20:0] cd_ring_base_lba; +reg [5:0] cd_ring_count; +wire [20:0] cd_ring_end_lba = cd_ring_base_lba + {15'h0, cd_ring_count}; +reg [31:0] load_cnt; +reg [31:0] max_load_cnt; +wire lcnt = max_load_cnt == load_cnt; +reg [31:0] cload_cnt; +reg [31:0] max_cload_cnt; +wire clcnt = max_cload_cnt == cload_cnt; +wire [5:0] cd_ring_target_depth = stream_idle ? CD_RING_DEPTH : 6'd1; +reg [4:0] cd_state; +reg [29:0] cd_size; +reg [29:0] cd_bus_out; +reg cd_ce; +assign imgbus_out = stream_idle ? audbus_out : cd_bus_out; +reg [2:0] cd_cnt; +reg [31:0] cd_header; +reg [1:0] cd_fmt; +reg [7:0] cd_sessions; +reg [7:0] cd_session; +wire [7:0] cd_session_count = (cd_sessions != 8'h00) ? cd_sessions : 8'h01; +reg [7:0] cd_tracks; +reg [7:0] cd_track; +reg [7:0] cd_desc_index; +reg [7:0] cd_jcd_track_first; +reg [7:0] cd_jcd_track_last; +reg [7:0] cd_jcd_desc_count; +reg [7:0] cd_jcd_start_min; +reg [7:0] cd_jcd_start_sec; +reg [7:0] cd_jcd_start_frame; +reg [7:0] cd_jcd_len_min; +reg [7:0] cd_jcd_len_sec; +reg [7:0] cd_jcd_len_frame; +reg [7:0] cd_jcd_lba_hi; +reg [7:0] cd_jcd_lba_mid; +reg [31:0] cd_jcd_data_off; +reg [7:0] cd_jcd_leadout_min; +reg [7:0] cd_jcd_leadout_sec; +reg [7:0] cd_jcd_leadout_frame; +reg [7:0] cd_jcd_prev_session; +reg [23:0] cd_jcd_start_tab [0:119]; +reg [23:0] cd_jcd_len_tab [0:119]; +reg [29:0] cd_jcd_off_tab [0:119]; +reg [7:0] cd_jcd_session_tab [0:119]; +reg [7:0] cd_jcd_track_tab [0:119]; +wire cd_jcd_last_desc = ((cd_desc_index + 8'h1) == cd_jcd_desc_count); +wire [7:0] cd_data = cdram_dout[8*(7-cd_bus_out[2:0]) +:8]; +reg [23:0] cd_pregap; +reg [29:4] cd_pregap_pos; +reg [31:0] cd_length; +reg [31:0] cd_startlba; +reg [31:0] cd_totlength; +reg [31:0] cd_start; +reg [31:0] cd_track_end; +reg [29:0] cd_file_offset; +reg cd_emit_calc_offset; +reg [19:0] cd_add1; // max lba fits in 19bits +reg [23:0] cd_tomsf; +reg do_tomsf; +reg [6:0] cd_min; +reg [5:0] cd_sec; +wire [6:0] cd_frame = cd_tomsf[6:0]; +wire [23:0] cd_msf = {1'b0, cd_min, 2'b0, cd_sec, 1'b0, cd_frame}; +wire [23:0] min_to_frames = 24'd4500; // 60*75 +wire [23:0] sec_to_frames = 24'd75; // 75 +reg [7:0] cd_tmp; +reg [29:0] cd_bus_add; +reg [3:0] cd_boot_sr; +reg cd_bus_size; +reg cd_bus_header; +reg old_msf; +reg djv2; +reg djv3; +wire [9:0] cd_toc_addr = {cd_track[6:0], cd_toc_type[2:0]}; +function [23:0] msf_to_frames24; + input [7:0] mins; + input [7:0] secs; + input [7:0] frms; + reg [23:0] mins_frames; + reg [23:0] secs_frames; +begin + // mins * 4500 = mins * (4096 + 256 + 128 + 16 + 4) + mins_frames = {mins,12'h000} + {mins,8'h00} + {mins,7'h00} + {mins,4'h0} + {mins,2'h0}; + // secs * 75 = secs * (64 + 8 + 2 + 1) + secs_frames = {secs,6'h00} + {secs,3'h0} + {secs,1'b0} + {16'h0000,secs}; + msf_to_frames24 = mins_frames + secs_frames + {16'h0000,frms}; +end +endfunction +wire [20:0] cd_file_lba = ringbus_out[29:9]; +wire cd_lba_in_ring = (cd_ring_count != 6'd0) && (cd_file_lba >= cd_ring_base_lba) && (cd_file_lba < cd_ring_end_lba); +wire cd_lba_loading = load_state && (cd_hps_lba[20:0] == cd_file_lba); +wire [7:0] cd_line_last_word = {ringbus_out[8:3], 2'b11}; +wire cd_fresh = !(cd_lba_loading && (cd_line_last_word >= sd_buff_addr[7:0])); +wire jcd_track_is_session_first = (cd_desc_index == 8'h00) || (cd_session != cd_jcd_prev_session); +wire jcd_track_is_disc_first = jcd_track_is_session_first && (cd_startlba == 32'h0000_0000); +wire [23:0] jcd_track_pregap = jcd_track_is_session_first ? 24'd150 : 24'd0; +wire [31:0] jcd_track_pregap_ext = {8'h00, jcd_track_pregap}; +wire [23:0] jcd_track_start_shift = jcd_track_is_disc_first ? 24'd150 : 24'd0; +wire [31:0] jcd_track_start_shift_ext = {8'h00, jcd_track_start_shift}; +wire cd_valid = cd_img_mounted && cd_lba_in_ring && cd_fresh; + +assign cd_boot = |cd_boot_sr; + +// This controller multiplexes two related state machines into one sequential +// process: +// - metadata parsing / TOC generation during mount and re-mount +// - cache fill / refill for streaming reads once Butch starts consuming data +always @(posedge clk_sys) begin + reg old_ack; + reg [20:0] lba_delta; + reg miss_request_now; + reg [31:0] jcd_cur_start_lba; + reg [31:0] jcd_cur_len_lba; + reg [29:0] jcd_cur_file_off; + reg [7:0] jcd_cur_session; + reg [7:0] jcd_cur_track; + reg [31:0] jcd_src_start_lba; + reg [31:0] jcd_prog_len_lba; + reg [31:0] jcd_total_len_lba; + reg jcd_emit_session_first; + reg jcd_emit_disc_first; + miss_request_now = 1'b0; + jcd_cur_start_lba = 32'h0; + jcd_cur_len_lba = 32'h0; + jcd_cur_file_off = 30'h0; + jcd_cur_session = 8'h0; + jcd_cur_track = 8'h0; + jcd_src_start_lba = 32'h0; + jcd_prog_len_lba = 32'h0; + jcd_total_len_lba = 32'h0; + jcd_emit_session_first = 1'b0; + jcd_emit_disc_first = 1'b0; + + cd_boot_sr <= {cd_boot_sr[2:0], 1'b0}; + + if (reset) begin + cd_boot_sr <= 4'd0; + load_state <= 1'b0; + cd_ring_armed <= 1'b0; + cd_ring_base_lba <= 21'h0; + cd_ring_count <= 6'h0; + cd_img_mounted <= 1'b0; + cd_hps_req <= 0; + cd_hps_lba[31:0] <= 32'h0; + load_cnt[31:0] <= 32'h0; + max_load_cnt[31:0] <= 32'h0; + cload_cnt[31:0] <= 32'h0; + max_cload_cnt[31:0] <= 32'h0; + cd_state <= CD_STATE_IDLE; + cd_size[29:0] <= 30'h0; + cd_fmt <= CD_FMT_UNKNOWN; + cd_sessions <= 8'h1; + cd_session <= 8'h0; + cd_toc_type[2:0] <= 3'h0; + cd_toc_data[15:0] <= 16'h0; + cd_toc_wr <= 0; + cd_desc_index <= 8'h0; + cd_jcd_track_first <= 8'h1; + cd_jcd_track_last <= 8'h1; + cd_jcd_desc_count <= 8'h0; + cd_jcd_start_min <= 8'h0; + cd_jcd_start_sec <= 8'h0; + cd_jcd_start_frame <= 8'h0; + cd_jcd_len_min <= 8'h0; + cd_jcd_len_sec <= 8'h0; + cd_jcd_len_frame <= 8'h0; + cd_jcd_lba_hi <= 8'h0; + cd_jcd_lba_mid <= 8'h0; + cd_jcd_data_off <= 32'h0; + cd_jcd_leadout_min <= 8'h0; + cd_jcd_leadout_sec <= 8'h0; + cd_jcd_leadout_frame <= 8'h0; + cd_jcd_prev_session <= 8'hFF; + cd_track_end <= 32'h0; + cd_file_offset <= 30'h0; + cd_emit_calc_offset <= 1'b0; + meta_active <= 1'b0; + meta_is_jcd <= 1'b0; + meta_sector <= 2'h0; + meta_magic <= 32'h0; + dbg_first_aud_count <= 2'h0; + dbg_first_aud_addr0 <= 30'h0; + dbg_first_aud_addr1 <= 30'h0; + dbg_first_grant_seen <= 1'b0; + dbg_first_grant_addr <= 30'h0; + dbg_first_grant_file_addr <= 30'h0; + dbg_first_grant_word <= 64'h0; + end + + if (cd_stream_start) begin + cd_boot_sr[0] <= 1'b1; + cd_img_mounted <= 1'b1; + cd_state <= CD_STATE_META_PREFETCH; + cd_size[29:0] <= img_size[29:0]; + load_state <= 1'b0; + cd_ring_armed <= 1'b0; + cd_ring_base_lba <= 21'h0; + cd_ring_count <= 6'h0; + cd_hps_req <= 1'b1; + cd_hps_lba[31:0] <= 32'h0; + load_cnt[31:0] <= 32'h0; + max_load_cnt[31:0] <= 32'h0; + cload_cnt[31:0] <= 32'h0; + max_cload_cnt[31:0] <= 32'h0; + cd_bus_out[29:0] <= 30'h0; + cd_cnt <= 3'h0; + cd_header <= 32'h0; + cd_fmt <= CD_FMT_UNKNOWN; + cd_sessions <= 8'h1; + cd_session <= 8'h0; + cd_tracks <= 8'h0; + cd_track <= 8'h1; + cd_desc_index <= 8'h0; + cd_jcd_track_first <= 8'h1; + cd_jcd_track_last <= 8'h1; + cd_jcd_desc_count <= 8'h0; + cd_jcd_start_min <= 8'h0; + cd_jcd_start_sec <= 8'h0; + cd_jcd_start_frame <= 8'h0; + cd_jcd_len_min <= 8'h0; + cd_jcd_len_sec <= 8'h0; + cd_jcd_len_frame <= 8'h0; + cd_jcd_lba_hi <= 8'h0; + cd_jcd_lba_mid <= 8'h0; + cd_jcd_data_off <= 32'h0; + cd_jcd_leadout_min <= 8'h0; + cd_jcd_leadout_sec <= 8'h0; + cd_jcd_leadout_frame <= 8'h0; + cd_jcd_prev_session <= 8'hFF; + cd_toc_type[2:0] <= 3'h0; + cd_toc_data[15:0] <= 16'h0; + cd_toc_wr <= 1'b0; + cd_pregap <= 24'h0; + cd_pregap_pos <= 26'h0; + cd_length <= 32'h0; + cd_startlba <= 32'h0; + cd_totlength <= 32'h0; + cd_start <= 32'h0; + cd_track_end <= 32'h0; + cd_file_offset <= 30'h0; + cd_emit_calc_offset <= 1'b0; + cd_add1 <= 20'h0; + cd_tomsf <= 24'h0; + do_tomsf <= 1'b0; + cd_min <= 7'h0; + cd_sec <= 6'h0; + cd_tmp <= 8'h0; + old_msf <= 1'b0; + meta_active <= 1'b1; + meta_is_jcd <= 1'b0; + meta_sector <= 2'h0; + meta_magic <= 32'h0; + dbg_first_aud_count <= 2'h0; + dbg_first_aud_addr0 <= 30'h0; + dbg_first_aud_addr1 <= 30'h0; + dbg_first_grant_seen <= 1'b0; + dbg_first_grant_addr <= 30'h0; + dbg_first_grant_file_addr <= 30'h0; + dbg_first_grant_word <= 64'h0; + end + + // Mount-time metadata rides through the same ring RAM the stream path already + // owns. To keep logic density under control, only the few fixed header bytes + // that matter globally are latched here; JCD descriptors are read back from + // the ring one byte at a time during the JCD parser walk. + if (meta_active && cd_hps_ack && sd_buff_wr) begin + if (meta_sector == 2'd0) begin + case (sd_buff_addr) + 8'h00: begin + meta_magic[7:0] <= sd_buff_dout[7:0]; + meta_magic[15:8] <= sd_buff_dout[15:8]; + end + 8'h01: begin + meta_magic[23:16] <= sd_buff_dout[7:0]; + meta_magic[31:24] <= sd_buff_dout[15:8]; + end + 8'h03: begin + cd_jcd_track_first <= (sd_buff_dout[7:0] != 8'h00) ? sd_buff_dout[7:0] : 8'h1; + cd_jcd_track_last <= sd_buff_dout[15:8]; + end + 8'h04: begin + cd_sessions <= (sd_buff_dout[7:0] != 8'h00) ? sd_buff_dout[7:0] : 8'h1; + cd_jcd_leadout_min <= sd_buff_dout[15:8]; + end + 8'h05: begin + cd_jcd_leadout_sec <= sd_buff_dout[7:0]; + cd_jcd_leadout_frame <= sd_buff_dout[15:8]; + end + default: ; + endcase + end + end + + cd_ce <= 0; + cd_toc_wr <= 0; + old_msf <= do_tomsf; + if (do_tomsf) begin + if (!old_msf) begin + cd_min <= 'h0; + cd_sec <= 'h0; + end else if (cd_tomsf >= min_to_frames) begin + cd_tomsf <= cd_tomsf - min_to_frames; + cd_min <= cd_min + 7'd1; + end else if (cd_tomsf >= sec_to_frames) begin + cd_tomsf <= cd_tomsf - sec_to_frames; + cd_sec <= cd_sec + 6'd1; + end else begin + do_tomsf <= 0; + end + end + if (!audbus_busy && !do_tomsf) begin + // Parser byte-source conventions: + // - cd_data is one byte selected from the 64-bit cache line by cd_bus_out[2:0]. + // - cd_cnt advances once per parser beat and is used as the byte index inside each state. + // - cd_bus_out updates at the end of the cycle, so each state uses the same + // "prime then consume" access pattern. + // + // Format references used for field mapping: + // - CDI container layout and traversal: CDIrip parser flow (session header, + // track blocks, tail footer). + // - JCD layout: resources/JCD_FILE_SPEC.txt (header 0x00..0x0B, then 12-byte + // descriptors starting at 0x0C). + cd_cnt <= cd_cnt + 3'h1; + cd_bus_header = 0; + cd_bus_size = 0; + cd_bus_add = 30'h0; + + if ((cd_state == CD_STATE_EMIT_START) || + (cd_state == CD_STATE_EMIT_OFFSET) || + (cd_state == CD_STATE_EMIT_LENGTH) || + (cd_state == CD_STATE_EMIT_PREGAP) || + (cd_state == CD_STATE_EMIT_SESSION) || + (cd_state == CD_STATE_EMIT_END) || + (cd_state == CD_STATE_EMIT_TRACK_DONE)) begin + if (cd_state == CD_STATE_EMIT_START) begin + cd_toc_type[2:0] <= 3'h0; + cd_toc_data[15:0] <= cd_msf[23:8]; + cd_tmp[7:0] <= cd_msf[7:0]; + cd_toc_wr <= 1; + cd_state <= CD_STATE_EMIT_OFFSET; + cd_tomsf <= cd_totlength[23:0]; + do_tomsf <= 1; + cd_cnt <= 3'h0; + end else if (cd_state == CD_STATE_EMIT_OFFSET) begin + if (cd_emit_calc_offset) begin + if (cd_cnt[1:0] == 2'b00) begin + cd_pregap_pos[29:4] <= {7'h0, cd_add1[18:0]}; + end else if (cd_cnt[1:0] == 2'b01) begin + cd_pregap_pos[29:5] <= cd_pregap_pos[29:5] + {6'h0, cd_add1[18:0]}; + end else if (cd_cnt[1:0] == 2'b10) begin + cd_pregap_pos[29:8] <= cd_pregap_pos[29:8] + {3'h0, cd_add1[18:0]}; + end else begin + cd_pregap_pos[29:11] <= cd_pregap_pos[29:11] + {cd_add1[18:0]}; + end + end else if (cd_cnt[1:0] == 2'b00) begin + cd_pregap_pos[29:4] <= cd_file_offset[29:4]; + end + if (cd_cnt[1:0] == 2'b11) begin + cd_toc_type[2:0] <= 3'h1; + cd_toc_data[15:8] <= cd_tmp[7:0]; + cd_toc_data[7:0] <= cd_msf[23:16]; + cd_toc_wr <= 1; + cd_state <= CD_STATE_EMIT_LENGTH; + end + end else if (cd_state == CD_STATE_EMIT_LENGTH) begin + cd_toc_type[2:0] <= 3'h2; + cd_toc_data[15:0] <= cd_msf[15:0]; + cd_toc_wr <= 1; + cd_state <= CD_STATE_EMIT_PREGAP; + cd_tomsf <= cd_pregap; + do_tomsf <= 1; + cd_add1[18:0] <= cd_add1[18:0] + cd_pregap[18:0]; + end else if (cd_state == CD_STATE_EMIT_PREGAP) begin + cd_toc_type[2:0] <= 3'h3; + cd_toc_data[15:0] <= cd_msf[15:0]; + cd_toc_wr <= 1; + cd_state <= CD_STATE_EMIT_SESSION; + cd_add1[18:0] <= cd_add1[18:0] + cd_length[18:0]; + cd_cnt <= 3'h0; + end else if (cd_state == CD_STATE_EMIT_SESSION) begin + if (cd_cnt[0] == 1'b0) begin + cd_toc_type[2:0] <= 3'h4; + // Legacy Butch TOC ingest expects session index in bits [15:9]. + // Using [15:8] shifts the value and collapses session 1 to 0. + cd_toc_data[15:9] <= cd_session[6:0]; + cd_toc_data[8] <= 1'b0; + cd_toc_data[7:0] <= {2'b00, cd_pregap_pos[29:24]}; + end else begin + cd_toc_type[2:0] <= 3'h5; + cd_toc_data[15:0] <= {cd_pregap_pos[23:8]}; + cd_state <= CD_STATE_EMIT_END; + cd_cnt <= 3'h0; + end + cd_toc_wr <= 1; + cd_tomsf <= cd_track_end[23:0]; + do_tomsf <= 1; + end else if (cd_state == CD_STATE_EMIT_END) begin + if (cd_cnt[0] == 1'b0) begin + cd_toc_type[2:0] <= 3'h6; + cd_toc_data[15:8] <= {cd_pregap_pos[7:4], 4'h0}; + cd_toc_data[7:0] <= cd_msf[23:16]; + end else begin + cd_toc_type[2:0] <= 3'h7; + cd_toc_data[15:0] <= cd_msf[15:0]; + cd_state <= CD_STATE_EMIT_TRACK_DONE; + end + cd_toc_wr <= 1; + end else if (cd_state == CD_STATE_EMIT_TRACK_DONE) begin + if (cd_fmt == CD_FMT_CDI) begin + cd_state <= CD_STATE_CDI_FILENAME; + cd_cnt <= 3'h0; + cd_track <= cd_track + 8'h1; + cd_bus_add = 30'h1C; + cd_ce <= 1; + if (cd_track == cd_tracks) begin + if ((cd_session + 8'h1) < cd_session_count) begin + cd_session <= cd_session + 8'h1; + cd_state <= CD_STATE_CDI_TRACKS; + cd_bus_add = djv2 ? 30'hC : 30'hD; + end else begin + cd_state <= CD_STATE_IDLE; + cd_img_mounted <= 1'b1; + cd_bus_add = 30'h0; + end + end + end else begin + cd_jcd_prev_session <= cd_session; + if ((cd_desc_index + 8'h1) < cd_jcd_desc_count) begin + cd_desc_index <= cd_desc_index + 8'h1; + cd_state <= CD_STATE_JCD_PREP_START; + cd_cnt <= 3'h0; + end else begin + cd_state <= CD_STATE_IDLE; + cd_img_mounted <= 1'b1; + cd_bus_add = 30'h0; + end + end + end + end else if (cd_fmt == CD_FMT_CDI) begin + // CDI parser: footer lookup, session-header traversal, then per-track + // canonical field decode before handing off to the shared emitter. + if (cd_state == CD_STATE_CDI_TAIL_REQ) begin + cd_bus_size = 1; + cd_bus_add = 30'h8; + cd_ce <= 1; + cd_cnt <= 3'h0; + cd_state <= CD_STATE_CDI_TAIL_READ; + end else if (cd_state == CD_STATE_CDI_TAIL_READ) begin + if (cd_cnt == 3'h0) begin + if (cd_data == 8'h6) begin + djv2 <= 1'b0; + djv3 <= 1'b0; + end else if (cd_data == 8'h5) begin + djv2 <= 1'b0; + djv3 <= 1'b1; + end else if (cd_data == 8'h4) begin + djv2 <= 1'b1; + djv3 <= 1'b0; + end else begin + cd_state <= CD_STATE_IDLE; + //cd_img_mounted <= 1'b0; + end + end + if ((cd_cnt == 3'h1) || (cd_cnt == 3'h2)) begin + if (cd_data != 8'h0) begin + cd_state <= CD_STATE_IDLE; + //cd_img_mounted <= 1'b0; + end + end + if ((cd_cnt == 3'h3) && (cd_data != 8'h80)) begin + cd_state <= CD_STATE_IDLE; + //cd_img_mounted <= 1'b0; + end + if (cd_cnt[2] == 1'b1) begin + cd_header[8*cd_cnt[1:0] +:8] <= cd_data; + end + cd_bus_add = 30'h1; + cd_ce <= 1; + if (cd_cnt == 3'h7) begin + cd_state <= CD_STATE_CDI_SESSIONS; + cd_bus_add = 30'h0; + end + end else if (cd_state == CD_STATE_CDI_SESSIONS) begin + if (cd_cnt[0] == 1'b0) begin + cd_bus_size = 1; + cd_bus_header = djv2 || djv3; + cd_bus_add = cd_header[29:0]; + end else begin + cd_sessions <= cd_data; + cd_state <= CD_STATE_CDI_TRACKS; + cd_bus_add = 30'h2; + end + cd_ce <= 1; + cd_session <= 8'h0; + cd_track <= 8'h1; + cd_tracks <= 8'h0; + cd_add1 <= 'h0; + end else if (cd_state == CD_STATE_CDI_TRACKS) begin + cd_tracks <= cd_tracks + cd_data; + cd_state <= CD_STATE_CDI_FILENAME; + cd_bus_add = 30'h1E; + cd_ce <= 1; + cd_cnt <= 3'h0; + end else if (cd_state == CD_STATE_CDI_FILENAME) begin + if (cd_cnt[0] == 1'b0) begin + cd_bus_add = cd_data; + end else begin + cd_bus_add = djv2 ? 30'h1A : 30'h22; + cd_state <= CD_STATE_CDI_PREGAP_LEN; + cd_ce <= 1; + cd_cnt <= 3'h0; + end + end else if (cd_state == CD_STATE_CDI_PREGAP_LEN) begin + if (cd_cnt[2] == 1'b0) begin + cd_pregap[8*cd_cnt[1:0] +:8] <= cd_data; + end else begin + cd_length[8*cd_cnt[1:0] +:8] <= cd_data; + end + cd_bus_add = 30'h1; + cd_ce <= 1; + if (cd_cnt == 3'h7) begin + cd_state <= CD_STATE_CDI_START_TOTLEN; + cd_cnt <= 3'h0; + cd_bus_add = 30'h17; + end + end else if (cd_state == CD_STATE_CDI_START_TOTLEN) begin + if (cd_cnt[2] == 1'b0) begin + cd_startlba[8*cd_cnt[1:0] +:8] <= cd_data; + end else begin + cd_totlength[8*cd_cnt[1:0] +:8] <= cd_data; + end + cd_bus_add = 30'h1; + cd_ce <= 1; + if (cd_cnt == 3'h7) begin + cd_state <= CD_STATE_CDI_PREP_START; + cd_cnt <= 3'h0; + cd_bus_add = djv2 ? 30'h32 : 30'h89; + end + end else if (cd_state == CD_STATE_CDI_PREP_START) begin + cd_state <= CD_STATE_EMIT_START; + cd_emit_calc_offset <= 1'b1; + cd_file_offset <= 30'h0; + cd_start <= cd_startlba + cd_pregap; + cd_track_end <= cd_startlba + cd_pregap + cd_length; + cd_tomsf <= cd_startlba[23:0] + cd_pregap; + do_tomsf <= 1; + end + end else if (cd_fmt == CD_FMT_JCD) begin + // JCD parser: fixed header fields were already latched during sector 0 + // capture. Each 12-byte descriptor fills the same canonical track fields + // used by CDI, then hands off to the shared emitter. + if (cd_state == CD_STATE_JCD_HEADER) begin + if (cd_jcd_track_last == 8'h00) begin + cd_state <= CD_STATE_IDLE; + //cd_img_mounted <= 1'b0; + end else begin + if (cd_jcd_track_last >= cd_jcd_track_first) begin + cd_jcd_desc_count <= (cd_jcd_track_last - cd_jcd_track_first) + 8'h1; + end else begin + cd_jcd_desc_count <= cd_jcd_track_last; + end + cd_state <= CD_STATE_JCD_DESC_HEAD; + cd_cnt <= 3'h0; + cd_desc_index <= 8'h0; + cd_track <= cd_jcd_track_first; + cd_session <= 8'h0; + cd_add1 <= 20'h0; + cd_bus_header = 1; + cd_bus_add = 30'hC; + cd_ce <= 1; + end + end else if (cd_state == CD_STATE_JCD_DESC_HEAD) begin + case (cd_cnt) + 3'h0: cd_track <= cd_data; + 3'h1: cd_jcd_start_min <= cd_data; + 3'h2: cd_jcd_start_sec <= cd_data; + 3'h3: cd_jcd_start_frame <= cd_data; + 3'h4: cd_session <= cd_data; + 3'h5: cd_jcd_len_min <= cd_data; + 3'h6: cd_jcd_len_sec <= cd_data; + 3'h7: begin + cd_jcd_len_frame <= cd_data; + cd_state <= CD_STATE_JCD_DESC_TAIL; + cd_cnt <= 3'h0; + end + default: ; + endcase + cd_ce <= 1; + cd_bus_add = 30'h1; + end else if (cd_state == CD_STATE_JCD_DESC_TAIL) begin + case (cd_cnt) + 3'h1: cd_jcd_lba_hi <= cd_data; + 3'h2: cd_jcd_lba_mid <= cd_data; + 3'h3: begin + if (({8'h00, cd_jcd_lba_hi, cd_jcd_lba_mid, cd_data} << 9) >= JCD_PAYLOAD_BASE) begin + // JCD descriptor +0x09..+0x0B points at the already-converted + // payload inside the JCD container. The converter's "+2" source + // bias was applied when the file was created; re-applying it here + // is wrong and becomes catastrophic because Butch stores the track + // offset in 16-byte granularity. Keep the TOC offset aligned to the + // true 0x200-byte payload sector start and let the normal JCD lane + // shuffle present the expected logical bytes on the bus. + cd_jcd_data_off <= (({8'h00, cd_jcd_lba_hi, cd_jcd_lba_mid, cd_data}) << 9) - JCD_PAYLOAD_BASE; + end else begin + cd_jcd_data_off <= 32'h0; + end + if (cd_desc_index < JCD_MAX_TRACKS) begin + cd_jcd_track_tab[cd_desc_index] <= cd_track; + cd_jcd_session_tab[cd_desc_index] <= cd_session; + cd_jcd_start_tab[cd_desc_index] <= msf_to_frames24(cd_jcd_start_min, cd_jcd_start_sec, cd_jcd_start_frame); + cd_jcd_len_tab[cd_desc_index] <= msf_to_frames24(cd_jcd_len_min, cd_jcd_len_sec, cd_jcd_len_frame); + if (({8'h00, cd_jcd_lba_hi, cd_jcd_lba_mid, cd_data} << 9) >= JCD_PAYLOAD_BASE) begin + cd_jcd_off_tab[cd_desc_index] <= (({8'h00, cd_jcd_lba_hi, cd_jcd_lba_mid, cd_data}) << 9) - JCD_PAYLOAD_BASE; + end else begin + cd_jcd_off_tab[cd_desc_index] <= 30'h0; + end + end + if ((cd_desc_index + 8'h1) < cd_jcd_desc_count) begin + cd_desc_index <= cd_desc_index + 8'h1; + cd_state <= CD_STATE_JCD_DESC_HEAD; + cd_cnt <= 3'h0; + cd_bus_add = 30'h1; + cd_ce <= 1; + end else begin + cd_desc_index <= 8'h0; + cd_jcd_prev_session <= 8'hFF; + cd_state <= CD_STATE_JCD_PREP_START; + cd_cnt <= 3'h0; + end + end + default: ; + endcase + if (cd_cnt != 3'h3) begin + cd_ce <= 1; + cd_bus_add = 30'h1; + end + end else if (cd_state == CD_STATE_JCD_PREP_START) begin + jcd_cur_track = cd_jcd_track_tab[cd_desc_index]; + jcd_cur_session = cd_jcd_session_tab[cd_desc_index]; + jcd_cur_start_lba = {8'h00, cd_jcd_start_tab[cd_desc_index]}; + jcd_cur_len_lba = {8'h00, cd_jcd_len_tab[cd_desc_index]}; + jcd_cur_file_off = cd_jcd_off_tab[cd_desc_index]; + jcd_emit_session_first = (cd_desc_index == 8'h00) || + (jcd_cur_session != cd_jcd_session_tab[cd_desc_index - 8'h1]); + jcd_emit_disc_first = jcd_emit_session_first && (jcd_cur_start_lba == 32'h0000_0000); + + // cd2jcd stores descriptor starts as: + // - track 1: 00:00:00 + // - later tracks: source_start + 1 frame + // Convert back to source-style absolute starts so JCD TOC timing + // tracks CDI behavior at Butch. + if (jcd_emit_disc_first) begin + jcd_src_start_lba = 32'h0; + end else if (jcd_cur_start_lba != 32'h0) begin + jcd_src_start_lba = jcd_cur_start_lba - 32'h1; + end else begin + jcd_src_start_lba = 32'h0; + end + + // Descriptor-native model: + // Use descriptor length and offset as authoritative values and keep + // the session-first 150-frame pregap policy for Butch TOC generation. + jcd_prog_len_lba = jcd_cur_len_lba; + jcd_total_len_lba = jcd_cur_len_lba + (jcd_emit_session_first ? 32'd150 : 32'd0); + + cd_track <= jcd_cur_track; + cd_session <= jcd_cur_session; + cd_startlba <= jcd_src_start_lba; + cd_file_offset <= jcd_cur_file_off; + cd_totlength <= jcd_total_len_lba; + cd_length <= jcd_prog_len_lba; + cd_pregap <= jcd_emit_session_first ? 24'd150 : 24'd0; + cd_start <= jcd_src_start_lba + (jcd_emit_session_first ? 32'd150 : 32'd0); + cd_track_end <= jcd_src_start_lba + (jcd_emit_session_first ? 32'd150 : 32'd0) + jcd_prog_len_lba; + cd_state <= CD_STATE_EMIT_START; + cd_emit_calc_offset <= 1'b0; + cd_tomsf <= jcd_src_start_lba[23:0] + (jcd_emit_session_first ? 24'd150 : 24'd0); + do_tomsf <= 1; + end + end + + cd_bus_out[29:0] <= cd_bus_header ? cd_bus_add[29:0] : + cd_bus_size ? (cd_size[29:0] - cd_bus_add[29:0]) : + (cd_bus_out[29:0] + cd_bus_add[29:0]); + end +// 3 lbatomsf(startlba+pregap) +// 3 lbatomsf(length) +// 2 0 (pregap) +// 1 session index (0-based) +// 4 troffset = position + pregap*2352 +// 3 lbatomsf(sessend = startlba+pregap+length) + // Latch the first couple of mounted data-read addresses after each stream + // start. Later VLM traffic can move the live BUS display far away from the + // initial boot probe, so keeping these values stable makes it easier to see + // where the BIOS first tried to read game data. + if (cd_img_mounted && aud_rd_trig && (dbg_first_aud_count != 2'h2)) begin + if (dbg_first_aud_count == 2'h0) begin + dbg_first_aud_addr0 <= audbus_out; + end else begin + dbg_first_aud_addr1 <= audbus_out; + end + dbg_first_aud_count <= dbg_first_aud_count + 2'h1; + end + if (cd_img_mounted && !dbg_first_grant_seen && + (((img_rd_trig && cd_valid) || (!xwaitl_latch && cd_valid)))) begin + dbg_first_grant_seen <= 1'b1; + dbg_first_grant_addr <= imgbus_out; + dbg_first_grant_file_addr <= ringbus_out; + dbg_first_grant_word <= cdram_q_stream_final; + end + + old_ack <= cd_hps_ack; + + if (~old_ack && cd_hps_ack) begin + cd_hps_req <= 1'b0; + end + + // The first metadata sector is always fetched outside the ring buffer so the + // mount path can identify the image type without perturbing stream cache + // state. If it is JCD, fetch the rest of the fixed 0x600-byte metadata area + // (sectors 1 and 2) before starting the parser. + if (meta_active && old_ack && ~cd_hps_ack) begin + if (!meta_is_jcd) begin + if (meta_magic == 32'h0044434A) begin + cd_fmt <= CD_FMT_JCD; + meta_is_jcd <= 1'b1; + meta_sector <= 2'd1; + cd_hps_lba <= 32'h1; + cd_hps_req <= 1'b1; + end else begin + meta_active <= 1'b0; + meta_is_jcd <= 1'b0; + meta_sector <= 2'h0; + cd_fmt <= CD_FMT_CDI; + cd_state <= CD_STATE_CDI_TAIL_REQ; + cd_cnt <= 3'h0; + cd_bus_out <= 30'h0; + end + end else if (meta_sector < 2'd2) begin + meta_sector <= meta_sector + 2'd1; + cd_hps_lba <= ({30'h0, meta_sector} + 32'd1); + cd_hps_req <= 1'b1; + end else begin + meta_active <= 1'b0; + cd_state <= CD_STATE_JCD_HEADER; + cd_cnt <= 3'h0; + cd_bus_out <= 30'h4; + end + end else if (load_state && old_ack && ~cd_hps_ack) begin + load_state <= 1'b0; + if ((cd_ring_count < CD_RING_DEPTH) && (cd_hps_lba[20:0] == cd_ring_end_lba)) begin + cd_ring_count <= cd_ring_count + 6'd1; + end + end + + // Mounted playback always uses the ring. During mount, only the CDI parser + // should be allowed to trigger cache-miss servicing before cd_img_mounted + // goes high. JCD mount parsing reads only the prefetched metadata bytes that + // were already written into the ring and should not initiate new misses. + if (img_rd_trig && (cd_img_mounted || ((cd_fmt == CD_FMT_CDI) && !stream_idle))) begin + load_cnt[31:0] <= 32'h0; + cload_cnt[31:0] <= 32'h0; + cd_ring_armed <= 1'b1; + if (!cd_lba_in_ring) begin + cd_ring_base_lba <= cd_file_lba; + cd_ring_count <= 6'd0; + if (!load_state) begin + cd_hps_lba <= {11'h000, cd_file_lba}; + cd_hps_req <= 1'b1; + load_state <= 1'b1; + miss_request_now = 1'b1; + end + end else if (cd_file_lba != cd_ring_base_lba) begin + lba_delta = cd_file_lba - cd_ring_base_lba; + cd_ring_base_lba <= cd_file_lba; + // Saturate window shrink on forward base moves to avoid modulo underflow + // when the consumer jumps beyond current cache depth. + if (lba_delta >= {15'h0000, cd_ring_count}) begin + cd_ring_count <= 6'd0; + end else begin + cd_ring_count <= cd_ring_count - lba_delta[5:0]; + end + end + end + + if (cd_ring_armed && !cd_lba_in_ring) begin + load_cnt <= load_cnt + 1'd1; + if (load_cnt > max_load_cnt) begin + max_load_cnt <= load_cnt; + end + end + + if (load_state || (cd_hps_req && !meta_active)) begin + cload_cnt <= cload_cnt + 1'd1; + if (cload_cnt > max_cload_cnt) begin + max_cload_cnt <= cload_cnt; + end + end + + if (cd_img_mounted && cd_ring_armed && !load_state && !miss_request_now && (cd_ring_count < cd_ring_target_depth)) begin + cd_hps_lba <= {11'h000, cd_ring_end_lba}; + cd_hps_req <= 1'b1; + load_state <= 1'b1; + end + +end + +// Present cached data in the exact 64-bit lane order the top level previously +// exposed. JCD needs one extra shuffle here because its payload packing differs +// from CDI after the metadata/header region. +wire [63:0] cdram_q_stream = {cdram_dout[31:00],cdram_dout[63:32]}; +wire [63:0] cdram_q_stream_jcd = {cdram_q_stream[47:32], cdram_q_stream[63:48], cdram_q_stream[15:0], cdram_q_stream[31:16]}; +wire [63:0] cdram_q_stream_final = (cd_fmt == CD_FMT_JCD) ? cdram_q_stream_jcd : cdram_q_stream; +assign cd_state_idle = (cd_state == CD_STATE_IDLE); +assign dbg_cd_fmt = cd_fmt; +assign dbg_cd_state = cd_state; +assign dbg_cd_track = cd_track; +assign dbg_cd_session = cd_session; +assign dbg_cd_desc_index = cd_desc_index; +assign dbg_cur_file_addr = ringbus_out; +assign stream_q = cdram_q_stream_final; + +endmodule diff --git a/rtl/debug_overlay.sv b/rtl/debug_overlay.sv new file mode 100644 index 0000000..7567540 --- /dev/null +++ b/rtl/debug_overlay.sv @@ -0,0 +1,908 @@ +module jaguar_debug_overlay +( + input logic clk_sys, + input logic ce_pix, + input logic reset, + input logic enable, + input logic hblank, + input logic vblank, + input logic [7:0] in_r, + input logic [7:0] in_g, + input logic [7:0] in_b, + input logic cd_drive_en, + input logic cd_img_mounted, + input logic cd_inserted, + input logic cd_valid, + input logic [1:0] cd_fmt, + input logic [4:0] cd_state, + input logic [7:0] cd_track, + input logic [7:0] cd_session, + input logic [7:0] cd_desc_index, + input logic [7:0] cd_session_count, + input logic cd_toc_wr, + input logic [9:0] cd_toc_addr, + input logic [15:0] cd_toc_data, + input logic cd_hps_req, + input logic cd_hps_ack, + input logic xwaitl, + input logic cd_stream_boot_pending, + input logic cd_state_idle_dbg, + input logic xresetlp_dbg, + input logic xresetl_dbg, + input logic bootcopy_active, + input logic [31:0] cd_hps_lba, + input logic [31:0] audbus_out_dbg, + input logic butch_aud_sess, + input logic [6:0] butch_cue_tracks, + input logic [6:0] butch_aud_tracks, + input logic [6:0] butch_dat_track, + input logic [7:0] butch_dsa_sessions, + input logic butch_sess1_valid, + input logic [15:0] butch_last_ds, + input logic [7:0] butch_last_err, + input logic [6:0] butch_track_idx, + input logic [6:0] butch_cues_addr, + input logic [6:0] butch_cuet_addr, + input logic [15:0] butch_resp_54, + input logic [39:0] butch_toc0, + input logic [39:0] butch_toc1, + input logic [15:0] butch_spin, + input logic [15:0] butch_ltoc0, + input logic [15:0] butch_ltoc1, + input logic [29:0] first_aud_addr0, + input logic [29:0] first_aud_addr1, + input logic first_grant_seen, + input logic [6:0] first_grant_track, + input logic [6:0] first_grant_cuet, + input logic [29:0] first_grant_addr, + input logic [29:0] first_grant_file_addr, + input logic [63:0] first_grant_word, + input logic [63:0] stream_q_dbg, + output logic [7:0] out_r, + output logic [7:0] out_g, + output logic [7:0] out_b +); + + // This is intentionally simple and self-contained: + // - It watches the existing pixel cadence (`ce_pix`) and blanking signals. + // - It reconstructs an active-video X/Y coordinate in the same clock domain. + // - It renders a compact fixed text block using an internal 8x8 bitmap font. + // - It overlays useful CD state so image-format and mount issues can be seen + // immediately on screen without external probes. + // + // The text map is fixed on purpose. Editing the `char_code` case below is the + // low-risk way to swap in different debug fields later. + + localparam int BOX_X = 8; + localparam int BOX_Y = 8; + localparam int CHAR_W = 8; + localparam int CHAR_H = 8; + localparam int TEXT_COLS = 32; + localparam int TEXT_ROWS = 15; + localparam int BOX_W = TEXT_COLS * CHAR_W; + localparam int BOX_H = TEXT_ROWS * CHAR_H; + + logic [9:0] pix_x; + logic [9:0] pix_y; + logic old_hblank; + logic old_vblank; + logic toc_seen; + logic [9:0] toc_addr_last; + logic [15:0] toc_data_last; + logic [15:0] track2_toc [0:7]; + integer track2_i; + + wire frame_start = old_vblank && !vblank; + wire line_start = old_hblank && !hblank; + + always_ff @(posedge clk_sys) begin + if (reset) begin + pix_x <= 10'd0; + pix_y <= 10'd0; + old_hblank <= 1'b1; + old_vblank <= 1'b1; + toc_seen <= 1'b0; + toc_addr_last <= 10'h000; + toc_data_last <= 16'h0000; + for (track2_i = 0; track2_i < 8; track2_i = track2_i + 1) begin + track2_toc[track2_i] <= 16'h0000; + end + end else begin + if (cd_toc_wr) begin + toc_seen <= 1'b1; + toc_addr_last <= cd_toc_addr; + toc_data_last <= cd_toc_data; + if (cd_toc_addr == 10'h008) begin + for (track2_i = 0; track2_i < 8; track2_i = track2_i + 1) begin + track2_toc[track2_i] <= 16'h0000; + end + end + if (cd_toc_addr[9:3] == 7'h02) begin + track2_toc[cd_toc_addr[2:0]] <= cd_toc_data; + end + end + + if (ce_pix) begin + old_hblank <= hblank; + old_vblank <= vblank; + + if (frame_start) begin + pix_x <= 10'd0; + pix_y <= 10'd0; + end else if (line_start) begin + pix_x <= 10'd0; + if (!vblank) pix_y <= pix_y + 10'd1; + end else if (!hblank && !vblank) begin + pix_x <= pix_x + 10'd1; + end + end + end + end + + function automatic [7:0] bit_char(input logic bit_value); + bit_char = bit_value ? "1" : "0"; + endfunction + + function automatic [7:0] hex_char(input logic [3:0] nibble); + case (nibble) + 4'h0: hex_char = "0"; + 4'h1: hex_char = "1"; + 4'h2: hex_char = "2"; + 4'h3: hex_char = "3"; + 4'h4: hex_char = "4"; + 4'h5: hex_char = "5"; + 4'h6: hex_char = "6"; + 4'h7: hex_char = "7"; + 4'h8: hex_char = "8"; + 4'h9: hex_char = "9"; + 4'hA: hex_char = "A"; + 4'hB: hex_char = "B"; + 4'hC: hex_char = "C"; + 4'hD: hex_char = "D"; + 4'hE: hex_char = "E"; + default: hex_char = "F"; + endcase + endfunction + + function automatic [7:0] font_row(input logic [7:0] ch, input logic [2:0] row); + begin + font_row = 8'h00; + case (ch) + " ": font_row = 8'h00; + ":": case (row) + 3'd1: font_row = 8'h18; + 3'd2: font_row = 8'h18; + 3'd5: font_row = 8'h18; + 3'd6: font_row = 8'h18; + default: font_row = 8'h00; + endcase + "0": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h6E; + 3'd3: font_row = 8'h76; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "1": case (row) + 3'd0: font_row = 8'h18; + 3'd1: font_row = 8'h38; + 3'd2: font_row = 8'h18; + 3'd3: font_row = 8'h18; + 3'd4: font_row = 8'h18; + 3'd5: font_row = 8'h18; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "2": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h06; + 3'd3: font_row = 8'h0C; + 3'd4: font_row = 8'h18; + 3'd5: font_row = 8'h30; + 3'd6: font_row = 8'h7E; + default: font_row = 8'h00; + endcase + "3": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h06; + 3'd3: font_row = 8'h1C; + 3'd4: font_row = 8'h06; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "4": case (row) + 3'd0: font_row = 8'h0C; + 3'd1: font_row = 8'h1C; + 3'd2: font_row = 8'h3C; + 3'd3: font_row = 8'h6C; + 3'd4: font_row = 8'h7E; + 3'd5: font_row = 8'h0C; + 3'd6: font_row = 8'h0C; + default: font_row = 8'h00; + endcase + "5": case (row) + 3'd0: font_row = 8'h7E; + 3'd1: font_row = 8'h60; + 3'd2: font_row = 8'h7C; + 3'd3: font_row = 8'h06; + 3'd4: font_row = 8'h06; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "6": case (row) + 3'd0: font_row = 8'h1C; + 3'd1: font_row = 8'h30; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "7": case (row) + 3'd0: font_row = 8'h7E; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h06; + 3'd3: font_row = 8'h0C; + 3'd4: font_row = 8'h18; + 3'd5: font_row = 8'h18; + 3'd6: font_row = 8'h18; + default: font_row = 8'h00; + endcase + "8": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h3C; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "9": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h3E; + 3'd4: font_row = 8'h06; + 3'd5: font_row = 8'h0C; + 3'd6: font_row = 8'h38; + default: font_row = 8'h00; + endcase + "A": case (row) + 3'd0: font_row = 8'h18; + 3'd1: font_row = 8'h3C; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h66; + 3'd4: font_row = 8'h7E; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h66; + default: font_row = 8'h00; + endcase + "B": case (row) + 3'd0: font_row = 8'h7C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h7C; + default: font_row = 8'h00; + endcase + "C": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h60; + 3'd4: font_row = 8'h60; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "D": case (row) + 3'd0: font_row = 8'h78; + 3'd1: font_row = 8'h6C; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h66; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h6C; + 3'd6: font_row = 8'h78; + default: font_row = 8'h00; + endcase + "E": case (row) + 3'd0: font_row = 8'h7E; + 3'd1: font_row = 8'h60; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h60; + 3'd5: font_row = 8'h60; + 3'd6: font_row = 8'h7E; + default: font_row = 8'h00; + endcase + "F": case (row) + 3'd0: font_row = 8'h7E; + 3'd1: font_row = 8'h60; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h60; + 3'd5: font_row = 8'h60; + 3'd6: font_row = 8'h60; + default: font_row = 8'h00; + endcase + "I": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h18; + 3'd2: font_row = 8'h18; + 3'd3: font_row = 8'h18; + 3'd4: font_row = 8'h18; + 3'd5: font_row = 8'h18; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "L": case (row) + 3'd0: font_row = 8'h60; + 3'd1: font_row = 8'h60; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h60; + 3'd4: font_row = 8'h60; + 3'd5: font_row = 8'h60; + 3'd6: font_row = 8'h7E; + default: font_row = 8'h00; + endcase + "M": case (row) + 3'd0: font_row = 8'h63; + 3'd1: font_row = 8'h77; + 3'd2: font_row = 8'h7F; + 3'd3: font_row = 8'h6B; + 3'd4: font_row = 8'h63; + 3'd5: font_row = 8'h63; + 3'd6: font_row = 8'h63; + default: font_row = 8'h00; + endcase + "P": case (row) + 3'd0: font_row = 8'h7C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h60; + 3'd5: font_row = 8'h60; + 3'd6: font_row = 8'h60; + default: font_row = 8'h00; + endcase + "Q": case (row) + 3'd0: font_row = 8'h3C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h66; + 3'd4: font_row = 8'h6E; + 3'd5: font_row = 8'h3C; + 3'd6: font_row = 8'h0E; + default: font_row = 8'h00; + endcase + "R": case (row) + 3'd0: font_row = 8'h7C; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h7C; + 3'd4: font_row = 8'h6C; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h66; + default: font_row = 8'h00; + endcase + "S": case (row) + 3'd0: font_row = 8'h3E; + 3'd1: font_row = 8'h60; + 3'd2: font_row = 8'h60; + 3'd3: font_row = 8'h3C; + 3'd4: font_row = 8'h06; + 3'd5: font_row = 8'h06; + 3'd6: font_row = 8'h7C; + default: font_row = 8'h00; + endcase + "T": case (row) + 3'd0: font_row = 8'h7E; + 3'd1: font_row = 8'h18; + 3'd2: font_row = 8'h18; + 3'd3: font_row = 8'h18; + 3'd4: font_row = 8'h18; + 3'd5: font_row = 8'h18; + 3'd6: font_row = 8'h18; + default: font_row = 8'h00; + endcase + "U": case (row) + 3'd0: font_row = 8'h66; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h66; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h3C; + default: font_row = 8'h00; + endcase + "V": case (row) + 3'd0: font_row = 8'h66; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h66; + 3'd3: font_row = 8'h66; + 3'd4: font_row = 8'h66; + 3'd5: font_row = 8'h3C; + 3'd6: font_row = 8'h18; + default: font_row = 8'h00; + endcase + "W": case (row) + 3'd0: font_row = 8'h63; + 3'd1: font_row = 8'h63; + 3'd2: font_row = 8'h63; + 3'd3: font_row = 8'h6B; + 3'd4: font_row = 8'h7F; + 3'd5: font_row = 8'h77; + 3'd6: font_row = 8'h63; + default: font_row = 8'h00; + endcase + "X": case (row) + 3'd0: font_row = 8'h66; + 3'd1: font_row = 8'h66; + 3'd2: font_row = 8'h3C; + 3'd3: font_row = 8'h18; + 3'd4: font_row = 8'h3C; + 3'd5: font_row = 8'h66; + 3'd6: font_row = 8'h66; + default: font_row = 8'h00; + endcase + default: font_row = 8'h00; + endcase + end + endfunction + + logic in_box; + logic [5:0] text_col; + logic [3:0] text_row; + logic [2:0] glyph_x; + logic [2:0] glyph_y; + logic [7:0] char_code; + logic [7:0] glyph_bits; + logic text_pixel; + + always @* begin + in_box = enable && + !hblank && + !vblank && + (pix_x >= BOX_X) && + (pix_x < (BOX_X + BOX_W)) && + (pix_y >= BOX_Y) && + (pix_y < (BOX_Y + BOX_H)); + + text_col = 6'd0; + text_row = 4'd0; + glyph_x = 3'd0; + glyph_y = 3'd0; + char_code = " "; + glyph_bits = 8'h00; + text_pixel = 1'b0; + + if (in_box) begin + text_col = (pix_x - BOX_X) >> 3; + text_row = (pix_y - BOX_Y) >> 3; + glyph_x = (pix_x - BOX_X) & 10'd7; + glyph_y = (pix_y - BOX_Y) & 10'd7; + + case (text_row) + 4'd0: begin + case (text_col) + 6'd0: char_code = "C"; + 6'd1: char_code = "D"; + 6'd2: char_code = " "; + 6'd3: char_code = "D"; + 6'd4: char_code = ":"; + 6'd5: char_code = bit_char(cd_drive_en); + 6'd6: char_code = " "; + 6'd7: char_code = "M"; + 6'd8: char_code = ":"; + 6'd9: char_code = bit_char(cd_img_mounted); + 6'd10: char_code = " "; + 6'd11: char_code = "I"; + 6'd12: char_code = ":"; + 6'd13: char_code = bit_char(cd_inserted); + 6'd14: char_code = " "; + 6'd15: char_code = "V"; + 6'd16: char_code = ":"; + 6'd17: char_code = bit_char(cd_valid); + 6'd18: char_code = " "; + 6'd19: char_code = "F"; + 6'd20: char_code = ":"; + 6'd21: char_code = hex_char({2'b00, cd_fmt}); + 6'd22: char_code = " "; + 6'd23: char_code = "T"; + 6'd24: char_code = ":"; + 6'd25: char_code = hex_char({3'b000, cd_state[4]}); + 6'd26: char_code = hex_char(cd_state[3:0]); + default: char_code = " "; + endcase + end + 4'd1: begin + case (text_col) + 6'd0: char_code = "S"; + 6'd1: char_code = ":"; + 6'd2: char_code = hex_char(cd_session_count[7:4]); + 6'd3: char_code = hex_char(cd_session_count[3:0]); + 6'd4: char_code = " "; + 6'd5: char_code = "Q"; + 6'd6: char_code = ":"; + 6'd7: char_code = bit_char(cd_hps_req); + 6'd8: char_code = " "; + 6'd9: char_code = "A"; + 6'd10: char_code = ":"; + 6'd11: char_code = bit_char(cd_hps_ack); + 6'd12: char_code = " "; + 6'd13: char_code = "X"; + 6'd14: char_code = ":"; + 6'd15: char_code = bit_char(xwaitl); + 6'd16: char_code = " "; + 6'd17: char_code = "P"; + 6'd18: char_code = ":"; + 6'd19: char_code = bit_char(cd_stream_boot_pending); + default: char_code = " "; + endcase + end + 4'd2: begin + case (text_col) + 6'd0: char_code = "T"; + 6'd1: char_code = "R"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(cd_track[7:4]); + 6'd4: char_code = hex_char(cd_track[3:0]); + 6'd5: char_code = " "; + 6'd6: char_code = "S"; + 6'd7: char_code = "S"; + 6'd8: char_code = ":"; + 6'd9: char_code = hex_char(cd_session[7:4]); + 6'd10: char_code = hex_char(cd_session[3:0]); + 6'd11: char_code = " "; + 6'd12: char_code = "D"; + 6'd13: char_code = "I"; + 6'd14: char_code = ":"; + 6'd15: char_code = hex_char(cd_desc_index[7:4]); + 6'd16: char_code = hex_char(cd_desc_index[3:0]); + default: char_code = " "; + endcase + end + 4'd3: begin + case (text_col) + 6'd0: char_code = "T"; + 6'd1: char_code = "W"; + 6'd2: char_code = ":"; + 6'd3: char_code = bit_char(toc_seen); + 6'd4: char_code = " "; + 6'd5: char_code = "A"; + 6'd6: char_code = ":"; + 6'd7: char_code = hex_char({2'b00, toc_addr_last[9:8]}); + 6'd8: char_code = hex_char(toc_addr_last[7:4]); + 6'd9: char_code = hex_char(toc_addr_last[3:0]); + 6'd10: char_code = " "; + 6'd11: char_code = "D"; + 6'd12: char_code = ":"; + 6'd13: char_code = hex_char(toc_data_last[15:12]); + 6'd14: char_code = hex_char(toc_data_last[11:8]); + 6'd15: char_code = hex_char(toc_data_last[7:4]); + 6'd16: char_code = hex_char(toc_data_last[3:0]); + default: char_code = " "; + endcase + end + 4'd4: begin + case (text_col) + 6'd0: char_code = "C"; + 6'd1: char_code = "Q"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char({1'b0, butch_cue_tracks[6:4]}); + 6'd4: char_code = hex_char(butch_cue_tracks[3:0]); + 6'd5: char_code = " "; + 6'd6: char_code = "A"; + 6'd7: char_code = "Q"; + 6'd8: char_code = ":"; + 6'd9: char_code = hex_char({1'b0, butch_aud_tracks[6:4]}); + 6'd10: char_code = hex_char(butch_aud_tracks[3:0]); + 6'd11: char_code = " "; + 6'd12: char_code = "D"; + 6'd13: char_code = "T"; + 6'd14: char_code = ":"; + 6'd15: char_code = hex_char({1'b0, butch_dat_track[6:4]}); + 6'd16: char_code = hex_char(butch_dat_track[3:0]); + 6'd17: char_code = " "; + 6'd18: char_code = "S"; + 6'd19: char_code = "1"; + 6'd20: char_code = ":"; + 6'd21: char_code = bit_char(butch_sess1_valid); + 6'd22: char_code = " "; + 6'd23: char_code = "S"; + 6'd24: char_code = "C"; + 6'd25: char_code = ":"; + 6'd26: char_code = hex_char(butch_dsa_sessions[7:4]); + 6'd27: char_code = hex_char(butch_dsa_sessions[3:0]); + default: char_code = " "; + endcase + end + 4'd5: begin + case (text_col) + 6'd0: char_code = "T"; + 6'd1: char_code = "I"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char({1'b0, butch_track_idx[6:4]}); + 6'd4: char_code = hex_char(butch_track_idx[3:0]); + 6'd5: char_code = " "; + 6'd6: char_code = "C"; + 6'd7: char_code = "S"; + 6'd8: char_code = ":"; + 6'd9: char_code = hex_char({1'b0, butch_cues_addr[6:4]}); + 6'd10: char_code = hex_char(butch_cues_addr[3:0]); + 6'd11: char_code = " "; + 6'd12: char_code = "C"; + 6'd13: char_code = "T"; + 6'd14: char_code = ":"; + 6'd15: char_code = hex_char({1'b0, butch_cuet_addr[6:4]}); + 6'd16: char_code = hex_char(butch_cuet_addr[3:0]); + default: char_code = " "; + endcase + end + 4'd6: begin + case (text_col) + 6'd0: char_code = "F"; + 6'd1: char_code = "T"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char({1'b0, first_grant_track[6:4]}); + 6'd4: char_code = hex_char(first_grant_track[3:0]); + 6'd5: char_code = " "; + 6'd6: char_code = "F"; + 6'd7: char_code = "C"; + 6'd8: char_code = ":"; + 6'd9: char_code = hex_char({1'b0, first_grant_cuet[6:4]}); + 6'd10: char_code = hex_char(first_grant_cuet[3:0]); + 6'd11: char_code = " "; + 6'd12: char_code = "G"; + 6'd13: char_code = "S"; + 6'd14: char_code = ":"; + 6'd15: char_code = bit_char(first_grant_seen); + default: char_code = " "; + endcase + end + 4'd7: begin + case (text_col) + 6'd0: char_code = "2"; + 6'd1: char_code = "0"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(track2_toc[0][15:12]); + 6'd4: char_code = hex_char(track2_toc[0][11:8]); + 6'd5: char_code = hex_char(track2_toc[0][7:4]); + 6'd6: char_code = hex_char(track2_toc[0][3:0]); + 6'd7: char_code = " "; + 6'd8: char_code = "2"; + 6'd9: char_code = "1"; + 6'd10: char_code = ":"; + 6'd11: char_code = hex_char(track2_toc[1][15:12]); + 6'd12: char_code = hex_char(track2_toc[1][11:8]); + 6'd13: char_code = hex_char(track2_toc[1][7:4]); + 6'd14: char_code = hex_char(track2_toc[1][3:0]); + 6'd15: char_code = " "; + 6'd16: char_code = "2"; + 6'd17: char_code = "2"; + 6'd18: char_code = ":"; + 6'd19: char_code = hex_char(track2_toc[2][15:12]); + 6'd20: char_code = hex_char(track2_toc[2][11:8]); + 6'd21: char_code = hex_char(track2_toc[2][7:4]); + 6'd22: char_code = hex_char(track2_toc[2][3:0]); + default: char_code = " "; + endcase + end + 4'd8: begin + case (text_col) + 6'd0: char_code = "2"; + 6'd1: char_code = "3"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(track2_toc[3][15:12]); + 6'd4: char_code = hex_char(track2_toc[3][11:8]); + 6'd5: char_code = hex_char(track2_toc[3][7:4]); + 6'd6: char_code = hex_char(track2_toc[3][3:0]); + 6'd7: char_code = " "; + 6'd8: char_code = "2"; + 6'd9: char_code = "4"; + 6'd10: char_code = ":"; + 6'd11: char_code = hex_char(track2_toc[4][15:12]); + 6'd12: char_code = hex_char(track2_toc[4][11:8]); + 6'd13: char_code = hex_char(track2_toc[4][7:4]); + 6'd14: char_code = hex_char(track2_toc[4][3:0]); + 6'd15: char_code = " "; + 6'd16: char_code = "2"; + 6'd17: char_code = "5"; + 6'd18: char_code = ":"; + 6'd19: char_code = hex_char(track2_toc[5][15:12]); + 6'd20: char_code = hex_char(track2_toc[5][11:8]); + 6'd21: char_code = hex_char(track2_toc[5][7:4]); + 6'd22: char_code = hex_char(track2_toc[5][3:0]); + default: char_code = " "; + endcase + end + 4'd9: begin + case (text_col) + 6'd0: char_code = "2"; + 6'd1: char_code = "6"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(track2_toc[6][15:12]); + 6'd4: char_code = hex_char(track2_toc[6][11:8]); + 6'd5: char_code = hex_char(track2_toc[6][7:4]); + 6'd6: char_code = hex_char(track2_toc[6][3:0]); + 6'd7: char_code = " "; + 6'd8: char_code = "2"; + 6'd9: char_code = "7"; + 6'd10: char_code = ":"; + 6'd11: char_code = hex_char(track2_toc[7][15:12]); + 6'd12: char_code = hex_char(track2_toc[7][11:8]); + 6'd13: char_code = hex_char(track2_toc[7][7:4]); + 6'd14: char_code = hex_char(track2_toc[7][3:0]); + default: char_code = " "; + endcase + end + 4'd10: begin + case (text_col) + 6'd0: char_code = "R"; + 6'd1: char_code = ":"; + 6'd2: char_code = bit_char(reset); + 6'd3: char_code = " "; + 6'd4: char_code = "C"; + 6'd5: char_code = ":"; + 6'd6: char_code = bit_char(cd_state_idle_dbg); + 6'd7: char_code = " "; + 6'd8: char_code = "P"; + 6'd9: char_code = ":"; + 6'd10: char_code = bit_char(xresetlp_dbg); + 6'd11: char_code = " "; + 6'd12: char_code = "X"; + 6'd13: char_code = ":"; + 6'd14: char_code = bit_char(xresetl_dbg); + 6'd15: char_code = " "; + 6'd16: char_code = "B"; + 6'd17: char_code = ":"; + 6'd18: char_code = bit_char(bootcopy_active); + 6'd19: char_code = " "; + 6'd20: char_code = "L"; + 6'd21: char_code = ":"; + 6'd22: char_code = hex_char(butch_last_ds[15:12]); + 6'd23: char_code = hex_char(butch_last_ds[11:8]); + 6'd24: char_code = hex_char(butch_last_ds[7:4]); + 6'd25: char_code = hex_char(butch_last_ds[3:0]); + 6'd26: char_code = " "; + 6'd27: char_code = "E"; + 6'd28: char_code = ":"; + 6'd29: char_code = hex_char(butch_last_err[7:4]); + 6'd30: char_code = hex_char(butch_last_err[3:0]); + default: char_code = " "; + endcase + end + 4'd11: begin + case (text_col) + 6'd0: char_code = "3"; + 6'd1: char_code = "0"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(butch_toc0[39:36]); + 6'd4: char_code = hex_char(butch_toc0[35:32]); + 6'd5: char_code = " "; + 6'd6: char_code = hex_char(butch_toc0[31:28]); + 6'd7: char_code = hex_char(butch_toc0[27:24]); + 6'd8: char_code = " "; + 6'd9: char_code = hex_char(butch_toc0[23:20]); + 6'd10: char_code = hex_char(butch_toc0[19:16]); + 6'd11: char_code = hex_char(butch_toc0[15:12]); + 6'd12: char_code = hex_char(butch_toc0[11:8]); + 6'd13: char_code = hex_char(butch_toc0[7:4]); + 6'd14: char_code = hex_char(butch_toc0[3:0]); + default: char_code = " "; + endcase + end + 4'd12: begin + case (text_col) + 6'd0: char_code = "3"; + 6'd1: char_code = "1"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(butch_toc1[39:36]); + 6'd4: char_code = hex_char(butch_toc1[35:32]); + 6'd5: char_code = " "; + 6'd6: char_code = hex_char(butch_toc1[31:28]); + 6'd7: char_code = hex_char(butch_toc1[27:24]); + 6'd8: char_code = " "; + 6'd9: char_code = hex_char(butch_toc1[23:20]); + 6'd10: char_code = hex_char(butch_toc1[19:16]); + 6'd11: char_code = hex_char(butch_toc1[15:12]); + 6'd12: char_code = hex_char(butch_toc1[11:8]); + 6'd13: char_code = hex_char(butch_toc1[7:4]); + 6'd14: char_code = hex_char(butch_toc1[3:0]); + default: char_code = " "; + endcase + end + 4'd13: begin + case (text_col) + 6'd0: char_code = "L"; + 6'd1: char_code = "0"; + 6'd2: char_code = ":"; + 6'd3: char_code = bit_char(butch_ltoc0[15]); + 6'd4: char_code = " "; + 6'd5: char_code = hex_char({1'b0, butch_ltoc0[14:12]}); + 6'd6: char_code = hex_char(butch_ltoc0[11:8]); + 6'd7: char_code = " "; + 6'd8: char_code = hex_char(butch_ltoc0[7:4]); + 6'd9: char_code = hex_char(butch_ltoc0[3:0]); + 6'd10: char_code = " "; + 6'd11: char_code = "L"; + 6'd12: char_code = "1"; + 6'd13: char_code = ":"; + 6'd14: char_code = bit_char(butch_ltoc1[15]); + 6'd15: char_code = " "; + 6'd16: char_code = hex_char({1'b0, butch_ltoc1[14:12]}); + 6'd17: char_code = hex_char(butch_ltoc1[11:8]); + 6'd18: char_code = " "; + 6'd19: char_code = hex_char(butch_ltoc1[7:4]); + 6'd20: char_code = hex_char(butch_ltoc1[3:0]); + default: char_code = " "; + endcase + end + 4'd14: begin + case (text_col) + 6'd0: char_code = "F"; + 6'd1: char_code = "0"; + 6'd2: char_code = ":"; + 6'd3: char_code = hex_char(first_aud_addr0[29:28]); + 6'd4: char_code = hex_char(first_aud_addr0[27:24]); + 6'd5: char_code = hex_char(first_aud_addr0[23:20]); + 6'd6: char_code = hex_char(first_aud_addr0[19:16]); + 6'd7: char_code = hex_char(first_aud_addr0[15:12]); + 6'd8: char_code = hex_char(first_aud_addr0[11:8]); + 6'd9: char_code = hex_char(first_aud_addr0[7:4]); + 6'd10: char_code = hex_char(first_aud_addr0[3:0]); + 6'd11: char_code = " "; + 6'd12: char_code = "F"; + 6'd13: char_code = "1"; + 6'd14: char_code = ":"; + 6'd15: char_code = hex_char(first_aud_addr1[29:28]); + 6'd16: char_code = hex_char(first_aud_addr1[27:24]); + 6'd17: char_code = hex_char(first_aud_addr1[23:20]); + 6'd18: char_code = hex_char(first_aud_addr1[19:16]); + 6'd19: char_code = hex_char(first_aud_addr1[15:12]); + 6'd20: char_code = hex_char(first_aud_addr1[11:8]); + 6'd21: char_code = hex_char(first_aud_addr1[7:4]); + 6'd22: char_code = hex_char(first_aud_addr1[3:0]); + default: char_code = " "; + endcase + end + default: char_code = " "; + endcase + + glyph_bits = font_row(char_code, glyph_y); + text_pixel = glyph_bits[7 - glyph_x]; + end + end + + always @* begin + out_r = in_r; + out_g = in_g; + out_b = in_b; + + if (in_box) begin + // Darkened backdrop makes the text readable on bright scenes while + // still letting the underlying image show through. + out_r = {2'b00, in_r[7:2]}; + out_g = {2'b00, in_g[7:2]}; + out_b = {1'b0, in_b[7:1]}; + + if (text_pixel) begin + out_r = 8'hFF; + out_g = 8'hF0; + out_b = 8'h80; + end + end + end + +endmodule