Files
Arcade-TimePilot_MiSTer/rtl/TimePilot_CPU.sv

833 lines
22 KiB
Systemverilog

//============================================================================
//
// Time Pilot main PCB model
// Copyright (C) 2021 Ace, Artemio Urbina & RTLEngineering
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
//============================================================================
//Module declaration, I/O ports
module TimePilot_CPU
(
input reset,
input clk_49m, //Actual frequency: 49.152MHz
output [4:0] red, green, blue, //15-bit RGB, 5 bits per color
output video_hsync, video_vsync, video_csync, //CSync not needed for MISTer
output video_hblank, video_vblank,
output ce_pix,
input [7:0] controls_dip,
output [7:0] cpubrd_Dout,
output cpubrd_A5, cpubrd_A6,
output cs_sounddata, irq_trigger,
output cs_dip2, cs_controls_dip1,
//Screen centering (alters HSync, VSync and VBlank timing in the Konami 082 to reposition the video output)
input [3:0] h_center, v_center,
input ep1_cs_i,
input ep2_cs_i,
input ep3_cs_i,
input ep4_cs_i,
input ep5_cs_i,
input ep6_cs_i,
input cp1_cs_i,
input cp2_cs_i,
input tl_cs_i,
input sl_cs_i,
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr,
input pause,
input [15:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write
);
//------------------------------------------------------- Signal outputs -------------------------------------------------------//
//Assign active high HBlank and VBlank outputs
assign video_hblank = hblk;
assign video_vblank = vblk;
//Output pixel clock enable
assign ce_pix = cen_6m;
//Output select lines for player inputs and DIP switches to sound board
assign cs_controls_dip1 = (~n_cs_k501 & z80_A[14]) & (z80_A[13:12] == 2'b00) & (z80_A[9:8] == 2'b11) & n_ram_write;
assign cs_dip2 = (~n_cs_k501 & z80_A[14]) & (z80_A[13:12] == 2'b00) & (z80_A[9:8] == 2'b10) & n_ram_write;
//Output primary MC6809E address lines A5 and A6 to sound board
assign cpubrd_A5 = z80_A[5];
assign cpubrd_A6 = z80_A[6];
//Assign CPU board data output to sound board
assign cpubrd_Dout = z80_Dout;
//Generate and output chip select for latching sound data to sound CPU
assign cs_sounddata = (~n_cs_k501 & z80_A[14]) & (z80_A[13:12] == 2'b00) & (z80_A[9:8] == 2'b00) & ~n_ram_write;
//Generate sound IRQ trigger
reg sound_irq = 1;
always_ff @(posedge clk_49m) begin
if(cen_3m) begin
if(cs_soundirq)
sound_irq <= 1;
else
sound_irq <= 0;
end
end
assign irq_trigger = sound_irq;
//------------------------------------------------------- Clock division -------------------------------------------------------//
//Generate 12.288MHz, 6.144MHz and 3.072MHz clock enables
reg [3:0] div = 4'd0;
always_ff @(posedge clk_49m) begin
div <= div + 4'd1;
end
wire cen_12m = !div[1:0];
wire cen_6m = !div[2:0];
wire cen_3m = !div;
//------------------------------------------------------------ CPUs ------------------------------------------------------------//
//Primary CPU - Zilog Z80 (uses T80s version of the T80 soft core)
wire [15:0] z80_A;
wire [7:0] z80_Dout;
wire n_mreq, n_rd, n_rfsh;
T80s E3
(
.RESET_n(reset),
.CLK(clk_49m),
.CEN(cen_3m & ~pause),
.NMI_n(n_nmi),
.WAIT_n(n_wait),
.MREQ_n(n_mreq),
.RD_n(n_rd),
.RFSH_n(n_rfsh),
.A(z80_A),
.DI(z80_Din),
.DO(z80_Dout)
);
//Address decoding for Z80
//Most of the decoding is handled by the Konami 526 (82S153 PLA) - instantiate an instance of this IC here
wire n_cs_rom1, n_cs_rom2, n_cs_rom3, n_cs_k501, n_cs_vram_wram, n_cs_spriteram0, n_cs_spriteram1;
k526 E1
(
.MREQ(n_mreq),
.RFSH(n_rfsh),
.A15(z80_A[15]),
.A14(z80_A[14]),
.A13(z80_A[13]),
.A12(z80_A[12]),
.A10(z80_A[10]),
.ENABLE(n_k501_enable),
.SPRAM0(n_cs_spriteram0),
.SPRAM1(n_cs_spriteram1),
.WRAM(n_cs_vram_wram),
.K501(n_cs_k501),
.ROM({1'bZ, n_cs_rom3, n_cs_rom2, n_cs_rom1})
);
//The rest of the decoding is handled by the Konami 501 custom chip - instantiate an instance of this IC here
wire n_wait, n_ram_write, n_k501_enable;
wire [7:0] k501_D, k501_Dout;
k501 C3
(
.CLK(clk_49m),
.CEN(cen_12m),
.H1(h_cnt[0]),
.H2(h_cnt[1]),
.RAM(n_cs_k501),
.RD(n_rd),
.WAIT(n_wait),
.WRITE(n_ram_write),
.ENABLE(n_k501_enable),
.Di(z80_Dout),
.XDi(k501_Din),
.Do(k501_Dout),
.XDo(k501_D)
);
wire cs_beam = (~n_cs_k501 & z80_A[14]) & (z80_A[13:12] == 2'b00) & (z80_A[9:8] == 2'b00) & n_ram_write;
wire cs_mainlatch = (~n_cs_k501 & z80_A[14]) & (z80_A[13:12] == 2'b00) & (z80_A[9:8] == 2'b11) & ~n_ram_write;
//Multiplex data inputs to Z80
wire [7:0] z80_Din = ~n_cs_rom1 ? eprom1_D:
~n_cs_rom2 ? eprom2_D:
~n_cs_rom3 ? eprom3_D:
~n_cs_k501 ? k501_Dout:
8'hFF;
//Game ROMs
//ROM 1/3
wire [7:0] eprom1_D;
eprom_1 H2
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom1_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep1_cs_i),
.WR(ioctl_wr)
);
//ROM 2/3
wire [7:0] eprom2_D;
eprom_2 H3
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom2_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep2_cs_i),
.WR(ioctl_wr)
);
//ROM 3/3
wire [7:0] eprom3_D;
eprom_3 H4
(
.ADDR(z80_A[12:0]),
.CLK(clk_49m),
.DATA(eprom3_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep3_cs_i),
.WR(ioctl_wr)
);
//Multiplex data input to Konami 501 data bus passthrough
wire [7:0] k501_Din = (~n_cs_vram_wram & ~vram_wram_sel & n_vram_wram_wr) ? vram_D:
(~n_cs_vram_wram & vram_wram_sel & n_vram_wram_wr) ? wram_D:
(~n_cs_spriteram0 & n_spriteram0_wr) ? spriteram_D[7:0]:
(~n_cs_spriteram1 & n_spriteram1_wr) ? spriteram_D[15:8]:
cs_beam ? v_cnt:
(cs_controls_dip1 | cs_dip2) ? controls_dip:
8'hFF;
//Z80 work RAM
wire [7:0] wram_D;
dpram_dc #(.widthad_a(11)) F9
(
.clock_a(clk_49m),
.wren_a(~n_cs_vram_wram & vram_wram_sel & ~n_vram_wram_wr),
.address_a(vram_wram_A),
.data_a(k501_D),
.q_a(wram_D),
.clock_b(clk_49m),
.wren_b(hs_write),
.address_b(hs_address),
.data_b(hs_data_in),
.q_b(hs_data_out)
);
//VRAM
wire [7:0] vram_D;
spram #(8, 11) F10
(
.clk(clk_49m),
.we(~n_cs_vram_wram & ~vram_wram_sel & ~n_vram_wram_wr),
.addr(vram_wram_A),
.data(k501_D),
.q(vram_D)
);
//Generate select line and write enable for work RAM/VRAM
wire vram_wram_sel = z80_A[11] & ~h_cnt[1];
wire n_vram_wram_wr = n_cs_vram_wram | n_ram_write;
//Main latch
reg nmi_mask = 0;
reg flip = 0;
reg cs_soundirq = 0;
reg pixel_en = 0;
always_ff @(posedge clk_49m) begin
if(!reset) begin
nmi_mask <= 0;
flip <= 0;
cs_soundirq <= 0;
end
else if(cen_3m) begin
if(cs_mainlatch)
case(z80_A[3:1])
3'b000: nmi_mask <= k501_D[0];
3'b001: flip <= k501_D[0];
3'b010: cs_soundirq <= k501_D[0];
3'b100: pixel_en <= k501_D[0];
default:;
endcase
end
end
//Generate VBlank NMI for Z80
reg n_nmi = 1;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(!nmi_mask)
n_nmi <= 1;
else if(vblank_irq_en)
n_nmi <= 0;
end
end
//-------------------------------------------------------- Video timing --------------------------------------------------------//
//Konami 082 custom chip - responsible for all video timings
wire vblk, vblank_irq_en, h256;
wire [8:0] h_cnt;
wire [7:0] v_cnt;
k082 F5
(
.reset(1),
.clk(clk_49m),
.cen(cen_6m),
.h_center(h_center),
.v_center(v_center),
.n_vsync(video_vsync),
.sync(video_csync),
.n_hsync(video_hsync),
.vblk(vblk),
.vblk_irq_en(vblank_irq_en),
.h1(h_cnt[0]),
.h2(h_cnt[1]),
.h4(h_cnt[2]),
.h8(h_cnt[3]),
.h16(h_cnt[4]),
.h32(h_cnt[5]),
.h64(h_cnt[6]),
.h128(h_cnt[7]),
.n_h256(h_cnt[8]),
.h256(h256),
.v1(v_cnt[0]),
.v2(v_cnt[1]),
.v4(v_cnt[2]),
.v8(v_cnt[3]),
.v16(v_cnt[4]),
.v32(v_cnt[5]),
.v64(v_cnt[6]),
.v128(v_cnt[7])
);
//Time Pilot has an additional 82S153 PLA labelled K528 that controls the vertical counter latch and is used to control the state of
//the Konami 082's vertical counter (active or high impedance as the vertical counter is directly connected to the Z80's data bus on
//the PCB) - instantiate an instance of this IC here
wire vcntlatch, vcntlatch_clr;
k528 E10
(
.HCNT(h_cnt[6:0]),
.H256(h256),
.RESET0(reset),
.RESET1(reset),
.NVEN(~cs_beam),
.PEN(pixel_en),
.CLR(vcntlatch_clr),
.CLKO(vcntlatch)
);
//Latch vertical counter bits from 082 custom chip
reg [7:0] vcnt_lat = 8'd0;
reg old_vcntlatch;
always_ff @(posedge clk_49m) begin
old_vcntlatch <= vcntlatch;
if(!vcntlatch_clr)
vcnt_lat <= 8'd0;
else if(!old_vcntlatch && vcntlatch)
vcnt_lat <= v_cnt;
end
//XOR horizontal counter bits [7:2] with the inverse of the flip flag
wire [7:2] hcnt_x = h_cnt[7:2] ^ {6{~flip}};
//XOR latched vertical counter bits with the inverse of the flip flag
wire [7:0] vcnt_x = vcnt_lat ^ {8{~flip}};
//--------------------------------------------------------- Tile layer ---------------------------------------------------------//
//Generate addresses for VRAM
wire [10:0] vram_wram_A = h_cnt[1] ? {h_cnt[2], vcnt_x[7:3], hcnt_x[7:3]} : z80_A[10:0];
//LDO, labelled D1 in the Time Pilot schematics, signals to the tilemap logic when to latch tilemap codes from VRAM. It pulses
//low when the lower 3 bits of the horizontal counter are all 1, then on the rising edge, tilemap codes are latched.
//Set LDO as a register and latch a 0 when the 3 least significant bits of the horizontal counter are set to 1, otherwise latch
//a 1
reg ldo = 1;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b111)
ldo <= 0;
else
ldo <= 1;
end
//Latch tilemap code from VRAM at every rising edge of LDO
reg [7:0] tile_code = 8'd0;
always_ff @(posedge clk_49m) begin
if(!ldo && h_cnt[2:0] == 3'b000)
tile_code <= vram_D;
end
//Latch tilemap attributes from VRAM at the rising edge of bit 2 of the horizontal counter when H256 is low
reg tile_attrib_latch = 1;
always_ff @(posedge clk_49m) begin
if(h_cnt[2:0] == 3'b011)
tile_attrib_latch <= 0;
else
tile_attrib_latch <= 1;
end
reg [7:0] tile_attrib = 8'd0;
always_ff @(posedge clk_49m) begin
if(!h256 && !tile_attrib_latch && h_cnt[2:0] == 3'b100)
tile_attrib <= vram_D;
end
//Latch tile color information, tilemap enable and flip signal for tilemap 083 custom chip every 8 pixels
wire tile_flip = tile_hflip ^ flip;
reg tile_083_flip = 0;
reg tilemap_en = 0;
reg [3:0] tile_color = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(h_cnt[2:0] == 3'b011) begin
tile_083_flip <= tile_flip;
tile_color <= tile_attrib[3:0];
tilemap_en <= tile_attrib[4];
end
else begin
tile_083_flip <= tile_083_flip;
tile_color <= tile_color;
tilemap_en <= tilemap_en;
end
end
end
//Generate address lines A1 - A3 of tilemap ROMs, CRA and tile flip attributes on the falling edge of horizontal
//counter bit 2
reg v1l, v2l, v4l, cra, tile_hflip, tile_vflip;
reg old_h4;
always_ff @(posedge clk_49m) begin
old_h4 <= h_cnt[2];
if(old_h4 && !h_cnt[2]) begin
v1l <= vcnt_x[0];
v2l <= vcnt_x[1];
v4l <= vcnt_x[2];
tile_hflip <= tile_attrib[6];
tile_vflip <= tile_attrib[7];
cra <= tile_attrib[5];
end
end
//Address tilemap ROM
assign tilerom_A[12] = cra;
assign tilerom_A[11:4] = tile_code[7:0];
assign tilerom_A[3:0] = {hcnt_x[2] ^ tile_hflip, v4l ^ tile_vflip, v2l ^ tile_vflip, v1l ^ tile_vflip};
//Tilemap ROM
wire [12:0] tilerom_A;
wire [7:0] eprom4_D;
eprom_4 F11
(
.ADDR(tilerom_A),
.CLK(clk_49m),
.DATA(eprom4_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep4_cs_i),
.WR(ioctl_wr)
);
//Konami 083 custom chip 1/2 - this one shifts the pixel data from tilemap ROMs
k083 F12
(
.CK(clk_49m),
.CEN(cen_6m),
.LOAD(h_cnt[1:0] == 2'b11),
.FLIP(tile_083_flip),
.DB0i(eprom4_D),
.DSH0(tile_lut_A[1:0])
);
//Tilemap lookup PROM
wire [7:0] tile_lut_A;
assign tile_lut_A[7] = 0;
assign tile_lut_A[6] = tilemap_en;
assign tile_lut_A[5:2] = tile_color;
wire [3:0] tile_D;
tile_lut_prom E12
(
.ADDR(tile_lut_A),
.CLK(clk_49m),
.DATA(tile_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(tl_cs_i),
.WR(ioctl_wr)
);
//-------------------------------------------------------- Sprite layer --------------------------------------------------------//
//Generate write enables for sprite RAM (active low)
wire n_spriteram0_wr = n_cs_spriteram0 | n_ram_write;
wire n_spriteram1_wr = n_cs_spriteram1 | n_ram_write;
//Sprite RAM
wire [15:0] spriteram_D;
wire [7:0] spriteram_A = h_cnt[1] ? {2'b00, h_cnt[7], (h_cnt[8] ^ h_cnt[7]), h_cnt[6:4], h_cnt[2]} : z80_A[7:0];
//Bank 0 (lower 4 bits)
spram #(4, 8) D7
(
.clk(clk_49m),
.we(~n_cs_spriteram0 & ~n_spriteram0_wr),
.addr(spriteram_A),
.data(k501_D[3:0]),
.q(spriteram_D[3:0])
);
//Bank 0 (upper 4 bits)
spram #(4, 8) D6
(
.clk(clk_49m),
.we(~n_cs_spriteram0 & ~n_spriteram0_wr),
.addr(spriteram_A),
.data(k501_D[7:4]),
.q(spriteram_D[7:4])
);
//Bank 1 (lower 4 bits)
spram #(4, 8) D9
(
.clk(clk_49m),
.we(~n_cs_spriteram1 & ~n_spriteram1_wr),
.addr(spriteram_A),
.data(k501_D[3:0]),
.q(spriteram_D[11:8])
);
//Bank 1 (upper 4 bits)
spram #(4, 8) D8
(
.clk(clk_49m),
.we(~n_cs_spriteram1 & ~n_spriteram1_wr),
.addr(spriteram_A),
.data(k501_D[7:4]),
.q(spriteram_D[15:12])
);
//Konami 503 custom chip - generates sprite addresses for lower half of sprite ROMs, sprite line buffer control, enable for
//sprite write and sprite flip for 083 custom chip.
wire cs_linebuffer, sprite_flip;
k503 F8
(
.CLK(clk_49m),
.OB(spriteram_D[7:0]),
.VCNT(vcnt_lat),
.H4(h_cnt[2]),
.H8(h_cnt[3]),
.LD(h_cnt[1:0] != 2'b11),
.OCS(cs_linebuffer),
.OFLP(sprite_flip),
.R(spriterom_A[5:0])
);
//Latch sprite code from sprite RAM bank 1 every 16 pixels
reg [7:0] sprite_code = 8'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(h_cnt[3:0] == 4'b0111)
sprite_code <= spriteram_D[15:8];
else
sprite_code <= sprite_code;
end
end
//Assign sprite code to address the upper 7 bits of the sprite ROMs
assign spriterom_A[12:6] = sprite_code[6:0];
//Sprite ROMs
wire [12:0] spriterom_A;
//ROM 1/2
wire [7:0] eprom5_D;
eprom_5 C10
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom5_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep5_cs_i),
.WR(ioctl_wr)
);
//ROM 2/2
wire [7:0] eprom6_D;
eprom_6 C11
(
.ADDR(spriterom_A),
.CLK(clk_49m),
.DATA(eprom6_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(ep6_cs_i),
.WR(ioctl_wr)
);
//Multiplex sprite ROM data outputs based on the state of the most significant bit of the sprite code
wire spriterom_sel = sprite_code[7];
wire [7:0] spriterom_D = spriterom_sel ? eprom6_D : eprom5_D;
//Konami 083 custom chip 2/2 - shifts the pixel data from sprite ROMs
k083 C12
(
.CK(clk_49m),
.CEN(cen_6m),
.LOAD(h_cnt[1:0] == 2'b11),
.FLIP(sprite_k083_flip),
.DB0i(spriterom_D),
.DSH0(sprite_lut_A[1:0])
);
//Latch sprite color information, enable for sprite line buffer, sprite 083 flip at every 12 pixels
reg [5:0] sprite_color = 6'd0;
reg sprite_lbuff_en, sprite_k083_flip;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(h_cnt[3:0] == 4'b1011) begin
sprite_color <= spriteram_D[5:0];
sprite_lbuff_en <= cs_linebuffer;
sprite_k083_flip <= sprite_flip;
end
end
end
//Assign sprite color information to bits [7:2] of the sprite lookup PROM
assign sprite_lut_A[7:2] = sprite_color;
//Sprite lookup PROM
wire [7:0] sprite_lut_A;
wire [3:0] sprite_lut_D;
sprite_lut_prom E9
(
.ADDR(sprite_lut_A),
.CLK(clk_49m),
.DATA(sprite_lut_D),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(sl_cs_i),
.WR(ioctl_wr)
);
//Konami 502 custom chip, responsible for generating sprites (sits between sprite ROMs and the sprite line buffer)
wire [7:0] sprite_lbuff_Do;
wire [4:0] sprite_D;
wire sprite_lbuff_sel, sprite_lbuff_dec0, sprite_lbuff_dec1;
k502 C8
(
.RESET(1),
.CK1(clk_49m),
.CK2(clk_49m),
.CEN(cen_6m),
.LDO(h_cnt[2:0] != 3'b111),
.H2(h_cnt[1]),
.H256(h_cnt[8]),
.SPAL(sprite_lut_D),
.SPLBi({sprite_lbuff1_D, sprite_lbuff0_D}),
.SPLBo(sprite_lbuff_Do),
.OSEL(sprite_lbuff_sel),
.OLD(sprite_lbuff_dec1),
.OCLR(sprite_lbuff_dec0),
.COL(sprite_D)
);
//----------------------------------------------------- Sprite line buffer -----------------------------------------------------//
//Generate load and clear signals for counters generating addresses to sprite line buffer
reg sprite_lbuff0_ld, sprite_lbuff1_ld, sprite_lbuff0_clr, sprite_lbuff1_clr;
always_ff @(posedge clk_49m) begin
if(h_cnt[1:0] == 2'b11) begin
if(sprite_lbuff_dec0 && !sprite_lbuff_dec1) begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 1;
end
else if(!sprite_lbuff_dec0 && sprite_lbuff_dec1) begin
sprite_lbuff0_clr <= 1;
sprite_lbuff1_clr <= 0;
end
else begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 0;
end
end
else begin
sprite_lbuff0_clr <= 0;
sprite_lbuff1_clr <= 0;
end
if(h_cnt[3:0] == 4'b1011) begin
if(!sprite_lbuff_dec1) begin
sprite_lbuff0_ld <= 1;
sprite_lbuff1_ld <= 0;
end
else begin
sprite_lbuff0_ld <= 0;
sprite_lbuff1_ld <= 1;
end
end
else begin
sprite_lbuff0_ld <= 0;
sprite_lbuff1_ld <= 0;
end
end
//Generate addresses for sprite line buffer
//Bank 0, lower 4 bits
reg [3:0] linebuffer0_l = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff0_clr)
linebuffer0_l <= 4'd0;
else
if(sprite_lbuff0_ld)
linebuffer0_l <= spriteram_D[11:8];
else
linebuffer0_l <= linebuffer0_l + 4'd1;
end
end
//Bank 0, upper 4 bits
reg [3:0] linebuffer0_h = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff0_clr)
linebuffer0_h <= 4'd0;
else
if(sprite_lbuff0_ld)
linebuffer0_h <= spriteram_D[15:12];
else if(linebuffer0_l == 4'hF)
linebuffer0_h <= linebuffer0_h + 4'd1;
end
end
wire [7:0] sprite_lbuff0_A = {linebuffer0_h, linebuffer0_l};
//Bank 1, lower 4 bits
reg [3:0] linebuffer1_l = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff1_clr)
linebuffer1_l <= 4'd0;
else
if(sprite_lbuff1_ld)
linebuffer1_l <= spriteram_D[11:8];
else
linebuffer1_l <= linebuffer1_l + 4'd1;
end
end
//Bank 1, upper 4 bits
reg [3:0] linebuffer1_h = 4'd0;
always_ff @(posedge clk_49m) begin
if(cen_6m) begin
if(sprite_lbuff1_clr)
linebuffer1_h <= 4'd0;
else
if(sprite_lbuff1_ld)
linebuffer1_h <= spriteram_D[15:12];
else if(linebuffer1_l == 4'hF)
linebuffer1_h <= linebuffer1_h + 4'd1;
end
end
wire [7:0] sprite_lbuff1_A = {linebuffer1_h, linebuffer1_l};
//Generate chip select signals for sprite line buffer
wire cs_sprite_lbuff0 = ~(sprite_lbuff_en & sprite_lbuff_sel);
wire cs_sprite_lbuff1 = ~(sprite_lbuff_en & ~sprite_lbuff_sel);
//Sprite line buffer bank 0
wire [3:0] sprite_lbuff0_D;
spram #(4, 8) B7
(
.clk(clk_49m),
.we(cen_6m & cs_sprite_lbuff0),
.addr(sprite_lbuff0_A),
.data({sprite_lbuff_Do[0], sprite_lbuff_Do[1], sprite_lbuff_Do[2], sprite_lbuff_Do[3]}),
.q({sprite_lbuff0_D[0], sprite_lbuff0_D[1], sprite_lbuff0_D[2], sprite_lbuff0_D[3]})
);
//Sprite line buffer bank 1
wire [3:0] sprite_lbuff1_D;
spram #(4, 8) C7
(
.clk(clk_49m),
.we(cen_6m & cs_sprite_lbuff1),
.addr(sprite_lbuff1_A),
.data({sprite_lbuff_Do[4], sprite_lbuff_Do[5], sprite_lbuff_Do[6], sprite_lbuff_Do[7]}),
.q({sprite_lbuff1_D[0], sprite_lbuff1_D[1], sprite_lbuff1_D[2], sprite_lbuff1_D[3]})
);
//----------------------------------------------------- Final video output -----------------------------------------------------//
//Generate HBlank (active high) while the horizontal counter is between 141 and 268
wire hblk = (h_cnt > 140 && h_cnt < 269);
//Multiplex tile and sprite data
wire tile_sprite_sel = (tilemap_en | sprite_D[4]);
wire [3:0] tile_sprite_D = tile_sprite_sel ? tile_D[3:0] : sprite_D[3:0];
//Latch pixel data for color PROMs
reg [3:0] pixel_D;
always_ff @(posedge clk_49m) begin
if(cen_6m)
pixel_D <= tile_sprite_D;
end
//Color PROMs
wire [4:0] color_A = {tile_sprite_sel, pixel_D};
//Red & green (bits 1 and 0)
wire [4:0] prom_red, prom_green;
color_prom_1 B5
(
.ADDR(color_A),
.CLK(clk_49m),
.DATA({prom_green[1:0], prom_red, 1'bZ}),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(cp1_cs_i),
.WR(ioctl_wr)
);
//Blue & green (bits 2 - 4)
wire [4:0] prom_blue;
color_prom_2 B4
(
.ADDR(color_A),
.CLK(clk_49m),
.DATA({prom_blue, prom_green[4:2]}),
.ADDR_DL(ioctl_addr),
.CLK_DL(clk_49m),
.DATA_IN(ioctl_data),
.CS_DL(cp2_cs_i),
.WR(ioctl_wr)
);
//Output video signal from color PROMs, otherwise output black if in HBlank or VBlank
assign red = (hblk | vblk) ? 5'h0 : prom_red;
assign green = (hblk | vblk) ? 5'h0 : prom_green;
assign blue = (hblk | vblk) ? 5'h0 : prom_blue;
endmodule