pPropQL

yeti edited this page Jul 7, 2017 · 4 revisions

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

Introduction

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. pPropQl first prototype

Interfacing the propeller to a 5V MC68008

Level shifting

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.

ppropql_dbusr.png

Address bus

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).

ppropql_amux.png

Memory organization

The QL has a simple memory map and pPropQL is hardwired to it.

Memory region Use
0x00000-0x0BFFF ROM/EPROM
0x0C000-0x0FFFF External ROM
0x10000-0x17FFF Unused
0x18000-0x1BFFF I/O
0x1C000-0x1FFFF External I/O
0x20000-0x27FFF Video RAM first screen
0x28000-0x2FFFF Video RAM second screen, used for system variables
0x30000-0x3FFFF User RAM

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!).

ppropql_memdecode

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


Report:

Macrocells 16/36 (45%)
Pterms 56/180 (32%)
Registers 4/36 (12%)
Pins 23/34 (68%)
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.

ppropql_dtack

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. mc68008write 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.

Video

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.

SANY2841r.jpg

At this point the image is 512x256 pixels in a 640x480 frame with a 25 MHz clock. The unused area is regarded as border.

ROM

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.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.