mirror of
https://github.com/mist-devel/mist-board.git
synced 2026-05-01 14:16:55 +00:00
STE video and DMA audio mostly implemented
This commit is contained in:
108
cores/mist/mfp.v
108
cores/mist/mfp.v
@@ -1,15 +1,17 @@
|
||||
//
|
||||
|
||||
// define this for cubase acia irq hack
|
||||
// `define CUBHACK
|
||||
// TODO:
|
||||
// - map remaining inputs to irqs
|
||||
// - implement pulse mode
|
||||
// - suppress input irqs when timer_a/b are in event or pulse mode
|
||||
|
||||
module mfp (
|
||||
// cpu register interface
|
||||
input clk,
|
||||
input reset,
|
||||
input [7:0] din,
|
||||
input [7:0] din,
|
||||
input sel,
|
||||
input [4:0] addr,
|
||||
input [4:0] addr,
|
||||
input ds,
|
||||
input rw,
|
||||
output reg [7:0] dout,
|
||||
@@ -21,12 +23,10 @@ module mfp (
|
||||
input serial_strobe_out,
|
||||
output [7:0] serial_data_out,
|
||||
|
||||
// inputs
|
||||
input clk_ext, // external 2.457MHz
|
||||
input de,
|
||||
input dma_irq,
|
||||
input acia_irq,
|
||||
input blitter_irq,
|
||||
input mono_detect
|
||||
input [1:0] t_i, // timer input
|
||||
input [7:0] i // input port
|
||||
);
|
||||
|
||||
localparam FIFO_ADDR_BITS = 4;
|
||||
@@ -63,6 +63,7 @@ wire write = sel && ~ds && ~rw;
|
||||
wire timera_done;
|
||||
wire [7:0] timera_dat_o;
|
||||
wire [4:0] timera_ctrl_o;
|
||||
wire i4_irq_disable;
|
||||
|
||||
mfp_timer timer_a (
|
||||
.CLK (clk),
|
||||
@@ -74,13 +75,15 @@ mfp_timer timer_a (
|
||||
.DAT_I (din),
|
||||
.DAT_O (timera_dat_o),
|
||||
.DAT_WE ((addr == 5'h0f) && write),
|
||||
.IRQ_DIS (i4_irq_disable),
|
||||
.T_I (t_i[0] ^ aer[4]),
|
||||
.T_O_PULSE (timera_done)
|
||||
);
|
||||
|
||||
|
||||
wire timerb_done;
|
||||
wire [7:0] timerb_dat_o;
|
||||
wire [4:0] timerb_ctrl_o;
|
||||
wire i3_irq_disable;
|
||||
|
||||
mfp_timer timer_b (
|
||||
.CLK (clk),
|
||||
@@ -92,7 +95,8 @@ mfp_timer timer_b (
|
||||
.DAT_I (din),
|
||||
.DAT_O (timerb_dat_o),
|
||||
.DAT_WE ((addr == 5'h10) && write),
|
||||
.T_I (de),
|
||||
.IRQ_DIS (i3_irq_disable),
|
||||
.T_I (t_i[1] ^ aer[3]),
|
||||
.T_O_PULSE (timerb_done)
|
||||
);
|
||||
|
||||
@@ -188,14 +192,8 @@ wire [3:0] highest_irq_pending =
|
||||
((irq_pending_map[15:0] == 16'b000000000000001)?4'd0:
|
||||
4'd0)))))))))))))));
|
||||
|
||||
// wire inputs to external gpip in
|
||||
// acia/dma irq is active low in st, but active high in this config, we thus invert
|
||||
// it to make sure tos sees what it expects to see when it looks at the registers
|
||||
wire [7:0] gpip_in;
|
||||
assign gpip_in = { mono_detect, 1'b0, !dma_irq, !acia_irq, !blitter_irq, 3'b000 };
|
||||
|
||||
// gpip as output to the cpu (ddr bit == 1 -> gpip pin is output)
|
||||
wire [7:0] gpip_cpu_out = (gpip_in & ~ddr) | (gpip & ddr);
|
||||
wire [7:0] gpip_cpu_out = (i & ~ddr) | (gpip & ddr);
|
||||
|
||||
// cpu read interface
|
||||
always @(iack, sel, ds, rw, addr, gpip_cpu_out, aer, ddr, ier, ipr, isr, imr,
|
||||
@@ -236,15 +234,15 @@ always @(iack, sel, ds, rw, addr, gpip_cpu_out, aer, ddr, ier, ipr, isr, imr,
|
||||
end
|
||||
end
|
||||
|
||||
// delay irqs to detect changes
|
||||
reg dma_irqD, dma_irqD2, blitter_irqD, blitter_irqD2;
|
||||
|
||||
`ifndef CUBHACK
|
||||
reg acia_irqD, acia_irqD2;
|
||||
`endif
|
||||
// delay inputs to detect changes
|
||||
reg [7:0] iD, iD2;
|
||||
|
||||
reg [7:0] irq_vec;
|
||||
|
||||
// mask of input irqs which are overwritten by timer a/b inputs
|
||||
wire [7:0] ti_irq_mask = { 3'b000, i4_irq_disable, i3_irq_disable, 3'b000};
|
||||
wire [7:0] ti_irq = { 3'b000, t_i[0], t_i[1], 3'b000};
|
||||
|
||||
reg iackD;
|
||||
always @(posedge clk) begin
|
||||
iackD <= iack;
|
||||
@@ -254,31 +252,9 @@ always @(posedge clk) begin
|
||||
// during the entire cpu read
|
||||
irq_vec <= { vr[7:4], highest_irq_pending };
|
||||
|
||||
// ST default for most aer bits is 0 (except I2/CTS)
|
||||
// since the irq polarity is inverted over normal ST
|
||||
// interrupts we'll invert the irq signal if a aer bit
|
||||
// is 1
|
||||
|
||||
if(aer[3]) blitter_irqD <= !blitter_irq;
|
||||
else blitter_irqD <= blitter_irq;
|
||||
|
||||
`ifndef CUBHACK
|
||||
if(aer[4]) acia_irqD <= !acia_irq;
|
||||
else acia_irqD <= acia_irq;
|
||||
`endif
|
||||
|
||||
// the polarity of the irq is negated over a real st.
|
||||
if(aer[5]) dma_irqD <= !dma_irq;
|
||||
else dma_irqD <= dma_irq;
|
||||
|
||||
|
||||
dma_irqD2 <= dma_irqD;
|
||||
blitter_irqD2 <= blitter_irqD;
|
||||
|
||||
`ifndef CUBHACK
|
||||
acia_irqD2 <= acia_irqD;
|
||||
`endif
|
||||
|
||||
// delay inputs for irq generation, apply aer (irq edge)
|
||||
iD <= aer ^ ((i & ~ti_irq_mask) | (ti_irq & ti_irq_mask));
|
||||
iD2 <= iD;
|
||||
end
|
||||
|
||||
always @(negedge clk) begin
|
||||
@@ -300,32 +276,16 @@ always @(negedge clk) begin
|
||||
end
|
||||
|
||||
// map timer interrupts
|
||||
if(timera_done && ier[13]) ipr[13] <= 1'b1; // timer_a
|
||||
if(timerb_done && ier[ 8]) ipr[ 8] <= 1'b1; // timer_b
|
||||
if(timerc_done && ier[ 5]) ipr[ 5] <= 1'b1; // timer_c
|
||||
if(timerd_done && ier[ 4]) ipr[ 4] <= 1'b1; // timer_d
|
||||
if(timera_done && ier[13]) ipr[13] <= 1'b1; // timer_a
|
||||
if(timerb_done && ier[ 8]) ipr[ 8] <= 1'b1; // timer_b
|
||||
if(timerc_done && ier[ 5]) ipr[ 5] <= 1'b1; // timer_c
|
||||
if(timerd_done && ier[ 4]) ipr[ 4] <= 1'b1; // timer_d
|
||||
|
||||
`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 && ier[6])
|
||||
ipr[6] <= 1'b1;
|
||||
`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;
|
||||
`endif
|
||||
|
||||
// ... dma ...
|
||||
if(dma_irqD && !dma_irqD2 && ier[7])
|
||||
ipr[7] <= 1'b1;
|
||||
|
||||
// ... and blitter
|
||||
if(blitter_irqD && !blitter_irqD2 && ier[3])
|
||||
ipr[3] <= 1'b1;
|
||||
// input port irqs are edge sensitive
|
||||
if(!iD[3] && iD2[3] && ier[ 3]) ipr[ 3] <= 1'b1; // blitter
|
||||
if(!iD[4] && iD2[4] && ier[ 6]) ipr[ 6] <= 1'b1; // acia
|
||||
if(!iD[5] && iD2[5] && ier[ 7]) ipr[ 7] <= 1'b1; // dma
|
||||
if(!iD[7] && iD2[7] && ier[15]) ipr[15] <= 1'b1; // mono detect
|
||||
|
||||
if(sel && ~ds && ~rw) begin
|
||||
if(addr == 5'h00) gpip <= din;
|
||||
|
||||
@@ -13,6 +13,8 @@ module mfp_timer(
|
||||
inout XCLK_I,
|
||||
input T_I, // ext. trigger in
|
||||
|
||||
output IRQ_DIS, // pulse mode disables input port irq
|
||||
|
||||
output reg T_O,
|
||||
output reg T_O_PULSE
|
||||
);
|
||||
@@ -20,6 +22,8 @@ module mfp_timer(
|
||||
reg [7:0] data, down_counter, cur_counter;
|
||||
reg [3:0] control;
|
||||
|
||||
assign IRQ_DIS = pulse_mode;
|
||||
|
||||
wire[7:0] prescaler; // prescaler value
|
||||
reg [7:0] prescaler_counter; // prescaler counter
|
||||
|
||||
|
||||
@@ -327,6 +327,8 @@ set_global_assignment -name VERILOG_FILE mfp.v
|
||||
set_global_assignment -name VERILOG_FILE dma.v
|
||||
set_global_assignment -name VERILOG_FILE sigma_delta_dac.v
|
||||
set_global_assignment -name VERILOG_FILE mmu.v
|
||||
set_global_assignment -name VERILOG_FILE ste_dma_snd.v
|
||||
set_global_assignment -name VERILOG_FILE ste_joystick.v
|
||||
|
||||
set_global_assignment -name ENABLE_BENEFICIAL_SKEW_OPTIMIZATION ON
|
||||
set_global_assignment -name SLD_NODE_CREATOR_ID 110 -section_id auto_signaltap_0
|
||||
|
||||
@@ -42,7 +42,8 @@ module mist_top (
|
||||
input wire CONF_DATA0 // SPI_SS for user_io
|
||||
);
|
||||
|
||||
wire [15:0] video_data;
|
||||
wire ste = system_ctrl[23];
|
||||
|
||||
wire video_read;
|
||||
wire [22:0] video_address;
|
||||
wire st_de, st_hs, st_vs;
|
||||
@@ -53,7 +54,7 @@ wire [1:0] buttons;
|
||||
|
||||
// generate dtack for all implemented peripherals
|
||||
wire io_dtack = vreg_sel || mmu_sel || mfp_sel || mfp_iack ||
|
||||
acia_sel || psg_sel || dma_sel || auto_iack || blitter_sel;
|
||||
acia_sel || psg_sel || dma_sel || auto_iack || blitter_sel || ste_joy_sel || ste_dma_snd_sel;
|
||||
|
||||
// the original tg68k did not contain working support for bus fault exceptions. While earlier
|
||||
// TOS versions cope with that, TOS versions with blitter support need this to work as this is
|
||||
@@ -137,8 +138,6 @@ wire [7:0] auto_vector = auto_vector_vbi | auto_vector_hbi;
|
||||
// interfaces not implemented:
|
||||
// $fff00000 - $fff000ff - IDE
|
||||
// $ffff8780 - $ffff878f - SCSI
|
||||
// $ffff8901 - $ffff893f - STE DMA audio
|
||||
// $ffff9200 - $ffff923f - STE joystick ports
|
||||
// $fffffa40 - $fffffa7f - FPU
|
||||
// $fffffc20 - $fffffc3f - RTC
|
||||
|
||||
@@ -152,6 +151,14 @@ wire [7:0] mmu_data_out;
|
||||
wire vreg_sel = io_sel && ({tg68_adr[15:7], 7'd0} == 16'h8200);
|
||||
wire [15:0] vreg_data_out;
|
||||
|
||||
// ste joystick 16 bit interface at $ff9200 - $ff923f
|
||||
wire ste_joy_sel = ste && io_sel && ({tg68_adr[15:6], 6'd0} == 16'h9200);
|
||||
wire [15:0] ste_joy_data_out;
|
||||
|
||||
// ste dma snd 8 bit interface at $ff8900 - $ff893f
|
||||
wire ste_dma_snd_sel = ste && io_sel && ({tg68_adr[15:6], 6'd0} == 16'h8900);
|
||||
wire [15:0] ste_dma_snd_data_out;
|
||||
|
||||
// mfp 8 bit interface at $fffa00 - $fffa3f
|
||||
wire mfp_sel = io_sel && ({tg68_adr[15:6], 6'd0} == 16'hfa00);
|
||||
wire [7:0] mfp_data_out;
|
||||
@@ -160,8 +167,8 @@ wire [7:0] mfp_data_out;
|
||||
wire acia_sel = io_sel && ({tg68_adr[15:8], 8'd0} == 16'hfc00);
|
||||
wire [7:0] acia_data_out;
|
||||
|
||||
// blitter 16 bit interface at $ff8a00 - $ff8a3f
|
||||
wire blitter_sel = system_ctrl[19] && io_sel && ({tg68_adr[15:8], 8'd0} == 16'h8a00);
|
||||
// blitter 16 bit interface at $ff8a00 - $ff8a3f, STE always has a blitter
|
||||
wire blitter_sel = (system_ctrl[19] || ste) && io_sel && ({tg68_adr[15:8], 8'd0} == 16'h8a00);
|
||||
wire [15:0] blitter_data_out;
|
||||
|
||||
// psg 8 bit interface at $ff8800 - $ff8803
|
||||
@@ -175,20 +182,21 @@ wire [15:0] dma_data_out;
|
||||
// de-multiplex the various io data output ports into one
|
||||
wire [7:0] io_data_out_8u = acia_data_out | psg_data_out;
|
||||
wire [7:0] io_data_out_8l = mmu_data_out | mfp_data_out | auto_vector;
|
||||
wire [15:0] io_data_out = vreg_data_out | dma_data_out | blitter_data_out |
|
||||
{8'h00, io_data_out_8l} | {io_data_out_8u, 8'h00};
|
||||
wire [15:0] io_data_out = vreg_data_out | dma_data_out | blitter_data_out |
|
||||
ste_joy_data_out | ste_dma_snd_data_out |
|
||||
{8'h00, io_data_out_8l} | {io_data_out_8u, 8'h00};
|
||||
|
||||
wire init = ~pll_locked;
|
||||
|
||||
video video (
|
||||
.clk (clk_32 ),
|
||||
.clk27 (CLOCK_27[0]),
|
||||
.bus_cycle (bus_cycle ),
|
||||
.clk (clk_32 ),
|
||||
.clk27 (CLOCK_27[0]),
|
||||
.bus_cycle (bus_cycle ),
|
||||
|
||||
// spi for OSD
|
||||
.sdi (SPI_DI ),
|
||||
.sck (SPI_SCK ),
|
||||
.ss (SPI_SS3 ),
|
||||
.sdi (SPI_DI ),
|
||||
.sck (SPI_SCK ),
|
||||
.ss (SPI_SS3 ),
|
||||
|
||||
// cpu register interface
|
||||
.reg_clk (clk_8 ),
|
||||
@@ -211,9 +219,11 @@ video video (
|
||||
.video_g (VGA_G ),
|
||||
.video_b (VGA_B ),
|
||||
|
||||
// configuration signals
|
||||
.adjust (video_adj ),
|
||||
.pal56 (~system_ctrl[9]),
|
||||
.scanlines (system_ctrl[21:20]),
|
||||
.ste (ste ),
|
||||
|
||||
// signals not affected by scan doubler required for
|
||||
// irq generation
|
||||
@@ -235,6 +245,15 @@ mmu mmu (
|
||||
|
||||
wire acia_irq, dma_irq;
|
||||
|
||||
// the STE delays the xsirq by 1/250000 second before feeding it into timer_a
|
||||
wire ste_dma_snd_xsirq, ste_dma_snd_xsirq_delayed;
|
||||
|
||||
// mfp io7 is mono_detect which in ste is xor'd with the dma sound irq
|
||||
wire mfp_io7 = system_ctrl[8] ^ (ste?ste_dma_snd_xsirq:1'b0);
|
||||
|
||||
wire [7:0] mfp_gpio_in = {mfp_io7, 1'b0, !dma_irq, !acia_irq, !blitter_irq, 3'b000 };
|
||||
wire [1:0] mfp_timer_in = {st_de, ste?ste_dma_snd_xsirq_delayed:1'b0};
|
||||
|
||||
mfp mfp (
|
||||
// cpu register interface
|
||||
.clk (clk_8 ),
|
||||
@@ -254,12 +273,9 @@ mfp mfp (
|
||||
.serial_data_out (serial_data_from_mfp),
|
||||
|
||||
// input signals
|
||||
.clk_ext (clk_mfp ),
|
||||
.de (st_de ),
|
||||
.dma_irq (dma_irq ),
|
||||
.acia_irq (acia_irq ),
|
||||
.blitter_irq (blitter_irq ),
|
||||
.mono_detect (system_ctrl[8])
|
||||
.clk_ext (clk_mfp ), // 2.457MHz clock
|
||||
.t_i (mfp_timer_in ), // timer a/b inputs
|
||||
.i (mfp_gpio_in ) // gpio-in
|
||||
);
|
||||
|
||||
acia acia (
|
||||
@@ -318,6 +334,73 @@ blitter blitter (
|
||||
.irq (blitter_irq )
|
||||
);
|
||||
|
||||
ste_joystick ste_joystick (
|
||||
.clk (clk_8 ),
|
||||
.reset (reset ),
|
||||
.din (tg68_dat_out),
|
||||
.sel (ste_joy_sel ),
|
||||
.addr (tg68_adr[5:1]),
|
||||
.uds (tg68_uds ),
|
||||
.lds (tg68_lds ),
|
||||
.rw (tg68_rw ),
|
||||
.dout (ste_joy_data_out),
|
||||
);
|
||||
|
||||
wire [22:0] ste_dma_snd_addr;
|
||||
wire ste_dma_snd_read;
|
||||
wire [7:0] ste_audio_out_l, ste_audio_out_r;
|
||||
|
||||
ste_dma_snd ste_dma_snd (
|
||||
// cpu interface
|
||||
.clk (clk_8 ),
|
||||
.reset (reset ),
|
||||
.din (tg68_dat_out),
|
||||
.sel (ste_dma_snd_sel ),
|
||||
.addr (tg68_adr[5:1]),
|
||||
.uds (tg68_uds ),
|
||||
.lds (tg68_lds ),
|
||||
.rw (tg68_rw ),
|
||||
.dout (ste_dma_snd_data_out),
|
||||
|
||||
// memory interface
|
||||
.clk32 (clk_32 ),
|
||||
.bus_cycle (bus_cycle ),
|
||||
.hsync (st_hs ),
|
||||
.saddr (ste_dma_snd_addr ),
|
||||
.read (ste_dma_snd_read ),
|
||||
.data (ram_data_out ),
|
||||
|
||||
.audio_l (ste_audio_out_l ),
|
||||
.audio_r (ste_audio_out_r ),
|
||||
|
||||
.xsint (ste_dma_snd_xsirq ),
|
||||
.xsint_d (ste_dma_snd_xsirq_delayed ) // 4 usec delayed
|
||||
);
|
||||
|
||||
// audio output processing
|
||||
|
||||
// simple mixer for ste and ym audio. result is 9 bits
|
||||
// this will later be handled by the lmc1992
|
||||
wire [8:0] audio_mix_l = { 1'b0, ym_audio_out_l} + { 1'b0, ste_audio_out_l };
|
||||
wire [8:0] audio_mix_r = { 1'b0, ym_audio_out_r} + { 1'b0, ste_audio_out_r };
|
||||
|
||||
// limit audio to 8 bit range
|
||||
wire [7:0] audio_out_l = audio_mix_l[8]?8'd255:audio_mix_l[7:0];
|
||||
wire [7:0] audio_out_r = audio_mix_r[8]?8'd255:audio_mix_r[7:0];
|
||||
|
||||
sigma_delta_dac sigma_delta_dac_l (
|
||||
.DACout (AUDIO_L),
|
||||
.DACin (audio_out_l),
|
||||
.CLK (clk_32),
|
||||
.RESET (reset)
|
||||
);
|
||||
|
||||
sigma_delta_dac sigma_delta_dac_r (
|
||||
.DACout (AUDIO_R),
|
||||
.DACin (audio_out_r),
|
||||
.CLK (clk_32),
|
||||
.RESET (reset)
|
||||
);
|
||||
|
||||
//// ym2149 sound chip ////
|
||||
reg [1:0] sclk;
|
||||
@@ -328,22 +411,7 @@ wire [7:0] port_a_out;
|
||||
assign floppy_side = port_a_out[0];
|
||||
assign floppy_sel = port_a_out[2:1];
|
||||
|
||||
wire [7:0] audio_out_l,audio_out_r;
|
||||
//assign AUDIO_R = AUDIO_L;
|
||||
|
||||
sigma_delta_dac sigma_delta_dac_l (
|
||||
.DACout (AUDIO_L),
|
||||
.DACin (audio_out_l),
|
||||
.CLK (clk_32),
|
||||
.RESET (reset)
|
||||
);
|
||||
|
||||
sigma_delta_dac sigma_delta_dac_r (
|
||||
.DACout (AUDIO_R),
|
||||
.DACin (audio_out_r),
|
||||
.CLK (clk_32),
|
||||
.RESET (reset)
|
||||
);
|
||||
wire [7:0] ym_audio_out_l, ym_audio_out_r;
|
||||
|
||||
YM2149 ym2149 (
|
||||
.I_DA ( tg68_dat_out[15:8] ),
|
||||
@@ -358,8 +426,8 @@ YM2149 ym2149 (
|
||||
.I_BC1 ( psg_sel && !tg68_adr[1]),
|
||||
.I_SEL_L ( 1'b1 ),
|
||||
|
||||
.O_AUDIO_L (audio_out_l ),
|
||||
.O_AUDIO_R (audio_out_r ),
|
||||
.O_AUDIO_L (ym_audio_out_l ),
|
||||
.O_AUDIO_R (ym_audio_out_r ),
|
||||
|
||||
.stereo (system_ctrl[22] ),
|
||||
|
||||
@@ -604,8 +672,8 @@ wire [15:0] ram_data_out;
|
||||
wire video_cycle = (bus_cycle[3:2] == 0);
|
||||
wire cpu_cycle = (bus_cycle[3:2] == 1); // || (bus_cycle[3:2] == 3);
|
||||
|
||||
assign ram_address = video_cycle?video_address:(blitter_br?blitter_master_addr:tg68_adr[23:1]);
|
||||
assign video_data = ram_data_out;
|
||||
assign ram_address = video_cycle?((st_hs&&ste)?ste_dma_snd_addr:video_address):
|
||||
(blitter_br?blitter_master_addr:tg68_adr[23:1]);
|
||||
|
||||
// TODO: put 0x000000 to 0x000007 into tos section so it's write protected
|
||||
wire MEM512K = (system_ctrl[3:1] == 3'd0);
|
||||
@@ -655,8 +723,13 @@ always @(posedge clk_8)
|
||||
// generate dtack (for st ram only and rom), TODO: no dtack for rom write
|
||||
assign tg68_dtack = ~(((cpu2mem && address_strobe) || io_dtack ) && !br);
|
||||
|
||||
wire ram_oe = video_cycle?~video_read:
|
||||
(cpu_cycle?~(blitter_br?blitter_master_read:(address_strobe && tg68_rw && cpu2mem)):1'b1);
|
||||
// memory access during the video cycle is shared between video and ste_dma_snd
|
||||
wire video_cycle_oe = (st_hs && ste)?ste_dma_snd_read:video_read;
|
||||
// memory access during the cpu cycle is shared between blitter and cpu
|
||||
wire cpu_cycle_oe = (blitter_br?blitter_master_read:(address_strobe && tg68_rw && cpu2mem));
|
||||
|
||||
wire ram_oe = ~(video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0));
|
||||
// (cpu_cycle?~(blitter_br?blitter_master_read:(address_strobe && tg68_rw && cpu2mem)):1'b1);
|
||||
// (cpu_cycle?~(address_strobe && tg68_rw && cpu2mem):1'b1);
|
||||
|
||||
wire ram_wr = cpu_cycle?~(blitter_br?blitter_master_write:(address_strobe && ~tg68_rw && cpu2ram)):1'b1;
|
||||
|
||||
347
cores/mist/ste_dma_snd.v
Normal file
347
cores/mist/ste_dma_snd.v
Normal file
@@ -0,0 +1,347 @@
|
||||
//
|
||||
// ste_dma_snd.v
|
||||
//
|
||||
// Atari STE dma sound implementation for the MiST board
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
//
|
||||
// This source file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module ste_dma_snd (
|
||||
// system interface
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
// cpu register interface
|
||||
input [15:0] din,
|
||||
input sel,
|
||||
input [4:0] addr,
|
||||
input uds,
|
||||
input lds,
|
||||
input rw,
|
||||
output reg [15:0] dout,
|
||||
|
||||
// memory interface
|
||||
input clk32, // 31.875 MHz
|
||||
input [3:0] bus_cycle, // bus-cycle
|
||||
input hsync, // to synchronize with video
|
||||
output read,
|
||||
output [22:0] saddr,
|
||||
input [15:0] data,
|
||||
|
||||
// audio
|
||||
output reg [7:0] audio_l,
|
||||
output reg [7:0] audio_r,
|
||||
|
||||
output xsint,
|
||||
output xsint_d
|
||||
);
|
||||
|
||||
assign saddr = snd_adr; // drive data
|
||||
assign read = (bus_cycle[3:2] == 0) && hsync && !fifo_full && dma_enable;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------ clock generation ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// dma sound internally works on a 2MHz clock
|
||||
reg [1:0] sclk;
|
||||
always @(posedge clk)
|
||||
sclk <= sclk + 2'd1;
|
||||
|
||||
wire clk_2 = sclk[1]; // 2Mhz
|
||||
|
||||
// generate audio sample rate base == 50kHz == 2Mhz/40
|
||||
reg abase;
|
||||
reg [4:0] acnt;
|
||||
|
||||
always @(posedge clk_2) begin
|
||||
if(acnt == 5'd19) begin
|
||||
acnt <= 5'd0;
|
||||
abase <= ~abase;
|
||||
end else
|
||||
acnt <= acnt + 5'd1;
|
||||
end
|
||||
|
||||
// generate current audio clock
|
||||
reg [2:0] aclk_cnt;
|
||||
always @(posedge abase)
|
||||
aclk_cnt <= aclk_cnt + 3'd1;
|
||||
|
||||
wire aclk = (mode[1:0] == 2'b11)?abase: // 50 kHz
|
||||
((mode[1:0] == 2'b10)?aclk_cnt[0]: // 25 kHz
|
||||
((mode[1:0] == 2'b01)?aclk_cnt[1]: // 12.5 kHz
|
||||
aclk_cnt[2])); // 6.25 kHz
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------- irq generation ----------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// 74ls164
|
||||
reg [7:0] xsint_delay;
|
||||
always @(posedge clk_2 or negedge xsint) begin
|
||||
if(!xsint) xsint_delay <= 8'h00; // async reset
|
||||
else xsint_delay <= {xsint_delay[6:0], xsint};
|
||||
end
|
||||
|
||||
assign xsint_d = xsint_delay[7];
|
||||
|
||||
// dma sound
|
||||
reg [1:0] ctrl;
|
||||
reg [22:0] snd_bas, snd_adr, snd_end;
|
||||
reg [22:0] snd_end_latched;
|
||||
reg [7:0] mode;
|
||||
|
||||
// micro wire
|
||||
reg [15:0] mw_data_reg, mw_mask_reg;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register read ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
always @(sel, rw, addr, ctrl, snd_bas, snd_adr, snd_end, mode, mw_data_reg, mw_mask_reg) begin
|
||||
dout = 16'h0000;
|
||||
|
||||
if(sel && rw) begin
|
||||
// control register
|
||||
if(addr == 5'h00) dout[1:0] = { ctrl[1], xsint };
|
||||
|
||||
// frame start address
|
||||
if(addr == 5'h01) dout[7:0] = snd_bas[22:15];
|
||||
if(addr == 5'h02) dout[7:0] = snd_bas[14:7];
|
||||
if(addr == 5'h03) dout[7:1] = snd_bas[6:0];
|
||||
|
||||
// frame address counter
|
||||
if(addr == 5'h04) dout[7:0] = snd_adr[22:15];
|
||||
if(addr == 5'h05) dout[7:0] = snd_adr[14:7];
|
||||
if(addr == 5'h06) dout[7:1] = snd_adr[6:0];
|
||||
|
||||
// frame end address
|
||||
if(addr == 5'h07) dout[7:0] = snd_end_latched[22:15];
|
||||
if(addr == 5'h08) dout[7:0] = snd_end_latched[14:7];
|
||||
if(addr == 5'h09) dout[7:1] = snd_end_latched[6:0];
|
||||
|
||||
// sound mode register
|
||||
if(addr == 5'h10) dout[7:0] = mode;
|
||||
|
||||
// mircowire
|
||||
if(addr == 5'h11) dout = mw_data_reg;
|
||||
if(addr == 5'h12) dout = mw_mask_reg;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register write --------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
reg [6:0] mw_cnt; // micro wire shifter counter
|
||||
|
||||
// micro wire outputs
|
||||
reg mw_clk;
|
||||
reg mw_data;
|
||||
reg mw_done;
|
||||
|
||||
reg dma_start;
|
||||
|
||||
always @(negedge clk) begin
|
||||
if(reset) begin
|
||||
ctrl <= 2'b00; // default after reset: dma off
|
||||
mw_cnt <= 7'h00; // no micro wire transfer in progress
|
||||
dma_start <= 1'b0;
|
||||
end else begin
|
||||
// writing bit 0 of the ctrl register to 1 starts the dma engine
|
||||
dma_start <= sel && !rw && !lds && (addr == 5'h00) && din[0];
|
||||
|
||||
if(sel && !rw) begin
|
||||
if(!lds) begin
|
||||
// control register
|
||||
if(addr == 5'h00) ctrl <= din[1:0];
|
||||
|
||||
// frame start address
|
||||
if(addr == 5'h01) snd_bas[22:15] <= din[7:0];
|
||||
if(addr == 5'h02) snd_bas[14:7] <= din[7:0];
|
||||
if(addr == 5'h03) snd_bas[6:0] <= din[7:1];
|
||||
|
||||
// frame address counter is read only
|
||||
|
||||
// frame end address
|
||||
if(addr == 5'h07) snd_end[22:15] <= din[7:0];
|
||||
if(addr == 5'h08) snd_end[14:7] <= din[7:0];
|
||||
if(addr == 5'h09) snd_end[6:0] <= din[7:1];
|
||||
|
||||
// sound mode register
|
||||
if(addr == 5'h10) mode <= din[7:0];
|
||||
end
|
||||
|
||||
// micro wire has a 16 bit interface
|
||||
if(addr == 5'h12) mw_mask_reg <= din;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------- micro wire interface -----------
|
||||
|
||||
// writing the data register triggers the transfer
|
||||
if((sel && !rw && (addr == 5'h11)) || (mw_cnt != 0)) begin
|
||||
|
||||
if(sel && !rw && (addr == 5'h11)) begin
|
||||
// first bit is evaluated imediately
|
||||
mw_data_reg <= { din[14:0], 1'b0 };
|
||||
mw_data <= din[15];
|
||||
mw_cnt <= 7'h7f;
|
||||
end else if(mw_cnt[2:0] == 3'b000) begin
|
||||
// send/shift next bit every 8 clocks -> 1 MBit/s
|
||||
mw_data_reg <= { mw_data_reg[14:0], 1'b0 };
|
||||
mw_data <= mw_data_reg[15];
|
||||
end
|
||||
|
||||
// rotate mask on first access and on every further 8 clocks
|
||||
if((sel && !rw && (addr == 5'h11)) || (mw_cnt[2:0] == 3'b000)) begin
|
||||
mw_mask_reg <= { mw_mask_reg[14:0], mw_mask_reg[15]};
|
||||
// notify client of valid bits
|
||||
mw_clk <= mw_mask_reg[15];
|
||||
end
|
||||
|
||||
// decrease shift counter
|
||||
if(mw_cnt != 0)
|
||||
mw_cnt <= mw_cnt - 7'd1;
|
||||
|
||||
// indicate end of transfer
|
||||
mw_done <= (mw_cnt == 7'h01);
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------------- audio fifo ------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
localparam FIFO_ADDR_BITS = 2; // four words
|
||||
localparam FIFO_DEPTH = (1 << FIFO_ADDR_BITS);
|
||||
reg [15:0] fifo [FIFO_DEPTH-1:0];
|
||||
reg [FIFO_ADDR_BITS-1:0] writeP, readP;
|
||||
wire fifo_empty = (readP == writeP);
|
||||
wire fifo_full = (readP == (writeP + 2'd1));
|
||||
|
||||
reg [11:0] fifo_underflow /* synthesis noprune */;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// -------------------------------- audio engine -----------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
reg byte; // byte-in-word toggle flag
|
||||
wire [15:0] fifo_out = fifo[readP];
|
||||
wire [7:0] mono_byte = byte?fifo_out[7:0]:fifo_out[15:8]; // TODO: check byte order!!
|
||||
|
||||
// empty the fifo at the correct rate
|
||||
always @(posedge aclk) begin
|
||||
if(reset) begin
|
||||
readP <= 2'd0;
|
||||
fifo_underflow <= 12'd0;
|
||||
byte <= 1'b0;
|
||||
end else begin
|
||||
// no audio playing: silence
|
||||
if(!ctrl[0]) begin
|
||||
audio_l <= 8'd0;
|
||||
audio_r <= 8'd0;
|
||||
end else begin
|
||||
// audio enabled and data in fifo? play it!
|
||||
if(!fifo_empty) begin
|
||||
if(!mode[7]) begin
|
||||
audio_l <= fifo_out[15:8] + 8'd128; // high byte == left channel
|
||||
audio_r <= fifo_out[ 7:0] + 8'd128; // low byte == right channel
|
||||
end else begin
|
||||
audio_l <= mono_byte + 8'd128;
|
||||
audio_r <= mono_byte + 8'd128;
|
||||
byte <= !byte;
|
||||
end
|
||||
|
||||
// increase fifo read pointer everytime in stereo mode and every
|
||||
// second byte in mono mode
|
||||
if(!mode[7] || byte)
|
||||
readP <= readP + 2'd1;
|
||||
end else
|
||||
// for debugging: monitor if fifo runs out of data
|
||||
fifo_underflow <= fifo_underflow + 12'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------- memory engine -----------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// The memory engine is very similar to the one used by the video/shifter
|
||||
// implementation and access the ram while video doesn't need it (in the hsync)
|
||||
|
||||
reg dma_enable; // flag indicating dma engine is active
|
||||
|
||||
// the "dma_enable" signal is permanently active while playing. The adress counter will
|
||||
// reach snd_end, but this will not generate a bus transfer and thus xsint is
|
||||
// released for that event
|
||||
reg frame_done;
|
||||
assign xsint = dma_enable && (snd_adr != snd_end_latched);
|
||||
// assign xsint = dma_enable && !frame_done;
|
||||
|
||||
reg [7:0] frame_cnt /* synthesis noprune */;
|
||||
|
||||
always @(posedge clk32) begin
|
||||
if(reset) begin
|
||||
dma_enable <= 1'b0;
|
||||
writeP <= 2'd0;
|
||||
frame_cnt <= 8'h00;
|
||||
end else begin
|
||||
|
||||
if(!ctrl[0]) begin
|
||||
dma_enable <= 1'b0; // stop dma_enable
|
||||
frame_cnt <= 8'h00;
|
||||
end else begin
|
||||
// dma not enabled enabled, but should be playing? -> start dma
|
||||
if(!dma_enable) begin
|
||||
if(dma_start) begin
|
||||
// start
|
||||
dma_enable <= 1'b1; // start dma
|
||||
snd_adr <= snd_bas; // load audio start address
|
||||
snd_end_latched <= snd_end;
|
||||
frame_cnt <= frame_cnt + 8'h01;
|
||||
end
|
||||
end else begin
|
||||
|
||||
// address will reach end address in next step. indicate end of frame
|
||||
frame_done <= (snd_adr == snd_end_latched-23'd1);
|
||||
|
||||
// fifo not full? read something during hsync using the video cycle
|
||||
if((!fifo_full) && hsync && (bus_cycle == 3)) begin
|
||||
|
||||
if(snd_adr != snd_end_latched) begin
|
||||
// read word from ram
|
||||
fifo[writeP] <= data;
|
||||
writeP <= writeP + 2'd1; // advance fifo ptr
|
||||
snd_adr <= snd_adr + 23'd1; // advance address counter
|
||||
end else begin
|
||||
// check if we just loaded the last sample
|
||||
if(ctrl == 2'b11) begin
|
||||
snd_adr <= snd_bas; // load audio start address
|
||||
snd_end_latched <= snd_end;
|
||||
frame_cnt <= frame_cnt + 8'h01;
|
||||
end else
|
||||
dma_enable <= 1'b0; // else just stop dma
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
49
cores/mist/ste_joystick.v
Normal file
49
cores/mist/ste_joystick.v
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// ste_joystick.v
|
||||
//
|
||||
// Atari STE joystick port implementation for the MiST board
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
// Modified by Juan Carlos González Amestoy.
|
||||
//
|
||||
// This source file is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This source file is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module ste_joystick (
|
||||
// system interface
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
// cpu register interface
|
||||
input [15:0] din,
|
||||
input sel,
|
||||
input [4:0] addr,
|
||||
input uds,
|
||||
input lds,
|
||||
input rw,
|
||||
output reg [15:0] dout
|
||||
);
|
||||
|
||||
// no functionality implemented yet
|
||||
always @(sel, rw, uds, lds, addr) begin
|
||||
dout = 16'h0000;
|
||||
|
||||
if(sel && rw) begin
|
||||
if(addr == 5'h00) dout = 16'hffff; // no fire button pressed
|
||||
if(addr == 5'h02) dout = 16'hffff; // direction set
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -1,7 +1,7 @@
|
||||
//
|
||||
// video.v
|
||||
//
|
||||
// Atari ST shifter implementation for the MiST board
|
||||
// Atari ST(E) shifter implementation for the MiST board
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
@@ -25,10 +25,19 @@
|
||||
|
||||
// Overscan:
|
||||
// http://codercorner.com/fullscrn.txt
|
||||
|
||||
// Examples: automation 000 + 001 + 097: bottom border
|
||||
// automation 196: top + bottom border
|
||||
|
||||
// Todo STE:
|
||||
// http://alive.atari.org/alive12/ste_hwsc.php
|
||||
// http://atari-ste.anvil-soft.com/html/devdocu2.htm
|
||||
// + 3*4 bit palette (4096 colors)
|
||||
// + lowest video base address byte
|
||||
// + video counter writeable
|
||||
// + pixel offset
|
||||
// + line offset
|
||||
// - undocumented 16 pixel "line offset overscan"
|
||||
|
||||
module video (
|
||||
// system interface
|
||||
input clk, // 31.875 MHz
|
||||
@@ -67,6 +76,7 @@ module video (
|
||||
input pal56, // use VGA compatible 56hz for PAL
|
||||
input [1:0] scanlines, // scanlines (00-none 01-25% 10-50% 11-100%)
|
||||
input [15:0] adjust, // hor/ver video adjust
|
||||
input ste, // enable STE featurss
|
||||
|
||||
// signals not affected by scan doubler for internal use like irqs
|
||||
output st_de,
|
||||
@@ -77,6 +87,11 @@ module video (
|
||||
localparam LINE_WIDTH = 10'd640;
|
||||
localparam LINE_BORDER = 10'd80; // width of left and right screen border
|
||||
|
||||
localparam STATE_SYNC = 2'd0;
|
||||
localparam STATE_BLANK = 2'd1;
|
||||
localparam STATE_BORDER = 2'd2;
|
||||
localparam STATE_DISP = 2'd3;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------ internal signals ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -88,10 +103,10 @@ localparam LINE_BORDER = 10'd80; // width of left and right screen border
|
||||
assign st_de = ~me;
|
||||
|
||||
// hsync irq is generated at the rising edge of st_hs
|
||||
assign st_hs = (st_h_state == 2'd0);
|
||||
assign st_hs = (st_h_state == STATE_SYNC);
|
||||
|
||||
// vsync irq is generated at the rising edge of st_vs
|
||||
assign st_vs = (v_state == 2'd0);
|
||||
assign st_vs = (v_state == STATE_SYNC);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// -------------------------------- video mode -------------------------------
|
||||
@@ -149,7 +164,6 @@ wire low = (shmode == 2'd0);
|
||||
// derive number of planes from shiftmode
|
||||
wire [2:0] planes = mono?3'd1:(mid?3'd2:3'd4);
|
||||
|
||||
|
||||
reg [1:0] syncmode;
|
||||
reg [1:0] syncmode_latch;
|
||||
wire pal = (syncmode_latch[1] == 1'b1);
|
||||
@@ -160,101 +174,153 @@ reg [15:0] data_latch[4];
|
||||
localparam BASE_ADDR = 23'h8000; // default video base address 0x010000
|
||||
reg [22:0] _v_bas_ad; // video base address register
|
||||
|
||||
// 16 colors with 3*3 bits each
|
||||
reg [2:0] palette_r[15:0];
|
||||
reg [2:0] palette_g[15:0];
|
||||
reg [2:0] palette_b[15:0];
|
||||
// 16 colors with 3*4 bits each (4 bits for STE, ST only uses 3 bits)
|
||||
reg [3:0] palette_r[15:0];
|
||||
reg [3:0] palette_g[15:0];
|
||||
reg [3:0] palette_b[15:0];
|
||||
|
||||
// STE-only registers
|
||||
reg [7:0] line_offset; // number of words to skip at the end of each line
|
||||
reg [3:0] pixel_offset; // number of pixels to skip at begin of line
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register read ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
always @(reg_sel, reg_rw, reg_uds, reg_lds, reg_addr, _v_bas_ad, shmode, vaddr, syncmode) begin
|
||||
reg_dout = 16'h0000;
|
||||
|
||||
// read registers
|
||||
if(reg_sel && reg_rw) begin
|
||||
always @(reg_sel, reg_rw, reg_uds, reg_lds, reg_addr, _v_bas_ad, shmode, vaddr,
|
||||
syncmode, line_offset, pixel_offset, ste) begin
|
||||
reg_dout = 16'h0000;
|
||||
|
||||
// video base register (r/w)
|
||||
if(reg_addr == 6'h00) reg_dout <= { 8'h00, _v_bas_ad[22:15] };
|
||||
if(reg_addr == 6'h01) reg_dout <= { 8'h00, _v_bas_ad[14: 7] };
|
||||
// read registers
|
||||
if(reg_sel && reg_rw) begin
|
||||
|
||||
// video address counter (ro on ST)
|
||||
if(reg_addr == 6'h02) reg_dout <= { 8'h00, vaddr[22:15] };
|
||||
if(reg_addr == 6'h03) reg_dout <= { 8'h00, vaddr[14:7 ] };
|
||||
if(reg_addr == 6'h04) reg_dout <= { 8'h00, vaddr[6:0], 1'b0 };
|
||||
// video base register (r/w)
|
||||
if(reg_addr == 6'h00) reg_dout <= { 8'h00, _v_bas_ad[22:15] };
|
||||
if(reg_addr == 6'h01) reg_dout <= { 8'h00, _v_bas_ad[14: 7] };
|
||||
if(ste && reg_addr == 6'h06) reg_dout <= { 8'h00, _v_bas_ad[ 6: 0], 1'b0 };
|
||||
|
||||
// syncmode register
|
||||
if(reg_addr == 6'h05) reg_dout <= { 6'h00, syncmode, 8'h00 };
|
||||
// video address counter (ro on ST)
|
||||
if(reg_addr == 6'h02) reg_dout <= { 8'h00, vaddr[22:15] };
|
||||
if(reg_addr == 6'h03) reg_dout <= { 8'h00, vaddr[14:7 ] };
|
||||
if(reg_addr == 6'h04) reg_dout <= { 8'h00, vaddr[6:0], 1'b0 };
|
||||
|
||||
// the color palette registers
|
||||
if(reg_addr >= 6'h20 && reg_addr < 6'h30 ) begin
|
||||
reg_dout[2:0] <= palette_b[reg_addr[3:0]];
|
||||
reg_dout[6:4] <= palette_g[reg_addr[3:0]];
|
||||
reg_dout[10:8] <= palette_r[reg_addr[3:0]];
|
||||
end
|
||||
// syncmode register
|
||||
if(reg_addr == 6'h05) reg_dout <= { 6'h00, syncmode, 8'h00 };
|
||||
|
||||
// shift mode register
|
||||
if(reg_addr == 6'h30) reg_dout <= { 6'h00, shmode, 8'h00 };
|
||||
end
|
||||
if(ste) begin
|
||||
if(reg_addr == 6'h07) reg_dout <= { 8'h00, line_offset };
|
||||
if(reg_addr == 6'h32) reg_dout <= { 12'h000, pixel_offset };
|
||||
end
|
||||
|
||||
// the color palette registers
|
||||
if(reg_addr >= 6'h20 && reg_addr < 6'h30 ) begin
|
||||
reg_dout[3:0] <= palette_b[reg_addr[3:0]];
|
||||
reg_dout[7:4] <= palette_g[reg_addr[3:0]];
|
||||
reg_dout[11:8] <= palette_r[reg_addr[3:0]];
|
||||
|
||||
// return only the 3 msb in non-ste mode
|
||||
if(!ste) begin
|
||||
reg_dout[3] <= 1'b0;
|
||||
reg_dout[7] <= 1'b0;
|
||||
reg_dout[11] <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// shift mode register
|
||||
if(reg_addr == 6'h30) reg_dout <= { 6'h00, shmode, 8'h00 };
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register write --------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// STE video address write signal is evaluated inside memory engine
|
||||
wire ste_vaddr_write = ste && reg_sel && !reg_rw && !reg_lds;
|
||||
|
||||
always @(negedge reg_clk) begin
|
||||
if(reg_reset) begin
|
||||
_v_bas_ad <= BASE_ADDR;
|
||||
shmode <= DEFAULT_MODE; // default video mode 2 => mono
|
||||
syncmode <= 2'b00; // 60hz
|
||||
|
||||
if(DEFAULT_MODE == 0) begin
|
||||
// TOS default palette, can be disabled after tests
|
||||
palette_r[ 0] <= 3'b111; palette_g[ 0] <= 3'b111; palette_b[ 0] <= 3'b111;
|
||||
palette_r[ 1] <= 3'b111; palette_g[ 1] <= 3'b000; palette_b[ 1] <= 3'b000;
|
||||
palette_r[ 2] <= 3'b000; palette_g[ 2] <= 3'b111; palette_b[ 2] <= 3'b000;
|
||||
palette_r[ 3] <= 3'b111; palette_g[ 3] <= 3'b111; palette_b[ 3] <= 3'b000;
|
||||
palette_r[ 4] <= 3'b000; palette_g[ 4] <= 3'b000; palette_b[ 4] <= 3'b111;
|
||||
palette_r[ 5] <= 3'b111; palette_g[ 5] <= 3'b000; palette_b[ 5] <= 3'b111;
|
||||
palette_r[ 6] <= 3'b000; palette_g[ 6] <= 3'b111; palette_b[ 6] <= 3'b111;
|
||||
palette_r[ 7] <= 3'b101; palette_g[ 7] <= 3'b101; palette_b[ 7] <= 3'b101;
|
||||
palette_r[ 8] <= 3'b011; palette_g[ 8] <= 3'b011; palette_b[ 8] <= 3'b011;
|
||||
palette_r[ 9] <= 3'b111; palette_g[ 9] <= 3'b011; palette_b[ 9] <= 3'b011;
|
||||
palette_r[10] <= 3'b011; palette_g[10] <= 3'b111; palette_b[10] <= 3'b011;
|
||||
palette_r[11] <= 3'b111; palette_g[11] <= 3'b111; palette_b[11] <= 3'b011;
|
||||
palette_r[12] <= 3'b011; palette_g[12] <= 3'b011; palette_b[12] <= 3'b111;
|
||||
palette_r[13] <= 3'b111; palette_g[13] <= 3'b011; palette_b[13] <= 3'b111;
|
||||
palette_r[14] <= 3'b011; palette_g[14] <= 3'b111; palette_b[14] <= 3'b111;
|
||||
palette_r[15] <= 3'b000; palette_g[15] <= 3'b000; palette_b[15] <= 3'b000;
|
||||
end else
|
||||
palette_b[ 0] <= 3'b111;
|
||||
if(reg_reset) begin
|
||||
_v_bas_ad <= BASE_ADDR;
|
||||
shmode <= DEFAULT_MODE; // default video mode 2 => mono
|
||||
syncmode <= 2'b00; // 60hz
|
||||
|
||||
// disable STE hard scroll features
|
||||
line_offset <= 8'h00;
|
||||
pixel_offset <= 4'h0;
|
||||
// pixel_offset <= 4'h1; // XXX
|
||||
|
||||
if(DEFAULT_MODE == 0) begin
|
||||
// TOS default palette, can be disabled after tests
|
||||
palette_r[ 0] <= 4'b111; palette_g[ 0] <= 4'b111; palette_b[ 0] <= 4'b111;
|
||||
palette_r[ 1] <= 4'b111; palette_g[ 1] <= 4'b000; palette_b[ 1] <= 4'b000;
|
||||
palette_r[ 2] <= 4'b000; palette_g[ 2] <= 4'b111; palette_b[ 2] <= 4'b000;
|
||||
palette_r[ 3] <= 4'b111; palette_g[ 3] <= 4'b111; palette_b[ 3] <= 4'b000;
|
||||
palette_r[ 4] <= 4'b000; palette_g[ 4] <= 4'b000; palette_b[ 4] <= 4'b111;
|
||||
palette_r[ 5] <= 4'b111; palette_g[ 5] <= 4'b000; palette_b[ 5] <= 4'b111;
|
||||
palette_r[ 6] <= 4'b000; palette_g[ 6] <= 4'b111; palette_b[ 6] <= 4'b111;
|
||||
palette_r[ 7] <= 4'b101; palette_g[ 7] <= 4'b101; palette_b[ 7] <= 4'b101;
|
||||
palette_r[ 8] <= 4'b011; palette_g[ 8] <= 4'b011; palette_b[ 8] <= 4'b011;
|
||||
palette_r[ 9] <= 4'b111; palette_g[ 9] <= 4'b011; palette_b[ 9] <= 4'b011;
|
||||
palette_r[10] <= 4'b011; palette_g[10] <= 4'b111; palette_b[10] <= 4'b011;
|
||||
palette_r[11] <= 4'b111; palette_g[11] <= 4'b111; palette_b[11] <= 4'b011;
|
||||
palette_r[12] <= 4'b011; palette_g[12] <= 4'b011; palette_b[12] <= 4'b111;
|
||||
palette_r[13] <= 4'b111; palette_g[13] <= 4'b011; palette_b[13] <= 4'b111;
|
||||
palette_r[14] <= 4'b011; palette_g[14] <= 4'b111; palette_b[14] <= 4'b111;
|
||||
palette_r[15] <= 4'b000; palette_g[15] <= 4'b000; palette_b[15] <= 4'b000;
|
||||
end else
|
||||
palette_b[ 0] <= 4'b111;
|
||||
|
||||
end else begin
|
||||
// write registers
|
||||
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];
|
||||
end else begin
|
||||
// write registers
|
||||
if(reg_sel && !reg_rw) begin
|
||||
if(!reg_lds) begin
|
||||
|
||||
// video base address hi/mid (ST and STE)
|
||||
if(reg_addr == 6'h00) _v_bas_ad[22:15] <= reg_din[7:0];
|
||||
if(reg_addr == 6'h01) _v_bas_ad[14:7] <= reg_din[7:0];
|
||||
|
||||
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
|
||||
// In the STE setting hi or mid clears the low byte for ST compatibility
|
||||
// in ST mode this doesn't harm
|
||||
if(reg_addr[5:1] == 5'h00) _v_bas_ad[6:0] <= 7'h00;
|
||||
|
||||
// the color palette registers
|
||||
if(reg_addr >= 6'h20 && reg_addr < 6'h30 ) begin
|
||||
if(~reg_uds) begin
|
||||
palette_r[reg_addr[3:0]] <= reg_din[10:8];
|
||||
end
|
||||
// the low byte can only be written in STE mode
|
||||
if(ste && reg_addr == 6'h06) _v_bas_ad[6:0] <= reg_din[7:1];
|
||||
end
|
||||
|
||||
// writing to sync mode toggles between 50 and 60 hz modes
|
||||
if(reg_addr == 6'h05 && !reg_uds) syncmode <= reg_din[9:8];
|
||||
|
||||
// writing special STE registers
|
||||
if(ste && !reg_lds) begin
|
||||
if(reg_addr == 6'h07) line_offset <= reg_din[7:0];
|
||||
if(reg_addr == 6'h32) pixel_offset <= reg_din[3:0];
|
||||
|
||||
// Writing the video address counter happens directly inside the
|
||||
// memory engine further below!!!
|
||||
end
|
||||
|
||||
// the color palette registers, always write bit 3 with zero if not in
|
||||
// ste mode as this is the lsb of ste
|
||||
if(reg_addr >= 6'h20 && reg_addr < 6'h30 ) begin
|
||||
if(!reg_uds) begin
|
||||
if(!ste) palette_r[reg_addr[3:0]] <= { 1'b0 , reg_din[10:8] };
|
||||
else palette_r[reg_addr[3:0]] <= reg_din[11:8];
|
||||
end
|
||||
|
||||
if(~reg_lds) begin
|
||||
palette_g[reg_addr[3:0]] <= reg_din[6:4];
|
||||
palette_b[reg_addr[3:0]] <= reg_din[2:0];
|
||||
end
|
||||
end
|
||||
if(!reg_lds) begin
|
||||
if(!ste) begin
|
||||
palette_g[reg_addr[3:0]] <= { 1'b0, reg_din[6:4] };
|
||||
palette_b[reg_addr[3:0]] <= { 1'b0, reg_din[2:0] };
|
||||
end else begin
|
||||
palette_g[reg_addr[3:0]] <= reg_din[7:4];
|
||||
palette_b[reg_addr[3:0]] <= reg_din[3:0];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if(reg_addr == 6'h30 && ~reg_uds) shmode <= reg_din[9:8];
|
||||
end
|
||||
end
|
||||
if(reg_addr == 6'h30 && !reg_uds) shmode <= reg_din[9:8];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -274,9 +340,9 @@ osd osd (
|
||||
.clk (clk ),
|
||||
.hcnt (vga_hcnt ),
|
||||
.vcnt (vcnt ),
|
||||
.in_r ({stvid_r, stvid_r}),
|
||||
.in_g ({stvid_g, stvid_g}),
|
||||
.in_b ({stvid_b, stvid_b}),
|
||||
.in_r ({stvid_r, 2'b00}),
|
||||
.in_g ({stvid_g, 2'b00}),
|
||||
.in_b ({stvid_b, 2'b00}),
|
||||
|
||||
// receive signal with OSD overlayed
|
||||
.out_r (st_and_osd_r),
|
||||
@@ -286,21 +352,23 @@ osd osd (
|
||||
|
||||
// ----------------------- monochrome video signal ---------------------------
|
||||
// mono uses the lsb of blue palette entry 0 to invert video
|
||||
wire [2:0] blue0 = palette_b[0];
|
||||
wire [3:0] blue0 = palette_b[0];
|
||||
wire mono_bit = blue0[0]^shift_0[15];
|
||||
wire [2:0] mono_rgb = de?{mono_bit, mono_bit, mono_bit}:3'b100;
|
||||
wire [3:0] mono_rgb = de?{mono_bit, mono_bit, mono_bit, mono_bit}:4'b1000;
|
||||
|
||||
// ------------------------- colour video signal -----------------------------
|
||||
|
||||
reg [8:0] color;
|
||||
wire [2:0] color_r = color[8:6];
|
||||
wire [2:0] color_g = color[5:3];
|
||||
wire [2:0] color_b = color[2:0];
|
||||
// For ST compatibility reasons the STE has the color bit order 0321. This is
|
||||
// handled here
|
||||
reg [11:0] color;
|
||||
wire [3:0] color_r = { color[10:8], color[11] };
|
||||
wire [3:0] color_g = { color[ 6:4], color[ 7] };
|
||||
wire [3:0] color_b = { color[ 2:0], color[ 3] };
|
||||
|
||||
// --------------- de-multiplex color and mono into one vga signal -----------
|
||||
wire [2:0] stvid_r = mono?mono_rgb:color_r;
|
||||
wire [2:0] stvid_g = mono?mono_rgb:color_g;
|
||||
wire [2:0] stvid_b = mono?mono_rgb:color_b;
|
||||
wire [3:0] stvid_r = mono?mono_rgb:color_r;
|
||||
wire [3:0] stvid_g = mono?mono_rgb:color_g;
|
||||
wire [3:0] stvid_b = mono?mono_rgb:color_b;
|
||||
|
||||
// shift registers for up to 4 planes
|
||||
reg [15:0] shift_0, shift_1, shift_2, shift_3;
|
||||
@@ -390,6 +458,55 @@ always @(posedge clk) begin
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------- STE hard scroll shifter -----------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// When STE hard scrolling is being used (pixel_offset != 0) then memory reading starts
|
||||
// 16 pixels earlier and data is being moved through an additional shift register
|
||||
|
||||
// extra 32 bit registers required for STE hard scrolling
|
||||
reg [31:0] ste_shift_0, ste_shift_1, ste_shift_2, ste_shift_3;
|
||||
|
||||
// shifted data
|
||||
wire [15:0] ste_shifted_0, ste_shifted_1, ste_shifted_2, ste_shifted_3;
|
||||
|
||||
// connect STE scroll shifters for each plane
|
||||
ste_shifter ste_shifter_0 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_0),
|
||||
.out (ste_shifted_0)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_1 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_1),
|
||||
.out (ste_shifted_1)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_2 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_2),
|
||||
.out (ste_shifted_2)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_3 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_3),
|
||||
.out (ste_shifted_3)
|
||||
);
|
||||
|
||||
// move data into STE hard scroll shift registers
|
||||
always @(posedge clk) begin
|
||||
if((bus_cycle == 4'd14) && (plane == 2'd0)) begin
|
||||
// shift up 16 pixels and load new data into lower bits of shift registers
|
||||
ste_shift_0 <= { ste_shift_0[15:0], data_latch[0] };
|
||||
ste_shift_1 <= { ste_shift_1[15:0], (planes > 3'd1)?data_latch[1]:16'h0000 };
|
||||
ste_shift_2 <= { ste_shift_2[15:0], (planes > 3'd2)?data_latch[2]:16'h0000 };
|
||||
ste_shift_3 <= { ste_shift_3[15:0], (planes > 3'd2)?data_latch[3]:16'h0000 };
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------- scan doubler ------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -407,10 +524,9 @@ reg [15:0] sd_shift_0, sd_shift_1, sd_shift_2, sd_shift_3;
|
||||
// Return border color index (0) if outside display area
|
||||
wire [3:0] sd_index = (!me_v)?4'd0:
|
||||
{ sd_shift_3[15], sd_shift_2[15], sd_shift_1[15], sd_shift_0[15]};
|
||||
|
||||
|
||||
// line buffer for two lines of 720 pixels (640 + 2 * 40 border) 3 * 3 bit rgb data
|
||||
reg [8:0] sd_buffer [(2*(LINE_WIDTH+2*LINE_BORDER))-1:0];
|
||||
// line buffer for two lines of 720 pixels (640 + 2 * 40 border) 3 * 4 (STE!) bit rgb data
|
||||
reg [11:0] sd_buffer [(2*(LINE_WIDTH+2*LINE_BORDER))-1:0];
|
||||
|
||||
// the scan doubler needs to know which border (left or right) is currently being displayed
|
||||
reg sd_border_side;
|
||||
@@ -428,22 +544,27 @@ always @(posedge clk) begin
|
||||
else sd_vcnt <= sd_vcnt + 2'd1;
|
||||
end
|
||||
|
||||
// permanently move data from data_latch into scan doublers shift registers
|
||||
// permanently move data from data_latch into scan doublers shift registers
|
||||
if((bus_cycle == 4'd15) && (plane == 2'd0)) begin
|
||||
// clear shift registers since some of them may be unused and need to forced to 0
|
||||
sd_shift_1 <= 16'h0000;
|
||||
sd_shift_2 <= 16'h0000;
|
||||
sd_shift_3 <= 16'h0000;
|
||||
|
||||
// load data into shift registers as required by color depth
|
||||
sd_shift_0 <= data_latch[0];
|
||||
if(planes > 3'd1)
|
||||
sd_shift_1 <= data_latch[1];
|
||||
if(planes > 3'd2) begin
|
||||
sd_shift_2 <= data_latch[2];
|
||||
sd_shift_3 <= data_latch[3];
|
||||
// normally data is directly moved from the input latches into the
|
||||
// shift registers. Only on an ste with pixel scrolling enabled
|
||||
// the data is moved through additional shift registers
|
||||
if(!ste || (pixel_offset == 0)) begin
|
||||
// load data into shift registers as required by color depth
|
||||
sd_shift_0 <= data_latch[0];
|
||||
sd_shift_1 <= (planes > 3'd1)?data_latch[1]:16'h0000;
|
||||
sd_shift_2 <= (planes > 3'd2)?data_latch[2]:16'h0000;
|
||||
sd_shift_3 <= (planes > 3'd2)?data_latch[3]:16'h0000;
|
||||
end else begin
|
||||
sd_shift_0 <= ste_shifted_0;
|
||||
sd_shift_1 <= ste_shifted_1;
|
||||
sd_shift_2 <= ste_shifted_2;
|
||||
sd_shift_3 <= ste_shifted_3;
|
||||
end
|
||||
|
||||
end else begin
|
||||
// do the actual shifting
|
||||
if((planes == 3'd1) ||
|
||||
((planes == 3'd2) && (vga_hcnt[0] == 1'b1)) ||
|
||||
((planes == 3'd4) && (vga_hcnt[1:0] == 2'b11))) begin
|
||||
@@ -465,10 +586,10 @@ always @(posedge clk) begin
|
||||
// well to have the scan doubler delay in the border colors as well just in
|
||||
// case a program changes border colors dynamically (e.g. different colors
|
||||
// for left and right or top and bottom borders)
|
||||
if(st_h_state == 2'd3) begin
|
||||
if(st_h_state == STATE_DISP) begin
|
||||
sd_buffer[{LINE_BORDER + st_hcnt[9:0], sd_toggle}] <=
|
||||
{palette_r[sd_index], palette_g[sd_index], palette_b[sd_index]};
|
||||
end else if(st_h_state == 2'd2) begin
|
||||
end else if(st_h_state == STATE_BORDER) begin
|
||||
// move bites from left/right border into appropriate places in the line buffer
|
||||
if(!sd_border_side)
|
||||
// left border
|
||||
@@ -496,16 +617,21 @@ reg [1:0] plane;
|
||||
// 16, 32 or 64 pixel ahead of display enable
|
||||
reg me, me_v;
|
||||
|
||||
// required pixel offset allowing for prefetch of 1, 2 or 4 planes (16, 32 or 64 pixels)
|
||||
// required pixel offset allowing for prefetch of 16 pixels in 1, 2 or 4 planes (16, 32 or 64 cycles)
|
||||
wire [9:0] memory_prefetch = scan_doubler_enable?{ 4'd0, planes, 3'd0 }:{ 3'd0, planes, 4'd0 };
|
||||
wire [9:0] me_h_start = t5_h_end - memory_prefetch;
|
||||
// ste is starting another 16 pixels earlier if horizontal hard scroll is being used
|
||||
wire [9:0] ste_prefetch = (ste && (pixel_offset != 0))?memory_prefetch:10'd0;
|
||||
wire [9:0] me_h_start = t5_h_end - memory_prefetch - ste_prefetch;
|
||||
wire [9:0] me_h_end = t0_h_border_right - memory_prefetch;
|
||||
// line offset required for scan doubler
|
||||
wire [9:0] me_v_offset = scan_doubler_enable?10'd2:10'd0;
|
||||
wire [9:0] me_v_start = t11_v_end - me_v_offset;
|
||||
wire [9:0] me_v_end = t6_v_border_bot - me_v_offset;
|
||||
|
||||
|
||||
// with scan doubler being active, there are two main clock cycles per st hor counter
|
||||
// st_h_active makes sure these events only trigger once
|
||||
wire st_h_active = (!scan_doubler_enable || bus_cycle[0]);
|
||||
|
||||
always @(posedge clk) begin
|
||||
|
||||
// line in which memory access is enabled
|
||||
@@ -517,7 +643,7 @@ always @(posedge clk) begin
|
||||
|
||||
// memory enable signal 16/32/64 bits (16*planes) ahead of display enable (de)
|
||||
// include bus cycle to stay in sync in scna doubler mode
|
||||
if(me_v && bus_cycle[0]) begin
|
||||
if(me_v && st_h_active) begin
|
||||
if(st_hcnt == me_h_start) me <= 1'b1;
|
||||
if(st_hcnt == me_h_end) me <= 1'b0;
|
||||
end
|
||||
@@ -552,6 +678,22 @@ always @(posedge clk) begin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// STE has additional ways to influence video address
|
||||
if(ste) begin
|
||||
// add line offset at the end of each video line
|
||||
if(me_v && st_h_active && (st_hcnt == t2_h_sync))
|
||||
vaddr <= vaddr + line_offset;
|
||||
// vaddr <= vaddr - 23'd4; // XXX
|
||||
|
||||
// STE vaddr write handling
|
||||
// bus_cycle 6 is in the middle of a cpu cycle
|
||||
if((bus_cycle == 6) && ste_vaddr_write) begin
|
||||
if(reg_addr == 6'h02) vaddr[22:15] <= reg_din[7:0];
|
||||
if(reg_addr == 6'h03) vaddr[14: 7] <= reg_din[7:0];
|
||||
if(reg_addr == 6'h04) vaddr[ 6: 0] <= reg_din[7:1];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -573,8 +715,9 @@ reg [9:0] vcnt; // vertical line counter
|
||||
reg [1:0] v_state; // 0=sync, 1=blank, 2=border, 3=display
|
||||
|
||||
// blank level is also used during sync
|
||||
wire blank = (v_state == 2'd1) || (vga_h_state == 2'd1) || (v_state == 2'd0) || (vga_h_state == 2'd0);
|
||||
wire de = (v_state == 2'd3) && (vga_h_state == 2'd3);
|
||||
wire blank = (v_state == STATE_BLANK) || (vga_h_state == STATE_BLANK) ||
|
||||
(v_state == STATE_SYNC) || (vga_h_state == STATE_SYNC);
|
||||
wire de = (v_state == STATE_DISP) && (vga_h_state == STATE_DISP);
|
||||
|
||||
// time in horizontal timing where vertical states change (at the begin of the sync phase)
|
||||
wire [9:0] v_event = t2_h_sync;
|
||||
@@ -602,10 +745,10 @@ always @(posedge clk) begin
|
||||
if( vga_hcnt == t3_h_blank_left - adjust_h ) vga_h_sync <= 1'b0;
|
||||
|
||||
// generate horizontal video signal states
|
||||
if( vga_hcnt == t2_h_sync ) vga_h_state <= 2'd0;
|
||||
if((vga_hcnt == t0_h_border_right) || (vga_hcnt == t4_h_border_left)) vga_h_state <= 2'd2;
|
||||
if((vga_hcnt == t1_h_blank_right) || (vga_hcnt == t3_h_blank_left)) vga_h_state <= 2'd1;
|
||||
if( vga_hcnt == t5_h_end) vga_h_state <= 2'd3;
|
||||
if( vga_hcnt == t2_h_sync ) vga_h_state <= STATE_SYNC;
|
||||
if((vga_hcnt == t0_h_border_right) || (vga_hcnt == t4_h_border_left)) vga_h_state <= STATE_BORDER;
|
||||
if((vga_hcnt == t1_h_blank_right) || (vga_hcnt == t3_h_blank_left)) vga_h_state <= STATE_BLANK;
|
||||
if( vga_hcnt == t5_h_end) vga_h_state <= STATE_DISP;
|
||||
|
||||
// ------------- horizontal ST timing generation -------------
|
||||
// Run st timing at full speed if no scan doubler is being used. Otherwise run
|
||||
@@ -620,10 +763,10 @@ always @(posedge clk) begin
|
||||
st_hcnt <= st_hcnt + 10'd1;
|
||||
|
||||
// generate horizontal video signal states
|
||||
if( st_hcnt == t2_h_sync ) st_h_state <= 2'd0;
|
||||
if((st_hcnt == t0_h_border_right) || (st_hcnt == t4_h_border_left)) st_h_state <= 2'd2;
|
||||
if((st_hcnt == t1_h_blank_right) || (st_hcnt == t3_h_blank_left)) st_h_state <= 2'd1;
|
||||
if( st_hcnt == t5_h_end) st_h_state <= 2'd3;
|
||||
if( st_hcnt == t2_h_sync ) st_h_state <= STATE_SYNC;
|
||||
if((st_hcnt == t0_h_border_right) || (st_hcnt == t4_h_border_left)) st_h_state <= STATE_BORDER;
|
||||
if((st_hcnt == t1_h_blank_right) || (st_hcnt == t3_h_blank_left)) st_h_state <= STATE_BLANK;
|
||||
if( st_hcnt == t5_h_end) st_h_state <= STATE_DISP;
|
||||
end
|
||||
|
||||
// vertical state changes at end of hsync (begin of left blank)
|
||||
@@ -638,11 +781,46 @@ always @(posedge clk) begin
|
||||
if( vcnt == t9_v_blank_top - adjust_v ) vga_v_sync <= 1'b0;
|
||||
|
||||
// generate vertical video signal states
|
||||
if( vcnt == t8_v_sync ) v_state <= 2'd0;
|
||||
if((vcnt == t6_v_border_bot) || (vcnt == t10_v_border_top)) v_state <= 2'd2;
|
||||
if((vcnt == t7_v_blank_bot) || (vcnt == t9_v_blank_top)) v_state <= 2'd1;
|
||||
if( vcnt == t11_v_end) v_state <= 2'd3;
|
||||
if( vcnt == t8_v_sync ) v_state <= STATE_SYNC;
|
||||
if((vcnt == t6_v_border_bot) || (vcnt == t10_v_border_top)) v_state <= STATE_BORDER;
|
||||
if((vcnt == t7_v_blank_bot) || (vcnt == t9_v_blank_top)) v_state <= STATE_BLANK;
|
||||
if( vcnt == t11_v_end) v_state <= STATE_DISP;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------- STE hard scroll shifter -----------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module ste_shifter (
|
||||
input [3:0] skew,
|
||||
input [31:0] in,
|
||||
output reg [15:0] out
|
||||
);
|
||||
|
||||
always @(skew, in) begin
|
||||
out = 16'h0000;
|
||||
|
||||
case(skew)
|
||||
15: out = in[16:1];
|
||||
14: out = in[17:2];
|
||||
13: out = in[18:3];
|
||||
12: out = in[19:4];
|
||||
11: out = in[20:5];
|
||||
10: out = in[21:6];
|
||||
9: out = in[22:7];
|
||||
8: out = in[23:8];
|
||||
7: out = in[24:9];
|
||||
6: out = in[25:10];
|
||||
5: out = in[26:11];
|
||||
4: out = in[27:12];
|
||||
3: out = in[28:13];
|
||||
2: out = in[29:14];
|
||||
1: out = in[30:15];
|
||||
0: out = in[31:16];
|
||||
endcase; // case (skew)
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user