mirror of https://github.com/zxdos/zxuno.git
519 lines
14 KiB
Verilog
519 lines
14 KiB
Verilog
`timescale 1ns / 1ps
|
|
`default_nettype none
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Company:
|
|
// Engineer:
|
|
//
|
|
// Create Date: 16:54:55 02/16/2014
|
|
// Design Name:
|
|
// Module Name: ula
|
|
// Project Name:
|
|
// Target Devices:
|
|
// Tool versions:
|
|
// Description:
|
|
//
|
|
// Dependencies:
|
|
//
|
|
// Revision:
|
|
// Revision 0.01 - File Created
|
|
// Additional Comments:
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module ula (
|
|
// Clocks
|
|
input wire clk14, // 14MHz master clock
|
|
input wire wssclk, // 5MHz WSS clock
|
|
input wire rst_n, // reset para volver al modo normal
|
|
|
|
// CPU interface
|
|
input wire [15:0] a,
|
|
input wire mreq_n,
|
|
input wire iorq_n,
|
|
input wire rd_n,
|
|
input wire wr_n,
|
|
output wire cpuclk,
|
|
output reg int_n,
|
|
input wire [7:0] din,
|
|
output reg [7:0] dout,
|
|
|
|
// VRAM interface
|
|
output reg [13:0] va, // 16KB videoram
|
|
input wire [7:0] vramdata,
|
|
|
|
// I/O ports
|
|
input wire ear,
|
|
input wire [4:0] kbd,
|
|
output reg mic,
|
|
output reg spk,
|
|
output wire clkay,
|
|
output wire clkdac,
|
|
output wire clkkbd,
|
|
input wire issue2_keyboard,
|
|
|
|
// Video
|
|
output wire [2:0] r,
|
|
output wire [2:0] g,
|
|
output wire [2:0] b,
|
|
output wire csync,
|
|
output wire y_n
|
|
);
|
|
|
|
// RGB inputs to sync module
|
|
reg [2:0] ri;
|
|
reg [2:0] gi;
|
|
reg [2:0] bi;
|
|
|
|
// Counters from sync module
|
|
wire [8:0] hc;
|
|
wire [8:0] vc;
|
|
|
|
reg clk7 = 1'b0;
|
|
always @(posedge clk14)
|
|
clk7 <= ~clk7;
|
|
|
|
assign clkdac = clk7;
|
|
assign clkay = hc[0];
|
|
assign clkkbd = hc[3];
|
|
//assign cpuclk = hc[0]; // temporal. DeberÃa ser con contención
|
|
|
|
pal_sync_generator_progressive syncs (
|
|
.clk(clk7),
|
|
.wssclk(wssclk),
|
|
.ri(ri),
|
|
.gi(gi),
|
|
.bi(bi),
|
|
.hcnt(hc),
|
|
.vcnt(vc),
|
|
.ro(r),
|
|
.go(g),
|
|
.bo(b),
|
|
.csync(csync)
|
|
);
|
|
|
|
parameter
|
|
BHPIXEL = 0,
|
|
EHPIXEL = 255,
|
|
BBORDER = 256,
|
|
EBORDER = 320,
|
|
BHBLANK = 320,
|
|
EHBLANK = 416,
|
|
BHSYNC = 344,
|
|
EHSYNC = 376,
|
|
BBORDEL = 416,
|
|
EBORDEL = 447,
|
|
BVPIXEL = 0,
|
|
EVPIXEL = 191,
|
|
BBORDED = 192,
|
|
EBORDED = 247,
|
|
BVPERIOD = 248,
|
|
EVPERIOD = 255,
|
|
BVSYNC = 248,
|
|
EVSYNC = 251,
|
|
BBORDEU = 256,
|
|
EBORDEU = 311;
|
|
|
|
///////////////////////////////////////////////
|
|
// ULA datapath
|
|
///////////////////////////////////////////////
|
|
|
|
// Control signals generated from the control unit
|
|
// or the rest of modules
|
|
reg BitmapDataLoad;
|
|
reg AttrDataLoad;
|
|
reg WriteToPortFE;
|
|
reg SerializerLoad;
|
|
reg TimexConfigLoad;
|
|
reg AttrOutputLoad;
|
|
reg PaletteRegLoad;
|
|
reg ConfigRegLoad;
|
|
reg PaletteLoad;
|
|
reg BitmapAddr;
|
|
reg AttrAddr;
|
|
reg CALoad;
|
|
reg VideoEnable;
|
|
|
|
// BitmapData register
|
|
reg [7:0] BitmapData = 8'h00;
|
|
always @(posedge clk7) begin
|
|
if (BitmapDataLoad)
|
|
BitmapData <= vramdata;
|
|
end
|
|
|
|
// AttrData register
|
|
reg [7:0] AttrData = 8'h00;
|
|
always @(posedge clk7) begin
|
|
if (AttrDataLoad)
|
|
AttrData <= vramdata;
|
|
end
|
|
|
|
// Border register
|
|
reg [2:0] Border = 3'b010; // initial border colour is red
|
|
always @(posedge clk7) begin
|
|
if (WriteToPortFE)
|
|
Border <= din[2:0];
|
|
end
|
|
|
|
// BitmapSerializer register
|
|
reg [7:0] BitmapSerializer = 8'h00;
|
|
wire SerialOutput = BitmapSerializer[7];
|
|
always @(posedge clk7) begin
|
|
if (SerializerLoad)
|
|
BitmapSerializer <= BitmapData;
|
|
else
|
|
BitmapSerializer <= {BitmapSerializer[6:0],1'b0};
|
|
end
|
|
|
|
// BitmapSerializerHR register
|
|
reg [15:0] BitmapSerializerHR = 8'h00;
|
|
wire SerialOutputHR = BitmapSerializerHR[15];
|
|
always @(posedge clk14) begin
|
|
if (SerializerLoad && clk7) // load enable only for a single 14MHz cycle
|
|
BitmapSerializerHR <= {BitmapData,AttrData};
|
|
else
|
|
BitmapSerializerHR <= {BitmapSerializerHR[14:0],1'b0};
|
|
end
|
|
|
|
// Timex config register
|
|
reg [5:0] TimexConfigReg = 6'h00;
|
|
wire PG = TimexConfigReg[0];
|
|
wire HCL = TimexConfigReg[1];
|
|
wire HR = TimexConfigReg[2];
|
|
wire [2:0] HRInk = TimexConfigReg[5:3];
|
|
always @(posedge clk7) begin
|
|
if (rst_n == 1'b0)
|
|
TimexConfigReg <= 6'h00;
|
|
else if (TimexConfigLoad)
|
|
TimexConfigReg <= din[5:0];
|
|
end
|
|
|
|
// Combinational logic between AttrData and AttrOutput
|
|
reg [7:0] InputToAttrOutput;
|
|
always @* begin
|
|
InputToAttrOutput = AttrData;
|
|
case ({VideoEnable,HR})
|
|
2'b00 : InputToAttrOutput = {2'b00,Border,3'b000};
|
|
2'b01,
|
|
2'b11 : InputToAttrOutput = {2'b01,~HRInk,HRInk};
|
|
2'b10 : InputToAttrOutput = AttrData;
|
|
endcase
|
|
end
|
|
|
|
// AttrOutput register
|
|
reg [7:0] AttrOutput = 8'h00;
|
|
wire [2:0] StdPaperColour = AttrOutput[5:3];
|
|
wire [2:0] StdInkColour = AttrOutput[2:0];
|
|
wire Bright = AttrOutput[6];
|
|
wire Flash = AttrOutput[7];
|
|
always @(posedge clk7) begin
|
|
if (AttrOutputLoad)
|
|
AttrOutput <= InputToAttrOutput;
|
|
end
|
|
|
|
// Combinational logic to generate pixel bit
|
|
reg Pixel;
|
|
always @* begin
|
|
if (HR)
|
|
Pixel = SerialOutputHR;
|
|
else
|
|
Pixel = SerialOutput;
|
|
end
|
|
|
|
// Flash!
|
|
reg [4:0] FlashCounter = 5'h00;
|
|
wire FlashFF = FlashCounter[4];
|
|
wire PixelWFlash = Pixel ^ (Flash & FlashFF);
|
|
always @(posedge clk7) begin
|
|
if (vc==BVSYNC && hc==0)
|
|
FlashCounter <= FlashCounter + 1;
|
|
end
|
|
|
|
// Standard ULA final 4-bit IGRB colour
|
|
reg [3:0] StdPixelColour;
|
|
always @* begin
|
|
if (PixelWFlash)
|
|
StdPixelColour = {Bright,StdInkColour};
|
|
else
|
|
StdPixelColour = {Bright,StdPaperColour};
|
|
end
|
|
|
|
// LUT-based translatator from IGRB to 9-bit GRB
|
|
`define none 3'b000
|
|
`define half 3'b101
|
|
`define full 3'b111
|
|
reg [8:0] Std9bitColour;
|
|
always @* begin
|
|
Std9bitColour = {`none,`none,`none};
|
|
case (StdPixelColour) // speccy colour to GGGRRRBBB colour. If you want to alter the standard palette, this is what you need to touch ;)
|
|
0,8: Std9bitColour = {`none,`none,`none};
|
|
1: Std9bitColour = {`none,`none,`half};
|
|
2: Std9bitColour = {`none,`half,`none};
|
|
3: Std9bitColour = {`none,`half,`half};
|
|
4: Std9bitColour = {`half,`none,`none};
|
|
5: Std9bitColour = {`half,`none,`half};
|
|
6: Std9bitColour = {`half,`half,`none};
|
|
7: Std9bitColour = {`half,`half,`half};
|
|
9: Std9bitColour = {`none,`none,`full};
|
|
10: Std9bitColour = {`none,`full,`none};
|
|
11: Std9bitColour = {`none,`full,`full};
|
|
12: Std9bitColour = {`full,`none,`none};
|
|
13: Std9bitColour = {`full,`none,`full};
|
|
14: Std9bitColour = {`full,`full,`none};
|
|
15: Std9bitColour = {`full,`full,`full};
|
|
endcase
|
|
end
|
|
|
|
// PaletteReg register (ULAplus)
|
|
reg [6:0] PaletteReg = 7'h00;
|
|
always @(posedge clk7) begin
|
|
if (PaletteRegLoad)
|
|
PaletteReg <= din[6:0];
|
|
end
|
|
|
|
// ConfigReg register (ULAplus)
|
|
reg ConfigReg = 1'b0;
|
|
wire ULAplusEnabled = ConfigReg;
|
|
always @(posedge clk7) begin
|
|
if (rst_n == 1'b0)
|
|
ConfigReg <= 1'b0;
|
|
else if (ConfigRegLoad)
|
|
ConfigReg <= din[0];
|
|
end
|
|
|
|
// Palette LUT
|
|
wire [7:0] PaletteEntryToCPU;
|
|
wire [7:0] ULAplusPaperColour;
|
|
wire [7:0] ULAplusInkColour;
|
|
lut palette (
|
|
.clk(clk7),
|
|
.load(PaletteLoad),
|
|
.din(din),
|
|
.a1({InputToAttrOutput[7:6],1'b1,InputToAttrOutput[5:3]}),
|
|
.a2({InputToAttrOutput[7:6],1'b0,InputToAttrOutput[2:0]}),
|
|
.a3(PaletteReg[5:0]),
|
|
.do1(ULAplusPaperColour),
|
|
.do2(ULAplusInkColour),
|
|
.do3(PaletteEntryToCPU)
|
|
);
|
|
|
|
// AttrPlusOutput register
|
|
reg [15:0] AttrPlusOutput = 16'h0000;
|
|
always @(posedge clk7) begin
|
|
if (AttrOutputLoad)
|
|
AttrPlusOutput <= {ULAplusPaperColour,ULAplusInkColour};
|
|
end
|
|
|
|
// ULAplus final 8-bit GGGRRRBB colour
|
|
reg [7:0] ULAplusPixelColour;
|
|
always @* begin
|
|
if (Pixel)
|
|
ULAplusPixelColour = AttrPlusOutput[7:0];
|
|
else
|
|
ULAplusPixelColour = AttrPlusOutput[15:8];
|
|
end
|
|
|
|
// 332-GRB to 333-GRB (blue turns from B1 B0 into B1 B0 B1)
|
|
wire [8:0] ULAplus9bitColour = {ULAplusPixelColour,ULAplusPixelColour[1]};
|
|
|
|
// Final stage. Final colour is connected to PAL generator
|
|
always @* begin
|
|
if (ULAplusEnabled) begin
|
|
gi = ULAplus9bitColour[8:6];
|
|
ri = ULAplus9bitColour[5:3];
|
|
bi = ULAplus9bitColour[2:0];
|
|
end
|
|
else begin
|
|
gi = Std9bitColour[8:6];
|
|
ri = Std9bitColour[5:3];
|
|
bi = Std9bitColour[2:0];
|
|
end
|
|
end
|
|
|
|
// Column address register (CA)
|
|
reg [4:0] CA = 5'h00;
|
|
always @(posedge clk7) begin
|
|
if (CALoad)
|
|
CA <= hc[7:3];
|
|
end
|
|
|
|
// VRAM Address generation
|
|
always @* begin
|
|
if (BitmapAddr) begin
|
|
va = {PG,vc[7:6],vc[2:0],vc[5:3],CA};
|
|
end
|
|
else if (AttrAddr) begin
|
|
if (HCL==1'b0)
|
|
va = {PG,3'b110,vc[7:3],CA};
|
|
else
|
|
va = {1'b1,vc[7:6],vc[2:0],vc[5:3],CA};
|
|
end
|
|
else
|
|
va = 14'hZZZZ;
|
|
end
|
|
|
|
///////////////////////////////////////////////
|
|
// ULA control unit
|
|
///////////////////////////////////////////////
|
|
|
|
// control data flow from VRAM to RGB output
|
|
always @* begin
|
|
BitmapDataLoad = 1'b0;
|
|
AttrDataLoad = 1'b0;
|
|
SerializerLoad = 1'b0;
|
|
VideoEnable = 1'b0;
|
|
AttrOutputLoad = 1'b0;
|
|
BitmapAddr = 1'b0;
|
|
AttrAddr = 1'b0;
|
|
CALoad = 1'b0;
|
|
if (hc[2:0]==3'd4) begin // hc=4,12,20,28,etc
|
|
AttrOutputLoad = 1'b1; // updated every 8 pixel clocks
|
|
end
|
|
if (hc[2:0]==3'd3) begin
|
|
CALoad = 1'b1;
|
|
end
|
|
if (hc>=(BHPIXEL+8) && hc<=(EHPIXEL+8) && vc>=BVPIXEL && vc<=EVPIXEL) begin // VidEN_n is low here: paper area
|
|
VideoEnable = 1'b1;
|
|
if (hc[2:0]==3'd4) begin
|
|
SerializerLoad = 1'b1; // updated every 8 pixel clocks, if we are in paper area
|
|
end
|
|
if (hc[3:0]==4'd8 || hc[3:0]==4'd12) begin
|
|
BitmapAddr = 1'b1;
|
|
end
|
|
if (hc[3:0]==4'd9 || hc[3:0]==4'd13) begin
|
|
BitmapAddr = 1'b1;
|
|
BitmapDataLoad = 1'b1;
|
|
end
|
|
if (hc[3:0]==4'd10 || hc[3:0]==4'd14) begin
|
|
AttrAddr = 1'b1;
|
|
end
|
|
if (hc[3:0]==4'd11 || hc[3:0]==4'd15) begin
|
|
AttrAddr = 1'b1;
|
|
AttrDataLoad = 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
///////////////////////////////////////////////
|
|
// ULA interface with CPU
|
|
///////////////////////////////////////////////
|
|
|
|
parameter
|
|
TIMEXPORT = 8'hFF,
|
|
ULAPLUSADDR = 16'hBF3B,
|
|
ULAPLUSDATA = 16'hFF3B;
|
|
|
|
// Z80 writes values into registers
|
|
// Port 0xFE
|
|
always @(posedge clk7) begin
|
|
if (iorq_n==1'b0 && wr_n==1'b0) begin
|
|
if (a[0]==1'b0) begin
|
|
{spk,mic} <= din[4:3];
|
|
end
|
|
end
|
|
end
|
|
|
|
// TIMEX and ULAplus ports
|
|
always @* begin
|
|
TimexConfigLoad = 1'b0;
|
|
PaletteRegLoad = 1'b0;
|
|
ConfigRegLoad = 1'b0;
|
|
PaletteLoad = 1'b0;
|
|
WriteToPortFE = 1'b0;
|
|
if (iorq_n==1'b0 && wr_n==1'b0) begin
|
|
if (a[0]==1'b0)
|
|
WriteToPortFE = 1'b1;
|
|
else if (a[7:0]==TIMEXPORT)
|
|
TimexConfigLoad = 1'b1;
|
|
else if (a==ULAPLUSADDR)
|
|
PaletteRegLoad = 1'b1;
|
|
else if (a==ULAPLUSDATA) begin
|
|
if (PaletteReg[6]==1'b0) // writting a new value into palette LUT
|
|
PaletteLoad = 1'b1;
|
|
else
|
|
ConfigRegLoad = 1'b1; // writting a new value into ULAplus config register
|
|
end
|
|
end
|
|
end
|
|
|
|
// Z80 gets values from registers (or floating bus)
|
|
always @* begin
|
|
dout = 8'hFF;
|
|
if (iorq_n==1'b0 && rd_n==1'b0) begin
|
|
if (a[0]==1'b0)
|
|
dout = {1'b1,issue2_keyboard^ear,1'b1,kbd};
|
|
else if (a==ULAPLUSADDR)
|
|
dout = {1'b0,PaletteReg};
|
|
else if (a==ULAPLUSDATA && PaletteReg[6]==1'b0)
|
|
dout = PaletteEntryToCPU;
|
|
else if (a==ULAPLUSDATA && PaletteReg[6]==1'b1)
|
|
dout = {7'b0000000,ConfigReg};
|
|
else if (a[7:0]==TIMEXPORT) begin
|
|
if (BitmapAddr)
|
|
dout = BitmapData;
|
|
else if (AttrAddr)
|
|
dout = AttrData; // floating bus
|
|
else
|
|
dout = 8'hFF;
|
|
end
|
|
end
|
|
end
|
|
|
|
// INT generation
|
|
always @* begin
|
|
if (vc==BVSYNC && hc>=0 && hc<=63) // 32 T-states INT pulse width
|
|
int_n = 1'b0;
|
|
else
|
|
int_n = 1'b1;
|
|
end
|
|
|
|
///////////////////////////////////
|
|
// CPU CLOCK GENERATION (Altwasser method)
|
|
///////////////////////////////////
|
|
|
|
`define MASTERCPUCLK clk7
|
|
|
|
reg CPUInternalClock = 0;
|
|
reg ioreqtw3 = 0;
|
|
reg mreqt23 = 0;
|
|
|
|
wire iorequla = !iorq_n && (a[0]==0);
|
|
wire iorequlaplus = !iorq_n && (a==ULAPLUSADDR || a==ULAPLUSDATA);
|
|
wire ioreqall_n = !(iorequlaplus || iorequla);
|
|
|
|
reg Border_n;
|
|
always @(*) begin
|
|
if (vc>=BVPIXEL && vc<=EVPIXEL && hc>=BHPIXEL && hc<=EHPIXEL)
|
|
Border_n = 1;
|
|
else
|
|
Border_n = 0;
|
|
end
|
|
wire Nor1 = (~(a[14] | ~ioreqall_n)) |
|
|
(~(~a[15] | ~ioreqall_n)) |
|
|
( hc[3:0]<4'd4 ) |
|
|
(~Border_n | ~ioreqtw3 | ~cpuclk | ~mreqt23);
|
|
wire Nor2 = ( hc[3:0]<4'd4 ) |
|
|
~Border_n |
|
|
~cpuclk |
|
|
ioreqall_n |
|
|
~ioreqtw3;
|
|
wire CLKContention = ~Nor1 | ~Nor2;
|
|
|
|
always @(posedge cpuclk) begin
|
|
if (!CLKContention) begin
|
|
ioreqtw3 <= ioreqall_n;
|
|
mreqt23 <= mreq_n;
|
|
end
|
|
end
|
|
|
|
always @(posedge `MASTERCPUCLK) begin
|
|
if (!CLKContention)
|
|
CPUInternalClock = ~CPUInternalClock;
|
|
else
|
|
CPUInternalClock = 1'b1;
|
|
end
|
|
|
|
assign cpuclk = CPUInternalClock;
|
|
|
|
endmodule
|