1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-05-02 22:43:41 +00:00

BBC: update the SDRAM controls and Teletext chip

This commit is contained in:
Gyorgy Szombathelyi
2020-05-18 23:46:27 +02:00
parent c9a10cfe16
commit c550481866
12 changed files with 1315 additions and 4788 deletions

View File

@@ -47,8 +47,8 @@ set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (Verilog)"
set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "VERILOG HDL" -section_id eda_simulation
set_global_assignment -name EDA_SIMULATION_TOOL "<None>"
set_global_assignment -name EDA_OUTPUT_DATA_FORMAT NONE -section_id eda_simulation
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
@@ -162,7 +162,6 @@ set_global_assignment -name PHYSICAL_SYNTHESIS_REGISTER_DUPLICATION ON
set_global_assignment -name PHYSICAL_SYNTHESIS_COMBO_LOGIC_FOR_AREA ON
set_global_assignment -name PHYSICAL_SYNTHESIS_MAP_LOGIC_TO_MEMORY_FOR_AREA ON
set_location_assignment PLL_1 -to CLOCKS|altpll_component|auto_generated|pll1
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
set_global_assignment -name VERILOG_FILE ../rtl/sigma_delta_dac.v
set_global_assignment -name VERILOG_FILE ../rtl/audio.v
set_global_assignment -name VERILOG_FILE ../../rtl/adc.v
@@ -174,8 +173,8 @@ set_global_assignment -name VERILOG_FILE ../../rtl/clocks.v
set_global_assignment -name VERILOG_FILE ../../rtl/address_decode.v
set_global_assignment -name VERILOG_FILE ../../rtl/bbc.v
set_global_assignment -name VHDL_FILE ../../rtl/via6522.vhd
set_global_assignment -name VERILOG_FILE ../../rtl/saa5050/saa5050_rom.v
set_global_assignment -name VERILOG_FILE ../../rtl/saa5050/saa5050.v
set_global_assignment -name VHDL_FILE ../../rtl/saa5050/saa5050_rom_dual_port.vhd
set_global_assignment -name VHDL_FILE ../../rtl/saa5050/saa5050.vhd
set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_top.vhd"
set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_tone.vhd"
set_global_assignment -name VHDL_FILE "../../rtl/sn76489-1.0/sn76489_noise.vhd"
@@ -193,4 +192,5 @@ set_global_assignment -name VERILOG_FILE sdram.v
set_global_assignment -name VERILOG_FILE clockgen.v
set_global_assignment -name QIP_FILE "../../../../mist-modules/mist.qip"
set_global_assignment -name VERILOG_FILE "../../../../mist-modules/sd_card.v"
set_global_assignment -name SIGNALTAP_FILE output_files/via.stp
set_global_assignment -name SIGNALTAP_FILE output_files/via.stp
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

View File

