Join GitHub today
A hardware emulator of the SinclairQL using the propeller. For a MC68020 based one see pPropQL020
Note: The original thread at Parallax' forums is here
The SinclairQL launched to the market in 1984 is a MC68008 based computer with custom supporting chips. The simplicity of its design compared to the Amiga or the AtariST make it a suitable candidate to what I call hardware emulator. Using an original processor and replacing the logic and custom chips with some glue logic and 2 propellers it is possible to get a replica. The functionality provided via two propellers, one as video controller and the other one as IO controller is enough, in my humble opinion, to get a functioning QL without using a real QL using any of the ROMS available (JS, JM, Minerva).
The aim of this project is to recreate this machine to the point that unmodified firmware and software can be used in it. Two prototypes have been built so far but more could be built if desired/needed.
Interfacing the propeller to a 5V MC68008
The MC68008 is a 16/32 bit processor, the smallest in the M68K family, with an external 8 bit BUS. It has a 20 bit address space in its 48 pin version and 22 in its 52 PLCC variant. It was fabricated in a NMOS process and requires 5V to operate properly. At the time of designing of the original QL 5V systems where common but today they have been passed out by lower voltage and less power hungry systems. The propeller cannot withstand a 5V signal, it has no 5V tolerant inputs. So several ways of interface exist. The simplest, but not necessarily the best, is to use a current limiting resistor. The potential will be limited by the protection diode incorporated to the inputs. A better and recommended method is to use a level shifter.
8 bit bus
The MC68008 has one bus cycle every 4 clock ticks like the MC68000 but it only has an 8 bit bus so two bus cycles are needed to read instructions limiting the maximum throughput to 1 MIPS @ 8MHz. Despite that the original QL had a cycle-stealing video controller, this design has a mirrored video RAM (mirrored to the HUB RAM) and thus affords faster execution times. The bus is shared between the memory, the processor and the 2 propellers. The propellers are connected using current-limiting resistors and the bus is buffered using a 74HCT245 8 bit buffer.
The address bus that arrives to the propeller is multiplexed using a pair of HCT157 and controlled with the propeller. As only one propeller will access the BUS at a time the two controlling signals (HL0 and HL1) are level-shifted using a pair of tri-state buffers (HCT125).
The QL has a simple memory map and pPropQL is hardwired to it.
|0x20000-0x27FFF||Video RAM first screen|
|0x28000-0x2FFFF||Video RAM second screen, used for system variables|
Using some decoding logic this map can be easily implemented (This decoding logic was implemented in the board show above and in the code below, but it is contained in the CPLD, it remains here for easier understanding!).
An extension to this map is any RAM in the area 0x40000 to 0xFFFFF. The pPropQL provides one decoding signal for the upper 512Kbytes, those could be used for an EPROM (as provided) or for more RAM but a discontinuous area will exist. The code shown below will hold NRAMCS asserted (low) for the area between 0x20000 and 0x7FFFF. The area above is controlled by UCS.
All this glue logic can be put into a CPLD. Even the smallest with only 36 macrocells can do it (XC9536).
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: Pacito.Sys // // Create Date: 09:04:29 01/21/2010 // Design Name: pPropQL glue logic // Module Name: gluelogic // Project Name: pPropQL // Target Devices: XC9536 // Tool versions: Xilinx WebISE 10.1 // Description: Glue logic for the pPropQL // // Dependencies: // // Revision: // Revision 0.02 - Simulated // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module gluelogic( input in_clk, // clock input input in_fc0, // function code 0 input in_fc1, // function code 1 input in_nas, // Adress strobe (Asserted when low) input in_nds, // Data strobe (Asserted when low) input in_rw, // Read-write signal input A15, // input A16, input A17, input A18, input A19, output INTA, // Input to the uP, VMA output NDTACK, // DTACK, asserted when low output HL, // HL signal for the '157s output IOR, // IO read signal output IOW, // IO Write signal output VIDEOW, // Video W output NROMCS, // ROM CS output NRAMCS, // RAM CS output NOE, // OE for the RAM output W, // W for the RAM output PROMCS, // Extra ROMCS for propeller output UCS // Extra 80000..FFFFF chip select ); reg [3:0] r_dtackshift; /* pPropQL Memory Map * * 00000..0FFFF RW NROMCS * 10000..17FFF RW - * 18000..1FFFF R IOR (with waitstates) * 18000..1FFFF W IOW (with waitstates) * 20000..27FFF W VIDEOW, RAMCS (with waitstates) * 20000..27FFF R RAMCS * 28000..7FFFF RW RAMCS */ // Internal signals wire int_r = ~(in_nas | ~(in_fc0 & in_fc1)) | ~in_rw | in_nds; wire int_w = ~(in_nas | ~(in_fc0 & in_fc1)) | in_rw | in_nds; wire int_romcs = A19 | A18 | A17 | A16; wire int_extra = A19 | A18 | A17 | ~A16 | A15; wire int_io = A19 | A18 | A17 | ~A16 | ~A15; wire int_video = A19 | A18 | ~A17 | A16 | A15; wire int_ramcs = A19 | ~int_romcs | ~int_io | ~int_extra; // ** Outputs ** assign INTA = in_nas | ~(in_fc0 & in_fc1); // Chip selects assign NROMCS = int_romcs | int_r; assign PROMCS = int_romcs | int_r; assign NRAMCS = int_ramcs | in_nds; assign VIDEOW = int_video | int_w; assign IOR = int_r | int_io; assign IOW = int_w | int_io; assign UCS = ~A19 | in_nds; // Extra outputs assign NOE = int_r; assign W = int_w; // DTACK generation wire int_slow = VIDEOW & IOR & IOW; assign NDTACK = (r_dtackshift < 14) & ~int_slow; // High, low signal assign HL = r_dtackshift > 1; always @(posedge in_clk) begin if (in_nas) r_dtackshift <= 0; else if (~int_slow) begin r_dtackshift <= r_dtackshift + 1; end end initial begin r_dtackshift = 0; end endmodule
|Function blocks||26/72 (37%)|
Interfacing to software emulated peripherals
The fun part is actually the implementation of all I/O by means of two Propellers. One for video (32k RAM used) and the other one for the rest. The microdrives are replaced with a SD card. The keyboard is emulated using a PS/2 keyboard, the RTC is a DS1307 and one serial port is also provided.
As the 68K has an asynchronous bus, the bus termination signals are generated by the video propeller and level-shifted using a HCT125 tri-state buffer. This circuit defaults to 4 clock bus cycles for fastest memory access. The Video RAM is mirrored to one of the propellers and thus these write (but not read) accesses can also be slower. Note: Due to software development it has been noted that 500 ns for I/O access is not enough. The DTACK signal is generated then using a CPLD with code shown above.
To successfully interface to the 68K for I/O, the propeller has to latch the address bus, read or write onto the data bus between the stipulated time. For sake of argument let's think that a time of 500 ns is enough. Asynchronous write cycle. Note that DTACK has to be valid before the transition S4->S5. The code below only uses the low part of the address bus and does not touch the DTACK signal, because it is generated automatically.
IOR mov DIRA, #0 ior_loop waitpne c0_IOR, c0_IOR or DIRA, c0_SHIADDR ' Selects low address (0) mov c0_addrl, INA shr c0_addrl, #8 and c0_addrl, #255 ' low address rdbyte OUTA, c0_addrl ' reads data or DIRA, #DATABUS ' DATA bus is output nop nop nop mov DIRA, c0_dira_def ' Data bus as INPUTS jmp #ior_loop ' restarts loop c0_SHIADDR long %000_0_0_0_0_1_00000000_00000000_00000000 ' When high high address
In the example above a very simple mechanism where one COG answers the 68K with what is available in HUB RAM in the first 256 bytes. The QL only uses a few ports in this 32kbytes area, so all the address decode is not necessary. Housekeeping COGs are going to fill the HUB with the right values when an event occurs. Another method is to perform all the tasks, i.e. housekeeping, within the answering COG delaying the processor.
One of the two propellers acts as a video controller. The QL has only 2 modes in its standard form. 256x256 8 colors and 512x256 4 colors. An image of a working driver for 512x256 4 colors is shown below.
At this point the image is 512x256 pixels in a 640x480 frame with a 25 MHz clock. The unused area is regarded as border.
The goal is to use an unmodified JS or JM ROM. A Minerva ROM could also be used. In the sense of speeding up few parts of the IO, some modifications could be implemented. Specially slow is the access to the i8048, keyboard controller among other things.