diff --git a/cores/mist/TG68K.vhd b/cores/mist/TG68K.vhd index 403763d..0fd9892 100644 --- a/cores/mist/TG68K.vhd +++ b/cores/mist/TG68K.vhd @@ -322,6 +322,7 @@ PROCESS (clk, reset, state, as_s, as_e, rw_s, rw_e, uds_s, uds_e, lds_s, lds_e) data_akt_s <= '0'; CASE S_state IS WHEN "00" => IF state/="01" AND sel_fast='0' THEN + as_s <= '0'; rw_s <= wr; uds_s <= uds_in; lds_s <= lds_in; diff --git a/cores/mist/mfp.v b/cores/mist/mfp.v index f38907d..6bf8fcf 100644 --- a/cores/mist/mfp.v +++ b/cores/mist/mfp.v @@ -1,5 +1,8 @@ // +// define this for cubase acia irq hack +// `define CUBHACK + module mfp ( // cpu register interface input clk, @@ -235,12 +238,28 @@ always @(iack, sel, ds, rw, addr, gpip_cpu_out, aer, ddr, ier, ipr, isr, imr, end end +`ifndef CUBHACK // delay de and timer to detect changes +reg acia_irqD, acia_irqD2, dma_irqD, dma_irqD2; +`endif + reg [7:0] irq_vec; always @(posedge clk) begin - iackD <= iack; +`ifndef CUBHACK + dma_irqD <= dma_irq; + acia_irqD <= acia_irq; +`endif + iackD <= iack; + + // the polarity of the irq is negated over a real st. +// if(aer[7]) dma_irqD <= dma_irq; +// else dma_irqD <= !dma_irq; + +// if(aer[6]) acia_irqD <= acia_irq; +// else acia_irqD <= !acia_irq; + // the pending irq changes in the middle of an iack // phase, so we latch the current vector to keep is stable // during the entire cpu read @@ -249,6 +268,9 @@ end reg iackD; always @(negedge clk) begin + dma_irqD2 <= dma_irqD; + acia_irqD2 <= acia_irqD; + if(reset) begin ipr <= 16'h0000; ier <= 16'h0000; imr <= 16'h0000; isr <= 16'h0000; @@ -271,13 +293,29 @@ always @(negedge clk) begin if(timerc_done && ier[ 5]) ipr[ 5] <= 1'b1; // timer_c if(timerd_done && ier[ 4]) ipr[ 4] <= 1'b1; // timer_d - // irq by acia ... - if(acia_irq && ier[6]) - ipr[6] <= 1'b1; +`ifndef CUBHACK + // in the real atari st the irqs are edge sensitive. however, this makes problems + // with the acias in cubase as a work around ... + if(acia_irqD && !acia_irqD2) begin + if(ier[6]) ipr[6] <= 1'b1; + end + + // ... and dma + if(dma_irqD && !dma_irqD2) begin + if(ier[7]) ipr[7] <= 1'b1; + end +`else + // ... they can be implemented level sensitive. However, this breaks some games + // like eg. "no second place" and even the rainbow logo in the tos 1.04 desktop + + // irq by acia ... + if(acia_irq && ier[6]) + ipr[6] <= 1'b1; // ... and dma if(dma_irq && ier[7]) - ipr[7] <= 1'b1; + ipr[7] <= 1'b1; +`endif if(sel && ~ds && ~rw) begin if(addr == 5'h00) gpip <= din; diff --git a/cores/mist/mfp_timer.v b/cores/mist/mfp_timer.v index 8626028..4fba46e 100644 --- a/cores/mist/mfp_timer.v +++ b/cores/mist/mfp_timer.v @@ -111,7 +111,7 @@ always @(negedge CLK) begin down_counter <= data; T_O_PULSE <= 1'b1; - end else begin + end else begin down_counter <= down_counter - 8'd1; end diff --git a/cores/mist/mist_top.v b/cores/mist/mist_top.v index 056c64a..be34db6 100644 --- a/cores/mist/mist_top.v +++ b/cores/mist/mist_top.v @@ -100,7 +100,7 @@ always @(posedge clk_8) begin // timeout only when cpu owns the bus and when // neither dtack nor fast ram are active if(dtack_timeout != 3'd7) begin - if(!tg68_dtack || br || tg68_cpuena || tg68_as) + if(!tg68_dtack || br || tg68_cpuena || tg68_lds || tg68_uds) dtack_timeout <= 3'd0; else dtack_timeout <= dtack_timeout + 3'd1; @@ -540,7 +540,7 @@ wire [22:0] ram_address; wire [15:0] ram_data; wire video_cycle = (bus_cycle[3:2] == 0); -wire cpu_cycle = (bus_cycle[3:2] == 1); +wire cpu_cycle = (bus_cycle[3:2] == 1); // || (bus_cycle[3:2] == 3); wire io_cycle = (bus_cycle[3:2] == 2); assign ram_address = video_cycle?video_address:tg68_adr[23:1]; @@ -555,9 +555,9 @@ wire MEM8M = (system_ctrl[3:1] == 3'd4); wire MEM14M = (system_ctrl[3:1] == 3'd5); // ram from 0x000000 to 0x400000 -wire cpu2ram = (tg68_adr[23:22] == 2'b00) || // ordinary 4MB +wire cpu2ram = (tg68_adr[23:22] == 2'b00) || // ordinary 4MB ((MEM14M || MEM8M) && (tg68_adr[23:22] == 2'b01)) || // 8MB - (MEM14M && ((tg68_adr[23:22] == 2'b10) | // 12MB + (MEM14M && ((tg68_adr[23:22] == 2'b10) || // 12MB (tg68_adr[23:21] == 3'b110))); // 14MB wire cpu2ram14 = (tg68_adr[23:22] == 2'b00) || // ordinary 4MB @@ -588,7 +588,8 @@ wire cpu2iack = (tg68_adr[23:4] == 20'hfffff); // wire address_strobe = ~tg68_uds || ~tg68_lds; reg address_strobe; always @(posedge clk_8) - address_strobe <= (video_cycle) && ~tg68_as; +// address_strobe <= (video_cycle) && (~tg68_lds || ~tg68_uds); + address_strobe <= video_cycle && ~tg68_as; // generate dtack (for st ram only and rom), TODO: no dtack for rom write // assign tg68_dtack = ~(((cpu2mem && address_strobe && cpu_cycle) || io_dtack ) && !br); diff --git a/cores/mist/video.v b/cores/mist/video.v index 6d5c6e2..62b3ba3 100644 --- a/cores/mist/video.v +++ b/cores/mist/video.v @@ -13,6 +13,11 @@ // vdisp 400 200 // vtot 501 315/262 +// Overscan: +// http://codercorner.com/fullscrn.txt + +// Examples: automation 000 + 001: bottom border +// automation 097: top+ bottom border module video ( // system interface @@ -181,8 +186,13 @@ reg [1:0] shmode; wire mono = (shmode == 2'd2); wire low = (shmode == 2'd0); +// syncmode is delayed until next vsync to cope with "bottom border overscan" reg [1:0] syncmode; -wire pal = (syncmode[1] == 1'b1); +reg [1:0] syncmode_latch; +wire pal = (syncmode_latch[1] == 1'b1); + +reg overscan; // overscan detected in current frame +reg overscan_latched; // 16 colors with 3*3 bits each reg [2:0] palette_r[15:0]; @@ -257,7 +267,11 @@ always @(negedge reg_clk) begin if(reg_sel && ~reg_rw) begin if(reg_addr == 6'h00 && ~reg_lds) _v_bas_ad[22:15] <= reg_din[7:0]; if(reg_addr == 6'h01 && ~reg_lds) _v_bas_ad[14:7] <= reg_din[7:0]; - if(reg_addr == 6'h05 && ~reg_uds) syncmode <= reg_din[9:8]; + + if(reg_addr == 6'h05 && ~reg_uds) begin + // writing to sync mode toggles between 50 and 60 hz modes + syncmode <= reg_din[9:8]; + end // the color palette registers if(reg_addr >= 6'h20 && reg_addr < 6'h30 ) begin @@ -295,37 +309,37 @@ wire [2:0] stvid_g = mono?mono_rgb:color_g; wire [2:0] stvid_b = mono?mono_rgb:color_b; // ... add OSD overlay and feed into VGA outputs -//assign video_r = !osd_oe?{stvid_r,stvid_r}:{osd_pixel, osd_pixel, osd_pixel, stvid_r}; -//assign video_g = !osd_oe?{stvid_g,stvid_g}:{osd_pixel, osd_pixel, 1'b1, stvid_g}; -//assign video_b = !osd_oe?{stvid_b,stvid_b}:{osd_pixel, osd_pixel, osd_pixel, stvid_b}; - always @(posedge clk) begin video_r <= !osd_oe?{stvid_r,stvid_r}:{osd_pixel, osd_pixel, osd_pixel, stvid_r}; video_g <= !osd_oe?{stvid_g,stvid_g}:{osd_pixel, osd_pixel, 1'b1, stvid_g}; video_b <= !osd_oe?{stvid_b,stvid_b}:{osd_pixel, osd_pixel, osd_pixel, stvid_b}; end +wire [9:0] overscan_bottom = overscan_latched?10'd60:10'd0; + // display enable signal // the color modes use a scan doubler and output the data with 2 lines delay wire [9:0] v_offset = mono?10'd0:10'd2; -wire de = (hcnt >= H_PRE) && (hcnt < H_ACT+H_PRE) && (vcnt >= v_offset && vcnt < V_ACT+v_offset); +wire de = (hcnt >= H_PRE) && (hcnt < H_ACT+H_PRE) && (vcnt >= v_offset && vcnt < V_ACT+v_offset+overscan_bottom); // a fake de signal for timer a for color modes with half the hsync frequency wire deC = (((hcnt >= H_PRE) && !vcnt[0]) || ((hcnt < H_ACT+H_PRE-10'd160) && vcnt[0])) && - (vcnt >= (v_offset-10'd0) && vcnt < (V_ACT+v_offset-10'd0)); + (vcnt >= (v_offset-10'd0) && vcnt < (V_ACT+v_offset+overscan_bottom-10'd0)); // a fake hsync pulse for the scan doubled color modes wire hsC = vcnt[0] && hs; - + // create a read signal that's 16 clocks ahead of oe -assign read = (bus_cycle[3:2] == 0) && (hcnt < H_ACT) && (vcnt < V_ACT); +assign read = (bus_cycle[3:2] == 0) && (hcnt < H_ACT) && (vcnt < V_ACT + overscan_bottom); reg line; +reg last_syncmode; always @(posedge clk) begin if(reset) begin vaddr <= _v_bas_ad; end else begin + last_syncmode <= syncmode[1]; // delay syncmode to detect changes line <= vcnt[1]; // ---- scan doubler pointer handling ----- @@ -358,10 +372,27 @@ always @(posedge clk) begin vaddr <= vaddr + 23'd1; end end else begin + + // this is also the magic used to do "overscan". + // the magic actually involves more than writing zero (60hz) + // within line 200. But htis is sufficient for our detection + if(vcnt[9:1] == 8'd200) begin + // syncmode has changed from 1 to 0 (50 to 60 hz) + if((syncmode[1] == 1'b0) && (last_syncmode == 1'b1)) + overscan <= 1'b1; + end + // reached last possible pixel pos if(hmax && vmax) begin // reset video address counter vaddr <= _v_bas_ad; + + // copy syncmode + syncmode_latch <= syncmode; + + // save and reset overscan + overscan_latched <= overscan; + overscan <= 1'b0; end end