@@ -71,9 +71,6 @@ wire core_r, core_g, core_b, core_hs, core_vs;
wire core_clken;
// memory bus signals.
wire [14:0] vid_adr;
wire [7:0] vid_data;
wire [15:0] mem_adr;
wire [3:0] mem_romsel;
@@ -84,6 +81,7 @@ wire [7:0] ram_do;
wire [7:0] mem_do;
wire mem_we;
wire mem_sync;
wire phi0;
// core's raw audio
wire [15:0] coreaud_l, coreaud_r;
@@ -269,8 +267,8 @@ wire reset_in = ~pll_ready || ~sdram_ready || status[0] ||
// synchronize reset with memory state machine
reg reset;
always @(posedge mem_sync)
reset <= reset_in;
always @(posedge clk_32m)
if (mem_sync) reset <= reset_in;
// the autoboot feature simply works by pressing shift for 2 seconds after
// the bbc has been reset
@@ -297,6 +295,7 @@ bbc BBC(
.VIDEO_R ( core_r ),
.VIDEO_G ( core_g ),
.VIDEO_B ( core_b ),
.VIDEO_DE ( video_de ),
.MEM_ADR ( mem_adr ),
.MEM_WE ( mem_we ),
@@ -304,9 +303,7 @@ bbc BBC(
.MEM_DI ( mem_di ),
.MEM_SYNC ( mem_sync ),
.ROMSEL ( mem_romsel ),
.VID_ADR ( vid_adr ),
.VID_DI ( vid_data ),
.PHI0 ( phi0 ),
.SHIFT ( autoboot_shift ),
@@ -347,6 +344,8 @@ wire sdram_we = loader_active?loader_we:(mem_we && (cpu_ram || sideways_ram));
wire [7:0] sdram_di =
loader_active?loader_data:mem_do;
wire video_de;
sdram sdram (
// interface to the MT48LC16M16 chip
.sd_data ( SDRAM_DQ ),
@@ -370,9 +369,7 @@ sdram sdram (
.cpu_we ( sdram_we ),
.cpu_do ( ram_do ),
.vid_blnk ( loader_active ),
.vid_adr ( { 9'd0, vid_adr } ),
.vid_do ( vid_data )
.vid_blnk ( !video_de ) // for refresh
);
wire [7:0] os_do;
@@ -405,6 +402,7 @@ wire basic_map = rommap?(mem_romsel == 4'h0):(mem_romsel == 4'he);
wire mmfs_map = rommap?(mem_romsel == 4'h2):(mem_romsel == 4'hc);
assign mem_di =
~phi0 ? ram_do :
((mem_adr[15:14] == 2'b10) && basic_map) ? basic_do :
((mem_adr[15:14] == 2'b10) && mmfs_map) ? mmfs_do :
((mem_adr[15:14] == 2'b10) && (mem_romsel == 4'ha)) ? ram_do :

View File

@@ -23,14 +23,14 @@
module sdram (
// interface to the MT48LC16M16 chip
inout [15:0] sd_data, // 16 bit bidirectional data bus
output reg [12:0] sd_addr, // 13 bit multiplexed address bus
output reg [1:0] sd_dqm, // two byte masks
output reg[1:0] sd_ba, // two banks
output sd_cs, // a single chip select
output sd_we, // write enable
output sd_ras, // row address select
output sd_cas, // columns address select
inout reg [15:0] sd_data, // 16 bit bidirectional data bus
output reg [12:0] sd_addr, // 13 bit multiplexed address bus
output reg [1:0] sd_dqm, // two byte masks
output reg[1:0] sd_ba, // two banks
output sd_cs, // a single chip select
output sd_we, // write enable
output sd_ras, // row address select
output sd_cas, // columns address select
// cpu/chipset interface
input init, // init signal after FPGA config to initialize RAM
@@ -39,8 +39,6 @@ module sdram (
output ready, // sdram is done initializing
input vid_blnk,
input [24:0] vid_adr, // 24 bit video word address
output reg [7:0] vid_do, // data output to video
input [7:0] cpu_di, // data input from cpu
input [24:0] cpu_adr, // 24 bit cpu word address
@@ -49,10 +47,10 @@ module sdram (
);
// no burst configured
localparam RASCAS_DELAY = 3'd2; // tRCD>=20ns -> 2 cycles@64MHz
localparam RASCAS_DELAY = 3'd1; // tRCD>=20ns -> 1 cycle@32MHz
localparam BURST_LENGTH = 3'b000; // 000=none, 001=2, 010=4, 011=8
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd3; // 2/3 allowed
localparam CAS_LATENCY = 3'd2; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
@@ -71,18 +69,18 @@ localparam STATE_LAST = 3'd7; // last state in cycle
// itself to the cpu cycle. The first fill memory cycle is used for the CPU and the second
// and fourth is used for video. The third cycle is refresh
reg [4:0] q;
reg [3:0] q;
always @(posedge clk) begin
// 32Mhz counter synchronous to cpu
if( sync ) q <= 5'd0;
else q <= q + 5'd1;
if( sync ) q <= 0;
else q <= q + 1'd1;
end
wire cpu_cyc = (q[4:3] == 2'b00);
wire vid_cyc = (q[3] == 1'b1);
wire cpu_cyc = q[3];
wire vid_cyc = ~q[3];
// switch between video and cpu address
wire [24:0] addr = vid_cyc?vid_adr:cpu_adr;
wire [24:0] addr = cpu_adr;
// ---------------------------------------------------------------------
// --------------------------- startup/reset ---------------------------
@@ -122,24 +120,10 @@ assign sd_ras = sd_cmd[2];
assign sd_cas = sd_cmd[1];
assign sd_we = sd_cmd[0];
// drive data if it's the cpu cycle and if the cpu writes
wire cpu_cycle_write = cpu_we && (q[4:3]==2'b00);
assign sd_data = cpu_cycle_write?{cpu_di,cpu_di}:16'bZZZZZZZZZZZZZZZZ;
always @(posedge clk) begin
// latch cpu data at the end of the cpu cycle
if(cpu_cyc && (q[2:0] == 7) && !cpu_we)
cpu_do <= cpu_adr[0]?sd_data[7:0]:sd_data[15:8];
// latch video data at end of video cycle
if(vid_cyc && (q[2:0] == 7)) begin
if(vid_blnk)
vid_do = 8'h00;
else
vid_do <= vid_adr[0]?sd_data[7:0]:sd_data[15:8];
end
sd_cmd <= CMD_INHIBIT;
sd_data <= 16'bZZZZZZZZZZZZZZZZ;
if(reset != 0) begin
sd_ba <= 2'b00;
@@ -153,26 +137,32 @@ always @(posedge clk) begin
if(reset == 2) sd_cmd <= CMD_LOAD_MODE;
end
end else begin
if(q[2:0] <= STATE_CMD_START) begin
sd_addr <= addr[21:9];
sd_ba <= addr[23:22];
sd_dqm <= { addr[0], !addr[0] };
end else
sd_addr <= { 4'b0010, addr[24], addr[8:1]};
if(q[2:0] == STATE_IDLE) begin
sd_cmd <= CMD_AUTO_REFRESH;
if(cpu_cyc || (vid_cyc)&&(!vid_blnk)) // CPU or video transfers data
if(cpu_cyc || (vid_cyc && !vid_blnk)) begin// CPU or video transfers data
sd_cmd <= CMD_ACTIVE;
sd_addr <= addr[21:9];
sd_ba <= addr[23:22];
sd_dqm <= { addr[0], !addr[0] };
end
end else if(q[2:0] == STATE_CMD_CONT) begin
sd_addr <= { 4'b0010, addr[24], addr[8:1]};
if(cpu_cyc) begin // CPU reads or writes
if(cpu_we) sd_cmd <= CMD_WRITE;
if(cpu_we) begin
sd_cmd <= CMD_WRITE;
sd_data <= {cpu_di,cpu_di};
end
else sd_cmd <= CMD_READ;
end else if(vid_cyc && !vid_blnk) // video always reads
sd_cmd <= CMD_READ;
end
end else if (q[2:0] == 5) begin
cpu_do <= addr[0]?sd_data[7:0]:sd_data[15:8];
end
end
end
endmodule

View File

@@ -3,54 +3,52 @@
module bbc(
input CLK32M_I,
input CLK24M_I,
input RESET_I,
output HSYNC,
output VSYNC,
output VIDEO_CLKEN,
output VIDEO_R,
output VIDEO_G,
output VIDEO_B,
// RAM Interface (CPU)
output [15:0] MEM_ADR,
output MEM_WE,
output [7:0] MEM_DO,
input [7:0] MEM_DI,
output [3:0] ROMSEL,
output MEM_SYNC, // signal to synchronite sdram state machine
output [14:0] VID_ADR,
input [7:0] VID_DI,
input CLK24M_I,
input RESET_I,
output HSYNC,
output VSYNC,
output VIDEO_CLKEN,
output VIDEO_R,
output VIDEO_G,
output VIDEO_B,
output VIDEO_DE,
// RAM Interface (CPU)
output [15:0] MEM_ADR,
output MEM_WE,
output [7:0] MEM_DO,
input [7:0] MEM_DI,
output [3:0] ROMSEL,
output MEM_SYNC, // signal to synchronite sdram state machine
output PHI0,
// Keyboard interface
input PS2_CLK,
input PS2_DAT,
input PS2_CLK,
input PS2_DAT,
// audio signal.
output [15:0] AUDIO_L,
output [15:0] AUDIO_R,
// externally pressed "shift" key for autoboot
input SHIFT,
input SHIFT,
output SDSS,
output SDCLK,
output SDMOSI,
input SDMISO,
output SDSS,
output SDCLK,
output SDMOSI,
input SDMISO,
// analog joystick input
input [1:0] joy_but,
input [7:0] joy0_axis0,
input [7:0] joy0_axis1,
input [7:0] joy1_axis0,
input [7:0] joy1_axis1,
// boot settings
input [7:0] DIP_SWITCH
);
@@ -58,6 +56,8 @@ module bbc(
// let sdram state machine synchronize to cpu
assign MEM_SYNC = cpu_clken;
assign ROMSEL = romsel;
assign PHI0 = cpu_phi0;
assign VIDEO_DE = crtc_de;
wire ram_we;
@@ -76,6 +76,7 @@ wire tube_clken;
wire cpu_clken;
wire cpu_cycle;
wire cpu_phi0;
// decode signals
wire ddr_enable;
@@ -248,6 +249,7 @@ clocks CLOCKS(
.cpu_cycle ( cpu_cycle ),
.cpu_clken ( cpu_clken ),
.cpu_phi0 ( cpu_phi0 ),
.ttxt_clken ( ttxt_clken ),
.ttxt_clkenx2 ( ttxt_clkenx2 ),
@@ -262,9 +264,9 @@ address_decode ADDRDECODE(
.ram_enable(ram_enable),
.rom_enable(rom_enable),
.mos_enable(mos_enable),
.io_fred(io_fred),
.io_jim(io_jim),
.io_sheila(io_sheila),
.io_fred(io_fred),
.io_jim(io_jim),
.io_sheila(io_sheila),
.crtc_enable(crtc_enable),
.acia_enable(acia_enable),
.serproc_enable(serproc_enable),
@@ -290,7 +292,7 @@ T65 CPU (
.IRQ_n (cpu_irq_n),
.SO_n (cpu_so_n),
.R_W_n (cpu_r_nw),
.DI (cpu_di),
.DO (cpu_do),
.A (cpu_a)
@@ -412,7 +414,6 @@ adc ADC (
mc6845 CRTC (
.CLOCK(CLK32M_I),
.CLKEN(crtc_clken),
.CLKEN_ADR(crtc_clken_adr),
.nRESET(reset_n),
.ENABLE(crtc_enable),
.R_nW(cpu_r_nw),
@@ -449,11 +450,10 @@ vidproc VIDEO_ULA (
.CLKEN(VIDEO_CLKEN),
.nRESET(reset_n),
.CLKEN_CRTC(crtc_clken),
.CLKEN_CRTC_ADR(crtc_clken_adr),
.ENABLE(vidproc_enable),
.A0(cpu_a[0]),
.DI_CPU(cpu_do),
.DI_RAM(VID_DI[7:0]),
.DI_RAM(MEM_DI[7:0]),
.nINVERT(vidproc_invert_n),
.DISEN(vidproc_disen),
.CURSOR(crtc_cursor),
@@ -469,26 +469,24 @@ vidproc VIDEO_ULA (
saa5050 TELETEXT (
// This runs at 6 MHz, which we can't derive from the 32 MHz clock
.CLOCK ( CLK24M_I ),
.CLKEN ( ttxt_clken ),
.PIXCLKEN ( ttxt_clkenx2 ),
.nRESET ( reset_n ),
// Data input is synchronised to the main cpu bus clock.
.DI_CLOCK ( CLK32M_I ),
.DI_CLKEN ( VIDEO_CLKEN ),
.DI ( VID_DI[6:0] ),
.GLR ( ttxt_glr ),
.DEW ( ttxt_dew ),
.CRS ( ttxt_crs ),
.LOSE ( ttxt_lose ),
.R ( ttxt_r ),
.G ( ttxt_g ),
.B ( ttxt_b )
// This runs at 6 MHz, which we can't derive from the 32 MHz clock
.CLOCK ( CLK24M_I ),
.CLKEN ( ttxt_clkenx2 ),
.nRESET ( reset_n ),
// Data input is synchronised to the main cpu bus clock.
.DI_CLOCK ( CLK32M_I ),
.DI_CLKEN ( VIDEO_CLKEN & mhz4_clken & ~mhz2_clken ),
.DI ( MEM_DI[6:0] ),
.GLR ( ttxt_glr ),
.DEW ( ttxt_dew ),
.CRS ( ttxt_crs ),
.LOSE ( ttxt_lose ),
.R ( ttxt_r ),
.G ( ttxt_g ),
.B ( ttxt_b )
);
initial begin : via_init
@@ -655,11 +653,8 @@ assign sys_via_pb_in[3:0] = sys_via_pb_out[3:0];
assign user_via_pa_in = user_via_pa_out;
assign user_via_pb_in = user_via_pb_out;
assign MEM_ADR = cpu_a[15:0];
assign VID_ADR = display_a;
assign MEM_WE = ram_we;
assign MEM_ADR = cpu_phi0 ? cpu_a[15:0] : display_a;
assign MEM_WE = ram_we & cpu_phi0;
assign MEM_DO = cpu_do;
endmodule

View File

@@ -39,6 +39,7 @@ module clocks(
output wire cpu_cycle,
output wire cpu_clken,
output wire cpu_phi0,
output wire vid_clken,
output wire ttxt_clken,
@@ -71,6 +72,9 @@ assign cpu_cycle = ~(clken_counter[0] | clken_counter[1] | clken_counter[2] | cl
// 0/16^M
assign cpu_clken = cpu_cycle & ~cpu_cycle_mask[1] & ~cpu_cycle_mask[0];
wire [4:0] clken_counter_next = clken_counter - 1'd1;
assign cpu_phi0 = clken_counter_next[3];
always @(posedge clk_32m)
begin : clk_gen
// if (reset_n === 1'b 0)

View File

@@ -47,7 +47,6 @@
module mc6845 (
CLOCK,
CLKEN,
CLKEN_ADR,
nRESET,
ENABLE,
R_nW,
@@ -67,7 +66,6 @@ module mc6845 (
input CLOCK;
input CLKEN;
input CLKEN_ADR;
input nRESET;
input ENABLE;
input R_nW;
@@ -537,7 +535,7 @@ always @(posedge CLOCK) begin
RA <= 'd0;
MA <= 'd0;
end
else if (CLKEN_ADR === 1'b 1 ) begin
else if (CLKEN === 1'b 1 ) begin
// Character row address is just the scan line counter delayed by
// one clock to line up with the syncs.

File diff suppressed because it is too large Load Diff

View File

@@ -1,455 +0,0 @@
// SAA5050 teletext generator
//
// Synchronous implementation for FPGA. Certain TV-specific functions are
// not implemented. e.g.
//
// No /SI pin - 'TEXT' mode is permanently enabled
// No remote control features (/DATA, DLIM)
// No large character support
// No support for box overlay (BLAN, PO, DE)
//
// FIXME: Hold graphics not supported - this needs to be added
//
// Copyright (c) 2011 Mike Stirling
// Copyright (c) 2015 Stephen J. Leary (sleary@vavi.co.uk)
//
// All rights reserved
//
// Redistribution and use in source and synthezised forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in synthesized form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software without
// specific prior written agreement from the author.
//
// * License is granted for non-commercial use only. A fee may not be charged
// for redistributions as source code or in synthesized/hardware form without
// specific prior written agreement from the author.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
module saa5050 (
CLOCK,
CLKEN,
PIXCLKEN,
nRESET,
DI_CLOCK,
DI_CLKEN,
DI,
GLR,
DEW,
CRS,
LOSE,
R,
G,
B,
Y);
input CLOCK;
input CLKEN;
input PIXCLKEN;
input nRESET;
input DI_CLOCK;
input DI_CLKEN;
input [6: 0] DI;
input GLR;
input DEW;
input CRS;
input LOSE;
output R;
output G;
output B;
output Y;
// 6 MHz dot clock enable
reg R;
reg G;
reg B;
reg Y;
// Register inputs in the bus clock domain
reg [6: 0] di_r;
reg dew_r;
reg lose_r;
// Data input registered in the pixel clock domain
reg [6: 0] code;
wire [3: 0] line_addr;
wire [11: 0] rom_address;
wire [7: 0] rom_data;
wire [3:0] line_addr_cmp;
wire [3:0] line_addr_p1;
wire [3:0] line_addr_m1;
wire [11:0] rom_address_cmp;
wire [7:0] rom_data_cmp;
// Delayed display enable derived from LOSE by delaying for one character
reg disp_enable;
// Latched timing signals for detection of falling edges
reg dew_latch;
reg lose_latch;
reg disp_enable_latch;
// Row and column addressing is handled externally. We just need to
// keep track of which of the 10 lines we are on within the character...
reg [3: 0] line_counter;
// ... and which of the 6 pixels we are on within each line
reg [2: 0] pixel_counter;
// We also need to count frames to implement the flash feature.
// The datasheet says this is 0.75 Hz with a 3:1 on/off ratio, so it
// is probably a /64 counter, which gives us 0.78 Hz
reg [5: 0] flash_counter;
// Output shift register
reg odd_pixel;
reg shift_reg_last;
reg [5: 0] shift_reg;
reg shift_reg_cmp_last;
reg [5:0] shift_reg_cmp;
// Flash mask
wire flash;
// Current display state
// Foreground colour (B2, G1, R0)
reg [2: 0] fg;
// Background colour (B2, G1, R0)
reg [2: 0] bg;
reg conceal;
reg gfx;
reg gfx_sep;
reg gfx_hold;
reg is_flash;
reg double_high;
// Set in first row of double height
reg double_high1;
// Set in second row of double height
reg double_high2;
saa5050_rom char_rom (.address(rom_address),
.clock(CLOCK),
.q(rom_data));
assign flash = flash_counter[5] & flash_counter[4];
// Generate flash signal for 3:1 ratio
// Sync inputs
always @(posedge DI_CLOCK) begin
if (nRESET === 1'b 0) begin
di_r <= {7{1'b 0}};
dew_r <= 1'b 0;
lose_r <= 1'b 0;
end else if (DI_CLKEN === 1'b 1 ) begin
di_r <= DI;
dew_r <= DEW;
lose_r <= LOSE;
end
end
// Register data into pixel clock domain
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
code <= {7{1'b 0}};
end else if (CLKEN === 1'b 1 ) begin
code <= di_r;
end
end
// Generate character rom address in pixel clock domain
// This is done combinatorially since all the inputs are already
// registered and the address is re-registered by the ROM
assign line_addr = double_high === 1'b 0 ? line_counter :
double_high2 === 1'b 0 ? {1'b 0, line_counter[3 : 1]} :
{1'b 0, line_counter[3 : 1]} + 4'd5;
assign rom_address = double_high === 1'b 0 & double_high2 === 1'b 1 ? {12{1'b 0}} :
{gfx, code, line_addr};
assign line_addr_p1 = double_high === 1'b 0 ? line_counter + 1 :
double_high2 === 1'b 0 ? {1'b 0, line_counter[3:1]} + 1 :
{1'b 0, line_counter[3:1]} + 1 + 5;
assign line_addr_m1 = double_high === 1'b 0 ? line_counter - 1 :
double_high2 === 1'b 0 ? {1'b 0, line_counter[3:1]} - 1 :
{1'b 0, line_counter[3:1]} - 1 + 5;
assign line_addr_cmp = CRS === 1'b 1 ? line_addr_p1 :
line_addr_m1;
assign rom_address_cmp = double_high === 1'b 0 & double_high2 === 1'b 1 ? {12{1'b 0}} :
{gfx, code, line_addr_cmp};
// Character row and pixel counters
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
dew_latch <= 1'b 0;
lose_latch <= 1'b 0;
disp_enable <= 1'b 0;
disp_enable_latch <= 1'b 0;
double_high1 <= 1'b 0;
double_high2 <= 1'b 0;
line_counter <= {4{1'b 0}};
pixel_counter <= {3{1'b 0}};
flash_counter <= {6{1'b 0}};
end else if (CLKEN === 1'b 1 ) begin
// Register syncs for edge detection
dew_latch <= dew_r;
lose_latch <= lose_r;
disp_enable_latch <= disp_enable;
// When first entering double-height mode start on top row
if (double_high === 1'b 1 & double_high1 === 1'b 0 &
double_high2 === 1'b 0) begin
double_high1 <= 1'b 1;
end
// Count pixels between 0 and 5
if (pixel_counter === 5) begin
// Start of next character and delayed display enable
pixel_counter <= {3{1'b 0}};
disp_enable <= lose_latch;
end else begin
pixel_counter <= pixel_counter + 1;
end
// Rising edge of LOSE is the start of the active line
if (lose_r === 1'b 1 & lose_latch === 1'b 0) begin
// Reset pixel counter - small offset to make the output
// line up with the cursor from the video ULA
pixel_counter <= 3'b 011;
end
// Count frames on end of VSYNC (falling edge of DEW)
if (dew_r === 1'b 0 & dew_latch === 1'b 1) begin
flash_counter <= flash_counter + 1;
end
if (dew_r === 1'b 1) begin
// Reset line counter and double height state during VSYNC
line_counter <= {4{1'b 0}};
double_high1 <= 1'b 0;
double_high2 <= 1'b 0;
// Count lines on end of active video (falling edge of disp_enable)
end else begin
if (disp_enable === 1'b 0 & disp_enable_latch === 1'b 1) begin
if (line_counter === 9) begin
line_counter <= {4{1'b 0}};
// Keep track of which row we are on for double-height
// The double_high flag can be cleared before the end of a row, but if
// double height characters are used anywhere on a row then the double_high1
// flag will be set and remain set until the next row. This is used
// to determine that the bottom half of the characters should be shown if
// double_high is set once again on the row below.
double_high1 <= 1'b 0;
double_high2 <= double_high1;
end
else begin
line_counter <= line_counter + 1;
end
end
end
end
end
// Shift register
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
shift_reg <= {6{1'b 0}};
end else if (CLKEN === 1'b 1 ) begin
if (disp_enable === 1'b 1 & pixel_counter === 0) begin
// Load the shift register with the ROM bit pattern
// at the start of each character while disp_enable is asserted.
shift_reg <= rom_data[5: 0];
shift_reg_cmp <= rom_data_cmp[5:0];
// If bit 7 of the ROM data is set then this is a graphics
// character and separated/hold graphics modes apply.
// We don't just assume this to be the case if gfx=1 because
// these modes don't apply to caps even in graphics mode
if (rom_data[7] === 1'b 1) begin
// Apply a mask for separated graphics mode
if (gfx_sep === 1'b 1) begin
shift_reg[5] <= 1'b 0;
shift_reg[2] <= 1'b 0;
if (line_counter === 2 | line_counter === 6 |
line_counter === 9) begin
shift_reg <= {6{1'b 0}};
end
end
end
if (rom_data_cmp[7] === 1'b 1) begin
// Apply a mask for separated graphics mode
if (gfx_sep === 1'b 1) begin
shift_reg_cmp[5] <= 1'b 0;
shift_reg_cmp[2] <= 1'b 0;
if (line_counter === 1 | line_counter === 5 |
line_counter === 8) begin
shift_reg_cmp <= {6{1'b 0}};
end
end
end // Pump the shift register
end else begin
shift_reg_last <= shift_reg[5];
shift_reg <= {shift_reg[4: 0], 1'b 0};
shift_reg_cmp_last <= shift_reg_cmp[5];
shift_reg_cmp <= {shift_reg_cmp[4:0], 1'b 0};
end
end
end
// Control character handling
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
fg <= {3{1'b 1}};
bg <= {3{1'b 0}};
conceal <= 1'b 0;
gfx <= 1'b 0;
gfx_sep <= 1'b 0;
gfx_hold <= 1'b 0;
is_flash <= 1'b 0;
double_high <= 1'b 0;
end else if (CLKEN === 1'b 1 ) begin
if (disp_enable === 1'b 0) begin
// Reset to start of line defaults
fg <= 3'b111;
bg <= 'd0;
conceal <= 1'b 0;
gfx <= 1'b 0;
gfx_sep <= 1'b 0;
gfx_hold <= 1'b 0;
is_flash <= 1'b 0;
double_high <= 1'b 0;
end else if (pixel_counter === 0 ) begin
// Latch new control codes at the start of each character
if (code[6: 5] === 2'b 00) begin
if (code[3] === 1'b 0) begin
// Colour and graphics setting clears conceal mode
conceal <= 1'b 0;
// Select graphics or alpha mode
gfx <= code[4];
// 0 would be black but is not allowed so has no effect,
// otherwise set the colour
if (code[2: 0] !== 3'b 000) begin
fg <= code[2: 0];
end
end else begin
case (code[4: 0])
5'b 01000: is_flash <= 1'b1; // FLASH
5'b 01001: is_flash <= 1'b0; // STEADY
5'b 01100: double_high <= 1'b0; // NORMAL HEIGHT
5'b 01101: double_high <= 1'b1; // DOUBLE HEIGHT
5'b 11000: conceal <= 1'b1; // CONCEAL
5'b 11001: gfx_sep <= 1'b0; // CONTIGUOUS GFX
5'b 11010: gfx_sep <= 1'b1; // SEPARATED GFX
5'b 11100: bg <= 'd0; // BLACK BACKGROUND
5'b 11101: bg <= fg; // NEW BACKGROUND
5'b 11110: gfx_hold <= 1'b1; // HOLD GFX
5'b 11111: gfx_hold <= 1'b0; // RELEASE GFX
endcase
end
end
end
end
end
// Output pixel calculation.
wire pixel = double_high === 1'b 1 ? shift_reg[5] & ~(flash & is_flash | conceal) :
odd_pixel === 1'b 0 ? (shift_reg[5] | shift_reg_cmp[5] & shift_reg[4] & ~shift_reg_cmp[4]) & ~(flash & is_flash | conceal) :
(shift_reg[5] | shift_reg_cmp[5] & shift_reg_last & ~shift_reg_cmp_last) & ~(flash & is_flash | conceal);
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
R <= 1'b 0;
G <= 1'b 0;
B <= 1'b 0;
end else if (PIXCLKEN === 1'b 1 ) begin
// Generate mono output
Y <= pixel;
odd_pixel <= ~odd_pixel;
// Generate colour output
if (pixel === 1'b 1) begin
R <= fg[0];
G <= fg[1];
B <= fg[2];
end else begin
R <= bg[0];
G <= bg[1];
B <= bg[2];
end
end
end
endmodule // module saa5050

View File

@@ -0,0 +1,582 @@
-- BBC Micro for Altera DE1
--
-- Copyright (c) 2011 Mike Stirling
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- * Redistributions in synthesized form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- * Neither the name of the author nor the names of other contributors may
-- be used to endorse or promote products derived from this software without
-- specific prior written agreement from the author.
--
-- * License is granted for non-commercial use only. A fee may not be charged
-- for redistributions as source code or in synthesized/hardware form without
-- specific prior written agreement from the author.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- SAA5050 teletext generator
--
-- Synchronous implementation for FPGA. Certain TV-specific functions are
-- not implemented. e.g.
--
-- No /SI pin - 'TEXT' mode is permanently enabled
-- No remote control features (/DATA, DLIM)
-- No large character support
-- No support for box overlay (BLAN, PO, DE)
--
--
--
-- (C) 2011 Mike Stirling
--
-- Updated to run at 12MHz rather than 6MHz
-- Character rounding added
-- Implemented Graphics Hold
-- Lots of fixes to correctly implement Set-At and Set-After control codes
--
-- (C) 2015 David Banks
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity saa5050 is
port (
CLOCK : in std_logic;
-- 6 MHz dot clock enable
CLKEN : in std_logic;
-- Async reset
nRESET : in std_logic;
-- Indicates special VGA Mode 7 (720x576p)
VGA : in std_logic;
-- Character data input (in the bus clock domain)
DI_CLOCK : in std_logic;
DI_CLKEN : in std_logic;
DI : in std_logic_vector(6 downto 0);
-- Timing inputs
-- General line reset (not used)
GLR : in std_logic; -- /HSYNC
-- Data entry window - high during VSYNC.
-- Resets ROM row counter and drives 'flash' signal
DEW : in std_logic; -- VSYNC
-- Character rounding select - high during even field
CRS : in std_logic; -- FIELD
-- Load output shift register enable - high during active video
LOSE : in std_logic; -- DE
-- Video out
R : out std_logic;
G : out std_logic;
B : out std_logic;
Y : out std_logic
);
end entity;
architecture rtl of saa5050 is
-- Register inputs in the bus clock domain
signal di_r : std_logic_vector(6 downto 0);
signal dew_r : std_logic;
signal lose_r : std_logic;
-- Data input registered in the pixel clock domain
signal code : std_logic_vector(6 downto 0);
signal line_addr : unsigned(3 downto 0);
signal rom_address1 : std_logic_vector(11 downto 0);
signal rom_address2 : std_logic_vector(11 downto 0);
signal rom_data1 : std_logic_vector(7 downto 0);
signal rom_data2 : std_logic_vector(7 downto 0);
-- Delayed display enable derived from LOSE by delaying for one and two characters
signal disp_enable : std_logic;
-- Latched timing signals for detection of falling edges
signal dew_latch : std_logic;
signal lose_latch : std_logic;
signal disp_enable_latch : std_logic;
-- Row and column addressing is handled externally. We just need to
-- keep track of which of the 10 lines we are on within the character...
signal line_counter : unsigned(3 downto 0);
-- ... and which of the 12 pixels we are on within each line
signal pixel_counter : unsigned(3 downto 0);
-- We also need to count frames to implement the flash feature.
-- The datasheet says this is 0.75 Hz with a 3:1 on/off ratio, so it
-- is probably a /64 counter, which gives us 0.78 Hz
signal flash_counter : unsigned(5 downto 0);
-- Output shift register
signal shift_reg : std_logic_vector(11 downto 0);
-- Flash mask
signal flash : std_logic;
-- Current display state
-- Foreground colour (B2, G1, R0)
signal fg : std_logic_vector(2 downto 0);
-- Background colour (B2, G1, R0)
signal bg : std_logic_vector(2 downto 0);
signal conceal : std_logic;
signal gfx : std_logic;
signal gfx_sep : std_logic;
signal gfx_hold : std_logic;
signal is_flash : std_logic;
signal double_high : std_logic;
-- One-shot versions of certain control signals to support "Set-After" semantics
signal fg_next : std_logic_vector(2 downto 0);
signal alpha_next : std_logic;
signal gfx_next : std_logic;
signal gfx_release_next : std_logic;
signal is_flash_next : std_logic;
signal double_high_next : std_logic;
signal unconceal_next : std_logic;
-- Once char delayed versions of all of the signals seen by the ROM/Shift Register/Output Stage
-- This is to compensate for the one char delay through the control state machine
signal code_r : std_logic_vector(6 downto 0);
signal disp_enable_r: std_logic;
signal fg_r : std_logic_vector(2 downto 0);
signal bg_r : std_logic_vector(2 downto 0);
signal conceal_r : std_logic;
signal is_flash_r : std_logic;
-- Last graphics character, for use by graphics hold
signal last_gfx_sep : std_logic;
signal last_gfx : std_logic_vector(6 downto 0);
signal hold_active : std_logic;
-- Set in first row of double height
signal double_high1 : std_logic;
-- Set in second row of double height
signal double_high2 : std_logic;
begin
-- Generate flash signal for 3:1 ratio
flash <= flash_counter(5) and flash_counter(4);
-- Sync inputs
process(DI_CLOCK,nRESET)
begin
if nRESET = '0' then
di_r <= (others => '0');
dew_r <= '0';
lose_r <= '0';
elsif rising_edge(DI_CLOCK) then
if DI_CLKEN = '1' then
di_r <= DI;
dew_r <= DEW;
lose_r <= LOSE;
end if;
end if;
end process;
-- Register data into pixel clock domain
process(CLOCK,nRESET)
begin
if nRESET = '0' then
code <= (others => '0');
code_r <= (others => '0');
disp_enable_r <= '0';
fg_r <= (others => '0');
bg_r <= (others => '0');
conceal_r <= '0';
is_flash_r <= '0';
elsif rising_edge(CLOCK) then
if CLKEN = '1' then
code <= di_r;
if pixel_counter = 0 then
code_r <= code;
disp_enable_r <= disp_enable;
fg_r <= fg;
bg_r <= bg;
conceal_r <= conceal;
is_flash_r <= is_flash;
end if;
end if;
end if;
end process;
-- Character row and pixel counters
process(CLOCK,nRESET)
begin
if nRESET = '0' then
dew_latch <= '0';
lose_latch <= '0';
disp_enable <= '0';
disp_enable_latch <= '0';
double_high1 <= '0';
double_high2 <= '0';
line_counter <= (others => '0');
pixel_counter <= (others => '0');
flash_counter <= (others => '0');
elsif rising_edge(CLOCK) then
if CLKEN = '1' then
-- Register syncs for edge detection
dew_latch <= dew_r;
lose_latch <= lose_r;
disp_enable_latch <= disp_enable;
-- When first entering double-height mode start on top row
if double_high = '1' and double_high1 = '0' and double_high2 = '0' then
double_high1 <= '1';
end if;
-- Count pixels between 0 and 11
if pixel_counter = 11 then
-- Start of next character and delayed display enable
pixel_counter <= (others => '0');
disp_enable <= lose_latch;
else
pixel_counter <= pixel_counter + 1;
end if;
-- Rising edge of LOSE is the start of the active line
if lose_r = '1' and lose_latch = '0' then
-- Reset pixel counter - small offset to make the output
-- line up with the cursor from the video ULA
pixel_counter <= "0010";
end if;
-- Count frames on end of VSYNC (falling edge of DEW)
if dew_r = '0' and dew_latch = '1' then
flash_counter <= flash_counter + 1;
end if;
if dew_r = '1' then
-- Reset line counter and double height state during VSYNC
line_counter <= (others => '0');
double_high1 <= '0';
double_high2 <= '0';
else
-- Count lines on end of active video (falling edge of disp_enable)
if disp_enable = '0' and disp_enable_latch = '1' and (VGA = '0' or CRS = '1') then
if line_counter = 9 then
line_counter <= (others => '0');
-- Keep track of which row we are on for double-height
-- The double_high flag can be cleared before the end of a row, but if
-- double height characters are used anywhere on a row then the double_high1
-- flag will be set and remain set until the next row. This is used
-- to determine that the bottom half of the characters should be shown if
-- double_high is set once again on the row below.
double_high1 <= '0';
double_high2 <= double_high1;
else
line_counter <= line_counter + 1;
end if;
end if;
end if;
end if;
end if;
end process;
-- Control character handling
process(CLOCK,nRESET)
begin
if nRESET = '0' then
-- Current Attributes
fg <= (others => '1');
bg <= (others => '0');
conceal <= '0';
gfx <= '0';
gfx_sep <= '0';
gfx_hold <= '0';
is_flash <= '0';
double_high <= '0';
-- One-shot versions to support "Set-After" semantics
fg_next <= (others => '0');
gfx_next <= '0';
alpha_next <= '0';
gfx_release_next <= '0';
is_flash_next <= '0';
double_high_next <= '0';
unconceal_next <= '0';
-- Last graphics character
last_gfx <= (others => '0');
last_gfx_sep <= '0';
elsif rising_edge(CLOCK) then
if CLKEN = '1' then
-- Reset to start of line defaults
if disp_enable = '0' then
-- Current Attributes
fg <= (others => '1');
bg <= (others => '0');
conceal <= '0';
gfx <= '0';
gfx_sep <= '0';
gfx_hold <= '0';
is_flash <= '0';
double_high <= '0';
-- One-shot versions to support "Set-After" semantics
fg_next <= (others => '0');
gfx_next <= '0';
alpha_next <= '0';
gfx_release_next <= '0';
is_flash_next <= '0';
double_high_next <= '0';
unconceal_next <= '0';
-- Last graphics character
last_gfx <= (others => '0');
last_gfx_sep <= '0';
elsif pixel_counter = 0 then
-- One-shot versions to support "Set-After" semantics
fg_next <= (others => '0');
gfx_next <= '0';
alpha_next <= '0';
gfx_release_next <= '0';
is_flash_next <= '0';
double_high_next <= '0';
unconceal_next <= '0';
-- Latch the last graphic character (inc seperation), to support graphics hold
if code(5) = '1' then
last_gfx <= code;
last_gfx_sep <= gfx_sep;
end if;
-- Latch new control codes at the start of each character
if code(6 downto 5) = "00" then
if code(3) = '0' then
-- 0 would be black but is not allowed so has no effect
if code(2 downto 0) /= "000" then
-- Colour and graphics setting clears conceal mode - Set After
unconceal_next <= '1';
-- Select the foreground colout - Set After
fg_next <= code(2 downto 0);
-- Select graphics or alpha mode - Set After
if code(4) = '1' then
gfx_next <= '1';
else
alpha_next <= '1';
gfx_release_next <= '1';
end if;
end if;
else
case code(4 downto 0) is
-- FLASH - Set After
when "01000" =>
is_flash_next <= '1';
-- STEADY - Set At
when "01001" =>
is_flash <= '0';
-- NORMAL HEIGHT - Set At
when "01100" =>
double_high <= '0';
-- Graphics hold character is cleared by a *change* of height
if (double_high = '0') then
last_gfx <= (others => '0');
end if;
-- DOUBLE HEIGHT - Set After
when "01101" =>
double_high_next <= '1';
-- Graphics hold character is cleared by a *change* of height
if (double_high = '0') then
last_gfx <= (others => '0');
end if;
-- CONCEAL - Set At
when "11000" =>
conceal <= '1';
-- CONTIGUOUS GFX - Set At
when "11001" =>
gfx_sep <= '0';
-- SEPARATED GFX - Set At
when "11010" =>
gfx_sep <= '1';
-- BLACK BACKGROUND - Set At
when "11100" =>
bg <= (others => '0');
-- NEW BACKGROUND - Set At
when "11101" =>
-- if there is a pending foreground change, then use it
if fg_next /= "000" then
bg <= fg_next;
else
bg <= fg;
end if;
-- HOLD GFX - Set At
when "11110" =>
gfx_hold <= '1';
-- RELEASE GFX - Set After
when "11111" =>
gfx_release_next <= '1';
when others => null;
end case;
end if;
end if;
-- Delay the "Set After" control code effect until the next character
if fg_next /= "000" then
fg <= fg_next;
end if;
if gfx_next = '1' then
gfx <= '1';
end if;
if alpha_next = '1' then
gfx <= '0';
end if;
if is_flash_next = '1' then
is_flash <= '1';
end if;
if double_high_next = '1' then
double_high <= '1';
end if;
if gfx_release_next = '1' then
gfx_hold <= '0';
end if;
-- Note, conflicts can arise as setting/clearing happen in different cycles
-- e.g. 03 (Alpha Yellow) 18 (Conceal) should leave us in a conceal state
if conceal = '1' and unconceal_next = '1' then
conceal <= '0';
end if;
end if;
end if;
end if;
end process;
--------------------------------------------------------------------
-- Character ROM
--------------------------------------------------------------------
-- Generate character rom address in pixel clock domain
-- This is done combinatorially since all the inputs are already
-- registered and the address is re-registered by the ROM
line_addr <= line_counter when double_high = '0' else
("0" & line_counter(3 downto 1)) when double_high2 = '0' else
("0" & line_counter(3 downto 1)) + 5;
hold_active <= '1' when gfx_hold = '1' and code_r(6 downto 5) = "00" else '0';
rom_address1 <= (others => '0') when (double_high = '0' and double_high2 = '1') else
gfx & last_gfx & std_logic_vector(line_addr) when hold_active = '1' else
gfx & code_r & std_logic_vector(line_addr);
-- reference row for character rounding
rom_address2 <= rom_address1 + 1 when ((double_high = '0' and CRS = '1') or (double_high = '1' and line_counter(0) = '1')) else
rom_address1 - 1;
char_rom : entity work.saa5050_rom_dual_port port map (
clock => CLOCK,
addressA => rom_address1,
QA => rom_data1,
addressB => rom_address2,
QB => rom_data2
);
--------------------------------------------------------------------
-- Shift register
--------------------------------------------------------------------
process(CLOCK,nRESET)
variable a : std_logic_vector(11 downto 0);
variable b : std_logic_vector(11 downto 0);
begin
if nRESET = '0' then
shift_reg <= (others => '0');
elsif rising_edge(CLOCK) then
if CLKEN = '1' then
if disp_enable_r = '1' and pixel_counter = 0 then
-- Character rounding
-- a is the current row of pixels, doubled up
a := rom_data1(5) & rom_data1(5) &
rom_data1(4) & rom_data1(4) &
rom_data1(3) & rom_data1(3) &
rom_data1(2) & rom_data1(2) &
rom_data1(1) & rom_data1(1) &
rom_data1(0) & rom_data1(0);
-- b is the adjacent row of pixels, doubled up
b := rom_data2(5) & rom_data2(5) &
rom_data2(4) & rom_data2(4) &
rom_data2(3) & rom_data2(3) &
rom_data2(2) & rom_data2(2) &
rom_data2(1) & rom_data2(1) &
rom_data2(0) & rom_data2(0);
-- If bit 7 of the ROM data is set then this is a graphics
-- character and separated/hold graphics modes apply.
-- We don't just assume this to be the case if gfx=1 because
-- these modes don't apply to caps even in graphics mode
if rom_data1(7) = '1' then
-- Apply a mask for separated graphics mode
if (hold_active = '0' and gfx_sep = '1') or (hold_active = '1' and last_gfx_sep = '1') then
a(10) := '0';
a(11) := '0';
a(4) := '0';
a(5) := '0';
if line_counter = 2 or line_counter = 6 or line_counter = 9 then
a := (others => '0');
end if;
end if;
else
-- Perform character rounding on alpha-numeric characters
a := a or
(('0' & a(11 downto 1)) and b and not('0' & b(11 downto 1))) or
((a(10 downto 0) & '0') and b and not(b(10 downto 0) & '0'));
end if;
-- Load the shift register with the ROM bit pattern
-- at the start of each character while disp_enable is asserted.
shift_reg <= a;
else
-- Pump the shift register
shift_reg <= shift_reg(10 downto 0) & "0";
end if;
end if;
end if;
end process;
--------------------------------------------------------------------
-- Output Pixel Colouring
--------------------------------------------------------------------
process(CLOCK,nRESET)
variable pixel : std_logic;
begin
if nRESET = '0' then
R <= '0';
G <= '0';
B <= '0';
elsif rising_edge(CLOCK) then
if CLKEN = '1' then
pixel := shift_reg(11) and not ((flash and is_flash_r) or conceal_r);
-- Generate mono output
Y <= pixel;
-- Generate colour output
if pixel = '1' then
R <= fg_r(0);
G <= fg_r(1);
B <= fg_r(2);
else
R <= bg_r(0);
G <= bg_r(1);
B <= bg_r(2);
end if;
end if;
end if;
end process;
end architecture;

View File

@@ -1,45 +0,0 @@
//
// saa5050_rom.v
//
// Copyright (c) 2015 Stephen J. Leary (sleary@vavi.co.uk)
//
// 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 saa5050_rom
(
input clock,
input [MEM_BITS-1:0] address, // Port A address input
output [7:0] q
);
parameter MEM_BITS = 12;
localparam MEM_SIZE = 2**MEM_BITS;
reg [MEM_BITS-1:0] address_latched;
reg [7:0] mem_data [0:MEM_SIZE-1];
initial begin
$readmemh("saa5050.mif", mem_data);
end
always @(posedge clock) begin
address_latched <= address;
end
assign q = mem_data[address_latched];
endmodule

View File

@@ -0,0 +1,555 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
entity saa5050_rom_dual_port is
generic (
ADDR_WIDTH : integer := 12;
DATA_WIDTH : integer := 8
);
port(
clock : in std_logic;
addressA : in std_logic_vector(ADDR_WIDTH-1 downto 0);
QA : out std_logic_vector(DATA_WIDTH-1 downto 0);
addressB : in std_logic_vector(ADDR_WIDTH-1 downto 0);
QB : out std_logic_vector(DATA_WIDTH-1 downto 0)
);
end saa5050_rom_dual_port;
architecture RTL of saa5050_rom_dual_port is
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array (0 to MEM_DEPTH-1) of signed(DATA_WIDTH-1 downto 0);
shared variable mem : mem_type := (
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"04",x"04",x"04",x"04",x"00",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0A",x"0A",x"0A",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"06",x"09",x"08",x"1C",x"08",x"08",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"15",x"14",x"0E",x"05",x"15",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"18",x"19",x"02",x"04",x"08",x"13",x"03",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"08",x"14",x"14",x"08",x"15",x"12",x"0D",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"04",x"04",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"02",x"04",x"08",x"08",x"08",x"04",x"02",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"08",x"04",x"02",x"02",x"02",x"04",x"08",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"15",x"0E",x"04",x"0E",x"15",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"04",x"1F",x"04",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"04",x"04",
x"08",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"0E",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"01",x"02",x"04",x"08",x"10",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"0A",x"11",x"11",x"11",x"0A",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"0C",x"04",x"04",x"04",x"04",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"01",x"06",x"08",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"01",x"02",x"06",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"02",x"06",x"0A",x"12",x"1F",x"02",x"02",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"10",x"1E",x"01",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"06",x"08",x"10",x"1E",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"01",x"02",x"04",x"08",x"08",x"08",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"0E",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"0F",x"01",x"02",x"0C",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"04",x"00",x"00",x"00",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"04",x"00",x"00",x"04",x"04",
x"08",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"02",x"04",x"08",x"10",x"08",x"04",x"02",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"1F",x"00",x"1F",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"08",x"04",x"02",x"01",x"02",x"04",x"08",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"02",x"04",x"04",x"00",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"17",x"15",x"17",x"10",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"0A",x"11",x"11",x"1F",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"11",x"11",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"10",x"10",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"11",x"11",x"11",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"10",x"10",x"1E",x"10",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"10",x"10",x"1E",x"10",x"10",x"10",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"10",x"13",x"11",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"1F",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"04",x"04",x"04",x"04",x"04",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"01",x"01",x"01",x"01",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"12",x"14",x"18",x"14",x"12",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"10",x"10",x"10",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"1B",x"15",x"15",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"19",x"15",x"13",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"11",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"10",x"10",x"10",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"11",x"15",x"12",x"0D",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"14",x"12",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"0E",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"04",x"04",x"04",x"04",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"11",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"0A",x"0A",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"15",x"15",x"15",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"0A",x"04",x"0A",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"0A",x"04",x"04",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"01",x"02",x"04",x"08",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"08",x"1F",x"08",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"10",x"10",x"16",x"01",x"02",
x"04",x"07",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"02",x"1F",x"02",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"0E",x"15",x"04",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0A",x"0A",x"1F",x"0A",x"1F",x"0A",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"1F",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0E",x"01",x"0F",x"11",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"1E",x"11",x"11",x"11",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0F",x"10",x"10",x"10",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"01",x"01",x"0F",x"11",x"11",x"11",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0E",x"11",x"1F",x"10",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"02",x"04",x"04",x"0E",x"04",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0F",x"11",x"11",x"11",x"0F",
x"01",x"0E",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"1E",x"11",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"00",x"0C",x"04",x"04",x"04",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"00",x"04",x"04",x"04",x"04",x"04",
x"04",x"08",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"08",x"08",x"09",x"0A",x"0C",x"0A",x"09",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0C",x"04",x"04",x"04",x"04",x"04",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"1A",x"15",x"15",x"15",x"15",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"1E",x"11",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0E",x"11",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"1E",x"11",x"11",x"11",x"1E",
x"10",x"10",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0F",x"11",x"11",x"11",x"0F",
x"01",x"01",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0B",x"0C",x"08",x"08",x"08",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"0F",x"10",x"0E",x"01",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"04",x"0E",x"04",x"04",x"04",x"02",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"11",x"11",x"11",x"11",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"11",x"11",x"0A",x"0A",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"11",x"11",x"15",x"15",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"11",x"0A",x"04",x"0A",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"11",x"11",x"11",x"11",x"0F",
x"01",x"0E",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"1F",x"02",x"04",x"08",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"08",x"08",x"08",x"08",x"09",x"03",x"05",
x"07",x"01",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0A",x"0A",x"0A",x"0A",x"0A",x"0A",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"18",x"04",x"18",x"04",x"19",x"03",x"05",
x"07",x"01",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"00",x"1F",x"00",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"1F",x"1F",x"1F",x"1F",x"1F",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"80",x"80",x"80",x"80",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"80",x"80",x"80",x"80",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"80",x"80",x"80",x"80",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"80",x"80",x"80",x"80",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"B8",x"B8",x"B8",x"B8",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"B8",x"B8",x"B8",x"B8",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"B8",x"B8",x"B8",x"B8",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"87",x"87",x"87",x"87",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"87",x"87",x"87",x"87",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"87",x"87",x"87",x"87",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"87",x"87",x"87",x"87",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"BF",x"BF",x"BF",x"BF",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"BF",x"BF",x"BF",x"BF",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"BF",x"BF",x"BF",x"BF",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"80",
x"80",x"80",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"80",x"80",x"80",x"80",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"80",x"80",x"80",x"80",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"80",x"80",x"80",x"80",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"80",x"80",x"80",x"80",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"B8",x"B8",x"B8",x"B8",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"B8",x"B8",x"B8",x"B8",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"B8",x"B8",x"B8",x"B8",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"87",x"87",x"87",x"87",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"87",x"87",x"87",x"87",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"87",x"87",x"87",x"87",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"87",x"87",x"87",x"87",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"BF",x"BF",x"BF",x"BF",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"BF",x"BF",x"BF",x"BF",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"BF",x"BF",x"BF",x"BF",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"B8",
x"B8",x"B8",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"17",x"15",x"17",x"10",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"04",x"0A",x"11",x"11",x"1F",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"11",x"11",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"10",x"10",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"11",x"11",x"11",x"1E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"10",x"10",x"1E",x"10",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"10",x"10",x"1E",x"10",x"10",x"10",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"10",x"13",x"11",x"0F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"1F",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"04",x"04",x"04",x"04",x"04",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"01",x"01",x"01",x"01",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"12",x"14",x"18",x"14",x"12",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"10",x"10",x"10",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"1B",x"15",x"15",x"11",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"19",x"15",x"13",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"11",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"10",x"10",x"10",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"11",x"11",x"15",x"12",x"0D",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1E",x"11",x"11",x"1E",x"14",x"12",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0E",x"11",x"10",x"0E",x"01",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"04",x"04",x"04",x"04",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"11",x"11",x"11",x"0E",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"0A",x"0A",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"11",x"15",x"15",x"15",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"0A",x"04",x"0A",x"11",x"11",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"11",x"11",x"0A",x"04",x"04",x"04",x"04",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"1F",x"01",x"02",x"04",x"08",x"10",x"1F",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"08",x"1F",x"08",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"10",x"10",x"10",x"10",x"16",x"01",x"02",
x"04",x"07",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"02",x"1F",x"02",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"00",x"04",x"0E",x"15",x"04",x"04",x"00",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"00",x"0A",x"0A",x"1F",x"0A",x"1F",x"0A",x"0A",
x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"80",x"80",x"80",x"80",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"80",x"80",x"80",x"80",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"80",x"80",x"80",x"80",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"80",x"80",x"80",x"80",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"B8",x"B8",x"B8",x"B8",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"B8",x"B8",x"B8",x"B8",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"B8",x"B8",x"B8",x"B8",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"87",x"87",x"87",x"87",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"87",x"87",x"87",x"87",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"87",x"87",x"87",x"87",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"87",x"87",x"87",x"87",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"BF",x"BF",x"BF",x"BF",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"BF",x"BF",x"BF",x"BF",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"BF",x"BF",x"BF",x"BF",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"87",
x"87",x"87",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"80",x"80",x"80",x"80",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"80",x"80",x"80",x"80",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"80",x"80",x"80",x"80",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"80",x"80",x"80",x"80",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"B8",x"B8",x"B8",x"B8",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"B8",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"B8",x"B8",x"B8",x"B8",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"B8",x"B8",x"B8",x"B8",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"87",x"87",x"87",x"87",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"87",x"87",x"87",x"87",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"87",x"87",x"87",x"87",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"87",x"87",x"87",x"87",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"80",x"80",x"80",x"BF",x"BF",x"BF",x"BF",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"B8",x"B8",x"B8",x"BF",x"BF",x"BF",x"BF",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"87",x"87",x"87",x"BF",x"BF",x"BF",x"BF",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00",
x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",x"BF",
x"BF",x"BF",x"00",x"00",x"00",x"00",x"00",x"00");
begin
process(clock) is
begin
if (rising_edge(clock)) then
QA <= std_logic_vector(mem(to_integer(unsigned(addressA))));
end if;
end process;
process(clock) is
begin
if (rising_edge(clock)) then
QB <= std_logic_vector(mem(to_integer(unsigned(addressB))));
end if;
end process;
end RTL;

View File

@@ -46,8 +46,8 @@ module vidproc (
input CLOCK,
input CLKEN,
input nRESET,
input MHZ1_CLKEN, // sync to the clock generator
output CLKEN_CRTC,
output CLKEN_CRTC_ADR,
input ENABLE,
input A0,
input [7:0] DI_CPU,
@@ -77,17 +77,19 @@ reg [3:0] palette [0:15];
reg [7:0] shiftreg;
// Delayed display enable
reg delayed_disen;
reg delayed_disen1, delayed_disen2;
// Internal clock enable generation
wire clken_pixel;
wire clken_fetch;
wire mhz4_clken;
reg [3:0] clken_counter;
// Cursor generation - can span up to 32 pixels
// Segments 0 and 1 are 8 pixels wide
// Segment 2 is 16 pixels wide
wire cursor_invert;
reg cursor_invert_delayed;
reg cursor_active;
reg [1:0] cursor_counter;
@@ -96,7 +98,7 @@ integer V2V_colour;
always @(posedge CLOCK) begin : process_1
if (nRESET === 1'b 0) begin
if (~nRESET) begin
r0_cursor0 <= 1'b 0;
r0_cursor1 <= 1'b 0;
r0_cursor2 <= 1'b 0;
@@ -110,7 +112,7 @@ always @(posedge CLOCK) begin : process_1
end
end
else begin
if (ENABLE === 1'b 1) begin
if (ENABLE) begin
if (A0 === 1'b 0) begin
// Access control register
@@ -135,90 +137,87 @@ end
// Pixel clock can be divided by 1,2,4 or 8 depending on the value
// programmed at r0_pixel_rate
// 00 = /8, 01 = /4, 10 = /2, 11 = /1
assign clken_pixel = r0_pixel_rate === 2'b 11 ? CLKEN :
r0_pixel_rate === 2'b 10 ? CLKEN & ~clken_counter[0] :
r0_pixel_rate === 2'b 01 ? CLKEN & ~(clken_counter[0] | clken_counter[1]) :
CLKEN & ~(clken_counter[0] | clken_counter[1] | clken_counter[2]);
r0_pixel_rate === 2'b 10 ? CLKEN & clken_counter[0] :
r0_pixel_rate === 2'b 01 ? CLKEN & clken_counter[0] & clken_counter[1] :
CLKEN & clken_counter[0] & clken_counter[1] & ~clken_counter[2];
// The CRT controller is always enabled in the 15th cycle, so that the result
// is ready for latching into the shift register in cycle 0. If 2 MHz mode is
// is ready for latching into the shift register in cycle 3. If 2 MHz mode is
// selected then the CRTC is also enabled in the 7th cycle
assign CLKEN_CRTC = CLKEN & clken_counter[0] & clken_counter[1] & clken_counter[2] &
(clken_counter[3] | r0_crtc_2mhz);
// extra signal for crtc address setup a little earlier
assign CLKEN_CRTC_ADR = CLKEN & clken_counter[0] & clken_counter[1] & !clken_counter[2] &
(clken_counter[3] | r0_crtc_2mhz);
// The result is fetched from the CRTC in cycle 0 and also cycle 8 if 2 MHz
// The result is fetched from the RAM in cycle 11 and also cycle 3 if 2 MHz
// mode is selected. This is used for reloading the shift register as well as
// counting cursor pixels
assign clken_fetch = CLKEN & ~(clken_counter[0] | clken_counter[1] | clken_counter[2] | clken_counter[3] & ~r0_crtc_2mhz);
assign mhz4_clken = CLKEN & ~clken_counter[2] & clken_counter[1] & clken_counter[0];
assign clken_fetch = mhz4_clken & (clken_counter[3] | r0_crtc_2mhz);
wire [7:0] shiftreg_nxt = clken_fetch ? DI_RAM :
clken_pixel ? {shiftreg[6:0], 1'b 1} : shiftreg;
always @(posedge CLOCK) begin : process_2
if (nRESET === 1'b 0) begin
clken_counter <= 'd0;
if (~nRESET) begin
clken_counter <= 0;
end
else if (CLKEN === 1'b 1 ) begin
// Increment internal cycle counter during each video clock
clken_counter <= clken_counter + 1;
else if (CLKEN) begin
if (MHZ1_CLKEN)
clken_counter <= 0;
else
// Increment internal cycle counter during each video clock
clken_counter <= clken_counter + 1'd1;
end
end
// Fetch control
always @(posedge CLOCK) begin : process_3
if (nRESET === 1'b 0) begin
shiftreg <= 'd0;
if (~nRESET) begin
shiftreg <= 0;
end
else begin
shiftreg <= shiftreg_nxt;
end
end
// Cursor generation
assign cursor_invert = cursor_active & (r0_cursor0 & ~(cursor_counter[0] | cursor_counter[1]) |
r0_cursor1 & cursor_counter[0] & ~cursor_counter[1] | r0_cursor2 &
cursor_counter[1]);
(r0_cursor1 & cursor_counter[0] & ~cursor_counter[1]) |
(r0_cursor2 & cursor_counter[1]));
// delayed cursor for teletext
always @(posedge CLOCK) if (mhz4_clken) cursor_invert_delayed <= cursor_invert;
always @(posedge CLOCK) begin : process_4
if (nRESET === 1'b 0) begin
cursor_active <= 'd0;
cursor_counter <= 'd0;
if (~nRESET) begin
cursor_active <= 0;
cursor_counter <= 0;
end
else if (clken_fetch === 1'b 1 ) begin
if ((CURSOR | cursor_active) === 1'b 1) begin
else if (clken_fetch) begin
if (CURSOR | cursor_active) begin
// Latch cursor
cursor_active <= 1'b 1;
cursor_active <= 1;
// Reset on counter wrap
if (cursor_counter === 2'b 11) begin
cursor_active <= 1'b 0;
cursor_active <= 0;
end
// Increment counter
if (cursor_active === 1'b 0) begin
if (!cursor_active) begin
// Reset
cursor_counter <= {2{1'b 0}};
cursor_counter <= 0;
// Increment
end
else begin
cursor_counter <= cursor_counter + 1;
// Increment
cursor_counter <= cursor_counter + 1'd1;
end
end
end
@@ -226,7 +225,7 @@ end
// Pixel generation
// The new shift register contents are loaded during
// cycle 0 (and 8) but will not be read here until the next cycle.
// cycle 11 (and 3) but will not be read here until the next cycle.
// By running this process on every single video tick instead of at
// the pixel rate we ensure that the resulting delay is minimal and
// constant (running this at the pixel rate would cause
@@ -239,30 +238,35 @@ end
// bit 1 - Not GREEN
// bit 0 - Not RED
wire [3:0] palette_a = {shiftreg[7], shiftreg[5], shiftreg[3], shiftreg[1]};
wire [3:0] dot_val = palette[palette_a];
wire [3:0] palette_a = {shiftreg[7], shiftreg[5], shiftreg[3], shiftreg[1]};
wire [3:0] dot_val = palette[palette_a];
// Apply flash inversion if required
wire red_val = dot_val[3] & r0_flash ^ ~dot_val[0];
wire green_val = dot_val[3] & r0_flash ^ ~dot_val[1];
wire blue_val = dot_val[3] & r0_flash ^ ~dot_val[2];
wire red_val = dot_val[3] & r0_flash ^ ~dot_val[0];
wire green_val = dot_val[3] & r0_flash ^ ~dot_val[1];
wire blue_val = dot_val[3] & r0_flash ^ ~dot_val[2];
// Display enable signal delayed by one clock
always @(posedge CLOCK) begin
if (mhz4_clken) begin
delayed_disen1 <= DISEN;
delayed_disen2 <= delayed_disen1;
end
end
wire delayed_disen = r0_crtc_2mhz ? delayed_disen1 : delayed_disen2;
always @(posedge CLOCK) begin
if (nRESET === 1'b 0) begin
R <= 'd0;
G <= 'd0;
B <= 'd0;
delayed_disen <= 'd0;
if (~nRESET) begin
R <= 0;
G <= 0;
B <= 0;
end
else if (CLKEN === 1'b1) begin
else if (CLKEN) begin
// To output
// FIXME: INVERT option
if (r0_teletext === 1'b0) begin
if (!r0_teletext) begin
// Cursor can extend outside the bounds of the screen, so
// it is not affected by DISEN
@@ -273,17 +277,14 @@ always @(posedge CLOCK) begin
end
else begin
R <= R_IN ^ cursor_invert;
G <= G_IN ^ cursor_invert;
B <= B_IN ^ cursor_invert;
R <= R_IN ^ cursor_invert_delayed;
G <= G_IN ^ cursor_invert_delayed;
B <= B_IN ^ cursor_invert_delayed;
end
// Display enable signal delayed by one clock
delayed_disen <= DISEN;
end
end
endmodule // module vidproc