Hercules CRTC updated to UM6845R

Thank you @gyurco for the idea and the tips for implementation.
This commit is contained in:
Aitor Gómez
2023-03-12 17:33:42 +01:00
parent be57d55493
commit 6ea12e71f1
3 changed files with 26 additions and 309 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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 ]