Files
Vector-06C_MiSTer/Vector-06C.sv
jackyangantelope 8666c6b0f7 Update sys
2025-12-09 23:14:54 +08:00

1028 lines
25 KiB
Systemverilog

// ====================================================================
// Vector 06C FPGA REPLICA
//
// Copyright (C) 2016-2019 Sorgelig
//
// This core is distributed under modified BSD license.
// For complete licensing information see LICENSE.TXT.
// --------------------------------------------------------------------
//
// An open implementation of Vector 06C home computer
//
// Based on code from Dmitry Tselikov and Viacheslav Slavinsky
//
module emu
(
//Master input clock
input CLK_50M,
//Async reset from top-level module.
//Can be used as initial reset.
input RESET,
//Must be passed to hps_io module
inout [48:0] HPS_BUS,
//Base video clock. Usually equals to CLK_SYS.
output CLK_VIDEO,
//Multiple resolutions are supported using different CE_PIXEL rates.
//Must be based on CLK_VIDEO
output CE_PIXEL,
//Video aspect ratio for HDMI. Most retro systems have ratio 4:3.
//if VIDEO_ARX[12] or VIDEO_ARY[12] is set then [11:0] contains scaled size instead of aspect ratio.
output [12:0] VIDEO_ARX,
output [12:0] VIDEO_ARY,
output [7:0] VGA_R,
output [7:0] VGA_G,
output [7:0] VGA_B,
output VGA_HS,
output VGA_VS,
output VGA_DE, // = ~(VBlank | HBlank)
output VGA_F1,
output [1:0] VGA_SL,
output VGA_SCALER, // Force VGA scaler
output VGA_DISABLE, // analog out is off
input [11:0] HDMI_WIDTH,
input [11:0] HDMI_HEIGHT,
output HDMI_FREEZE,
output HDMI_BLACKOUT,
output HDMI_BOB_DEINT,
`ifdef MISTER_FB
// Use framebuffer in DDRAM
// FB_FORMAT:
// [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp
// [3] : 0=16bits 565 1=16bits 1555
// [4] : 0=RGB 1=BGR (for 16/24/32 modes)
//
// FB_STRIDE either 0 (rounded to 256 bytes) or multiple of pixel size (in bytes)
output FB_EN,
output [4:0] FB_FORMAT,
output [11:0] FB_WIDTH,
output [11:0] FB_HEIGHT,
output [31:0] FB_BASE,
output [13:0] FB_STRIDE,
input FB_VBL,
input FB_LL,
output FB_FORCE_BLANK,
`ifdef MISTER_FB_PALETTE
// Palette control for 8bit modes.
// Ignored for other video modes.
output FB_PAL_CLK,
output [7:0] FB_PAL_ADDR,
output [23:0] FB_PAL_DOUT,
input [23:0] FB_PAL_DIN,
output FB_PAL_WR,
`endif
`endif
output LED_USER, // 1 - ON, 0 - OFF.
// b[1]: 0 - LED status is system status OR'd with b[0]
// 1 - LED status is controled solely by b[0]
// hint: supply 2'b00 to let the system control the LED.
output [1:0] LED_POWER,
output [1:0] LED_DISK,
// I/O board button press simulation (active high)
// b[1]: user button
// b[0]: osd button
output [1:0] BUTTONS,
input CLK_AUDIO, // 24.576 MHz
output [15:0] AUDIO_L,
output [15:0] AUDIO_R,
output AUDIO_S, // 1 - signed audio samples, 0 - unsigned
output [1:0] AUDIO_MIX, // 0 - no mix, 1 - 25%, 2 - 50%, 3 - 100% (mono)
//ADC
inout [3:0] ADC_BUS,
//SD-SPI
output SD_SCK,
output SD_MOSI,
input SD_MISO,
output SD_CS,
input SD_CD,
//High latency DDR3 RAM interface
//Use for non-critical time purposes
output DDRAM_CLK,
input DDRAM_BUSY,
output [7:0] DDRAM_BURSTCNT,
output [28:0] DDRAM_ADDR,
input [63:0] DDRAM_DOUT,
input DDRAM_DOUT_READY,
output DDRAM_RD,
output [63:0] DDRAM_DIN,
output [7:0] DDRAM_BE,
output DDRAM_WE,
//SDRAM interface with lower latency
output SDRAM_CLK,
output SDRAM_CKE,
output [12:0] SDRAM_A,
output [1:0] SDRAM_BA,
inout [15:0] SDRAM_DQ,
output SDRAM_DQML,
output SDRAM_DQMH,
output SDRAM_nCS,
output SDRAM_nCAS,
output SDRAM_nRAS,
output SDRAM_nWE,
`ifdef MISTER_DUAL_SDRAM
//Secondary SDRAM
//Set all output SDRAM_* signals to Z ASAP if SDRAM2_EN is 0
input SDRAM2_EN,
output SDRAM2_CLK,
output [12:0] SDRAM2_A,
output [1:0] SDRAM2_BA,
inout [15:0] SDRAM2_DQ,
output SDRAM2_nCS,
output SDRAM2_nCAS,
output SDRAM2_nRAS,
output SDRAM2_nWE,
`endif
input UART_CTS,
output UART_RTS,
input UART_RXD,
output UART_TXD,
output UART_DTR,
input UART_DSR,
// Open-drain User port.
// 0 - D+/RX
// 1 - D-/TX
// 2..6 - USR2..USR6
// Set USER_OUT to 1 to read from USER_IN.
input [6:0] USER_IN,
output [6:0] USER_OUT,
input OSD_STATUS
);
assign ADC_BUS = 'Z;
assign USER_OUT = '1;
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
assign {SDRAM_DQ, SDRAM_A, SDRAM_BA, SDRAM_CLK, SDRAM_CKE, SDRAM_DQML, SDRAM_DQMH, SDRAM_nWE, SDRAM_nCAS, SDRAM_nRAS, SDRAM_nCS} = 'Z;
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0;
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
assign AUDIO_S = 0;
assign AUDIO_MIX = status[10:9];
assign LED_USER = ioctl_download | erasing;
assign LED_DISK = 0;
assign LED_POWER = 0;
assign BUTTONS = 0;
assign VGA_SCALER= 0;
assign VGA_DISABLE = 0;
assign HDMI_FREEZE = 0;
assign HDMI_BLACKOUT = 0;
assign HDMI_BOB_DEINT = 0;
wire [1:0] ar = status[12:11];
wire vcrop_en = status[15];
reg en270p;
always @(posedge CLK_VIDEO) begin
en270p <= ((HDMI_WIDTH == 1920) && (HDMI_HEIGHT == 1080) && !forced_scandoubler && !scale);
end
wire vga_de;
video_freak video_freak
(
.*,
.VGA_DE_IN(vga_de),
.ARX((!ar) ? 12'd4 : (ar - 1'd1)),
.ARY((!ar) ? 12'd3 : 12'd0),
.CROP_SIZE((en270p & vcrop_en) ? 10'd270 : 10'd0),
.CROP_OFF(0),
.SCALE(status[14:13])
);
assign CLK_VIDEO = clk_sys;
`include "build_id.v"
localparam CONF_STR =
{
"VECTOR06;;",
"-;",
"F,ROMCOMC00EDD;",
"-;",
"S0,FDD,Mount Disk A;",
"S1,FDD,Mount Disk B;",
"-;",
"OBC,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
"O12,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;",
"O7,Reset Palette,Yes,No;",
"-;",
"d0OF,Vertical Crop,Disabled,270p(5x);",
"ODE,Scale,Normal,V-Integer,Narrower HV-Integer,Wider HV-Integer;",
"-;",
"O9A,Stereo mix,none,25%,50%,100%;",
"-;",
"O4,CPU Speed,3MHz,6MHz;",
"O5,CPU Type,i8080,Z80;",
"-;",
"R6,Cold Reboot;",
"J,Fire 1,Fire 2;",
"V,v",`BUILD_DATE
};
/////////////// HPS I/O /////////////////
wire forced_scandoubler;
wire [10:0] ps2_key;
wire [21:0] gamma_bus;
wire [31:0] status;
wire [1:0] buttons;
wire [15:0] joyA;
wire [15:0] joyB;
wire ioctl_wr;
wire [24:0] ioctl_addr;
wire [24:0] ioctl_addr_orig;
wire [7:0] ioctl_data;
wire ioctl_download;
wire [7:0] ioctl_index;
wire [31:0] sd_lba[2];
wire [1:0] sd_rd;
wire [1:0] sd_wr;
wire [1:0] sd_ack;
wire [8:0] sd_buff_addr;
wire [7:0] sd_buff_dout;
wire [7:0] sd_buff_din[2];
wire sd_buff_wr;
wire [1:0] img_mounted;
wire [63:0] img_size;
wire img_readonly;
hps_io #(.CONF_STR(CONF_STR), .VDNUM(2)) hps_io
(
.clk_sys(clk_sys),
.HPS_BUS(HPS_BUS),
.ps2_key(ps2_key),
.joystick_0(joyA),
.joystick_1(joyB),
.gamma_bus(gamma_bus),
.buttons(buttons),
.forced_scandoubler(forced_scandoubler),
.status(status),
.status_menumask({en270p}),
.sd_lba(sd_lba),
.sd_rd(sd_rd),
.sd_wr(sd_wr),
.sd_ack(sd_ack),
.sd_buff_addr(sd_buff_addr),
.sd_buff_dout(sd_buff_dout),
.sd_buff_din(sd_buff_din),
.sd_buff_wr(sd_buff_wr),
.img_mounted(img_mounted),
.img_size(img_size),
.img_readonly(img_readonly),
.ioctl_wr(ioctl_wr),
.ioctl_addr(ioctl_addr_orig),
.ioctl_dout(ioctl_data),
.ioctl_download(ioctl_download),
.ioctl_index(ioctl_index)
);
//////////////////// CLOCKS ///////////////////
wire locked;
pll pll
(
.refclk(CLK_50M),
.rst(0),
.outclk_0(clk_sys),
.locked(locked)
);
wire clk_sys; // 96MHz
reg ce_f1, ce_f2; // 3MHz/6MHz
reg ce_12mp;
reg ce_12mn;
reg ce_psg; // 1.75MHz
reg clk_pit; // 1.5MHz
always @(negedge clk_sys) begin
reg [6:0] div = 0;
reg [5:0] psg_div = 0;
reg turbo;
div <= div + 1'd1;
if(&div) turbo <= status[4];
if(turbo) begin
ce_f1 <= !div[3] & !div[2:0];
ce_f2 <= div[3] & !div[2:0];
cpu_ready <= 1;
end else begin
ce_f1 <= !div[4] & !div[3:0];
ce_f2 <= div[4] & !div[3:0];
if(div[6:4]==3'b100) cpu_ready <= 1;
else if(!div[4:2] & cpu_sync & mreq) cpu_ready <= 0;
end
ce_12mp <= !div[2] & !div[1:0];
ce_12mn <= div[2] & !div[1:0];
psg_div <= psg_div + 1'b1;
if(psg_div == 54) psg_div <= 0;
ce_psg <= !psg_div;
clk_pit <= div[5];
end
//////////////////// RESET ////////////////////
reg cold_reset = 0;
reg reset;
reg rom_enable;
always @(posedge clk_sys) begin
reg reset_flg = 1;
int reset_hold = 0;
reg old_rst = 0;
// initial reset
old_rst <= status[0];
if(old_rst & ~status[0]) begin
reset_flg <= 1;
rom_enable <= 1;
cold_reset <= 1;
end
if(erasing | ioctl_download) begin
reset_flg <= 1;
reset <= 1;
if(ioctl_download) rom_enable <= ~((ioctl_index[4:0]==1) && (ioctl_index[7:6]<3));
end else begin
if(reset_flg) begin
reset_flg <= 0;
cpu_type <= status[5];
reset <= 1;
reset_hold <= 10000;
end else if(reset_hold) reset_hold <= reset_hold - 1;
else {cold_reset,reset} <= 0;
// reset by button or key
if(status[6] | buttons[1] | reset_key[0] | (fdd_busy & rom_enable)) begin
rom_enable <= ~reset_key[2]; // disable boot rom if Alt is held.
reset_flg <= 1;
cold_reset <= status[6];
end
end
if(cpu_type != status[5]) reset_flg <= 1;
end
//////////////////// CPU ////////////////////
wire [15:0] addr = cpu_type ? addr_z80 : addr_i80;
reg [7:0] cpu_i;
wire [7:0] cpu_o = cpu_type ? cpu_o_z80 : cpu_o_i80;
wire cpu_sync = cpu_type ? cpu_sync_z80 : cpu_sync_i80;
wire cpu_rd = cpu_type ? cpu_rd_z80 : cpu_rd_i80;
wire cpu_wr_n = cpu_type ? cpu_wr_n_z80 : cpu_wr_n_i80;
reg cpu_ready;
reg cpu_type = 0;
reg [7:0] status_word;
always @(posedge clk_sys) begin
reg old_sync;
old_sync <= cpu_sync;
if(~old_sync & cpu_sync) status_word <= cpu_o;
end
wire int_ack = status_word[0];
wire write_n = status_word[1];
wire io_stack = status_word[2];
//wire halt_ack = status_word[3];
wire io_write = status_word[4];
//wire m1 = status_word[5];
wire io_read = status_word[6];
wire ram_read = status_word[7];
wire mreq = (ram_read | ~write_n) & ~io_write & ~io_read;
reg ppi1_sel, joy_sel, vox_sel, pit_sel, pal_sel, psg_sel, edsk_sel, fdd_sel;
reg [7:0] io_data;
always_comb begin
ppi1_sel =0;
joy_sel =0;
vox_sel =0;
pit_sel =0;
pal_sel =0;
edsk_sel =0;
psg_sel =0;
fdd_sel =0;
io_data =255;
casex(addr[7:0])
8'b000000XX: begin ppi1_sel =1; io_data = ppi1_o; end
8'b0000010X: begin joy_sel =1; io_data = 0; end
8'b00000110: begin io_data = joyP_o; end
8'b00000111: begin vox_sel =1; io_data = joyPU_o; end
8'b000010XX: begin pit_sel =1; io_data = pit_o; end
8'b0000110X: begin pal_sel =1; end
8'b00001110: begin pal_sel =1; io_data = joyA_o; end
8'b00001111: begin pal_sel =1; io_data = joyB_o; end
8'b00010000: begin edsk_sel =1; end
8'b0001010X: begin psg_sel =1; io_data = psg_o; end
8'b000110XX: begin fdd_sel =1; io_data = fdd_o; end
8'b000111XX: begin fdd_sel =1; end
default: ;
endcase
end
always_comb begin
casex({int_ack, io_read, rom_enable && !rom_size && !ed_page && !addr[15:11]})
3'b001: cpu_i = rom_o;
3'b01X: cpu_i = io_data;
3'b1XX: cpu_i = 255;
default: cpu_i = ram_o;
endcase
end
wire io_rd = io_read & cpu_rd;
wire io_wr = io_write & ~cpu_wr_n;
wire [15:0] addr_i80;
wire [7:0] cpu_o_i80;
wire cpu_inte_i80;
wire cpu_sync_i80;
wire cpu_rd_i80;
wire cpu_wr_n_i80;
reg cpu_int_i80;
k580vm80a cpu_i80
(
.pin_clk(clk_sys),
.pin_f1(ce_f1),
.pin_f2(ce_f2),
.pin_reset(reset | cpu_type),
.pin_a(addr_i80),
.pin_dout(cpu_o_i80),
.pin_din(cpu_i),
.pin_hold(0),
.pin_ready(cpu_ready),
.pin_int(cpu_int_i80),
.pin_inte(cpu_inte_i80),
.pin_sync(cpu_sync_i80),
.pin_dbin(cpu_rd_i80),
.pin_wr_n(cpu_wr_n_i80)
);
wire [15:0] addr_z80;
wire [7:0] cpu_o_z80;
wire cpu_inte_z80;
wire cpu_sync_z80;
wire cpu_rd_z80;
wire cpu_wr_n_z80;
reg cpu_int_z80;
T8080se cpu_z80
(
.CLK(clk_sys),
.CLKEN(ce_f1),
.RESET_n(~reset & cpu_type),
.A(addr_z80),
.DO(cpu_o_z80),
.DI(cpu_i),
.HOLD(0),
.READY(cpu_ready),
.INT(cpu_int_z80),
.INTE(cpu_inte_z80),
.SYNC(cpu_sync_z80),
.DBIN(cpu_rd_z80),
.WR_n(cpu_wr_n_z80)
);
//////////////////// MEM ////////////////////
wire [7:0] ram_o;
wire [18:0] ram_addr = ioctl_download ? ioctl_addr[18:0] : erasing ? erase_addr[18:0] : {read_rom ? 3'h5 : ed_page, addr};
dpram #(8, 19, 393216, 32, 17, 98304) ram
(
.clock(clk_sys),
.address_a({ram_addr[18:15], ram_addr[12:0], ram_addr[14:13]}),
.data_a(ioctl_download ? ioctl_data : erasing ? 8'd0 : cpu_o),
.wren_a(ioctl_download ? ioctl_wr : erasing ? erase_wr : ~cpu_wr_n & ~io_write),
.q_a(ram_o),
.address_b({1'b1, vaddr}),
.data_b(0),
.wren_b(0),
.q_b(vdata)
);
reg [15:0] rom_size = 0;
wire read_rom = rom_enable && (addr<rom_size) && !ed_page && ram_read;
always @(posedge clk_sys) begin
reg old_download;
old_download <= ioctl_download;
if(~ioctl_download & old_download & !ioctl_index) rom_size <= ioctl_addr[15:0];
end
wire [7:0] rom_o;
bios rom(.address(addr[10:0]), .clock(clk_sys), .q(rom_o));
///////////////// E-DISK 256KB ///////////////////
reg [2:0] ed_page;
reg [7:0] ed_reg;
wire edsk_we = io_wr & edsk_sel;
always @(posedge clk_sys) begin
reg old_we;
old_we <= edsk_we;
if(reset) ed_reg <= 0;
else if(~old_we & edsk_we) ed_reg <= cpu_o;
end
wire ed_win = addr[15] & ((addr[13] ^ addr[14]) | (ed_reg[7] & addr[13] & addr[14]) | (ed_reg[6] & ~addr[13] & ~addr[14]));
wire ed_ram = ed_reg[5] & ed_win & (ram_read | ~write_n);
wire ed_stack = ed_reg[4] & io_stack & (ram_read | ~write_n);
always_comb begin
casex({ed_stack, ed_ram, ed_reg[3:0]})
6'b1X00XX, 6'b01XX00: ed_page = 1;
6'b1X01XX, 6'b01XX01: ed_page = 2;
6'b1X10XX, 6'b01XX10: ed_page = 3;
6'b1X11XX, 6'b01XX11: ed_page = 4;
default: ed_page = 0;
endcase
end
///////////////////// FDD /////////////////////
reg fdd_drive;
reg fdd_side;
wire fdd_busy = fdd_drive ? fdd2_busy : fdd1_busy;
wire fdd_ready = fdd_drive ? fdd2_ready : fdd1_ready;
wire [7:0] fdd_o = fdd_ready ? (fdd_drive ? fdd2_o : fdd1_o) : 8'd0;
always @(posedge clk_sys) begin
reg old_wr;
old_wr <= io_wr;
if(~old_wr & io_wr & fdd_sel & addr[2]) {fdd_side, fdd_drive} <= {~cpu_o[2], cpu_o[0]};
if(cold_reset) fdd_drive <= 0;
end
//FDD1
wire [7:0] fdd1_o;
reg fdd1_ready;
wire fdd1_busy;
always @(posedge clk_sys) begin
reg old_mounted;
old_mounted <= img_mounted[0];
if(cold_reset) fdd1_ready <= 0;
else if(~old_mounted & img_mounted[0]) fdd1_ready <= |img_size;
end
wd1793 #(1) fdd1
(
.clk_sys(clk_sys),
.ce(ce_f1),
.reset(reset),
.io_en(fdd_sel & fdd1_ready & ~fdd_drive & ~addr[2]),
.rd(io_rd),
.wr(io_wr),
.addr(~addr[1:0]),
.din(cpu_o),
.dout(fdd1_o),
.img_mounted(img_mounted[0]),
.img_size(img_size[19:0]),
.sd_lba(sd_lba[0]),
.sd_rd(sd_rd[0]),
.sd_wr(sd_wr[0]),
.sd_ack(sd_ack[0]),
.sd_buff_addr(sd_buff_addr),
.sd_buff_dout(sd_buff_dout),
.sd_buff_din(sd_buff_din[0]),
.sd_buff_wr(sd_buff_wr),
.wp(0),
.size_code(3),
.layout(0),
.side(fdd_side),
.ready(~fdd_drive & fdd1_ready),
.prepare(fdd1_busy),
.input_active(0),
.input_addr(0),
.input_data(0),
.input_wr(0),
.buff_din(0)
);
//FDD2
wire [7:0] fdd2_o;
reg fdd2_ready;
wire fdd2_busy;
always @(posedge clk_sys) begin
reg old_mounted;
old_mounted <= img_mounted[1];
if(cold_reset) fdd2_ready <= 0;
else if(~old_mounted & img_mounted[1]) fdd2_ready <= |img_size;
end
wd1793 #(1) fdd2
(
.clk_sys(clk_sys),
.ce(ce_f1),
.reset(reset),
.io_en(fdd_sel & fdd2_ready & fdd_drive & ~addr[2]),
.rd(io_rd),
.wr(io_wr),
.addr(~addr[1:0]),
.din(cpu_o),
.dout(fdd2_o),
.img_mounted(img_mounted[1]),
.img_size(img_size[19:0]),
.sd_lba(sd_lba[1]),
.sd_rd(sd_rd[1]),
.sd_wr(sd_wr[1]),
.sd_ack(sd_ack[1]),
.sd_buff_addr(sd_buff_addr),
.sd_buff_dout(sd_buff_dout),
.sd_buff_din(sd_buff_din[1]),
.sd_buff_wr(sd_buff_wr),
.wp(0),
.size_code(3),
.layout(0),
.side(fdd_side),
.ready(fdd_drive & fdd2_ready),
.prepare(fdd2_busy),
.input_active(0),
.input_addr(0),
.input_data(0),
.input_wr(0),
.buff_din(0)
);
//////////////////// VIDEO ////////////////////
wire retrace;
wire [12:0] vaddr;
wire [31:0] vdata;
wire [1:0] scale = status[2:1];
video video
(
.*,
.reset(reset & ~status[7]),
.scroll(ppi1_a),
.din(cpu_o),
.io_we(pal_sel & io_wr),
.border(ppi1_b[3:0]),
.mode512(ppi1_b[4]),
.hq2x(scale == 1),
.scandoubler(scale || forced_scandoubler),
.VGA_DE(vga_de)
);
assign VGA_SL = scale ? scale - 1'd1 : 2'd0;
assign VGA_F1 = 0;
always @(posedge clk_sys) begin
reg old_retrace;
int z80_delay;
old_retrace <= retrace;
if(!cpu_inte_i80) cpu_int_i80 <= 0;
else if(~old_retrace & retrace) cpu_int_i80 <= 1;
if(!cpu_inte_z80) {z80_delay,cpu_int_z80} <= 0;
else begin
if(~old_retrace & retrace) z80_delay <= 1;
if(ce_12mp && z80_delay) z80_delay <= z80_delay + 1;
if(z80_delay == 700) begin
z80_delay <= 0;
cpu_int_z80 <= 1;
end
end
end
///////////////////// KBD /////////////////////
wire [7:0] kbd_o;
wire [2:0] kbd_shift;
wire [2:0] reset_key;
keyboard kbd
(
.clk(clk_sys),
.reset(cold_reset),
.ps2_key(ps2_key),
.addr(~ppi1_a),
.odata(kbd_o),
.shift(kbd_shift),
.reset_key(reset_key)
);
///////////////// PPI1 (SYS) //////////////////
wire [7:0] ppi1_o;
wire [7:0] ppi1_a;
wire [7:0] ppi1_b;
wire [7:0] ppi1_c;
k580vv55 ppi1
(
.clk_sys(clk_sys),
.reset(0),
.addr(~addr[1:0]),
.we_n(~(io_wr & ppi1_sel)),
.idata(cpu_o),
.odata(ppi1_o),
.opa(ppi1_a),
.ipb(~kbd_o),
.opb(ppi1_b),
.ipc({~kbd_shift,tapein,4'b1111}),
.opc(ppi1_c)
);
///////////////// Joystick Zoo /////////////////
wire [7:0] joyPU = joyA[7:0] | joyB[7:0];
wire [7:0] joyPU_o = {joyPU[3], joyPU[0], joyPU[2], joyPU[1], joyPU[4], joyPU[5], 2'b00};
wire [7:0] joyA_o = ~{joyA[5], joyA[4], 2'b00, joyA[2], joyA[3], joyA[1], joyA[0]};
wire [7:0] joyB_o = ~{joyB[5], joyB[4], 2'b00, joyB[2], joyB[3], joyB[1], joyB[0]};
reg [7:0] joy_port;
wire joy_we = io_wr & joy_sel;
always @(posedge clk_sys) begin
reg old_we;
old_we <= joy_we;
if(reset) joy_port <= 0;
else if(~old_we & joy_we) begin
if(addr[0]) joy_port <= cpu_o;
else if(!cpu_o[7]) joy_port[cpu_o[3:1]] <= cpu_o[0];
end
end
reg [7:0] joyP_o;
always_comb begin
case(joy_port[6:5])
2'b00: joyP_o = joyA_o & joyB_o;
2'b01: joyP_o = joyA_o;
2'b10: joyP_o = joyB_o;
2'b11: joyP_o = 255;
endcase
end
//////////////////// SOUND ////////////////////
wire tapein = 0;
wire [7:0] pit_o;
wire [2:0] pit_out;
wire [2:0] pit_active;
wire [2:0] pit_snd = pit_out & pit_active;
k580vi53 pit
(
.reset(reset),
.clk_sys(clk_sys),
.clk_timer({clk_pit,clk_pit,clk_pit}),
.addr(~addr[1:0]),
.wr(io_wr & pit_sel),
.rd(io_rd & pit_sel),
.din(cpu_o),
.dout(pit_o),
.gate(3'b111),
.out(pit_out),
.sound_active(pit_active)
);
wire [1:0] legacy_audio = 2'd0 + ppi1_c[0] + pit_snd[0] + pit_snd[1] + pit_snd[2];
wire [7:0] psg_o;
wire [7:0] psg_ch_a;
wire [7:0] psg_ch_b;
wire [7:0] psg_ch_c;
wire [5:0] psg_active;
ym2149 ym2149
(
.CLK(clk_sys),
.CE(ce_psg),
.RESET(reset),
.BDIR(io_wr & psg_sel),
.BC(addr[0]),
.DI(cpu_o),
.DO(psg_o),
.CHANNEL_A(psg_ch_a),
.CHANNEL_B(psg_ch_b),
.CHANNEL_C(psg_ch_c),
.ACTIVE(psg_active),
.SEL(0),
.MODE(0)
);
reg [7:0] covox;
integer covox_timeout;
wire vox_we = io_wr & vox_sel & !covox_timeout;
always @(posedge clk_sys) begin
reg old_we;
if(reset | rom_enable) covox_timeout <= 200000000;
else if(covox_timeout) covox_timeout <= covox_timeout - 1;
old_we <= vox_we;
if(reset) covox <= 0;
else if(~old_we & vox_we) covox <= cpu_o;
end
assign AUDIO_L = {psg_active ? {1'b0, psg_ch_a, 1'b0} + {2'b00, psg_ch_b} + {1'b0, legacy_audio, 7'd0} : {1'b0, legacy_audio, 8'd0} + {1'b0, covox, 1'b0}, 5'd0};
assign AUDIO_R = {psg_active ? {1'b0, psg_ch_c, 1'b0} + {2'b00, psg_ch_b} + {1'b0, legacy_audio, 7'd0} : {1'b0, legacy_audio, 8'd0} + {1'b0, covox, 1'b0}, 5'd0};
/////////////////////////////////////////////////
wire force_erase = cold_reset;
reg erasing = 0;
reg erase_wr;
reg [24:0] erase_addr;
always_comb begin
case(ioctl_index)
0: ioctl_addr = 25'h050000 + ioctl_addr_orig; // BOOT ROM
'h01: ioctl_addr = 25'h000100 + ioctl_addr_orig; // ROM file
'h41: ioctl_addr = 25'h000100 + ioctl_addr_orig; // COM file
'h81: ioctl_addr = 25'h000000 + ioctl_addr_orig; // C00 file
'hC1: ioctl_addr = 25'h010000 + ioctl_addr_orig; // EDD file
default: ioctl_addr = ioctl_addr_orig; // ??
endcase
end
reg [24:0] erase_mask;
wire [24:0] next_erase = (erase_addr + 1'd1) & erase_mask;
always@(posedge clk_sys) begin
reg [15:0] cmd;
reg has_cmd;
reg [24:0] addr;
reg wr;
reg old_force = 0;
reg [5:0] erase_clk_div;
reg [24:0] end_addr;
reg erase_trigger = 0;
erase_wr <= wr;
wr <= 0;
if(ioctl_download) begin
old_force <= 0;
erasing <= 0;
erase_trigger <= (ioctl_index == 1) || (ioctl_index == 'h41);
end else begin
old_force <= force_erase;
// start erasing
if(erase_trigger) begin
erase_trigger <= 0;
erase_mask <= 'hFFFF;
end_addr <= 'h0100;
erase_addr <= ioctl_addr;
erase_clk_div <= 1;
erasing <= 1;
end else if(~old_force & force_erase) begin
erase_trigger <= 0;
erase_addr <= 'h1FFFFFF;
erase_mask <= 'h1FFFFFF;
end_addr <= 'h0050000;
erase_clk_div <= 1;
erasing <= 1;
end else if(erasing) begin
erase_clk_div <= erase_clk_div + 1'd1;
if(!erase_clk_div) begin
if(next_erase == end_addr) erasing <= 0;
else begin
erase_addr <= next_erase;
wr <= 1;
end
end
end
end
end
endmodule
module dpram #(parameter DATAWIDTH_A=8, ADDRWIDTH_A=8, NUMWORDS_A=1<<ADDRWIDTH_A,
DATAWIDTH_B=8, ADDRWIDTH_B=8, NUMWORDS_B=1<<ADDRWIDTH_B,
MEM_INIT_FILE="" )
(
input clock,
input [ADDRWIDTH_A-1:0] address_a,
input [DATAWIDTH_A-1:0] data_a,
input wren_a,
output reg [DATAWIDTH_A-1:0] q_a,
input [ADDRWIDTH_B-1:0] address_b,
input [DATAWIDTH_B-1:0] data_b,
input wren_b,
output reg [DATAWIDTH_B-1:0] q_b
);
altsyncram altsyncram_component
(
.address_a (address_a),
.address_b (address_b),
.clock0 (clock),
.data_a (data_a),
.data_b (data_b),
.wren_a (wren_a),
.wren_b (wren_b),
.q_a (q_a),
.q_b (q_b),
.aclr0 (1'b0),
.aclr1 (1'b0),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.eccstatus (),
.rden_a (1'b1),
.rden_b (1'b1));
defparam
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK0",
altsyncram_component.address_reg_b = "CLOCK0",
altsyncram_component.indata_reg_b = "CLOCK0",
altsyncram_component.numwords_a = NUMWORDS_A,
altsyncram_component.numwords_b = NUMWORDS_B,
altsyncram_component.widthad_a = ADDRWIDTH_A,
altsyncram_component.widthad_b = ADDRWIDTH_B,
altsyncram_component.width_a = DATAWIDTH_A,
altsyncram_component.width_b = DATAWIDTH_B,
altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1,
altsyncram_component.init_file = MEM_INIT_FILE,
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone V",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.outdata_reg_b = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
altsyncram_component.read_during_write_mode_port_b = "NEW_DATA_NO_NBE_READ";
endmodule