1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-02-16 12:42:49 +00:00
Files
Gehstock.Mist_FPGA/Console_MiST/Gamate_MiST/rtl/lcd.sv
2022-06-19 19:13:03 +02:00

134 lines
3.7 KiB
Systemverilog

module lcd
(
input clk,
input ce,
input reset,
input lcd_cs,
input cpu_rwn,
input [2:0] AB,
input [7:0] dbus_in,
output [7:0] dbus_out,
output dbus_oe,
output ce_pix,
output reg [1:0]pixel,
output hsync,
output vsync,
output reg hblank,
output reg vblank
);
// frame_len = 72900 cycles at 4.433mhz
localparam H_WIDTH = 9'd282;
localparam V_HEIGHT = 9'd259;
localparam LCD_XSIZE = 9'd160;
localparam LCD_YSIZE = 9'd150;
localparam FRAME_LEN = 72900;
reg [7:0] lcd_ctl, xscroll, yscroll, xpos;
reg [8:0] hblank_start, hblank_end, vblank_start, vblank_end, vpos, hpos;
integer dot_count;
wire [7:0] vram_buf_high, vram_buf_low, vram_high_dout, vram_low_dout;
wire [7:0] yscroll_adj = yscroll > 8'hC7 ? 8'h00 : yscroll;
wire ram_write = AB == 7 && ~cpu_rwn && lcd_cs;
wire [12:0] draw_addr;
reg [12:0] vram_addr;
wire plane = xpos[7];
dpram #(.addr_width(13)) vram_high
(
.clock (clk),
.address_a (vram_addr),
.data_a (dbus_in),
.q_a (vram_high_dout),
.wren_a ((lcd_ctl[4] ? ~plane : plane) & ram_write),
.address_b (draw_addr),
.q_b (vram_buf_high)
);
dpram #(.addr_width(13)) vram_low
(
.clock (clk),
.address_a (vram_addr),
.data_a (dbus_in),
.q_a (vram_low_dout),
.wren_a ((lcd_ctl[4] ? plane : ~plane) & ram_write),
.address_b (draw_addr),
.q_b (vram_buf_low)
);
assign dbus_out = (lcd_ctl[4] ? ~plane : plane) ? vram_high_dout : vram_low_dout;
wire hblank_im = hpos <= hblank_end || hpos > hblank_end + LCD_XSIZE;
wire vblank_im = vpos < vblank_end || vpos >= vblank_end + LCD_YSIZE;
assign vsync = vpos < 2 || vpos > V_HEIGHT - 1'd1; // Catch the uneven line in vsync to see if it helps
assign hsync = hpos < 16 || hpos > (H_WIDTH - 8'd16);
// There is a complex quirky "mode2" that no games use that is not implemented. There's no software
// with which to test it, so it's really pointless unless someone makes some homebrew or something
// that wants to draw graphics in this way.
wire in_window = lcd_ctl[5] && (vpos - vblank_end) < 16; // Account for window mode
wire [8:0] vpos_off = (vpos - vblank_end) + (in_window ? 8'hD0 : yscroll_adj);
wire [8:0] hpos_off = (hpos - hblank_end) + (in_window ? 8'h00 : xscroll);
wire [8:0] vpos_wrap = vpos_off > 199 && ~in_window ? vpos_off - 8'd200 : vpos_off;
assign draw_addr = (vpos_wrap * 8'h20) + (hpos_off >> 3);
assign ce_pix = ce;
assign dbus_oe = AB == 6;
always_ff @(posedge clk) begin
if (ce) begin
hblank <= hblank_im;
vblank <= vblank_im;
pixel <= lcd_ctl[7] ? 2'b00 : {vram_buf_high[~hpos_off[2:0]], vram_buf_low[~hpos_off[2:0]]};
dot_count <= dot_count + 1'd1;
hpos <= hpos + 1'd1;
if (hpos == (H_WIDTH - 1'd1)) begin
hpos <= 0;
vpos <= vpos + 1'd1;
end
// Synchronize with real frame, we'll see how it goes. This assumes 160x160.
if (dot_count == FRAME_LEN) begin
hpos <= 0;
vpos <= 0;
dot_count <= 0;
hblank_end <= (H_WIDTH - LCD_XSIZE) >> 1'd1;
vblank_end <= (V_HEIGHT - LCD_YSIZE) >> 1'd1;
end
if (lcd_cs) begin
if (~cpu_rwn) begin
case(AB)
6'h01: lcd_ctl <= dbus_in;
6'h02: xscroll <= dbus_in;
6'h03: yscroll <= dbus_in;
6'h04: begin xpos <= dbus_in; vram_addr[4:0] <= dbus_in[4:0]; end
6'h05: begin vram_addr[12:5] <= dbus_in; end
6'h07: vram_addr <= vram_addr + (lcd_ctl[6] ? 6'd32 : 1'd1);
endcase
end else begin
if (AB == 6'h06)
vram_addr <= vram_addr + (lcd_ctl[6] ? 6'd32 : 1'd1);
end
end
end
if (reset) begin
xscroll <= 0;
yscroll <= 0;
vram_addr <= 0;
xpos <= 0;
hblank_end <= (H_WIDTH - LCD_XSIZE) >> 1'd1;
vblank_end <= (V_HEIGHT - LCD_YSIZE) >> 1'd1;
lcd_ctl <= 0;
end
end
endmodule