1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-04-29 13:32:38 +00:00
Files
Gehstock.Mist_FPGA/Arcade_MiST/Atari MissileCommand/rtl/missile.v
2022-10-17 01:20:45 +02:00

631 lines
15 KiB
Verilog

/*============================================================================
Missile Command for MiSTer FPGA - Main core
Copyright (C) 2022 - Jim Gregory - https://github.com/JimmyStones/
This program 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 program 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/>.
===========================================================================*/
`timescale 1 ps / 1 ps
`default_nettype none
module missile(
input clk_10M,
input ce_5M,
input reset,
input pause,
input vtb_dir1,
input vtb_clk1,
input htb_dir1,
input htb_clk1,
input vtb_dir2,
input vtb_clk2,
input htb_dir2,
input htb_clk2,
input coin,
input p1_start,
input p2_start,
input p1_fire_l,
input p1_fire_c,
input p1_fire_r,
input p2_fire_l,
input p2_fire_c,
input p2_fire_r,
input [7:0] in2,
input [7:0] switches,
input self_test,
input slam,
output flip,
output r,
output g,
output b,
output h_sync/*verilator public_flat*/,
output v_sync/*verilator public_flat*/,
output h_blank/*verilator public_flat*/,
output v_blank/*verilator public_flat*/,
output [5:0] audio_o,
input [15:0] dn_addr,
input [7:0] dn_data,
input dn_wr,
input [13:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write,
input hs_access
);
//// SHARED SIGNALS
wire [8:0] hcnt;
wire [7:0] vcnt;
wire sync;
wire s_phi_0;
wire s_phi_2;
wire s_phi_x;
reg s_phi_extend = 1'b0;
wire s_3INH;
wire s_irq_n;
wire s_READWRITE;
wire s_WRITE_n;
wire s_br_w_n;
wire [15:0] s_addr /* synthesis preserve */;
wire [7:0] s_pgrom0_out/* synthesis preserve */;
wire [7:0] s_pgrom1_out/* synthesis preserve */;
wire [7:0] s_pgrom2_out/* synthesis preserve */;
wire [7:0] s_db_in/* synthesis preserve */;
wire [7:0] s_db_out/* synthesis preserve */;
wire [7:0] s_pokey_out/* synthesis preserve */;
wire s_16FLIP;
wire s_INTACK_n;
reg s_MADSEL = 1'b0;
reg s_MADSELDEL = 1'b0;
reg s_MUSHROOM = 1'b0;
wire [2:0] s_PROGSEL_n;
wire s_RAM_n;
wire s_POKEY_n;
wire s_NIO_n;
wire s_WDOG_n;
wire s_COLRAM_n;
wire s_OUT_n;
////////////////////////
///// SYNC CIRCUIT /////
////////////////////////
sync sync_circuit(
.clk_10M(clk_10M),
.ce_5M(ce_5M),
.reset(reset),
.flip(s_FLIP),
.h_blank(h_blank),
.h_sync(h_sync),
.v_blank(v_blank),
.v_sync(v_sync),
.s_phi_x(s_phi_x),
.s_3INH(s_3INH),
.hcnt(hcnt),
.vcnt(vcnt)
);
//////////////////////////////////
///// MICROPROCESSOR CIRCUIT /////
//////////////////////////////////
micro mp(
.clk_10M(clk_10M),
.s_phi_x(s_phi_x),
.s_phi_extend(s_phi_extend),
.reset(reset),
.pause(pause),
.flip(s_FLIP),
.hcnt(hcnt),
.vcnt(vcnt),
.s_db_in(s_db_in),
.sync(sync),
.s_phi_2(s_phi_2),
.s_phi_0(s_phi_0),
.s_irq_n(s_irq_n),
.s_READWRITE(s_READWRITE),
.s_WRITE_n(s_WRITE_n),
.s_br_w_n(s_br_w_n),
.s_16FLIP(s_16FLIP),
.s_INTACK_n(s_INTACK_n),
.s_addr(s_addr),
.s_db_out(s_db_out)
);
///////////////////////////////
///// ADDRESS DECODING ////////
// Page 1, Side B, Top right //
///////////////////////////////
wire [9:0] s_N2;
wire s_N3;
// N2 - 7442 address decoder
// -------------------------
ls42 N2 (
.a(s_addr[12]),
.b(s_addr[13]),
.c(s_addr[14]),
.d(s_MADSELDEL),
.o(s_N2)
);
assign s_PROGSEL_n = s_N2[7:5];
assign s_N3 = s_N2[1] & s_N2[2] & s_N2[3];
assign s_RAM_n = s_N3 & s_N2[0];
assign s_POKEY_n = s_addr[11] | s_N2[4];
assign s_NIO_n = (~s_addr[11]) | s_N2[4];
// E8 - 7442 address decoder
// ----------------------------------
wire s_mem_write = ~(s_WRITE_n | s_NIO_n);
assign s_OUT_n = ~(s_mem_write && s_addr[15:8] == 8'b01001000);
assign s_COLRAM_n = ~(s_mem_write && s_addr[15:8] == 8'b01001011);
assign s_WDOG_n = ~(s_mem_write && (s_addr == 16'h4C00));
assign s_INTACK_n = ~(s_mem_write && (s_addr == 16'h4D00));
// ROM upload enables
// ----------------------------------
wire dn_pgrom0_we = (dn_addr[13:12] == 2'b00) & dn_wr;
wire dn_pgrom1_we = (dn_addr[13:12] == 2'b01) & dn_wr;
wire dn_pgrom2_we = (dn_addr[13:12] == 2'b10) & dn_wr;
wire dn_L6_prom_we = (dn_addr[15:12] == 4'b0011) & dn_wr;
wire [4:0] dn_L6_prom_addr = dn_addr[4:0];
// Simplified address decodes for inputs
// ----------------------------------
// IN0 $4800-$48FF
wire in0_cs = s_addr[14:8] == 7'b1001000;
// IN1 $4900-$49FF
wire in1_cs = s_addr[14:8] == 7'b1001001;
// IN2 $4A00-$4AFF
wire in2_cs = s_addr[14:8] == 7'b1001010;
assign s_db_in = ~s_PROGSEL_n[0] ? s_pgrom0_out :
~s_PROGSEL_n[1] ? s_pgrom1_out :
~s_PROGSEL_n[2] ? s_pgrom2_out :
s_MADSEL ? s_VRAM_in :
~s_POKEY_n ? s_pokey_out :
~s_RAM_n ? vram_data_out :
in0_cs ? in0 :
in1_cs ? in1 :
in2_cs ? in2 :
~s_COLRAM_n ? { 4'b0, s_cram_out} :
8'bXXXXXXXX;
localparam debug_counter_width = 20;
reg [debug_counter_width-1:0] debug_counter = {debug_counter_width{1'b0}};
wire debug_data/*verilator public_flat*/;
//////////////////////////////////
///// PROGRAM MEMORY CIRCUIT /////
//////////////////////////////////
dpram #(12,8) pgrom0
(
.clock(clk_10M),
.enable_a(dn_pgrom0_we),
.wren_a(dn_pgrom0_we),
.address_a(dn_addr[11:0]),
.data_a(dn_data),
.q_a(),
.enable_b(1'b1),
.wren_b(1'b0),
.address_b(s_addr[11:0]),
.data_b(),
.q_b(s_pgrom0_out)
);
dpram #(12,8) pgrom1
(
.clock(clk_10M),
.enable_a(dn_pgrom1_we),
.wren_a(dn_pgrom1_we),
.address_a(dn_addr[11:0]),
.data_a(dn_data),
.q_a(),
.enable_b(1'b1),
.wren_b(1'b0),
.address_b(s_addr[11:0]),
.data_b(),
.q_b(s_pgrom1_out)
);
dpram #(12,8) pgrom2
(
.clock(clk_10M),
.enable_a(dn_pgrom2_we),
.wren_a(dn_pgrom2_we),
.address_a(dn_addr[11:0]),
.data_a(dn_data),
.q_a(),
.enable_b(1'b1),
.wren_b(1'b0),
.address_b(s_addr[11:0]),
.data_b(),
.q_b(s_pgrom2_out)
);
////////////////
///// DRAM /////
////////////////
wire [7:0] s_VRAM;
reg [7:0] s_VRAM_in = 8'b0;
wire [13:0] vram_addr = hs_access ? hs_address : dead_cpu;
wire [7:0] vram_we_n = hs_access ? ~{8{hs_write}} : s_WP_n;
wire [7:0] vram_data_in = hs_access ? hs_data_in : s_MD;
wire [7:0] vram_data_out;
assign hs_data_out = vram_data_out;
dpvram #(14,8) ram
(
.clock_a(clk_10M),
.wren_a(vram_we_n),
.address_a(vram_addr),
.data_a(vram_data_in),
.q_a(vram_data_out),
.clock_b(clk_10M),
.address_b(dead_vid),
.q_b(s_VRAM)
);
///////////////////////////////
//// DRAM ADDRESS SELECTOR ////
// Page 2, Side A, Top left ///
///////////////////////////////
// When an instruction matching the bit pattern xxx00001 syncs and no IRQ is underway, start the MADSEL countdown
// - This should send MADSEL high ready for the read/write 5 cycles later
reg [3:0] MADSEL_count = 4'b0;
always @(posedge s_phi_0)
begin
if(s_db_in[4:0] == 5'b00001 && s_irq_n && sync) MADSEL_count <= 4'd3;
if(MADSEL_count > 4'd0)
begin
MADSEL_count <= MADSEL_count - 4'b1;
if(MADSEL_count == 4'd1) s_MADSEL <= 1'b1;
end
else
s_MADSEL <= 1'b0;
end
///////////////////////////////
/// 3RD COLOUR BIT SELECTOR ///
/// Page 2, Side A, Top mid //
///////////////////////////////
// Latch PHI EXTEND signal when MADSEL active and 3rd colour bit area is addressed
always @(posedge clk_10M)
begin
if(s_phi_x)
begin
if(s_MADSEL & s_addr[15:13] == 3'b111) s_phi_extend <= 1'b1;
end
else
begin
s_phi_extend <= 1'b0;
end
end
// Latch PHI EXTEND into MUSHROOM on next falling edge of PHI X
always @(negedge clk_10M)
begin
reg s_phi_x_last;
s_phi_x_last <= s_phi_x;
if(!s_phi_x && s_phi_x_last) s_MUSHROOM <= s_phi_extend;
end
////////////////////////////////
// DRAM PICTURE OUTPUT ENABLE //
// Page 2, Side A, Top right ///
////////////////////////////////
always @(posedge clk_10M)
begin
reg ce_5M_last;
ce_5M_last <= ce_5M;
if(ce_5M && !ce_5M_last) s_MADSELDEL <= s_MADSEL;
end
////////////////////////////////
// DRAM ADDRESS CONTROLLER /////
// Page 2, Side A, Lower left //
////////////////////////////////
wire [1:0] s_MADSEL_mode = {s_MUSHROOM, s_MADSEL};
wire [13:0] dead_cpu_2col = s_addr[15:2];
wire [13:0] dead_cpu_3col = { 3'b0, s_addr[11], ~s_addr[11], s_addr[10:3], s_addr[12] };
wire [13:0] dead_cpu = s_MADSEL_mode == 2'b00 ? s_addr[13:0] : // Standard RAM addressing
s_MADSEL_mode == 2'b01 ? dead_cpu_2col : // 2 colour VRAM addressing
s_MADSEL_mode == 2'b11 ? dead_cpu_3col : // 3 colour VRAM addressing
14'h00;
reg [13:0] dead_vid = 14'b0;
reg s_phi_0_last = 1'b0;
always @(posedge clk_10M)
begin
s_phi_0_last <= s_phi_0;
// CPU only reads VRAM data when MADSEL is high
if(s_MADSEL)
begin
if(s_MUSHROOM)
begin
// Latch 3rd colour bit data from VRAM when MUSHROOM is high (last 32 lines)
s_VRAM_in[5] <= vram_data_out[s_addr[2:0]];
end
else
begin
if(ce_5M)
begin
// Latch 2 colour bits from VRAM when MUSHROOM is low
s_VRAM_in[7] <= vram_data_out[s_addr[1:0]+4];
s_VRAM_in[6] <= vram_data_out[s_addr[1:0]+0];
end
end
end
else
begin
// Clear VRAM data when MADSEL is low
if(s_phi_0 && !s_phi_0_last) s_VRAM_in <= 8'hFF;
end
end
// L6 PROM (DRAM write mask)
wire [7:0] s_L6;
//dpram #(5,8) L6
//(
// // a - download from HPS
// .clock(clk_10M),
//
// .enable_a(dn_L6_prom_we),
// .wren_a(dn_L6_prom_we),
// .address_a(dn_L6_prom_addr),
// .data_a(dn_data),
// .q_a(),
//
// .enable_b(1'b1),
// .address_b({s_MADSEL, s_MUSHROOM, s_addr[2:0]}),
// .data_b(),
// .wren_b(),
// .q_b(s_L6)
//);
L6 L6(
.clk(clk_10M),
.addr({s_MADSEL, s_MUSHROOM, s_addr[2:0]}),
.data(s_L6)
);
wire s_MADSEL_write = (s_MADSEL & ~s_br_w_n & hcnt[0]);
wire s_RAM_write = (~s_RAM_n & ~s_br_w_n);
wire [7:0] s_WP_n = ~(s_MADSEL ? (s_MADSEL_write ? ~s_L6 : 8'h00) : s_RAM_write ? 8'hFF : 8'h00);
// DRAM DATA INPUT SELECTOR
reg [7:0] s_MD = 8'b0;
always @(posedge clk_10M)
begin
case ({s_MUSHROOM, s_MADSEL})
2'b00 : s_MD = s_db_out[7:0];
2'b01 : s_MD = {{4{s_db_out[7]}}, {4{s_db_out[6]}}};
2'b11 : s_MD = {8{s_db_out[5]}};
default : s_MD = 8'b0;
endcase
end
/// PICTURE BIT CONVERTER
wire [2:0] s_COLNUM;
reg [3:0] s_N7 = 4'b0;
reg [3:0] s_M6 = 4'b0;
reg [7:0] s_P6 = 8'b0;
// This part diverges from the original schematics quite a bit.
// We need to offset the lookups to give the BRAM enough time to return colour index data then look it up in the paletta RAM
// I'm sure this could be better but I'm scared to touch it because it works!
wire [8:0] hcnt_off_2col = h_blank ? (hcnt + 8'd192) : hcnt + 8'd4;
wire [8:0] hcnt_off_3col = h_blank ? (hcnt + 8'd192) : hcnt + 8'd8;
wire [8:0] hcnt_address_2col = h_blank ? ((hcnt == 9'd319) ? 9'd4 : 9'd0): (hcnt + 8'd4);
wire [8:0] hcnt_address_3col = h_blank ? ((hcnt == 9'd319) ? 9'd4 : 9'd0): (hcnt + 8'd2);
reg [7:0] vram_2col = 8'b0;
wire [1:0] pixel_index_2col = hcnt_off_2col[1:0];
wire [2:0] pixel_index_3col = hcnt_off_3col[2:0];
always @(posedge clk_10M)
begin
//$display("%d)\tVID: ce5=%b hcnt=%d hco2=%d hco3=%d pi2=%d pi3=%d vcnt=%d hb=%b vb=%b dead_vid=%x colnum=%x", debug_counter, ce_5M, hcnt, hcnt_off_2col, hcnt_off_3col, pixel_index_2col, pixel_index_3col, vcnt, h_blank, v_blank, dead_vid, s_COLNUM);
// 2 Colour bits
// - Set read address 3 10Mhz cycles before ce_5M goes high
// - Latch data 2 10Mhz cycles before ce_5M goes high
// - Select 2 bit colour index 1 10Mhz cycle before ce_5M goes high
// - Shift data right every 5Mhz cycle except load cycle
// 1st 2 colour bits
// Set address to read 2 colour bits data from VRAM
if(pixel_index_2col == 2'd2 && ce_5M) dead_vid <= {vcnt[7:0], hcnt_address_2col[7:2]};
// Latch 3rd colour bit data from VRAM
if(pixel_index_2col == 2'd3 && ~ce_5M) vram_2col <= s_VRAM;
// Load 2 colour bits data from latch
if(pixel_index_2col == 2'd3 && ce_5M)
begin
s_N7 <= vram_2col[7:4];
s_M6 <= vram_2col[3:0];
end
// Shift 2 colour bits data along
if(pixel_index_2col != 2'd3 && ce_5M)
begin
s_N7 <= s_N7 >> 1;
s_M6 <= s_M6 >> 1;
end
// 3rd colour bit
if(~s_3INH)
begin
// Clear 3rd colour bit if we are outside the last 32 lines
s_P6 <= 8'h00;
end
else
begin
// Set address to read 3rd colour bit data from VRAM
if(pixel_index_3col == 3'd7 && ~ce_5M) dead_vid <= { 3'b0, vcnt[3], ~vcnt[3], vcnt[2:0], hcnt_address_3col[7:3], vcnt[4] };
// Load 3rd colour bit data from VRAM
if(pixel_index_3col == 3'd7 && ce_5M) s_P6 <= s_VRAM;
// Shift 3rd colour bit data along
if(pixel_index_3col != 3'd7 && ce_5M) s_P6 <= s_P6 >> 1;
end
end
assign s_COLNUM = {s_N7[0],s_M6[0],s_P6[0]};
reg [2:0] rgbtemp = 0;
assign r = rgbtemp[2];
assign g = rgbtemp[1];
assign b = rgbtemp[0];
/// COLOR RAM
wire [2:0] s_M7 = ~v_blank ? s_COLNUM[2:0] : s_addr[2:0];
wire [3:0] s_cram_out;
wire s_cram_we = ~s_COLRAM_n && v_blank;
spram #(3,4) L7 (
.clock(clk_10M),
.address(s_M7[2:0]),
.data(s_MD[3:0]),
.wren(s_cram_we),
.q(s_cram_out)
);
// Latch RGB out from colour ram
always @(posedge clk_10M)
begin
if(ce_5M) rgbtemp <= (~h_blank && ~v_blank) ? ~s_cram_out[3:1] : 3'b0;
end
// POKEY
wire s_J7 = s_phi_2 | hcnt[1];
wire [3:0] pokey_ch0, pokey_ch1, pokey_ch2, pokey_ch3;
pokey pokey(
.clk(s_J7),
.enable_179(1'b1),
.addr(s_addr[3:0]),
.data_in(s_db_out),
.wr_en(~s_br_w_n & ~s_POKEY_n),
.reset_n(~reset),
.data_out(s_pokey_out),
.pot_in(switches),
.channel_0_out(pokey_ch0),
.channel_1_out(pokey_ch1),
.channel_2_out(pokey_ch2),
.channel_3_out(pokey_ch3)
);
assign audio_o = ({1'b0,pokey_ch0,1'b0}+{1'b0,pokey_ch1,1'b0})+({1'b0,pokey_ch2,1'b0}+{1'b0,pokey_ch3,1'b0});
// Outputs
// -------
reg [7:0] s_F9 = 8'b0;
always @(posedge s_OUT_n or posedge reset) // F9 latch
begin
s_F9 <= reset ? 8'b0 : s_db_out;
end
//wire s_FLIP = s_F9[6];
wire s_FLIP = 1'b1;
wire s_CTRLD_n = s_F9[0];
assign flip = s_FLIP;
// Inputs
// ------
wire [7:0] in0 = !s_CTRLD_n ? { ~coin, 2'b11, ~p1_start, ~p2_start, ~p2_fire_l, ~p2_fire_c, ~p2_fire_r } : {tb_count_v, tb_count_h};
wire [7:0] in1 = { v_blank, ~self_test, ~slam, 1'b1, 1'b1, ~p1_fire_l, ~p1_fire_c, ~p1_fire_r };
wire tb_dir_v, tb_clk_v, tb_dir_h, tb_clk_h;
reg [3:0] tb_count_v;
reg [3:0] tb_count_h;
assign {tb_dir_v, tb_clk_v, tb_dir_h, tb_clk_h} = { vtb_dir1, vtb_clk1, htb_dir1, htb_clk1 };
// assign {tb_dir_v, tb_clk_v, tb_dir_h, tb_clk_h} = (flip_n == 0) ? { vtb_dir1, vtb_clk1, htb_dir1, htb_clk1 } :
// { vtb_dir2, vtb_clk2, htb_dir2, htb_clk2 };
always @(posedge clk_10M)
begin
reg tb_clk_v_last;
reg tb_clk_h_last;
tb_clk_v_last <= tb_clk_v;
tb_clk_h_last <= tb_clk_h;
if (s_CTRLD_n)
begin
if(tb_clk_v && !tb_clk_v_last)
begin
if (!tb_dir_v)
tb_count_v <= tb_count_v + 4'b1;
else
tb_count_v <= tb_count_v - 4'b1;
end
if(tb_clk_h && !tb_clk_h_last)
begin
if (!tb_dir_h)
tb_count_h <= tb_count_h + 4'b1;
else
tb_count_h <= tb_count_h - 4'b1;
end
end
end
endmodule