1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-13 15:17:55 +00:00
2020-12-20 16:31:04 +01:00

627 lines
18 KiB
Systemverilog

//============================================================================
// MCR3 arcades top-level for MiST
// Sarge/Max RPM/Rampage/Power Drive/Demolition Derby
// Timber/Tapper/Discs of Tron
//
// 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 2 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, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//============================================================================
module MCR3_MiST(
output LED,
output [5:0] VGA_R,
output [5:0] VGA_G,
output [5:0] VGA_B,
output VGA_HS,
output VGA_VS,
output AUDIO_L,
output AUDIO_R,
input SPI_SCK,
inout SPI_DO,
input SPI_DI,
input SPI_SS2,
input SPI_SS3,
input SPI_SS4,
input CONF_DATA0,
input CLOCK_27,
output [12:0] SDRAM_A,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nWE,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nCS,
output [1:0] SDRAM_BA,
output SDRAM_CLK,
output SDRAM_CKE
);
`include "rtl/build_id.v"
`define CORE_NAME "RAMPAGE"
wire [6:0] core_mod;
localparam CONF_STR = {
`CORE_NAME,";;",
"O2,Rotate Controls,Off,On;",
"O5,Blend,Off,On;",
"O6,Swap Joystick,Off,On;",
"DIP;",
"O7,Service,Off,On;",
"R2048,Save NVRAM;",
"T0,Reset;",
"V,v1.1.",`BUILD_DATE
};
wire rotate = status[2];
wire blend = status[5];
wire joyswap = status[6];
wire service = status[7];
localparam SND_SSB = 2'd0;
localparam SND_TCS = 2'd1;
localparam SND_SG = 2'd2;
reg hflip;
reg monoboard;
reg [1:0] soundboard;
reg [7:0] input0;
reg [7:0] input1;
reg [7:0] input2;
reg [7:0] input3;
reg [7:0] input4;
reg [7:0] output5;
reg [7:0] output6;
// Game specific sound board/DIP/input settings
always @(*) begin
hflip = 0;
monoboard = 1;
soundboard = SND_SSB;
input0 = 8'hff;
input1 = 8'hff;
input2 = 8'hff;
input3 = 8'hff;
input4 = 8'hff;
case (core_mod)
7'h0: // RAMPAGE
begin
soundboard = SND_SG;
// normal controls for 3 players
input0 = ~{2'b00, service, 1'b0, 2'b00, m_coin2, m_coin1};
input1 = ~{2'b00, m_fireB, m_fireA, m_left, m_down, m_right, m_up};
input2 = ~{2'b00, m_fire2B, m_fire2A, m_left2, m_down2, m_right2, m_up2};
input3 = ~{/*cheat*/status[11], /*coin B*/3'b000, /*coin A*/1'b0, /*score opt*/status[10], /*difficulty*/status[9:8]};
input4 = ~{2'b00, m_fire3B, m_fire3A, m_left3, m_down3, m_right3, m_up3};
end
7'h1: // SARGE
begin
soundboard = SND_TCS;
// Two stick/player like the original
input0 = ~{2'b00, service, 1'b0, m_two_players, m_one_player, m_coin2, m_coin1};
input1 = ~{{2{sarge_fire1B}}, {2{sarge_fire1A}}, sarge_down2, sarge_up2, sarge_down1, sarge_up1};
input2 = ~{{2{sarge_fire2B}}, {2{sarge_fire2A}}, sarge_down3, sarge_up3, sarge_down4, sarge_up4};
input3 = ~{2'b00, /*coinage*/2'b00, /*free play*/status[8], 3'b000};
end
7'h2: //POWERDRV
begin
soundboard = SND_SG;
// Controls for 3 players using 4 buttons/joystick
input0 = ~{2'b00, service, 1'b0, 1'b0, m_coin3, m_coin2, m_coin1};
input1 = ~{m_fire2B, m_fire2A, powerdrv_gear[1], m_fire2C, m_fireB, m_fireA, powerdrv_gear[0], m_fireC};
input2 = ~{sndstat[0], 3'b000, m_fire3B, m_fire3A, powerdrv_gear[2], m_fire3C};
input3 = ~{/*cheat*/status[11], /*demosnd*/status[10], /*difficulty*/status[9:8], 1'b0, /*coinage*/2'b00};
end
7'h3: // MAXRPM
begin
soundboard = SND_TCS;
input0 = ~{service, 3'b000, m_one_player, m_two_players, m_coin1, m_coin2};
input1 = ~{maxrpm_adc_data};
input2 = ~{maxrpm_gear1, maxrpm_gear2};
input3[0] = ~status[8]; // free play
end
7'h4: // DEMODERB
begin
soundboard = SND_TCS;
input0 = ~{2'b00, service, 1'b0, m_two_players, m_one_player, m_coin2, m_coin1};
input1 = dderby_input_sel ? ~{wheel3, m_fireB, m_fireA} : ~{wheel1, m_fireB, m_fireA};
input2 = dderby_input_sel ? ~{wheel4, m_fire2B, m_fire2A} : ~{wheel2, m_fire2B, m_fire2A};
input3 = ~{3'b000, status[11], status[10], status[9], status[8]}; // NU, coins/credit, girl, free play, difficulty, 2player
input4 = ~{m_fire4B, m_fire4A, m_fire3B, m_fire3A, m_four_players, m_three_players, m_coin4, m_coin3};
end
7'h6, 7'h7: // TIMBER, TAPPER
begin
soundboard = SND_SSB;
monoboard = 0;
input0 = ~{service, 3'b000, m_two_players, m_one_player, m_coin2, m_coin1};
input1 = ~{2'b00, m_fireB, m_fireA, m_up, m_down, m_left, m_right};
input2 = ~{2'b00, m_fire2B, m_fire2A, m_up2, m_down2, m_left2, m_right2};
input3 = ~{/*coin meters*/1'b0, /*upright*/status[9], 3'b000, /*demosnd*/status[10], 2'b00};
end
7'h8: // DOTRON
begin
soundboard = SND_SSB;
monoboard = 0;
hflip = 1;
input0 = ~{service, 2'b00, m_fireA | m_down2, m_two_players, m_one_player, m_coin2, m_coin1};
input1 = ~{1'b0, dotron_spinner};
input2 = ~{1'b0, m_fireB, m_fireD, m_fireC, m_down, m_up, m_right, m_left};
end
default: ;
endcase
end
assign LED = ~ioctl_downl;
assign SDRAM_CLK = clk_mem;
assign SDRAM_CKE = 1;
wire clk_sys, clk_mem;
wire pll_locked;
pll_mist pll(
.inclk0(CLOCK_27),
.areset(0),
.c0(clk_sys),
.c1(clk_mem),
.locked(pll_locked)
);
wire [31:0] status;
wire [1:0] buttons;
wire [1:0] switches;
wire [15:0] joystick_0;
wire [15:0] joystick_1;
wire [15:0] joystick_2;
wire [15:0] joystick_3;
wire scandoublerD;
wire ypbpr;
wire no_csync;
wire key_pressed;
wire [7:0] key_code;
wire key_strobe;
user_io #(
.STRLEN(($size(CONF_STR)>>3)),
.ROM_DIRECT_UPLOAD(1))
user_io(
.clk_sys (clk_sys ),
.conf_str (CONF_STR ),
.SPI_CLK (SPI_SCK ),
.SPI_SS_IO (CONF_DATA0 ),
.SPI_MISO (SPI_DO ),
.SPI_MOSI (SPI_DI ),
.buttons (buttons ),
.switches (switches ),
.scandoubler_disable (scandoublerD ),
.ypbpr (ypbpr ),
.no_csync (no_csync ),
.core_mod (core_mod ),
.key_strobe (key_strobe ),
.key_pressed (key_pressed ),
.key_code (key_code ),
.joystick_0 (joystick_0 ),
.joystick_1 (joystick_1 ),
.joystick_2 (joystick_2 ),
.joystick_3 (joystick_3 ),
.status (status )
);
wire ioctl_downl;
wire ioctl_upl;
wire [7:0] ioctl_index;
wire ioctl_wr;
wire [24:0] ioctl_addr;
wire [7:0] ioctl_dout;
wire [7:0] ioctl_din;
/*
ROM structure:
Sarge, MaxRPM, Demolition Derby (Turbo Cheap Squeak board):
00000-0FFFF MAIN CPU 64k
10000-2FFFF GFX2 (Sprites) 128k
30000-37FFF GFX1 32k
38000- TCS 32k
Rampage, Power Drive (Sounds Good board):
00000-0FFFF MAIN CPU 64k
10000-4FFFF GFX2 (Sprites) 256k
50000-57FFF GFX1 32k
58000- SG 128k
*/
data_io #(.ROM_DIRECT_UPLOAD(1)) data_io(
.clk_sys ( clk_sys ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS2 ( SPI_SS2 ),
.SPI_SS4 ( SPI_SS4 ),
.SPI_DI ( SPI_DI ),
.SPI_DO ( SPI_DO ),
.ioctl_download( ioctl_downl ),
.ioctl_upload ( ioctl_upl ),
.ioctl_index ( ioctl_index ),
.ioctl_wr ( ioctl_wr ),
.ioctl_addr ( ioctl_addr ),
.ioctl_dout ( ioctl_dout ),
.ioctl_din ( ioctl_din )
);
wire [15:0] rom_addr;
wire [15:0] rom_do;
wire [17:0] snd_addr;
wire [15:0] snd_do;
wire [15:0] sp_addr;
wire [31:0] sp_do;
wire [24:0] sp_ioctl_addr = ioctl_addr - 17'h10000;
wire [24:0] snd_ioctl_addr = ioctl_addr - snd_offset;
reg port1_req, port2_req;
reg [23:0] port1_a;
reg [23:0] port2_a;
reg [19:0] snd_offset;
reg [19:0] gfx1_offset;
always @(*) begin
if (soundboard == SND_SG) begin
snd_offset = 20'h58000;
gfx1_offset = 20'h50000;
port1_a = ioctl_addr[23:0];
port1_a = (ioctl_addr < snd_offset) ? ioctl_addr[23:0] : // 8 bit main ROM
snd_offset + {snd_ioctl_addr[17], snd_ioctl_addr[15:0], snd_ioctl_addr[16]}; // 16 bit Sounds Good ROM
// merge sprite roms (4x64k) into 32-bit wide words
port2_a = {sp_ioctl_addr[23:18], sp_ioctl_addr[15:0], sp_ioctl_addr[17:16]};
end else begin
snd_offset = 20'h38000;
gfx1_offset = 20'h30000;
port1_a = ioctl_addr[23:0];
// merge sprite roms (4x32k) into 32-bit wide words
port2_a = {sp_ioctl_addr[23:17], sp_ioctl_addr[14:0], sp_ioctl_addr[16:15]};
end
end
sdram sdram(
.*,
.init_n ( pll_locked ),
.clk ( clk_mem ),
// port1 used for main + sound CPU
.port1_req ( port1_req ),
.port1_ack ( ),
.port1_a ( port1_a[23:1] ),
.port1_ds ( {port1_a[0], ~port1_a[0]} ),
.port1_we ( ioctl_downl ),
.port1_d ( {ioctl_dout, ioctl_dout} ),
.port1_q ( ),
.cpu1_addr ( cpu1_addr ), //Turbo Cheap Squeak/Sounds Good with higher priority
.cpu1_q ( snd_do ),
.cpu2_addr ( ioctl_downl ? 18'h3ffff : {3'b000, rom_addr[15:1]} ),
.cpu2_q ( rom_do ),
// port2 for sprite graphics
.port2_req ( port2_req ),
.port2_ack ( ),
.port2_a ( port2_a[23:1] ),
.port2_ds ( {port2_a[0], ~port2_a[0]} ),
.port2_we ( ioctl_downl ),
.port2_d ( {ioctl_dout, ioctl_dout} ),
.port2_q ( ),
.sp_addr ( ioctl_downl ? 16'hffff : sp_addr ),
.sp_q ( sp_do )
);
reg [19:1] cpu1_addr;
// ROM download controller
always @(posedge clk_sys) begin
reg ioctl_wr_last = 0;
ioctl_wr_last <= ioctl_wr;
if (ioctl_downl) begin
if (~ioctl_wr_last && ioctl_wr && ioctl_index == 0) begin
port1_req <= ~port1_req;
port2_req <= ~port2_req;
end
end
// register for better timings
cpu1_addr <= ioctl_downl ? 19'h7ffff : (snd_offset[19:1] + snd_addr[17:1]);
end
// reset signal generation
reg reset = 1;
reg rom_loaded = 0;
always @(posedge clk_sys) begin
reg ioctl_downlD;
reg [15:0] reset_count;
ioctl_downlD <= ioctl_downl;
// generate a second reset signal - needed for some reason
if (status[0] | buttons[1] | ~rom_loaded) reset_count <= 16'hffff;
else if (reset_count != 0) reset_count <= reset_count - 1'd1;
if (ioctl_downlD & ~ioctl_downl) rom_loaded <= 1;
reset <= status[0] | buttons[1] | ioctl_downl | ~rom_loaded | (reset_count == 16'h0001);
end
wire [1:0] sndstat;
wire [9:0] audio;
wire [15:0] audio_l;
wire [15:0] audio_r;
wire hs, vs, cs;
wire blankn;
wire [2:0] g, r, b;
mcr3 mcr3 (
.clock_40(clk_sys),
.reset(reset),
.video_r(r),
.video_g(g),
.video_b(b),
.video_blankn(blankn),
.video_hs(hs),
.video_vs(vs),
.video_csync(cs),
.tv15Khz_mode(scandoublerD),
.soundboard(soundboard),
.snd_stat(sndstat),
.audio_out(audio),
.audio_out_l(audio_l),
.audio_out_r(audio_r),
.monoboard(monoboard),
.h_flip(hflip),
.input_0(input0),
.input_1(input1),
.input_2(input2),
.input_3(input3),
.input_4(input4),
.output_5(output5),
.output_6(output6),
.cpu_rom_addr ( rom_addr ),
.cpu_rom_do ( rom_addr[0] ? rom_do[15:8] : rom_do[7:0] ),
.snd_rom_addr ( snd_addr ),
.snd_rom_do ( snd_do ),
.sp_addr ( sp_addr ),
.sp_graphx32_do ( sp_do ),
.dl_addr ( ioctl_addr-(ioctl_index == 0 ? gfx1_offset : 0) ),
.dl_data ( ioctl_dout ),
.dl_wr ( ioctl_wr && ioctl_index == 0 ),
.up_data ( ioctl_din ),
.cmos_wr ( ioctl_wr && ioctl_index == 8'hff )
);
wire vs_out;
wire hs_out;
assign VGA_HS = (~no_csync & scandoublerD & ~ypbpr)? cs : hs_out;
assign VGA_VS = (~no_csync & scandoublerD & ~ypbpr)? 1'b1 : vs_out;
mist_video #(.COLOR_DEPTH(3)) mist_video(
.clk_sys ( clk_sys ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS3 ( SPI_SS3 ),
.SPI_DI ( SPI_DI ),
.R ( blankn ? r : 0 ),
.G ( blankn ? g : 0 ),
.B ( blankn ? b : 0 ),
.HSync ( hs ),
.VSync ( vs ),
.VGA_R ( VGA_R ),
.VGA_G ( VGA_G ),
.VGA_B ( VGA_B ),
.VGA_VS ( vs_out ),
.VGA_HS ( hs_out ),
.rotate ( { 1'b1, rotate } ),
.ce_divider ( 1 ),
.blend ( blend ),
.scandoubler_disable(1),//scandoublerD ),
.no_csync ( 1'b1 ),
.ypbpr ( ypbpr )
);
dac #(
.C_bits(16))
dac_l(
.clk_i(clk_sys),
.res_n_i(1),
.dac_i(audio_l + { audio, 5'd0 }),
.dac_o(AUDIO_L)
);
dac #(
.C_bits(16))
dac_r(
.clk_i(clk_sys),
.res_n_i(1),
.dac_i(audio_r + { audio, 5'd0 }),
.dac_o(AUDIO_R)
);
// Sarge controls
wire onestick = status[9];
wire sarge_up1, sarge_up2, sarge_up3, sarge_up4;
wire sarge_down1, sarge_down2, sarge_down3, sarge_down4;
wire sarge_fire1A, sarge_fire1B, sarge_fire2A, sarge_fire2B;
always @(*) begin
if (~onestick) begin
sarge_up1 = m_up;
sarge_up2 = m_up2;
sarge_up3 = m_up3;
sarge_up4 = m_up4;
sarge_down1 = m_down;
sarge_down2 = m_down2;
sarge_down3 = m_down3;
sarge_down4 = m_down4;
sarge_fire1A = m_fireA | m_fire2A;
sarge_fire1B = m_fireB | m_fire2B;
sarge_fire2A = m_fire3A | m_fire4A;
sarge_fire2B = m_fire3B | m_fire4B;
end else begin
sarge_up1 = (m_up & ~m_left) | (m_right & ~m_down);
sarge_up2 = (m_up & ~m_right) | (m_left & ~m_down);
sarge_down1 = (m_down & ~m_right) | (m_left & ~m_up);
sarge_down2 = (m_down & ~m_left) | (m_right & ~m_up);
sarge_up3 = (m_up2 & ~m_left2) | (m_right2 & ~m_down2);
sarge_up4 = (m_up2 & ~m_right2) | (m_left2 & ~m_down2);
sarge_down3 = (m_down2 & ~m_right2) | (m_left2 & ~m_up2);
sarge_down4 = (m_down2 & ~m_left2) | (m_right2 & ~m_up2);
sarge_fire1A = m_fireA;
sarge_fire1B = m_fireB;
sarge_fire2A = m_fire2A;
sarge_fire2B = m_fire2B;
end
end
// Power Drive gear
reg [2:0] powerdrv_gear;
always @(posedge clk_sys) begin
reg [2:0] gear_old;
if (reset) powerdrv_gear <= 0;
else begin
gear_old <= {m_fire3D, m_fire2D, m_fireD};
if (~gear_old[0] & m_fireD) powerdrv_gear[0] <= ~powerdrv_gear[0];
if (~gear_old[1] & m_fire2D) powerdrv_gear[1] <= ~powerdrv_gear[1];
if (~gear_old[2] & m_fire3D) powerdrv_gear[2] <= ~powerdrv_gear[2];
end
end
//Pedals/Steering for Max RPM
reg [7:0] maxrpm_adc_data;
reg [3:0] maxrpm_adc_control;
always @(*) begin
case (maxrpm_adc_control[1:0])
2'b00: maxrpm_adc_data = gas2;
2'b01: maxrpm_adc_data = gas1;
2'b10: maxrpm_adc_data = steering2;
2'b11: maxrpm_adc_data = steering1;
endcase
end
always @(posedge clk_sys) if (~output6[6] & ~output6[5]) maxrpm_adc_control <= output5[4:1];
wire [7:0] gas1;
wire [7:0] steering1;
spy_hunter_control maxrpm_pl1 (
.clock_40(clk_sys),
.reset(reset),
.vsync(vs),
.gas_plus(m_up),
.gas_minus(m_down),
.steering_plus(m_right),
.steering_minus(m_left),
.steering(steering1),
.gas(gas1)
);
wire [7:0] gas2;
wire [7:0] steering2;
spy_hunter_control maxrpm_pl2 (
.clock_40(clk_sys),
.reset(reset),
.vsync(vs),
.gas_plus(m_up2),
.gas_minus(m_down2),
.steering_plus(m_right2),
.steering_minus(m_left2),
.steering(steering2),
.gas(gas2)
);
// MaxRPM gearbox
wire [3:0] maxrpm_gear_bits[5] = '{ 4'h0, 4'h5, 4'h6, 4'h1, 4'h2 };
wire [3:0] maxrpm_gear1 = maxrpm_gear_bits[gear1];
wire [3:0] maxrpm_gear2 = maxrpm_gear_bits[gear2];
reg [2:0] gear1;
reg [2:0] gear2;
always @(posedge clk_sys) begin
reg m_fireA_last, m_fireB_last;
reg m_fire2A_last, m_fire2B_last;
if (reset) begin
gear1 <= 0;
gear2 <= 0;
end else begin
m_fireA_last <= m_fireA;
m_fireB_last <= m_fireB;
m_fire2A_last <= m_fire2A;
m_fire2B_last <= m_fire2B;
if (m_one_player) gear1 <= 0;
else if (~m_fireA_last && m_fireA && gear1 != 3'd4) gear1 <= gear1 + 1'd1;
else if (~m_fireB_last && m_fireB && gear1 != 3'd0) gear1 <= gear1 - 1'd1;
if (m_two_players) gear2 <= 0;
else if (~m_fire2A_last && m_fire2A && gear2 != 3'd4) gear2 <= gear2 + 1'd1;
else if (~m_fire2B_last && m_fire2B && gear2 != 3'd0) gear2 <= gear2 - 1'd1;
end
end
// Demolition Derby
reg dderby_input_sel;
always @(posedge clk_sys) begin
if (reset)
dderby_input_sel <= 0;
else begin
if (output6[7]) dderby_input_sel <= 0;
else if (output6[6]) dderby_input_sel <= 1;
end
end
wire [5:0] wheel1, wheel2, wheel3, wheel4;
spinner spinner1 (clk_sys, reset, m_left, m_right, 1'b0, vs, wheel1);
spinner spinner2 (clk_sys, reset, m_left2, m_right2, 1'b0, vs, wheel2);
spinner spinner3 (clk_sys, reset, m_left3, m_right3, 1'b0, vs, wheel3);
spinner spinner4 (clk_sys, reset, m_left4, m_right4, 1'b0, vs, wheel4);
// dotron spinner
wire [5:0] dotron_spinner;
spinner #(15) dotron_spn (clk_sys, reset, m_fireE | m_fireG, m_fireF | m_fireH, 1'b0, vs, dotron_spinner);
// Common inputs
wire m_up, m_down, m_left, m_right, m_fireA, m_fireB, m_fireC, m_fireD, m_fireE, m_fireF, m_fireG, m_fireH;
wire m_up2, m_down2, m_left2, m_right2, m_fire2A, m_fire2B, m_fire2C, m_fire2D;
wire m_up3, m_down3, m_left3, m_right3, m_fire3A, m_fire3B, m_fire3C, m_fire3D;
wire m_up4, m_down4, m_left4, m_right4, m_fire4A, m_fire4B, m_fire4C, m_fire4D;
wire m_tilt, m_coin1, m_coin2, m_coin3, m_coin4, m_one_player, m_two_players, m_three_players, m_four_players;
arcade_inputs inputs (
.clk ( clk_sys ),
.key_strobe ( key_strobe ),
.key_pressed ( key_pressed ),
.key_code ( key_code ),
.joystick_0 ( joystick_0 ),
.joystick_1 ( joystick_1 ),
.joystick_2 ( joystick_2 ),
.joystick_3 ( joystick_3 ),
.rotate ( rotate ),
.orientation ( 2'b10 ),
.joyswap ( joyswap ),
.oneplayer ( 1'b0 ),
.controls ( {m_tilt, m_coin4, m_coin3, m_coin2, m_coin1, m_four_players, m_three_players, m_two_players, m_one_player} ),
.player1 ( {m_fireH, m_fireG, m_fireF, m_fireE, m_fireD, m_fireC, m_fireB, m_fireA, m_up, m_down, m_left, m_right} ),
.player2 ( {m_fire2D, m_fire2C, m_fire2B, m_fire2A, m_up2, m_down2, m_left2, m_right2} ),
.player3 ( {m_fire3D, m_fire3C, m_fire3B, m_fire3A, m_up3, m_down3, m_left3, m_right3} ),
.player4 ( {m_fire4D, m_fire4C, m_fire4B, m_fire4A, m_up4, m_down4, m_left4, m_right4} )
);
endmodule