mirror of
https://github.com/MiSTer-devel/PCXT_MiSTer.git
synced 2026-05-24 03:04:19 +00:00
Hercules CRTC updated to UM6845R
Thank you @gyurco for the idea and the tips for implementation.
This commit is contained in:
@@ -1,287 +0,0 @@
|
||||
// Graphics Gremlin
|
||||
//
|
||||
// Copyright (c) 2021 Eric Schlaepfer
|
||||
// This work is licensed under the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International License. To view a copy of this license, visit
|
||||
// http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative
|
||||
// Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
||||
//
|
||||
`default_nettype wire
|
||||
module crtc6845(
|
||||
input clk,
|
||||
input divclk,
|
||||
|
||||
// ISA bus
|
||||
input cs,
|
||||
input a0,
|
||||
input write,
|
||||
input read,
|
||||
input[7:0] bus,
|
||||
output reg [7:0] bus_out,
|
||||
|
||||
input lock,
|
||||
output std_hsyncwidth,
|
||||
|
||||
// Video control signals
|
||||
output hsync,
|
||||
output vsync,
|
||||
output hblank,
|
||||
output vblank,
|
||||
output vblank_border,
|
||||
output display_enable,
|
||||
output cursor,
|
||||
output [13:0] mem_addr,
|
||||
output [4:0] row_addr,
|
||||
output line_reset);
|
||||
|
||||
parameter H_TOTAL = 0;
|
||||
parameter H_DISP = 0;
|
||||
parameter H_SYNCPOS = 0;
|
||||
parameter H_SYNCWIDTH = 0;
|
||||
parameter V_TOTAL = 0;
|
||||
parameter V_TOTALADJ = 0;
|
||||
parameter V_DISP = 0;
|
||||
parameter V_SYNCPOS = 0;
|
||||
parameter V_MAXSCAN = 0;
|
||||
parameter C_START = 0;
|
||||
parameter C_END = 0;
|
||||
|
||||
reg[4:0] cur_addr;
|
||||
|
||||
// Address register
|
||||
always @ (posedge clk) begin
|
||||
if (~a0 & write & cs) begin
|
||||
cur_addr <= bus[4:0];
|
||||
end
|
||||
end
|
||||
|
||||
// Register file
|
||||
always @ (posedge clk) begin
|
||||
if (a0 & write & cs & (~lock | (cur_addr > 5'd9))) begin
|
||||
case (cur_addr)
|
||||
5'd0: h_total <= bus;
|
||||
5'd1: h_disp <= bus;
|
||||
5'd2: h_syncpos <= bus;
|
||||
5'd3: h_syncwidth <= bus[3:0];
|
||||
5'd4: v_total <= bus[6:0];
|
||||
5'd5: v_totaladj <= bus[4:0];
|
||||
5'd6: v_disp <= bus[6:0];
|
||||
5'd7: v_syncpos <= bus[6:0];
|
||||
// Register 8 not implemented
|
||||
5'd9: v_maxscan <= bus[4:0];
|
||||
5'd10: c_start <= bus[6:0];
|
||||
5'd11: c_end <= bus[4:0];
|
||||
5'd12: start_a_1[13:8] <= bus[5:0];
|
||||
5'd13: start_a_1[7:0] <= bus;
|
||||
5'd14: cursor_a[13:8] <= bus[5:0];
|
||||
5'd15: cursor_a[7:0] <= bus;
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
// TODO: Add light pen register (optional)
|
||||
always @ (*)
|
||||
begin
|
||||
case (cur_addr)
|
||||
5'd0: bus_out <= h_total;
|
||||
5'd1: bus_out <= h_disp;
|
||||
5'd2: bus_out <= h_syncpos;
|
||||
5'd3: bus_out <= h_syncwidth;
|
||||
5'd4: bus_out <= v_total;
|
||||
5'd5: bus_out <= v_totaladj;
|
||||
5'd6: bus_out <= v_disp;
|
||||
5'd7: bus_out <= v_syncpos;
|
||||
5'd8: bus_out <= 8'h00;
|
||||
5'd9: bus_out <= v_maxscan;
|
||||
5'd10: bus_out <= c_start;
|
||||
5'd11: bus_out <= c_end;
|
||||
5'd12: bus_out <= {2'b00, start_a[13:8]};
|
||||
5'd13: bus_out <= start_a[7:0];
|
||||
5'd14: bus_out <= {2'b00, cursor_a[13:8]};
|
||||
5'd15: bus_out <= cursor_a[7:0];
|
||||
5'd16: bus_out <= 8'h00; // Light pen regs
|
||||
5'd17: bus_out <= 8'h00;
|
||||
default: bus_out <= 8'h00;
|
||||
endcase;
|
||||
end
|
||||
|
||||
// TODO: parameterize these defaults
|
||||
reg [7:0] h_total = H_TOTAL; //R0 97
|
||||
reg [7:0] h_disp = H_DISP; //R1 80
|
||||
reg [7:0] h_syncpos = H_SYNCPOS; //R2 82
|
||||
reg [3:0] h_syncwidth = H_SYNCWIDTH; //R3 15
|
||||
|
||||
reg [6:0] v_total = V_TOTAL; //R4 25
|
||||
reg [4:0] v_totaladj = V_TOTALADJ; //R5 6
|
||||
reg [6:0] v_disp = V_DISP; //R6 25
|
||||
reg [6:0] v_syncpos = V_SYNCPOS; //R7 25
|
||||
reg [4:0] v_maxscan = V_MAXSCAN; //R9 13
|
||||
|
||||
reg [6:0] c_start = C_START; //R10 11
|
||||
reg [4:0] c_end = C_END; //R11 12
|
||||
|
||||
reg [13:0] start_a = 14'd0; //R13/R14
|
||||
reg [13:0] start_a_1 = 14'd0; //R13/R14
|
||||
|
||||
reg [13:0] cursor_a = 14'd92; //R14/R15
|
||||
|
||||
// Counters
|
||||
reg [7:0] h_count = 8'd0;
|
||||
reg [3:0] h_synccount = 4'd1; // Must start at 1
|
||||
reg [4:0] v_scancount = 5'd0;
|
||||
reg [6:0] v_rowcount = 7'd0;
|
||||
reg [3:0] v_synccount = 4'd0;
|
||||
reg [4:0] cursor_counter = 5'd0; // Cursor blink
|
||||
|
||||
|
||||
wire [4:0] next_v_scancount;
|
||||
wire [13:0] ma = 14'd0;
|
||||
reg [13:0] ma_rst = 14'd0; // Column reset of memory address
|
||||
|
||||
reg [1:0] vs_del;
|
||||
reg vs = 1'b0;
|
||||
reg hs = 1'b0;
|
||||
reg hdisp = 1'b1;
|
||||
reg vdisp = 1'b1;
|
||||
reg vdisp_border = 1'b1;
|
||||
reg [12:0] hdisp_del;
|
||||
|
||||
wire cur_on;
|
||||
wire blink;
|
||||
|
||||
wire h_end;
|
||||
wire v_end;
|
||||
|
||||
assign std_hsyncwidth = (h_syncwidth == 4'hA);
|
||||
|
||||
assign vsync = vs;
|
||||
assign hsync = hs;
|
||||
assign display_enable = hdisp & vdisp;
|
||||
assign hblank = ~hdisp;
|
||||
assign vblank = ~vdisp;
|
||||
assign vblank_border = ~vdisp_border;
|
||||
|
||||
assign row_addr = v_scancount;
|
||||
|
||||
assign h_end = (h_count == h_total);
|
||||
|
||||
assign line_reset = h_end;
|
||||
|
||||
// Horizontal counter
|
||||
always @ (posedge clk)
|
||||
begin
|
||||
if (divclk) begin
|
||||
if (h_count == h_total) begin
|
||||
h_count <= 8'd0;
|
||||
hdisp <= 1'b1;
|
||||
end else begin
|
||||
h_count <= h_count + 1'b1;
|
||||
// Blanking
|
||||
if (h_count + 1 == h_disp) begin
|
||||
hdisp <= 1'b0;
|
||||
end
|
||||
// Sync output
|
||||
if (h_count + 1 == h_syncpos) begin
|
||||
hs <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Horizontal sync timer
|
||||
if (divclk & hs) begin
|
||||
if (h_synccount == h_syncwidth) begin
|
||||
h_synccount <= 4'b1;
|
||||
hs <= 1'b0;
|
||||
end else begin
|
||||
h_synccount <= h_synccount + 4'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
assign v_end = (v_rowcount == v_total) &
|
||||
(v_scancount == v_maxscan + v_totaladj);
|
||||
|
||||
// Vertical counter
|
||||
always @ (posedge clk)
|
||||
begin
|
||||
if (divclk & (h_count == h_total)) begin // was h_syncpos
|
||||
|
||||
vs_del <= {vs_del[0], vs};
|
||||
|
||||
// Handle vertical border blanking
|
||||
if ((v_rowcount + 1 == v_syncpos) && (v_scancount + 1 == v_maxscan))
|
||||
vdisp_border <= 1'b0;
|
||||
|
||||
if (v_rowcount != v_total) begin
|
||||
// Vertical count event
|
||||
if (v_scancount != v_maxscan) begin
|
||||
v_scancount <= v_scancount + 1'b1;
|
||||
end else begin
|
||||
v_scancount <= 0;
|
||||
v_rowcount <= v_rowcount + 1'b1;
|
||||
|
||||
// Handle vertical pulse
|
||||
if (v_rowcount + 1 == v_syncpos) begin
|
||||
vs <= 1'b1;
|
||||
end
|
||||
|
||||
// Handle blanking
|
||||
if (v_rowcount + 1 == v_disp) begin
|
||||
vdisp <= 1'b0;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
// Pad with vertical adjust
|
||||
|
||||
if (v_scancount != v_maxscan + v_totaladj) begin
|
||||
v_scancount <= v_scancount + 1'b1;
|
||||
|
||||
end else begin
|
||||
|
||||
v_scancount <= 0;
|
||||
v_rowcount <= 0;
|
||||
vdisp <= 1'b1;
|
||||
cursor_counter <= cursor_counter + 1'b1;
|
||||
start_a <= start_a_1;
|
||||
end
|
||||
end
|
||||
|
||||
// Vertical sync pulse is fixed at 16 scan line times
|
||||
// Vsync pulse turns off after 16 lines
|
||||
if (vs) begin
|
||||
if (v_synccount == 4'd15) begin
|
||||
v_synccount <= 4'd0;
|
||||
vs <= 0;
|
||||
end else begin
|
||||
v_synccount <= v_synccount + 1'b1;
|
||||
end
|
||||
end
|
||||
else if (vs_del == 2'b10) vdisp_border <= 1'b1;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// Cursor
|
||||
assign cur_on = (v_scancount >= c_start[4:0]) &
|
||||
(v_scancount <= c_end[4:0]);
|
||||
assign blink = (c_start[6:5] == 2'b00) |
|
||||
(c_start[5] ? cursor_counter[4] : cursor_counter[3]);
|
||||
assign cursor = (cursor_a == mem_addr) & cur_on &
|
||||
blink & (c_start[6:5] != 2'b01) & display_enable;
|
||||
|
||||
// Memory address generator
|
||||
assign mem_addr = start_a + ma_rst + {6'b000000, h_count};
|
||||
always @ (posedge clk)
|
||||
begin
|
||||
if (divclk & (v_end | h_end)) begin
|
||||
if (v_end) begin
|
||||
ma_rst <= 14'd0;
|
||||
end else begin
|
||||
if (v_scancount == v_maxscan) begin
|
||||
ma_rst <= ma_rst + {6'b000000, h_disp};
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -168,28 +168,33 @@ module hgc(
|
||||
end
|
||||
end
|
||||
|
||||
// CRT controller (MC6845 compatible)
|
||||
crtc6845 crtc (
|
||||
.clk(clk),
|
||||
.divclk(crtc_clk),
|
||||
.cs(crtc_cs),
|
||||
.a0(bus_a[0]),
|
||||
.write(~bus_iow_synced_l),
|
||||
.read(~bus_ior_synced_l),
|
||||
.bus(bus_d),
|
||||
.bus_out(bus_out_crtc),
|
||||
// .lock(HGC_70HZ == 1),
|
||||
.lock(1'b0),
|
||||
.hsync(hsync_int),
|
||||
.vsync(vsync_l),
|
||||
.hblank(hblank),
|
||||
.vblank(vblank),
|
||||
.display_enable(display_enable),
|
||||
.cursor(cursor),
|
||||
.mem_addr(crtc_addr),
|
||||
.row_addr(row_addr)
|
||||
);
|
||||
UM6845R crtc (
|
||||
.CLOCK(clk),
|
||||
.CLKEN(crtc_clk),
|
||||
// .nCLKEN(),
|
||||
.nRESET(1'b1),
|
||||
.CRTC_TYPE(1'b1),
|
||||
|
||||
.ENABLE(1'b1),
|
||||
.nCS(~crtc_cs),
|
||||
.R_nW(bus_iow_synced_l),
|
||||
.RS(bus_a[0]),
|
||||
.DI(bus_d),
|
||||
.DO(bus_out_crtc),
|
||||
|
||||
.hblank(hblank),
|
||||
.vblank(vblank),
|
||||
|
||||
.VSYNC(vsync_l),
|
||||
.HSYNC(hsync_int),
|
||||
.DE(display_enable),
|
||||
// .FIELD(),
|
||||
.CURSOR(cursor),
|
||||
|
||||
.MA(crtc_addr),
|
||||
.RA(row_addr)
|
||||
|
||||
);
|
||||
|
||||
// if (HGC_70HZ) begin
|
||||
defparam crtc.H_TOTAL = 8'd99;
|
||||
|
||||
@@ -5,7 +5,6 @@ set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) hgc_att
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) hgc.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) vram.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) UM6845R.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) crtc6845.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) cga_vram.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) cga_vgaport.v ]
|
||||
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) cga_sequencer.v ]
|
||||
|
||||
Reference in New Issue
Block a user