diff --git a/cores/ql/mdv.v b/cores/ql/mdv.v index 3949b14..35bad04 100644 --- a/cores/ql/mdv.v +++ b/cores/ql/mdv.v @@ -24,6 +24,8 @@ module mdv ( input clk, // 21mhz clock input reset, + input reverse, + input sel, // control bits @@ -60,8 +62,6 @@ assign tx_empty = 1'b0; // the part of ram which is unavailable to the 68k CPU (>16MB). It is then continously // replayed from there at 200kbit/s -reg [7:0] mdv_sector /* synthesis noprune */; - reg [24:0] mdv_end /* synthesis noprune */; // determine mdv image size after download @@ -134,7 +134,6 @@ always @(posedge mdv_clk) begin mdv_gap_cnt <= 10'd0; // count bytes until gap mdv_gap_state <= 1'b1; // toggle header + data gap mdv_gap_active <= 1'b1; // gap atm - mdv_sector <= 8'd0; mdv_gap <= 1'b1; end else begin mdv_gap_cnt <= mdv_gap_cnt + 10'd1; @@ -162,18 +161,17 @@ always @(posedge mdv_clk) begin mdv_gap_active <= 1'b1; // now comes a gap mdv_gap <= 1'b1; - // The sectors on cartridges are written in descending order - // The images seem to contain them in ascending order. So we - // have to replay them backwards for better performance + if(reverse) begin + // The sectors on cartridges are written in descending order + // Some images seem to contain them in ascending order. So we + // have to replay them backwards for better performance - if(mem_addr == BASE_ADDR + 343 - 1) - mem_addr <= mdv_end - 343 + 1; - else - mem_addr <= mem_addr - 2*343 + 1; - - mdv_sector <= mdv_sector + 8'd1; + if(mem_addr == BASE_ADDR + 343 - 1) + mem_addr <= mdv_end - 343 + 1; + else + mem_addr <= mem_addr - 2*343 + 1; + end end - end end end diff --git a/cores/ql/qimi.v b/cores/ql/qimi.v new file mode 100644 index 0000000..bf2a4a0 --- /dev/null +++ b/cores/ql/qimi.v @@ -0,0 +1,150 @@ +// +// qimi.v - Ql mouse Interface +// +// Sinclair QL for the MiST +// https://github.com/mist-devel +// +// Copyright (c) 2015 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +module qimi ( + input clk, + input reset, + + input cpu_sel, + input [1:0] cpu_addr, // a5, a1 + output [7:0] cpu_data, + output reg irq, + + // ps2 interface + input ps2_clk, + input ps2_data +); + +// generate irq ack whenever cpu accesses 1bf9c +reg irq_ack; +always @(negedge clk) begin + irq_ack <= 1'b0; + if(cpu_sel && cpu_addr == 2'b11) + irq_ack <= 1'b1; +end + +wire x_dir = !x_pos[9]; +wire y_dir = !y_pos[9]; +wire x_mov = (x_pos != 0); +wire y_mov = (y_pos != 0); + +assign cpu_data = + (cpu_addr == 2'b00)?{2'b00,!b[0],!b[1],4'b0000}: // 1bf9c + (cpu_addr == 2'b10)?{2'b00,y_mov,x_dir,1'b0,x_mov,1'b0,y_dir }: // 1bfbc + 8'h00; + +// registers keeping state of current mouse state +reg [1:0] b; +reg [9:0] x_pos; +reg [9:0] y_pos; + +wire [7:0] byte; +wire valid; +wire error; + +reg [1:0] cnt; + +reg sign_x, sign_y; +reg [7:0] x_reg; + +// counter to limit irq rate to 2khz +reg [9:0] irq_holdoff; + +always @(posedge clk) begin + if(reset) begin + x_pos <= 10'd0; + y_pos <= 10'd0; + cnt <= 2'd0; + irq <= 1'b0; + irq_holdoff <= 10'd0; + end else begin + // check if we have to fire another irq + if(irq_holdoff != 0) begin + irq_holdoff <= irq_holdoff - 10'd1; + if((irq_holdoff == 1) && (x_mov || y_mov)) + irq <= 1'b1; + end + + // ps2 decoder has received a valid byte + if(valid) begin + // count through all three data bytes + cnt <= cnt + 2'd1; + + if(cnt == 0) begin + // bit 3 must be 1. Stay in state 0 otherwise + if(!byte[3]) cnt <= 2'd0; + b <= byte[1:0]; + sign_x <= byte[4]; + sign_y <= byte[5]; + end else if(cnt == 1) begin + x_reg <= byte; + end else begin + // the ps2 packet contains a 9 bit value. We only use the upper + // 7. Otherwise the mouse would be too fast for our low resolution + x_pos <= x_pos + { {2{sign_x}}, x_reg}; + y_pos <= y_pos + { {2{sign_y}}, byte}; + cnt <= 2'd0; + + if(irq_holdoff == 0) + irq_holdoff <= 10'd1; + end + end else begin + // one bit has been reported to host + if(irq_ack) begin + if(x_pos != 0) begin + if(x_pos[9]) x_pos <= x_pos + 10'd1; + else x_pos <= x_pos - 10'd1; + end + + if(y_pos != 0) begin + if(y_pos[9]) y_pos <= y_pos + 10'd1; + else y_pos <= y_pos - 10'd1; + end + + // clear irq + irq <= 1'b0; + + // next irq after some time ... + irq_holdoff <= 10'd1000; + end + end + end +end + +// the ps2 decoder has been taken from the zx spectrum core +ps2_intf ps2_mouse ( + .CLK ( clk ), + .nRESET ( !reset ), + + // PS/2 interface + .PS2_CLK ( ps2_clk ), + .PS2_DATA ( ps2_data ), + + // Byte-wide data interface - only valid for one clock + // so must be latched externally if required + .DATA ( byte ), + .VALID ( valid ), + .ERROR ( error ) +); + + +endmodule diff --git a/cores/ql/ql.qsf b/cores/ql/ql.qsf index f8fa771..6103dae 100644 --- a/cores/ql/ql.qsf +++ b/cores/ql/ql.qsf @@ -155,6 +155,7 @@ set_global_assignment -name FITTER_EFFORT "FAST FIT" set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" set_instance_assignment -name CURRENT_STRENGTH_NEW "MAXIMUM CURRENT" -to VGA_* +set_global_assignment -name VERILOG_FILE qimi.v set_global_assignment -name VHDL_FILE "t49_rom-struct-a.vhd" set_global_assignment -name VHDL_FILE "t48/rtl/vhdl/system/t49_rom-e.vhd" set_global_assignment -name VHDL_FILE t48/rtl/vhdl/system/generic_ram_ena.vhd diff --git a/cores/ql/ql.v b/cores/ql/ql.v index 8da8a51..90b44d4 100644 --- a/cores/ql/ql.v +++ b/cores/ql/ql.v @@ -62,13 +62,14 @@ module ql ( parameter CONF_STR = { "QL;;", "F1,MDV;", - "O2,RAM,128k,640k;", - "O3,Video mode,PAL,NTSC;", - "O4,Scanlines,Off,On;", - "T5,Reset" + "O2,MDV direction,normal,reverse;", + "O3,RAM,128k,640k;", + "O4,Video mode,PAL,NTSC;", + "O5,Scanlines,Off,On;", + "T6,Reset" }; -parameter CONF_STR_LEN = 4+7+17+23+20+8; +parameter CONF_STR_LEN = 4+7+32+17+23+20+8; // the status register is controlled by the on screen display (OSD) wire [7:0] status; @@ -78,6 +79,7 @@ wire [1:0] buttons; wire [7:0] js0, js1; wire ps2_kbd_clk, ps2_kbd_data; +wire ps2_mouse_clk, ps2_mouse_data; // generate ps2_clock wire ps2_clock = ps2_clk_div[6]; // ~20khz @@ -104,6 +106,8 @@ user_io #(.STRLEN(CONF_STR_LEN)) user_io ( .ps2_clk ( ps2_clock ), .ps2_kbd_clk ( ps2_kbd_clk ), .ps2_kbd_data ( ps2_kbd_data ), + .ps2_mouse_clk ( ps2_mouse_clk ), + .ps2_mouse_data ( ps2_mouse_data ), .status ( status ) ); @@ -242,9 +246,9 @@ zx8301 zx8301 ( .clk_video ( clk10 ), .video_cycle ( video_cycle ), - .ntsc ( status[3] ), + .ntsc ( status[4] ), .scandoubler ( !tv15khz ), - .scanlines ( status[4] ), + .scanlines ( status[5] ), .clk_bus ( clk2 ), .cpu_cs ( zx8301_cs ), @@ -271,7 +275,7 @@ wire rom_download = dio_download && (dio_index == 0); reg [11:0] reset_cnt; wire reset = (reset_cnt != 0); always @(posedge clk2) begin - if(buttons[1] || status[0] || status[5] || !pll_locked || rom_download) + if(buttons[1] || status[0] || status[6] || !pll_locked || rom_download) reset_cnt <= 12'hfff; else if(reset_cnt != 0) reset_cnt <= reset_cnt - 1; @@ -300,12 +304,13 @@ zx8302 zx8302 ( .clk_sys ( CLOCK_27[0] ), .clk ( clk21 ), + .xint ( qimi_irq ), .ipl ( cpu_ipl ), .led ( LED ), .audio ( audio ), // CPU connection - .clk_bus ( clk2 ), + .clk_bus ( clk2 ), .cpu_sel ( zx8302_sel ), .cpu_wr ( cpu_wr ), .cpu_addr ( zx8302_addr ), @@ -329,11 +334,35 @@ zx8302 zx8302 ( .mdv_men ( mdv_men ), .video_cycle ( video_cycle ), + .mdv_reverse ( status[2] ), + .mdv_download ( mdv_download ), .mdv_dl_addr ( dio_addr ) ); +// --------------------------------------------------------------------------------- +// --------------------------- QIMI compatible mouse ------------------------------- +// --------------------------------------------------------------------------------- + +// qimi is at 1bfxx +wire qimi_sel = cpu_io && (cpu_addr[13:8] == 6'b111111); +wire [7:0] qimi_data; +wire qimi_irq; + +qimi qimi( + .reset ( reset ), + .clk ( clk2 ), + + .cpu_sel ( qimi_sel ), + .cpu_addr ( { cpu_addr[5], cpu_addr[1] } ), + .cpu_data ( qimi_data ), + .irq ( qimi_irq ), + + .ps2_clk ( ps2_mouse_clk ), + .ps2_data ( ps2_mouse_data ) +); + // --------------------------------------------------------------------------------- // -------------------------------------- CPU -------------------------------------- // --------------------------------------------------------------------------------- @@ -341,13 +370,16 @@ zx8302 zx8302 ( // address decoding wire cpu_io = ({cpu_addr[19:14], 2'b00} == 8'h18); // internal IO $18000-$1bffff wire cpu_bram = (cpu_addr[19:17] == 3'b001); // 128k RAM at $20000 -wire cpu_xram = status[2] && ((cpu_addr[19:18] == 2'b01) || +wire cpu_xram = status[3] && ((cpu_addr[19:18] == 2'b01) || (cpu_addr[19:18] == 2'b10)); // 512k RAM at $40000 if enabled wire cpu_ram = cpu_bram || cpu_xram; // any RAM wire cpu_rom = (cpu_addr[19:16] == 4'h0); // 64k ROM at $0 wire cpu_mem = cpu_ram || cpu_rom; // any memory mapped to sdram -wire [15:0] io_dout = cpu_addr[6]?16'h0000:zx8302_dout; +wire [15:0] io_dout = + qimi_sel?{qimi_data, qimi_data}: + (!cpu_addr[6])?zx8302_dout: + 16'h0000; // demultiplex the various data sources wire [15:0] cpu_din = diff --git a/cores/ql/readme.md b/cores/ql/readme.md index c452e0b..131c431 100644 --- a/cores/ql/readme.md +++ b/cores/ql/readme.md @@ -3,4 +3,23 @@ QL for MIST This is an implementation of the Sinclair QL for the MIST board. -It's based on the TG68K CPU core. +It's based on the TG68K CPU core and the t48 core for the IPC. + +Features: +- tg68k + - 128k or 640k + - roughly twice the original speed +- zx8301 + - all video modes incl. blinking + - scan doubler for VGA output (can be disabled) + - optional scan line effect +- zx8302 + - IPC based on t48 using the original firmware + - audio + - keyboard + - PC keyboard shortcuts (backspace, ...) + - joysticks + - interrupt handling + - microdrive emulation + - qlay format +- qimi mouse diff --git a/cores/ql/zx8302.v b/cores/ql/zx8302.v index 264d9ec..86ac1fb 100644 --- a/cores/ql/zx8302.v +++ b/cores/ql/zx8302.v @@ -28,7 +28,8 @@ module zx8302 ( // interrupts output [1:0] ipl, - + input xint, + // sdram interface for microdrive emulation output [24:0] mdv_addr, input [15:0] mdv_din, @@ -39,7 +40,8 @@ module zx8302 ( // interface to watch MDV cartridge upload input [24:0] mdv_dl_addr, input mdv_download, - + + input mdv_reverse, output led, output audio, @@ -129,8 +131,8 @@ wire [7:0] io_status = { zx8302_comdata_in, ipc_busy, 2'b00, assign cpu_dout = // 18000/18001 and 18002/18003 - (cpu_addr == 2'b00)?rtc[46:31]: - (cpu_addr == 2'b01)?rtc[30:15]: + (cpu_addr == 2'b00)?rtc[47:32]: + (cpu_addr == 2'b01)?rtc[31:16]: // 18020/18021 and 18022/18023 (cpu_addr == 2'b10)?{io_status, irq_pending}: @@ -195,7 +197,7 @@ ipc ipc ( // --------------------------------------------------------------------------------- wire [7:0] irq_pending = {1'b0, (mdv_sel == 0), clk64k, - 1'b0, vsync_irq, 1'b0, 1'b0, gap_irq }; + xint_irq, vsync_irq, 1'b0, 1'b0, gap_irq }; reg [2:0] irq_mask; reg [4:0] irq_ack; @@ -219,6 +221,14 @@ always @(posedge gap_irq_in or posedge gap_irq_reset) begin else gap_irq <= 1'b1; end +// toggling the mask will also trigger irqs ... +wire xint_irq_in = xint && irq_mask[2]; +reg xint_irq; +wire xint_irq_reset = reset || irq_ack[4]; +always @(posedge xint_irq_in or posedge xint_irq_reset) begin + if(xint_irq_reset) xint_irq <= 1'b0; + else xint_irq <= 1'b1; +end // --------------------------------------------------------------------------------- @@ -233,11 +243,13 @@ wire [7:0] mdv_byte; assign led = !mdv_sel[0]; mdv mdv ( - .clk ( clk ), + .clk ( clk ), .reset ( init ), .sel ( mdv_sel[0] ), + .reverse ( mdv_reverse ), + // control bits .gap ( mdv_gap ), .tx_empty ( mdv_tx_empty ), @@ -267,9 +279,9 @@ always @(negedge mctrl[1]) // --------------------------------------------------------------------------------- // PLL for the real time clock (rtc) -reg [46:0] rtc; +reg [47:0] rtc; always @(posedge clk64k) - rtc <= rtc + 47'd1; + rtc <= rtc + 48'd1; wire clk64k; pll_rtc pll_rtc (