// PentEvo project (c) NedoPC 2008-2010 module zports ( input clk, input [7:0] din, output reg [7:0] dout, output dataout, input [15:0] a, input rst, // system reset input opfetch, input rd, input wr, input rdwr, input iorq, input iorq_s, input iord, input iord_s, input iowr, input iowr_s, input iordwr, input iordwr_s, output porthit, // when internal port hit occurs, this is 1, else 0; used for iorq1_n iorq2_n on zxbus output external_port, // asserts for AY and VG93 accesses output zborder_wr, output border_wr, output zvpage_wr, output vpage_wr, output vconf_wr, output gx_offsl_wr, output gx_offsh_wr, output gy_offsl_wr, output gy_offsh_wr, output t0x_offsl_wr, output t0x_offsh_wr, output t0y_offsl_wr, output t0y_offsh_wr, output t1x_offsl_wr, output t1x_offsh_wr, output t1y_offsl_wr, output t1y_offsh_wr, output tsconf_wr, output palsel_wr, output tmpage_wr, output t0gpage_wr, output t1gpage_wr, output sgpage_wr, output hint_beg_wr , output vint_begl_wr, output vint_begh_wr, output [31:0] xt_page, output reg [4:0] fmaddr, input regs_we, output reg [7:0] sysconf, output reg [7:0] memconf, output reg [3:0] cacheconf, output reg [7:0] fddvirt, output [8:0] dmaport_wr, input dma_act, output reg [1:0] dmawpdev, output reg [7:0] intmask, input dos, input vdos, output vdos_on, output vdos_off, output ay_bdir, output ay_bc1, output covox_wr, output beeper_wr, input tape_read, input [4:0] keys_in, // keys (port FE) input [7:0] mus_in, // mouse (xxDF) input [5:0] kj_in, input vg_intrq, input vg_drq, // from vg93 module - drq + irq read output vg_cs_n, output vg_wrFF, output [1:0] drive_sel, // disk drive selection // SPI output sdcs_n, output sd_start, output [7:0] sd_datain, input [7:0] sd_dataout, // WAIT-ports related output reg [7:0] wait_addr, output wait_start_gluclock, // begin wait from some ports output wait_start_comport, // output reg [7:0] wait_write, input [7:0] wait_read ); assign sdcs_n = spi_cs_n[0]; localparam FDR_VER = 1'b0; localparam VDAC_VER = 3'h3; localparam PORTFE = 8'hFE; localparam PORTFD = 8'hFD; localparam PORTXT = 8'hAF; localparam PORTF7 = 8'hF7; localparam COVOX = 8'hFB; localparam VGCOM = 8'h1F; localparam VGTRK = 8'h3F; localparam VGSEC = 8'h5F; localparam VGDAT = 8'h7F; localparam VGSYS = 8'hFF; localparam KJOY = 8'h1F; localparam KMOUSE = 8'hDF; localparam SDCFG = 8'h77; localparam SDDAT = 8'h57; localparam COMPORT = 8'hEF; // F8EF..FFEF - rs232 ports wire [7:0] loa = a[7:0]; wire [7:0] hoa = regs_we ? a[7:0] : a[15:8]; assign porthit = ((loa==PORTFE) || (loa==PORTXT) || (loa==PORTFD) || (loa==COVOX)) || ((loa==PORTF7) && !dos) || ((vg_port || vgsys_port) && (dos || open_vg)) || ((loa==KJOY) && !dos && !open_vg) || (loa==KMOUSE) || (((loa==SDCFG) || (loa==SDDAT)) && (!dos || vdos)) || (loa==COMPORT); wire vg_port = (loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT); wire vgsys_port = (loa==VGSYS); assign external_port = ((loa==PORTFD) && a[15]) // AY || (((loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT)) && (dos || open_vg)); assign dataout = porthit && iord && (~external_port); reg iowr_reg; reg iord_reg; reg port_wr; reg port_rd; always @(posedge clk) begin iowr_reg <= iowr; port_wr <= (!iowr_reg && iowr); iord_reg <= iord; port_rd <= (!iord_reg && iord); end // reading ports always @(*) begin case (loa) PORTFE: dout = {1'b1, tape_read, 1'b0, keys_in}; PORTXT: begin case (hoa) XSTAT: dout = {1'b0, pwr_up_reg, FDR_VER, 2'b0, VDAC_VER}; DMASTAT: dout = {dma_act, 7'b0}; RAMPAGE + 8'd2, RAMPAGE + 8'd3: dout = rampage[hoa[1:0]]; default: dout = 8'hFF; endcase end VGSYS: dout = {vg_intrq, vg_drq, 6'b111111}; KJOY: dout = {2'b00, kj_in}; KMOUSE: dout = mus_in; SDCFG: dout = 8'h00; // always SD inserted, SD is in R/W mode SDDAT: dout = sd_dataout; PORTF7: begin if (!a[14] && (a[8] ^ dos) && gluclock_on) dout = wait_read; // $BFF7 - data i/o else dout = 8'hFF; // any other $xxF7 port end COMPORT: dout = wait_read; // $F8EF..$FFEF default: dout = 8'hFF; endcase end // power-up // This bit is loaded as 1 while FPGA is configured // and automatically reset to 0 after STATUS port reading reg pwr_up_reg; reg pwr_up = 1; always @(posedge clk) begin if (iord_s & (loa == PORTXT) & (hoa == XSTAT)) begin pwr_up_reg <= pwr_up; pwr_up <= 1'b0; end end // writing ports //#nnAF localparam VCONF = 8'h00; localparam VPAGE = 8'h01; localparam GXOFFSL = 8'h02; localparam GXOFFSH = 8'h03; localparam GYOFFSL = 8'h04; localparam GYOFFSH = 8'h05; localparam TSCONF = 8'h06; localparam PALSEL = 8'h07; localparam XBORDER = 8'h0F; localparam T0XOFFSL = 8'h40; localparam T0XOFFSH = 8'h41; localparam T0YOFFSL = 8'h42; localparam T0YOFFSH = 8'h43; localparam T1XOFFSL = 8'h44; localparam T1XOFFSH = 8'h45; localparam T1YOFFSL = 8'h46; localparam T1YOFFSH = 8'h47; localparam RAMPAGE = 8'h10; // this covers #10-#13 localparam FMADDR = 8'h15; localparam TMPAGE = 8'h16; localparam T0GPAGE = 8'h17; localparam T1GPAGE = 8'h18; localparam SGPAGE = 8'h19; localparam DMASADDRL = 8'h1A; localparam DMASADDRH = 8'h1B; localparam DMASADDRX = 8'h1C; localparam DMADADDRL = 8'h1D; localparam DMADADDRH = 8'h1E; localparam DMADADDRX = 8'h1F; localparam SYSCONF = 8'h20; localparam MEMCONF = 8'h21; localparam HSINT = 8'h22; localparam VSINTL = 8'h23; localparam VSINTH = 8'h24; localparam DMAWPD = 8'h25; localparam DMALEN = 8'h26; localparam DMACTRL = 8'h27; localparam DMANUM = 8'h28; localparam FDDVIRT = 8'h29; localparam INTMASK = 8'h2A; localparam CACHECONF = 8'h2B; localparam DMAWPA = 8'h2D; localparam XSTAT = 8'h00; localparam DMASTAT = 8'h27; assign dmaport_wr[0] = portxt_wr && (hoa == DMASADDRL); assign dmaport_wr[1] = portxt_wr && (hoa == DMASADDRH); assign dmaport_wr[2] = portxt_wr && (hoa == DMASADDRX); assign dmaport_wr[3] = portxt_wr && (hoa == DMADADDRL); assign dmaport_wr[4] = portxt_wr && (hoa == DMADADDRH); assign dmaport_wr[5] = portxt_wr && (hoa == DMADADDRX); assign dmaport_wr[6] = portxt_wr && (hoa == DMALEN); assign dmaport_wr[7] = portxt_wr && (hoa == DMACTRL); assign dmaport_wr[8] = portxt_wr && (hoa == DMANUM); assign zborder_wr = portfe_wr; assign border_wr = (portxt_wr && (hoa == XBORDER)); assign zvpage_wr = p7ffd_wr; assign vpage_wr = (portxt_wr && (hoa == VPAGE )); assign vconf_wr = (portxt_wr && (hoa == VCONF )); assign gx_offsl_wr = (portxt_wr && (hoa == GXOFFSL)); assign gx_offsh_wr = (portxt_wr && (hoa == GXOFFSH)); assign gy_offsl_wr = (portxt_wr && (hoa == GYOFFSL)); assign gy_offsh_wr = (portxt_wr && (hoa == GYOFFSH)); assign t0x_offsl_wr = (portxt_wr && (hoa == T0XOFFSL)); assign t0x_offsh_wr = (portxt_wr && (hoa == T0XOFFSH)); assign t0y_offsl_wr = (portxt_wr && (hoa == T0YOFFSL)); assign t0y_offsh_wr = (portxt_wr && (hoa == T0YOFFSH)); assign t1x_offsl_wr = (portxt_wr && (hoa == T1XOFFSL)); assign t1x_offsh_wr = (portxt_wr && (hoa == T1XOFFSH)); assign t1y_offsl_wr = (portxt_wr && (hoa == T1YOFFSL)); assign t1y_offsh_wr = (portxt_wr && (hoa == T1YOFFSH)); assign tsconf_wr = (portxt_wr && (hoa == TSCONF)); assign palsel_wr = (portxt_wr && (hoa == PALSEL)); assign tmpage_wr = (portxt_wr && (hoa == TMPAGE)); assign t0gpage_wr = (portxt_wr && (hoa == T0GPAGE)); assign t1gpage_wr = (portxt_wr && (hoa == T1GPAGE)); assign sgpage_wr = (portxt_wr && (hoa == SGPAGE)); assign hint_beg_wr = (portxt_wr && (hoa == HSINT )); assign vint_begl_wr = (portxt_wr && (hoa == VSINTL)); assign vint_begh_wr = (portxt_wr && (hoa == VSINTH)); assign beeper_wr = portfe_wr; wire portfe_wr = (loa==PORTFE) && iowr_s; assign covox_wr = (loa==COVOX) && iowr_s; wire portxt_wr = ((loa==PORTXT) && iowr_s) || regs_we; reg [7:0] rampage[0:3]; assign xt_page = {rampage[3], rampage[2], rampage[1], rampage[0]}; wire lock128 = lock128_3 ? 1'b0 : (lock128_2 ? m1_lock128 : memconf[6]); wire lock128_2 = memconf[7:6] == 2'b10; // mode 2 wire lock128_3 = memconf[7:6] == 2'b11; // mode 3 reg m1_lock128; always @(posedge clk) if (opfetch) m1_lock128 <= !(din[7] ^ din[6]); always @(posedge clk) begin if (rst) begin fmaddr[4] <= 1'b0; intmask <= 8'b1; fddvirt <= 8'b0; sysconf <= 8'h00; // 3.5 MHz memconf <= 8'h04; // no map cacheconf <= 4'h0; // no cache rampage[0]<= 8'h00; rampage[1]<= 8'h05; rampage[2]<= 8'h02; rampage[3]<= 8'h00; end else if (p7ffd_wr) begin memconf[0] <= din[4]; rampage[3] <= {2'b0, lock128_3 ? {din[5], din[7:6]} : ({1'b0, lock128 ? 2'b0 : din[7:6]}), din[2:0]}; end else if (portxt_wr) begin if (hoa[7:2] == RAMPAGE[7:2]) rampage[hoa[1:0]] <= din; if (hoa == FMADDR) fmaddr <= din[4:0]; if (hoa == SYSCONF) begin sysconf <= din; cacheconf <= {4{din[2]}}; end if (hoa == DMAWPD) dmawpdev <= din[1:0]; if (hoa == CACHECONF) cacheconf <= din[3:0]; if (hoa == MEMCONF) memconf <= din; if (hoa == FDDVIRT) fddvirt <= din; if (hoa == INTMASK) intmask <= din; end end // 7FFD port wire p7ffd_wr = !a[15] && (loa==PORTFD) && iowr_s && !lock48; reg lock48; always @(posedge clk) begin if (rst) lock48 <= 1'b0; else if (p7ffd_wr && !lock128_3) lock48 <= din[5]; end // AY control wire ay_hit = (loa==PORTFD) & a[15]; assign ay_bc1 = ay_hit & a[14] & iordwr; assign ay_bdir = ay_hit & iowr; // VG93 wire [3:0] fddvrt = fddvirt[3:0]; wire virt_vg = fddvrt[drive_sel_raw]; wire open_vg = fddvirt[7]; assign drive_sel = {drive_sel_raw[1], drive_sel_raw[0]}; wire vg_wen = (dos || open_vg) && !vdos && !virt_vg; assign vg_cs_n = !(iordwr && vg_port && vg_wen); assign vg_wrFF = iowr_s && vgsys_port && vg_wen; wire vg_wrDS = iowr_s && vgsys_port && (dos || open_vg); assign vdos_on = iordwr_s && (vg_port || vgsys_port) && dos && !vdos && virt_vg; assign vdos_off = iordwr_s && vg_port && vdos; // write drive number reg [1:0] drive_sel_raw; always @(posedge clk) if (vg_wrDS) drive_sel_raw <= din[1:0]; // SD card (Z-controller compatible) wire sdcfg_wr; wire sddat_wr; wire sddat_rd; reg [1:0] spi_cs_n; assign sdcfg_wr = ((loa==SDCFG) && iowr_s && (!dos || vdos)); assign sddat_wr = ((loa==SDDAT) && iowr_s && (!dos || vdos)); assign sddat_rd = ((loa==SDDAT) && iord_s); // SDCFG write - sdcs_n control always @(posedge clk) begin if (rst) spi_cs_n <= 2'b11; else if (sdcfg_wr) spi_cs_n <= {~din[2], din[1]}; end // start signal for SPI module with resyncing to fclk assign sd_start = sddat_wr || sddat_rd; // data for SPI module assign sd_datain = wr ? din : 8'hFF; // xxF7 wire portf7_wr = ((loa==PORTF7) && (a[8]==1'b1) && port_wr && (!dos || vdos)); wire portf7_rd = ((loa==PORTF7) && (a[8]==1'b1) && port_rd && (!dos || vdos)); // EFF7 port reg [7:0] peff7; always @(posedge clk) begin if (rst) peff7 <= 8'h00; else if (!a[12] && portf7_wr && !dos) peff7 <= din; // #EEF7 in dos is not accessible end // gluclock ports wire gluclock_on = peff7[7] || dos; // in dos mode EEF7 is not accessible, gluclock access is ON in dos mode. // comports wire comport_wr = ((loa == COMPORT) && port_wr); wire comport_rd = ((loa == COMPORT) && port_rd); // write to wait registers always @(posedge clk) begin // gluclocks if (gluclock_on && portf7_wr) begin if (!a[14]) wait_write <= din; // $BFF7 - data reg if (!a[13]) wait_addr <= din; // $DFF7 - addr reg end // com ports if (comport_wr) wait_write <= din; // $xxEF if (comport_wr || comport_rd) wait_addr <= a[15:8]; if ((loa==PORTXT) && (hoa == DMAWPA)) wait_addr <= din; end // wait from wait registers // ACHTUNG!!!! here portxx_wr are ON Z80 CLOCK! logic must change when moving to clk strobes assign wait_start_gluclock = (gluclock_on && !a[14] && (portf7_rd || portf7_wr)); // $BFF7 - gluclock r/w assign wait_start_comport = (comport_rd || comport_wr); endmodule