1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-02-28 09:07:40 +00:00

Delete Leftovers

This commit is contained in:
Marcel
2026-02-22 14:17:38 +01:00
parent 415ca14e80
commit 6f2dbdc819
4 changed files with 0 additions and 1406 deletions

View File

@@ -1,249 +0,0 @@
// Port to MiST
// Copyright (C) 2026 Rodimus
//
// Blue Print for MiST
// Based on Time Pilot port, original design Copyright (C) 2017 Dar
// Initial port to MiSTer Copyright (C) 2017 Sorgelig
// Updated port to MiSTer Copyright (C) 2021, 2022 Ace,
// Ash Evans (aka ElectronAsh/OzOnE), Artemio Urbina and Kitrinx (aka Rysha)
//
// 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 BluePrint(
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,
output SPI_DO,
input SPI_DI,
input SPI_SS2,
input SPI_SS3,
input CONF_DATA0,
input CLOCK_27
);
`include "rtl/build_id.v"
localparam CONF_STR = {
"BluePrint;rom;",
"O1,Blend,Off,On;",
"O2,Rotate Controls,Off,On;",
"O34,Scanlines,Off,25%,50%,75%;",
"OOR,CRT H adjust,0,+1,+2,+3,+4,+5,+6,+7,-8,-7,-6,-5,-4,-3,-2,-1;",
"OSV,CRT V adjust,0,+1,+2,+3,+4,+5,+6,+7,-8,-7,-6,-5,-4,-3,-2,-1;",
// "O58,H Center,0,-1,-2,-3,-4,-5,-6,-7,+7,+6,+5,+4,+3,+2,+1;",
// "O9C,V Center,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12;",
"DIP;",
"T0,Reset;",
"V,v1.0.",`BUILD_DATE
};
wire blend = status[1];
wire rotate = status[2];
wire [1:0] orientation = 2'b10;
wire [1:0] scanlines = status[4:3];
//wire service = status[7];
assign LED = ~ioctl_downl;
assign AUDIO_R = AUDIO_L;
wire clk_sys;
wire pll_locked;
pll pll(
.inclk0(CLOCK_27),
.areset(0),
.c0(clk_sys),//49.152000
.locked(pll_locked)
);
wire [63:0] status;
wire [1:0] buttons;
wire [1:0] switches;
wire [31:0] joystick_0;
wire [31:0] joystick_1;
wire scandoublerD;
wire ypbpr;
wire no_csync;
wire key_pressed;
wire [7:0] key_code;
wire key_strobe;
wire ioctl_downl;
wire [7:0] ioctl_index;
wire ioctl_wr;
wire [24:0] ioctl_addr;
wire [7:0] ioctl_dout;
wire [15:0] audio;
wire hs, vs, cs, hb, vb;
wire blankn = ~(hb | vb);
wire [4:0] r, g, b;
wire [ 3:0] hoffset, voffset;
assign { voffset, hoffset } = status[31:24];
// DIP SWITCHES
reg [7:0] dip_sw[8]; // Active-LOW
always @(posedge clk_sys) begin
if(ioctl_wr && (ioctl_index==254) && !ioctl_addr[24:3])
dip_sw[ioctl_addr[2:0]] <= ioctl_dout;
end
wire [7:0] p1_controls = {m_down, m_up, m_right, m_left, m_fireA, 1'b0, m_one_player, m_coin1};
wire [7:0] p2_controls = {m_down2, m_up2, m_right2, m_left2, m_fire2A, 1'b0, m_two_players, m_coin2};
user_io #(
.STRLEN(($size(CONF_STR)>>3)))
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 ),
.key_strobe (key_strobe ),
.key_pressed (key_pressed ),
.key_code (key_code ),
.joystick_0 (joystick_0 ),
.joystick_1 (joystick_1 ),
.status (status )
);
data_io data_io (
.clk_sys ( clk_sys ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS2 ( SPI_SS2 ),
.SPI_DI ( SPI_DI ),
.ioctl_download( ioctl_downl ),
.ioctl_index ( ioctl_index ),
.ioctl_wr ( ioctl_wr ),
.ioctl_addr ( ioctl_addr ),
.ioctl_dout ( ioctl_dout )
);
reg reset = 1;
reg rom_loaded = 0;
always @(posedge clk_sys) begin
reg ioctl_downlD;
ioctl_downlD <= ioctl_downl;
if (ioctl_downlD & ~ioctl_downl) rom_loaded <= 1;
reset <= status[0] | buttons[1] | ~rom_loaded;
end
BluePrint_Top BluePrint_Top(
.reset (~reset),
.clk_49m (clk_sys),
.p1_controls (p1_controls),
.p2_controls (p2_controls),
.dip_sw ({dip_sw[1], dip_sw[0]}),
.video_hsync (hs),
.video_vsync (vs),
.video_csync (cs),
.video_hblank (hb),
.video_vblank (vb),
.ce_pix (),
.video_r (r),
.video_g (g),
.video_b (b),
.sound (audio),
.h_center (hoffset),
.v_center (voffset),
// ROM loading
.ioctl_addr (ioctl_addr),
.ioctl_data (ioctl_dout),
.ioctl_wr (ioctl_wr),
.ioctl_index (ioctl_index),
.pause (0),
// Hiscore interface
.hs_address (),
.hs_data_in (),
.hs_data_out (),
.hs_write ()
);
mist_video #(.COLOR_DEPTH(5), .SD_HCNT_WIDTH(11)) 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 ( VGA_VS ),
.VGA_HS ( VGA_HS ),
.scandoubler_disable( scandoublerD ),
.rotate ( { orientation[1], rotate } ),
.scanlines ( scanlines ),
.ypbpr ( ypbpr ),
.no_csync ( no_csync ),
.ce_divider ( 1'b0 ),
.blend ( blend )
);
dac #(
.C_bits(16))
dac(
.clk_i(clk_sys),
.res_n_i(1'b1),
.dac_i(audio),
.dac_o(AUDIO_L)
);
wire m_up, m_down, m_left, m_right, m_fireA, m_fireB, m_fireC, m_fireD, m_fireE, m_fireF;
wire m_up2, m_down2, m_left2, m_right2, m_fire2A, m_fire2B, m_fire2C, m_fire2D, m_fire2E, m_fire2F;
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 ),
.rotate ( rotate ),
.orientation ( orientation ),
.joyswap ( 1'b0 ),
.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_fireF, m_fireE, m_fireD, m_fireC, m_fireB, m_fireA, m_up, m_down, m_left, m_right} ),
.player2 ( {m_fire2F, m_fire2E, m_fire2D, m_fire2C, m_fire2B, m_fire2A, m_up2, m_down2, m_left2, m_right2} )
);
endmodule

View File

@@ -1,653 +0,0 @@
//============================================================================
//
// Blue Print main CPU board model
// Copyright (C) 2026 Rodimus
//
// 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 BluePrint_CPU
(
input reset,
input clk_49m,
// Video outputs
output [4:0] red, green, blue,
output video_hsync, video_vsync, video_csync,
output video_hblank, video_vblank,
output ce_pix,
// Player controls (active HIGH, directly from MiSTer)
input [7:0] p1_controls, // {coin, start2, start1, btn1, left, down, right, up}
input [7:0] p2_controls, // {3'b111, btn1, left, down, right, up}
// DIP switch readback from sound board
input [7:0] dipsw_readback, // from sound board AY1 port A
// Sound interface (directly to sound board)
output [7:0] sound_cmd,
output sound_cmd_wr,
// Screen centering
input [3:0] h_center, v_center,
// ROM loading
input main1_cs_i, main2_cs_i, main3_cs_i, main4_cs_i, main5_cs_i,
input tile0_cs_i, tile1_cs_i,
input spr_r_cs_i, spr_b_cs_i, spr_g_cs_i,
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr,
input pause,
// Hiscore interface
input [15:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write
);
//------------------------------------------------------- Clock enables -------------------------------------------------------//
// Generate ~5 MHz pixel clock enable from 49.152 MHz
// 49.152 * 89/875 ≈ 4.997 MHz
wire [1:0] pix_cen_o;
jtframe_frac_cen #(2) pix_cen
(
.clk(clk_49m),
.n(10'd89),
.m(10'd875),
.cen(pix_cen_o),
.cenb()
);
wire cen_5m = pix_cen_o[0];
// Generate ~3.5 MHz CPU clock enable from 49.152 MHz
// 49.152 * 5/70 ≈ 3.511 MHz
wire [1:0] cpu_cen_o;
jtframe_frac_cen #(2) cpu_cen
(
.clk(clk_49m),
.n(10'd5),
.m(10'd70),
.cen(cpu_cen_o),
.cenb()
);
wire cen_3m5 = cpu_cen_o[0];
assign ce_pix = cen_5m;
//-------------------------------------------------------- Video timing --------------------------------------------------------//
// H counter 0-319, V counter 0-263
// From MAME: set_raw(5MHz, 320, 0, 256, 264, 16, 240)
reg [8:0] h_cnt = 9'd0;
reg [8:0] v_cnt = 9'd0;
always_ff @(posedge clk_49m) begin
if (cen_5m) begin
if (h_cnt == 9'd319) begin
h_cnt <= 9'd0;
v_cnt <= (v_cnt == 9'd263) ? 9'd0 : v_cnt + 9'd1;
end else
h_cnt <= h_cnt + 9'd1;
end
end
// Blanking
wire hblk = (h_cnt >= 9'd256);
wire vblk = (v_cnt < 9'd16) | (v_cnt >= 9'd240);
assign video_hblank = hblk;
assign video_vblank = vblk;
// Sync generation with screen centering offsets
wire [8:0] hs_start = 9'd280 + {5'd0, h_center};
wire [8:0] hs_end = hs_start + 9'd32;
wire [8:0] vs_start = 9'd248 + {5'd0, v_center};
wire [8:0] vs_end = vs_start + 9'd4;
assign video_hsync = (h_cnt >= hs_start && h_cnt < hs_end);
assign video_vsync = (v_cnt >= vs_start && v_cnt < vs_end);
assign video_csync = ~(video_hsync ^ video_vsync);
//------------------------------------------------------------ CPU -------------------------------------------------------------//
// Main CPU - Zilog Z80 (T80s soft core)
wire [15:0] z80_A;
wire [7:0] z80_Dout;
wire n_mreq, n_iorq, n_rd, n_wr, n_rfsh, n_m1;
T80s cpu
(
.RESET_n(reset),
.CLK(clk_49m),
.CEN(cen_3m5 & ~pause),
.INT_n(n_irq),
.NMI_n(1'b1),
.WAIT_n(1'b1),
.MREQ_n(n_mreq),
.IORQ_n(n_iorq),
.RD_n(n_rd),
.WR_n(n_wr),
.RFSH_n(n_rfsh),
.M1_n(n_m1),
.A(z80_A),
.DI(z80_Din),
.DO(z80_Dout)
);
//--------------------------------------------------------- Interrupts ---------------------------------------------------------//
// VBlank IRQ (irq0_line_hold style): assert on VBlank rising edge, clear on IORQ+M1
reg n_irq = 1'b1;
reg vblk_last = 1'b0;
wire irq_ack = ~n_iorq & ~n_m1;
always_ff @(posedge clk_49m) begin
if (!reset) begin
n_irq <= 1'b1;
vblk_last <= 1'b0;
end else begin
if (irq_ack)
n_irq <= 1'b1;
else if (vblk && !vblk_last)
n_irq <= 1'b0;
vblk_last <= vblk;
end
end
//------------------------------------------------------ Address decoding ------------------------------------------------------//
wire mem_valid = ~n_mreq & n_rfsh;
wire cs_rom = mem_valid & ~z80_A[15] & (z80_A[14:13] != 2'b11); // 0x0000-0x5FFF
wire cs_wram = mem_valid & (z80_A[15:11] == 5'b10000); // 0x8000-0x87FF
wire cs_vram = mem_valid & (z80_A[15:12] == 4'h9); // 0x9000-0x9FFF
wire cs_scroll = mem_valid & (z80_A[15:8] == 8'hA0); // 0xA000-0xA0FF
wire cs_sprite = mem_valid & (z80_A[15:8] == 8'hB0); // 0xB000-0xB0FF
wire cs_io_c = mem_valid & (z80_A[15:12] == 4'hC); // 0xC000-0xCFFF
wire cs_sndcmd = mem_valid & (z80_A[15:12] == 4'hD) & ~n_wr; // 0xD000 write
wire cs_e000 = mem_valid & (z80_A[15:12] == 4'hE); // 0xE000
wire cs_cram = mem_valid & (z80_A[15:12] == 4'hF); // 0xF000-0xFFFF
//------------------------------------------------------------ ROMs ------------------------------------------------------------//
// Main program ROMs (5x 4KB)
wire [7:0] rom1_D, rom2_D, rom3_D, rom4_D, rom5_D;
eprom_4k main_rom1(.CLK(clk_49m), .ADDR(z80_A[11:0]), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(main1_cs_i), .WR(ioctl_wr), .DATA(rom1_D));
eprom_4k main_rom2(.CLK(clk_49m), .ADDR(z80_A[11:0]), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(main2_cs_i), .WR(ioctl_wr), .DATA(rom2_D));
eprom_4k main_rom3(.CLK(clk_49m), .ADDR(z80_A[11:0]), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(main3_cs_i), .WR(ioctl_wr), .DATA(rom3_D));
eprom_4k main_rom4(.CLK(clk_49m), .ADDR(z80_A[11:0]), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(main4_cs_i), .WR(ioctl_wr), .DATA(rom4_D));
eprom_4k main_rom5(.CLK(clk_49m), .ADDR(z80_A[11:0]), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(main5_cs_i), .WR(ioctl_wr), .DATA(rom5_D));
// ROM data mux based on address
wire [7:0] rom_D = (z80_A[14:12] == 3'd0) ? rom1_D :
(z80_A[14:12] == 3'd1) ? rom2_D :
(z80_A[14:12] == 3'd2) ? rom3_D :
(z80_A[14:12] == 3'd3) ? rom4_D :
(z80_A[14:12] == 3'd4) ? rom5_D :
8'hFF;
// Tile ROMs (2x 4KB) — addressed by rendering pipeline
wire [7:0] tile0_D, tile1_D;
reg [11:0] tile_render_addr;
eprom_4k tile_rom0(.CLK(clk_49m), .ADDR(tile_render_addr), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(tile0_cs_i), .WR(ioctl_wr), .DATA(tile0_D));
eprom_4k tile_rom1(.CLK(clk_49m), .ADDR(tile_render_addr), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(tile1_cs_i), .WR(ioctl_wr), .DATA(tile1_D));
// Sprite ROMs (3x 4KB) — addressed by sprite scanner
wire [7:0] spr_r_D, spr_b_D, spr_g_D;
reg [11:0] spr_render_addr;
eprom_4k spr_rom_r(.CLK(clk_49m), .ADDR(spr_render_addr), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(spr_r_cs_i), .WR(ioctl_wr), .DATA(spr_r_D));
eprom_4k spr_rom_b(.CLK(clk_49m), .ADDR(spr_render_addr), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(spr_b_cs_i), .WR(ioctl_wr), .DATA(spr_b_D));
eprom_4k spr_rom_g(.CLK(clk_49m), .ADDR(spr_render_addr), .CLK_DL(clk_49m), .ADDR_DL(ioctl_addr), .DATA_IN(ioctl_data), .CS_DL(spr_g_cs_i), .WR(ioctl_wr), .DATA(spr_g_D));
//-------------------------------------------------------------- RAM -----------------------------------------------------------//
// Work RAM (2KB, dual-port for hiscore)
wire [7:0] wram_D;
dpram_dc #(.widthad_a(11)) work_ram
(
.clock_a(clk_49m),
.wren_a(cs_wram & ~n_wr),
.address_a(z80_A[10:0]),
.data_a(z80_Dout),
.q_a(wram_D),
.clock_b(clk_49m),
.wren_b(hs_write),
.address_b(hs_address[10:0]),
.data_b(hs_data_in),
.q_b(hs_data_out)
);
// Video RAM (1KB) — port A: CPU, port B: video rendering
wire [7:0] vram_cpu_D, vram_render_D;
reg [9:0] vram_render_addr;
dpram_dc #(.widthad_a(10)) video_ram
(
.clock_a(clk_49m), .wren_a(cs_vram & ~n_wr),
.address_a(z80_A[9:0]), .data_a(z80_Dout), .q_a(vram_cpu_D),
.clock_b(clk_49m), .wren_b(1'b0),
.address_b(vram_render_addr), .data_b(8'd0), .q_b(vram_render_D)
);
// Color RAM (1KB) — port A: CPU, port B: video rendering
wire [7:0] cram_cpu_D, cram_render_D;
reg [9:0] cram_render_addr;
dpram_dc #(.widthad_a(10)) color_ram
(
.clock_a(clk_49m), .wren_a(cs_cram & ~n_wr),
.address_a(z80_A[9:0]), .data_a(z80_Dout), .q_a(cram_cpu_D),
.clock_b(clk_49m), .wren_b(1'b0),
.address_b(cram_render_addr), .data_b(8'd0), .q_b(cram_render_D)
);
// Scroll RAM (256 bytes) — port A: CPU, port B: video rendering
wire [7:0] scroll_cpu_D, scroll_render_D;
reg [7:0] scroll_render_addr;
dpram_dc #(.widthad_a(8)) scroll_ram
(
.clock_a(clk_49m), .wren_a(cs_scroll & ~n_wr),
.address_a(z80_A[7:0]), .data_a(z80_Dout), .q_a(scroll_cpu_D),
.clock_b(clk_49m), .wren_b(1'b0),
.address_b(scroll_render_addr), .data_b(8'd0), .q_b(scroll_render_D)
);
// Sprite RAM (256 bytes) — port A: CPU, port B: sprite scanner
wire [7:0] sprite_cpu_D, sprite_scan_D;
reg [7:0] sprite_scan_addr;
dpram_dc #(.widthad_a(8)) sprite_ram
(
.clock_a(clk_49m), .wren_a(cs_sprite & ~n_wr),
.address_a(z80_A[7:0]), .data_a(z80_Dout), .q_a(sprite_cpu_D),
.clock_b(clk_49m), .wren_b(1'b0),
.address_b(sprite_scan_addr), .data_b(8'd0), .q_b(sprite_scan_D)
);
//-------------------------------------------------------- I/O registers -------------------------------------------------------//
// Flip screen and gfx_bank (0xE000 write)
reg flip = 1'b0;
reg gfx_bank = 1'b0;
always_ff @(posedge clk_49m) begin
if (!reset) begin
flip <= 1'b0;
gfx_bank <= 1'b0;
end else if (cen_3m5 && cs_e000 && ~n_wr) begin
flip <= ~z80_Dout[1];
gfx_bank <= z80_Dout[2];
end
end
// Sound command (0xD000 write)
reg [7:0] snd_cmd_reg = 8'd0;
reg snd_cmd_wr_reg = 1'b0;
always_ff @(posedge clk_49m) begin
snd_cmd_wr_reg <= 1'b0;
if (cen_3m5 && cs_sndcmd) begin
snd_cmd_reg <= z80_Dout;
snd_cmd_wr_reg <= 1'b1;
end
end
assign sound_cmd = snd_cmd_reg;
assign sound_cmd_wr = snd_cmd_wr_reg;
//---------------------------------------------------- CPU data input mux -----------------------------------------------------//
wire [7:0] z80_Din = cs_rom ? rom_D :
(cs_wram & n_wr) ? wram_D :
(cs_vram & n_wr) ? vram_cpu_D :
(cs_cram & n_wr) ? cram_cpu_D :
(cs_scroll & n_wr) ? scroll_cpu_D :
(cs_sprite & n_wr) ? sprite_cpu_D :
(cs_io_c & ~n_rd & (z80_A[1:0] == 2'b00)) ? p1_controls :
(cs_io_c & ~n_rd & (z80_A[1:0] == 2'b01)) ? p2_controls :
(cs_io_c & ~n_rd & (z80_A[1:0] == 2'b11)) ? dipsw_readback :
8'hFF;
//--- Tilemap rendering pipeline ---
//
// TILEMAP_SCAN_COLS_FLIP_X layout: tile_index = (31 - col) * 32 + row
// VRAM/CRAM address: {(31 - screen_col), tile_row} (column-major, X-flipped)
//
// Timeline (all on cen_5m):
// fine_x=0: Transfer pipe→shifts. Start fetch for current col: set scroll_render_addr.
// fine_x=1: Scroll data ready. Compute scrolled_y. Set vram/cram addr.
// fine_x=2: Wait for VRAM/CRAM.
// fine_x=3: VRAM/CRAM data ready. Compute tile ROM addr. Update prev_bank_bit.
// fine_x=4: Tile ROM data ready. Latch into pipe_tile0/pipe_tile1.
// fine_x=5,6,7: Shift pixels out (MSB first).
reg [7:0] tile_shift0, tile_shift1; // Active shift registers (bit7 = current pixel)
reg [6:0] tile_color_latch; // Color byte latched for active column
reg tile_priority_latch; // Priority bit for active column
reg prev_bank_bit; // cram[6] from previous column (bank quirk)
reg [4:0] latched_col; // screen_col captured at fine_x=0
reg [7:0] pipe_tile0, pipe_tile1; // Pre-fetched tile ROM bytes
reg [6:0] pipe_color; // Pre-fetched color byte
reg pipe_priority; // Pre-fetched priority bit
reg [2:0] pipe_fine_y; // Fine Y for ROM address
wire [4:0] screen_col = h_cnt[7:3];
wire [2:0] fine_x = h_cnt[2:0];
wire [7:0] screen_y = v_cnt[7:0];
wire visible_line = (v_cnt >= 9'd16) && (v_cnt < 9'd240);
always_ff @(posedge clk_49m) begin
if (!reset) begin
tile_shift0 <= 8'd0;
tile_shift1 <= 8'd0;
tile_color_latch <= 7'd0;
tile_priority_latch <= 1'b0;
prev_bank_bit <= 1'b0;
latched_col <= 5'd0;
pipe_tile0 <= 8'd0;
pipe_tile1 <= 8'd0;
pipe_color <= 7'd0;
pipe_priority <= 1'b0;
pipe_fine_y <= 3'd0;
end else if (cen_5m) begin
if (!visible_line) begin
tile_shift0 <= 8'd0;
tile_shift1 <= 8'd0;
if (v_cnt == 9'd15 && h_cnt == 9'd0)
prev_bank_bit <= 1'b0;
end else begin
case (fine_x)
// fine_x=0: Transfer pipe to shift registers.
// Begin fetch for current column.
3'd0: begin
tile_shift0 <= pipe_tile0;
tile_shift1 <= pipe_tile1;
tile_color_latch <= pipe_color;
tile_priority_latch <= pipe_priority;
latched_col <= screen_col;
// scroll_ram[(30 - scr_col) & 0xFF]
scroll_render_addr <= 8'd30 - {3'd0, screen_col};
end
// fine_x=1: Scroll data ready. Compute scrolled Y, set VRAM/CRAM addr.
3'd1: begin
begin
reg [7:0] scrolled_y;
scrolled_y = screen_y + scroll_render_D;
pipe_fine_y <= scrolled_y[2:0];
// TILEMAP_SCAN_COLS_FLIP_X: VRAM col 0 = rightmost screen col (31)
vram_render_addr <= {(5'd31 - latched_col), scrolled_y[7:3]};
cram_render_addr <= {(5'd31 - latched_col), scrolled_y[7:3]};
end
tile_shift0 <= {tile_shift0[6:0], 1'b0};
tile_shift1 <= {tile_shift1[6:0], 1'b0};
end
// fine_x=2: Wait for VRAM/CRAM output to settle.
3'd2: begin
tile_shift0 <= {tile_shift0[6:0], 1'b0};
tile_shift1 <= {tile_shift1[6:0], 1'b0};
end
// fine_x=3: VRAM/CRAM data ready. Compute tile ROM address.
3'd3: begin
pipe_color <= cram_render_D[6:0];
pipe_priority <= cram_render_D[7];
// Bank bit from PREVIOUS column's cram[6], gated by gfx_bank register
tile_render_addr <= {prev_bank_bit & gfx_bank, vram_render_D, pipe_fine_y};
prev_bank_bit <= cram_render_D[6];
tile_shift0 <= {tile_shift0[6:0], 1'b0};
tile_shift1 <= {tile_shift1[6:0], 1'b0};
end
// fine_x=4: Tile ROM data ready. Latch into pipe.
3'd4: begin
pipe_tile0 <= tile0_D;
pipe_tile1 <= tile1_D;
tile_shift0 <= {tile_shift0[6:0], 1'b0};
tile_shift1 <= {tile_shift1[6:0], 1'b0};
end
// fine_x=5,6,7: Shift only.
default: begin
tile_shift0 <= {tile_shift0[6:0], 1'b0};
tile_shift1 <= {tile_shift1[6:0], 1'b0};
end
endcase
end
end
end
//--- Tile palette computation ---
// Shift left, MSB out first: tile_shift[7] = current pixel bit.
// Color bits are RBG order: cram bit0=R, bit1=B, bit2=G.
wire [1:0] tile_pixel = {tile_shift1[7], tile_shift0[7]};
wire tile_transparent = (tile_pixel == 2'b00);
wire tile_intensity = tile_color_latch[6];
wire [2:0] color_lo = tile_color_latch[2:0]; // RBG for pixel==01
wire [2:0] color_hi = tile_color_latch[5:3]; // RBG for pixel==10
wire [2:0] pen_rbg = (tile_pixel == 2'b01) ? color_lo :
(tile_pixel == 2'b10) ? color_hi :
(tile_pixel == 2'b11) ? (color_lo | color_hi) :
3'b000;
wire [4:0] r_tile = pen_rbg[0] ? (tile_intensity ? 5'd24 : 5'd31) : 5'd0;
wire [4:0] g_tile = pen_rbg[2] ? (tile_intensity ? 5'd24 : 5'd31) : 5'd0;
wire [4:0] b_tile = pen_rbg[1] ? (tile_intensity ? 5'd24 : 5'd31) : 5'd0;
//--- Sprite line buffers (double-buffered, 256×3-bit, 0 = transparent) ---
reg [2:0] linebuf0 [0:255];
reg [2:0] linebuf1 [0:255];
reg linebuf_sel = 1'b0; // 0: display buf0/write buf1; 1: display buf1/write buf0
// Swap at the start of each active line
always_ff @(posedge clk_49m) begin
if (!reset)
linebuf_sel <= 1'b0;
else if (cen_5m && h_cnt == 9'd0)
linebuf_sel <= ~linebuf_sel;
end
//--- Sprite scanner state machine (runs at clk_49m during HBlank) ---
// Sprites are 8×16. ROM address: {tile_code[7:0], line_in_sprite[3:0]}.
// flipY for sprite N comes from sprite N-1's byte2[7] (previous-sprite quirk).
reg [3:0] spr_state;
reg [5:0] spr_idx;
reg [7:0] spr_byte0; // Y position
reg [7:0] spr_byte1; // Tile code
reg [7:0] spr_byte2; // Flags: bit6=flipX, bit7=NEXT sprite's flipY
reg [7:0] spr_byte3; // X position
reg spr_flipy; // flipY for current sprite (from previous sprite's byte2[7])
reg prev_sprite_flipy; // Carry flipY forward
reg [7:0] spr_rom_r_lat, spr_rom_b_lat, spr_rom_g_lat;
reg [2:0] spr_pix_cnt;
reg [7:0] spr_clear_addr;
reg [7:0] next_scanline; // v_cnt of the line being prepared
localparam SPR_IDLE = 4'd0;
localparam SPR_CLEAR = 4'd1;
localparam SPR_INIT_RD = 4'd2;
localparam SPR_INIT_LAT = 4'd3;
localparam SPR_RD_B0 = 4'd4;
localparam SPR_RD_B1 = 4'd5;
localparam SPR_RD_B2 = 4'd6;
localparam SPR_RD_B3 = 4'd7;
localparam SPR_ROMADDR = 4'd8;
localparam SPR_ROMWAIT = 4'd9;
localparam SPR_PIXELS = 4'd10;
localparam SPR_NEXT = 4'd11;
always_ff @(posedge clk_49m) begin
if (!reset) begin
spr_state <= SPR_IDLE;
spr_idx <= 6'd0;
prev_sprite_flipy <= 1'b0;
spr_clear_addr <= 8'd0;
end else begin
case (spr_state)
SPR_IDLE: begin
if (cen_5m && h_cnt == 9'd256) begin
next_scanline <= v_cnt[7:0] + 8'd1;
spr_clear_addr <= 8'd0;
spr_state <= SPR_CLEAR;
end
end
SPR_CLEAR: begin
if (~linebuf_sel)
linebuf1[spr_clear_addr] <= 3'd0;
else
linebuf0[spr_clear_addr] <= 3'd0;
if (spr_clear_addr == 8'd255) begin
sprite_scan_addr <= 8'hFE; // sprite 63, byte 2
spr_state <= SPR_INIT_RD;
end else
spr_clear_addr <= spr_clear_addr + 8'd1;
end
SPR_INIT_RD: begin
spr_state <= SPR_INIT_LAT;
end
SPR_INIT_LAT: begin
prev_sprite_flipy <= sprite_scan_D[7];
spr_idx <= 6'd0;
sprite_scan_addr <= 8'd0; // sprite 0, byte 0
spr_state <= SPR_RD_B0;
end
SPR_RD_B0: begin
// RAM address already set; request byte 1 for next cycle
sprite_scan_addr <= {spr_idx, 2'd1};
spr_state <= SPR_RD_B1;
end
SPR_RD_B1: begin
// byte 0 (Y) now on bus; request byte 2
spr_byte0 <= sprite_scan_D;
sprite_scan_addr <= {spr_idx, 2'd2};
spr_state <= SPR_RD_B2;
end
SPR_RD_B2: begin
// byte 1 (tile code) now on bus; request byte 3
spr_byte1 <= sprite_scan_D;
sprite_scan_addr <= {spr_idx, 2'd3};
spr_state <= SPR_RD_B3;
end
SPR_RD_B3: begin
// byte 2 (flags) now on bus
spr_byte2 <= sprite_scan_D;
spr_flipy <= prev_sprite_flipy;
spr_state <= SPR_ROMADDR;
end
SPR_ROMADDR: begin
// byte 3 (X) now on bus
spr_byte3 <= sprite_scan_D;
prev_sprite_flipy <= spr_byte2[7];
begin
reg [8:0] raw_line;
// line_in_sprite = next_scanline - (sy - 1) = next_scanline - 239 + byte0
raw_line = {1'b0, next_scanline} - 9'd239 + {1'b0, spr_byte0};
if (raw_line[8] || raw_line[7:4] != 4'd0) begin
spr_state <= SPR_NEXT; // not on this scanline
end else begin
reg [3:0] line_in_sprite;
line_in_sprite = spr_flipy ? (4'd15 - raw_line[3:0]) : raw_line[3:0];
spr_render_addr <= {spr_byte1, line_in_sprite};
spr_state <= SPR_ROMWAIT;
end
end
end
SPR_ROMWAIT: begin
// ROM data valid this cycle; latch all three planes
spr_rom_r_lat <= spr_r_D;
spr_rom_b_lat <= spr_b_D;
spr_rom_g_lat <= spr_g_D;
spr_pix_cnt <= 3'd0;
spr_state <= SPR_PIXELS;
end
SPR_PIXELS: begin
begin
reg [2:0] bit_pos;
reg [2:0] pixel_val;
reg [7:0] x_pos;
// flipX: bit0 first (pixel 0 = LSB); normal: bit7 first (pixel 0 = MSB)
bit_pos = spr_byte2[6] ? spr_pix_cnt : (3'd7 - spr_pix_cnt);
pixel_val = {spr_rom_g_lat[bit_pos], spr_rom_b_lat[bit_pos], spr_rom_r_lat[bit_pos]};
x_pos = spr_byte3 + {5'd0, spr_pix_cnt} + 8'd2;
if (pixel_val != 3'd0) begin
if (~linebuf_sel)
linebuf1[x_pos] <= pixel_val;
else
linebuf0[x_pos] <= pixel_val;
end
if (spr_pix_cnt == 3'd7)
spr_state <= SPR_NEXT;
else
spr_pix_cnt <= spr_pix_cnt + 3'd1;
end
end
SPR_NEXT: begin
if (spr_idx == 6'd63)
spr_state <= SPR_IDLE;
else begin
spr_idx <= spr_idx + 6'd1;
sprite_scan_addr <= {spr_idx + 6'd1, 2'd0};
spr_state <= SPR_RD_B0;
end
end
default: spr_state <= SPR_IDLE;
endcase
end
end
//--- Sprite pixel readout ---
wire [2:0] sprite_pixel = linebuf_sel ? linebuf1[h_cnt[7:0]] : linebuf0[h_cnt[7:0]];
wire sprite_transparent = (sprite_pixel == 3'b000);
// Sprite pixel bits: bit0=R, bit1=B, bit2=G (full brightness only)
wire [4:0] r_sprite = sprite_pixel[0] ? 5'd31 : 5'd0;
wire [4:0] g_sprite = sprite_pixel[2] ? 5'd31 : 5'd0;
wire [4:0] b_sprite = sprite_pixel[1] ? 5'd31 : 5'd0;
//--- Compositing: priority-1 tiles > sprites > priority-0 tiles > black ---
wire [4:0] r_final = (tile_priority_latch && !tile_transparent) ? r_tile :
(!sprite_transparent) ? r_sprite :
r_tile;
wire [4:0] g_final = (tile_priority_latch && !tile_transparent) ? g_tile :
(!sprite_transparent) ? g_sprite :
g_tile;
wire [4:0] b_final = (tile_priority_latch && !tile_transparent) ? b_tile :
(!sprite_transparent) ? b_sprite :
b_tile;
assign red = (hblk | vblk) ? 5'd0 : r_final;
assign green = (hblk | vblk) ? 5'd0 : g_final;
assign blue = (hblk | vblk) ? 5'd0 : b_final;
endmodule

View File

@@ -1,334 +0,0 @@
//============================================================================
//
// Blue Print sound PCB model
// Copyright (C) 2026 Rodimus
//
// 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 BluePrint_SND
(
input reset,
input pause,
input clk_49m, // Master clock: 49.152MHz
// Sound command interface from main CPU
input [7:0] sound_cmd, // Sound command byte from main CPU latch
input sound_cmd_wr, // Pulse: main CPU wrote to 0xD000
// DIP switches (directly from MiSTer OSD)
input [15:0] dip_sw, // [7:0] = bank 1, [15:8] = bank 2
// DIP switch readback to main CPU
output [7:0] dipsw_readback, // Value main CPU reads at 0xC003
// Audio output
output signed [15:0] sound,
// VBlank input for IRQ generation (directly from video timing)
input vblank,
// ROM loading
input snd_rom1_cs_i, // Directly from ioctl for sound ROM 1
input snd_rom2_cs_i, // Directly from ioctl for sound ROM 2
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr
);
//------------------------------------------------------- Clock division -------------------------------------------------------//
// Generate 1.25 MHz clock enable for sound Z80 and AY1, and 0.625 MHz for AY2
// 49.152 MHz * 25/983 ≈ 1.2489 MHz ≈ 1.25 MHz
// W=2: cen[0] = 1.25 MHz, cen[1] = 0.625 MHz
wire cen_1m25, cen_0m625;
jtframe_frac_cen #(2) sound_cen
(
.clk(clk_49m),
.n(10'd25),
.m(10'd983),
.cen({cen_0m625, cen_1m25})
);
// DC removal filter clock
reg [8:0] div = 9'd0;
always_ff @(posedge clk_49m) begin
div <= div + 9'd1;
end
wire cen_dcrm = !div;
//------------------------------------------------------------ CPU -------------------------------------------------------------//
// Sound CPU - Zilog Z80 (uses T80s version of the T80 soft core)
wire [15:0] sound_A;
wire [7:0] sound_Dout;
wire n_m1, n_mreq, n_iorq, n_rd, n_wr, n_rfsh;
T80s C8
(
.RESET_n(reset),
.CLK(clk_49m),
.CEN(cen_1m25 & ~pause),
.INT_n(n_irq),
.NMI_n(n_nmi),
.M1_n(n_m1),
.MREQ_n(n_mreq),
.IORQ_n(n_iorq),
.RD_n(n_rd),
.WR_n(n_wr),
.RFSH_n(n_rfsh),
.A(sound_A),
.DI(sound_Din),
.DO(sound_Dout)
);
// Address decoding for Z80
// ROM1: 0x0000-0x1FFF (A[15:13] = 000)
wire cs_rom1 = ~n_mreq & n_rfsh & ~sound_A[15] & ~sound_A[14] & ~sound_A[13];
// ROM2: 0x2000-0x3FFF (A[15:13] = 001)
wire cs_rom2 = ~n_mreq & n_rfsh & ~sound_A[15] & ~sound_A[14] & sound_A[13];
// RAM: 0x4000-0x5FFF (A[15:13] = 010)
wire cs_ram = ~n_mreq & n_rfsh & ~sound_A[15] & sound_A[14] & ~sound_A[13];
// AY1: 0x6000-0x7FFF (A[15:13] = 011)
wire cs_ay1_w = ~n_mreq & n_rfsh & ~sound_A[15] & sound_A[14] & sound_A[13] & ~n_wr;
wire cs_ay1_r = ~n_mreq & n_rfsh & ~sound_A[15] & sound_A[14] & sound_A[13] & ~n_rd;
// AY2: 0x8000-0x9FFF (A[15:13] = 100)
wire cs_ay2_w = ~n_mreq & n_rfsh & sound_A[15] & ~sound_A[14] & ~sound_A[13] & ~n_wr;
wire cs_ay2_r = ~n_mreq & n_rfsh & sound_A[15] & ~sound_A[14] & ~sound_A[13] & ~n_rd;
// AY bus control signals
// AY1: 0x6000/0x6001 write (address_data_w), 0x6002 read (data_r)
wire ay1_bdir = cs_ay1_w & (sound_A[1:0] != 2'b10); // write to 0x6000 or 0x6001
wire ay1_bc1 = (cs_ay1_w & ~sound_A[0] & ~sound_A[1]) | // address select: write to 0x6000
(cs_ay1_r & sound_A[1]); // data read: read from 0x6002
// AY2: 0x8000/0x8001 write (address_data_w), 0x8002 read (data_r)
wire ay2_bdir = cs_ay2_w & (sound_A[1:0] != 2'b10);
wire ay2_bc1 = (cs_ay2_w & ~sound_A[0] & ~sound_A[1]) |
(cs_ay2_r & sound_A[1]);
// Data reading flags for mux
wire ay1_reading = ~ay1_bdir & ay1_bc1;
wire ay2_reading = ~ay2_bdir & ay2_bc1;
// Multiplex data input to Z80
wire [7:0] sound_Din = cs_rom1 ? rom1_D :
cs_rom2 ? rom2_D :
(cs_ram & n_wr) ? sndram_D :
ay1_reading ? ay1_D :
ay2_reading ? ay2_D :
8'hFF;
//-------------------------------------------------------------- ROMs ----------------------------------------------------------//
// Sound ROM 1 (0x0000-0x0FFF, mirrored at 0x1000-0x1FFF)
wire [7:0] rom1_D;
eprom_4k snd_rom1
(
.CLK(clk_49m),
.ADDR(sound_A[11:0]),
.CLK_DL(clk_49m),
.ADDR_DL(ioctl_addr),
.DATA_IN(ioctl_data),
.CS_DL(snd_rom1_cs_i),
.WR(ioctl_wr),
.DATA(rom1_D)
);
// Sound ROM 2 (0x2000-0x2FFF, mirrored at 0x3000-0x3FFF)
wire [7:0] rom2_D;
eprom_4k snd_rom2
(
.CLK(clk_49m),
.ADDR(sound_A[11:0]),
.CLK_DL(clk_49m),
.ADDR_DL(ioctl_addr),
.DATA_IN(ioctl_data),
.CS_DL(snd_rom2_cs_i),
.WR(ioctl_wr),
.DATA(rom2_D)
);
//-------------------------------------------------------------- RAM -----------------------------------------------------------//
// Sound RAM (lower 4 bits) - 1KB at 0x4000-0x43FF
wire [7:0] sndram_D;
spram #(4, 10) A2
(
.clk(clk_49m),
.we(cs_ram & ~n_wr),
.addr(sound_A[9:0]),
.data(sound_Dout[3:0]),
.q(sndram_D[3:0])
);
// Sound RAM (upper 4 bits)
spram #(4, 10) A3
(
.clk(clk_49m),
.we(cs_ram & ~n_wr),
.addr(sound_A[9:0]),
.data(sound_Dout[7:4]),
.q(sndram_D[7:4])
);
//--------------------------------------------------------- Interrupts ---------------------------------------------------------//
// IRQ generation - 240 Hz periodic (4x per frame at 60 Hz)
// 1,250,000 Hz / 240 Hz = 5208.3 cycles
reg [12:0] irq_cnt = 13'd0;
reg irq_pulse = 1'b0;
always_ff @(posedge clk_49m) begin
if (cen_1m25) begin
if (irq_cnt == 13'd5207) begin
irq_cnt <= 13'd0;
irq_pulse <= 1'b1;
end else begin
irq_cnt <= irq_cnt + 13'd1;
irq_pulse <= 1'b0;
end
end else
irq_pulse <= 1'b0;
end
// IRQ latch - cleared by interrupt acknowledge
wire irq_clr = (~reset | ~(n_iorq | n_m1));
reg n_irq = 1'b1;
always_ff @(posedge clk_49m or posedge irq_clr) begin
if (irq_clr)
n_irq <= 1'b1;
else if (irq_pulse)
n_irq <= 1'b0;
end
// NMI generation - pulse triggered by main CPU writing to 0xD000
reg n_nmi = 1'b1;
reg sound_cmd_wr_last = 1'b0;
always_ff @(posedge clk_49m) begin
sound_cmd_wr_last <= sound_cmd_wr;
if (!sound_cmd_wr_last && sound_cmd_wr)
n_nmi <= 1'b0; // Assert NMI on rising edge of write
else if (cen_1m25)
n_nmi <= 1'b1; // Release after one CPU cycle
end
//--------------------------------------------------------- Sound chips --------------------------------------------------------//
// Sound chip 1 (AY-3-8910 @ 1.25 MHz - uses JT49 by Jotego)
// Port A = output: DIP switch data written by sound CPU → main CPU reads at 0xC003
// Port B = input: sound command latch from main CPU
wire [7:0] ay1_D;
wire [7:0] ay1A_raw, ay1B_raw, ay1C_raw;
jt49_bus #(.COMP(3'b100)) ay1_chip
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_1m25 & ~pause),
.bdir(ay1_bdir),
.bc1(ay1_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay1_D),
.A(ay1A_raw),
.B(ay1B_raw),
.C(ay1C_raw),
.IOA_out(dipsw_readback),
.IOB_in(sound_cmd)
);
// Sound chip 2 (AY-3-8910 @ 0.625 MHz - HALF RATE - uses JT49 by Jotego)
// Port A = input: DIP switch bank 1
// Port B = input: DIP switch bank 2
wire [7:0] ay2_D;
wire [7:0] ay2A_raw, ay2B_raw, ay2C_raw;
jt49_bus #(.COMP(3'b100)) ay2_chip
(
.rst_n(reset),
.clk(clk_49m),
.clk_en(cen_0m625 & ~pause),
.bdir(ay2_bdir),
.bc1(ay2_bc1),
.din(sound_Dout),
.sel(1),
.dout(ay2_D),
.A(ay2A_raw),
.B(ay2B_raw),
.C(ay2C_raw),
.IOA_in(dip_sw[7:0]),
.IOB_in(dip_sw[15:8])
);
//----------------------------------------------------- Final audio output -----------------------------------------------------//
// Apply gain and remove DC offset from AY-3-8910s (uses jt49_dcrm2 from JT49 by Jotego)
wire signed [15:0] ay1A_dcrm, ay1B_dcrm, ay1C_dcrm, ay2A_dcrm, ay2B_dcrm, ay2C_dcrm;
jt49_dcrm2 #(16) dcrm_ay1A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay1A_raw, 5'd0}),
.dout(ay1A_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay1B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay1B_raw, 5'd0}),
.dout(ay1B_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay1C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay1C_raw, 5'd0}),
.dout(ay1C_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2A
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay2A_raw, 5'd0}),
.dout(ay2A_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2B
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay2B_raw, 5'd0}),
.dout(ay2B_dcrm)
);
jt49_dcrm2 #(16) dcrm_ay2C
(
.clk(clk_49m),
.cen(cen_dcrm),
.rst(~reset),
.din({3'd0, ay2C_raw, 5'd0}),
.dout(ay2C_dcrm)
);
// Mix all AY-3-8910 channels (no per-channel filters for Blue Print)
// Invert phase as the original PCB uses an inverting amplifier prior to the power amp
assign sound = 16'hFFFF - (ay1A_dcrm + ay1B_dcrm + ay1C_dcrm + ay2A_dcrm + ay2B_dcrm + ay2C_dcrm);
endmodule

View File

@@ -1,170 +0,0 @@
//============================================================================
//
// Port to MiSTer.
// Copyright (C) 2026 Rodimus
//
// 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 BluePrint_Top
(
input reset,
input clk_49m,
// Player controls (active HIGH, assembled from MiSTer inputs)
input [7:0] p1_controls,
input [7:0] p2_controls,
// DIP switches (directly from MiSTer OSD)
input [15:0] dip_sw,
// Video outputs
output video_hsync, video_vsync, video_csync,
output video_hblank, video_vblank,
output ce_pix,
output [4:0] video_r, video_g, video_b,
// Audio output
output signed [15:0] sound,
// Screen centering
input [3:0] h_center, v_center,
// ROM loading
input [24:0] ioctl_addr,
input [7:0] ioctl_data,
input ioctl_wr,
input [7:0] ioctl_index,
input pause,
// Hiscore interface
input [15:0] hs_address,
input [7:0] hs_data_in,
output [7:0] hs_data_out,
input hs_write
);
// Sound interface between CPU and sound board
wire [7:0] sound_cmd;
wire sound_cmd_wr;
// TEMPORARY: bypass sound board DIP readback until sound is verified working.
// The sound CPU reads dip_sw[7:0] from AY2 Port A and writes it to AY1 Port A
// which main CPU reads at 0xC003. Short-circuit this path for testing.
wire [7:0] dipsw_readback_from_snd;
wire [7:0] dipsw_readback = dipsw_readback_from_snd;
// ROM loader signals for MISTer (loads ROMs from SD card)
wire main1_cs_i, main2_cs_i, main3_cs_i, main4_cs_i, main5_cs_i;
wire tile0_cs_i, tile1_cs_i;
wire spr_r_cs_i, spr_b_cs_i, spr_g_cs_i;
// Filter ioctl_wr for index 0 (CPU board ROMs) and index 1 (sound ROMs)
wire ioctl_wr_cpu = ioctl_wr && (ioctl_index == 8'd0);
wire ioctl_wr_snd = ioctl_wr && (ioctl_index == 8'd1);
// Sound ROM chip selects (within index 1's address space)
wire snd_rom1_cs_i = (ioctl_addr < 25'h1000);
wire snd_rom2_cs_i = (ioctl_addr >= 25'h1000) && (ioctl_addr < 25'h2000);
// MiSTer data write selector (active for ROM index 0 only)
selector DLSEL
(
.ioctl_addr(ioctl_addr),
.main1_cs(main1_cs_i),
.main2_cs(main2_cs_i),
.main3_cs(main3_cs_i),
.main4_cs(main4_cs_i),
.main5_cs(main5_cs_i),
.tile0_cs(tile0_cs_i),
.tile1_cs(tile1_cs_i),
.spr_r_cs(spr_r_cs_i),
.spr_b_cs(spr_b_cs_i),
.spr_g_cs(spr_g_cs_i)
);
// Instantiate main CPU board
BluePrint_CPU main_pcb
(
.reset(reset),
.clk_49m(clk_49m),
.red(video_r),
.green(video_g),
.blue(video_b),
.video_hsync(video_hsync),
.video_vsync(video_vsync),
.video_csync(video_csync),
.video_hblank(video_hblank),
.video_vblank(video_vblank),
.ce_pix(ce_pix),
.p1_controls(p1_controls),
.p2_controls(p2_controls),
.dipsw_readback(dipsw_readback),
.sound_cmd(sound_cmd),
.sound_cmd_wr(sound_cmd_wr),
.h_center(h_center),
.v_center(v_center),
.main1_cs_i(main1_cs_i),
.main2_cs_i(main2_cs_i),
.main3_cs_i(main3_cs_i),
.main4_cs_i(main4_cs_i),
.main5_cs_i(main5_cs_i),
.tile0_cs_i(tile0_cs_i),
.tile1_cs_i(tile1_cs_i),
.spr_r_cs_i(spr_r_cs_i),
.spr_b_cs_i(spr_b_cs_i),
.spr_g_cs_i(spr_g_cs_i),
.ioctl_addr(ioctl_addr),
.ioctl_data(ioctl_data),
.ioctl_wr(ioctl_wr_cpu),
.pause(pause),
.hs_address(hs_address),
.hs_data_out(hs_data_out),
.hs_data_in(hs_data_in),
.hs_write(hs_write)
);
// Instantiate sound PCB
BluePrint_SND sound_pcb
(
.reset(reset),
.pause(pause),
.clk_49m(clk_49m),
.sound_cmd(sound_cmd),
.sound_cmd_wr(sound_cmd_wr),
.dip_sw(dip_sw),
.dipsw_readback(dipsw_readback_from_snd),
.sound(sound),
.vblank(video_vblank),
.snd_rom1_cs_i(snd_rom1_cs_i),
.snd_rom2_cs_i(snd_rom2_cs_i),
.ioctl_addr(ioctl_addr),
.ioctl_data(ioctl_data),
.ioctl_wr(ioctl_wr_snd)
);
endmodule