Close
0%
0%

Project: MillRACE

My entry for the RetroChallenge2021

Similar projects worth following
Run an "unmodified" Jupiter ACE ROM on the RC2014.

Yes, I thought that I might enter the RetroChallenge 2021/11.

The Jupiter ACE has always fascinated me; a machine created in the early 80’s. It used an implementation of Forth, for its “operating system”, rather than the much more common BASIC.

It’s a machine that I’ll probably never own. So, why not recreate it in the form of the RC2014? Why not create/adapt add-ons to allow the RC2014 to run the Jupiter ACE ROM image unmodified?

Yes, it’s easy enough to run a Jupiter ACE in an emulator, on a Raspberry PI, but where is the fun in that? I want to learn how the 80’s computer worked! I’m a software engineer, by trade, so hardware design is quite unknown to me…

I perfectly accept that this probably is not very challenging, to most. Personally, I think that real challenge is, for me, to actually finish something…

  • Don't scream, it's the screen...

    Justin Skists10/08/2021 at 17:14 0 comments

    Screen layout

    As we know, the jupiter ACE screen is a character based layout. Effectively, each character on the 32x24 layout is a pointer to the character RAM with pixel rows that define the graphic of the character.

    An interesting thing to note, is that the Character RAM only has room for 128 characters. The highest bit (7) indicates whether the character is to be rendered in reverse video, or not.

    Test rendering

    Here is an image of the software that will run on the Raspberry PI Zero, attached to the MillRACE Video module.

    Each character is defined as a block  of rows in incremental values from 0 to 0x7F. And the screen RAM is filled in a 0-0xFF cycle.

    The rendering is the easy bit ... now I need to read the data from the real RAM!

  • Reading the screen memory

    Justin Skists10/07/2021 at 18:22 0 comments

    Screen memory

    We know from a previous log that the screen is 768bytes @  0x2400 and the character map is 1024bytes @ 0x2C00.

    Hardware

    The PIC will be acting as a bridge between the Raspberry PI (used to render the screen image to a HDMI viewing device) and the Z80 bus. The PI will command the PIC to read a portion of the Z80 memory space, and then provide it as a SPI byte-stream. Once the PI has collected the required data, it will render it its own framebuffer.

    State machine

    I love state-machines! Here is a draft of the PIC's:

    Whilst the PIC is in IDLE state, it is prepared to receive commands from the PI over the SPI bus. One such command will be to fetch the "status" of the PIC, if one is signalled, and another is to request to read a block of memory.

    In the READ_DATA state, PIC has requested control of the Z80 bus, and is currently reading the requested number of bytes from memory space.

    Once all the data has been read from the Z80 bus, it goes into the XFER_DATA state. This is when the PI can read the collected bytes over the SPI.

    Once the data has been transferred, the PIC goes back to the IDLE state.

    The hardware

    As an 8-bit PIC, there is no SPI FIFO, so the speed can not be very fast.

    The PIC is perfectly happy with 3v3 as a "logic high". I plan to use a resistor-based voltage-divider for the PIC-to-PI signals....with the expected reduction speed of the SPI for the PIC to manage, I'm sure the voltage-divider will be fine. :) 

  • Tick-tock, tick-tock...

    Justin Skists10/06/2021 at 14:01 0 comments

    Frame interrupt

    The Jupiter ACE only had one interrupt source. It was a 50Hz (presumably, 60Hz in the USA) "frame" interrupt.

    The Jupiter ACE ROM listing shows that the ROM uses the HALT instruction to slow down printing of VLIST.

    The Reference manual has example of uing the HALT instruction to slow things down, too.

    Implementation

    We will use a timer on the PIC on the MillRACE IO module to generate the interrupt.

    The RC2014 Z80 CPU module uses an external pull-up resistor to keep the interrupt line high. This would mean that, if we wanted to assert the interrupt-line, then an open-drain GPIO output should be used. However, the PIC we're using doesn't have open-drain pins. 

    So, to give the same behaviour, we'll do the following to pulse the interrupt line:

    • set the pin to input
    • latch a 0
    • do
      • when timer fires, switch pin to output (assert INT)
      • wait a short time
      • switch pin to input (de-assert INT)
    • repeat

    How long do we pulse the interrupt line for?

    Rough maths: According to this page, we need pulse for at least 23 Z80 clock ticks. As the PIC (running at 16MHz) is running twice as fast as the Z80 (running at 7.3MHz), the PIC will need pulse the INT line at least 51 ((32*16)/7.3) PIC clock cycles. With each PIC NOP instruction taking 4 clock cycles, there will need to be at least 13 (51/4) NOPs between assertion and de-assertion....let's round it to 16.

    The future?

    If I were to make this a more general purpose module, I would normally like devices to own up to interrupt assertions. if this frame interrupt happened at the same time as, say, a serial interrupt, I would like to know that I need to do something with the frame interrupt.

    So, what I would probably do, is program the PIC to place a 1, indicating that the "frame" interrupt has pulsed, on BIT[7] of the input port. This would then be cleared on a read or on a write of 0x80.

    Yes, I'm thinking about stretch goals already....

  • Crazy Memory

    Justin Skists10/03/2021 at 16:20 0 comments

    Memory Map

    What can I say about the Jupiter memory map?

    A basic Jupiter ACE memory map looks a little like this:

    Yes, it's a little crazy, using a mixture of dual-port RAMs and a liberal requirement of address decoding...

    According to this little document, the ROM only accesses the video memory using the 0x2400-0x26FF range.

    This is a little "phew" moment, since I wasn't sure how to handle the mirrored memory using stock RC2014 memory boards. Without double checking, I'm really hoping the ROM does the same thing with the character RAM.

    Obviously, some applications/demos are likely to optimise for performance and use the non-contended video access RAM locations, along with the mirrored memory spaces. However, as they say in the corporate world, when greeted with a challenging, but not-specifically-written-down use-cases: "It's not in the requirements!"

    My requirements are to get the Jupiter ACE ROM running on the RC2014.

    Therefore, I think, for the ROM, the video module should be OK with reading 0x2400-0x26FF for the video RAM, and 0x2C00-0x2FFF for the character RAM.

  • IO Ports

    Justin Skists10/02/2021 at 16:59 0 comments

    IN, OUT, IN, OUT, shake it all about!

    According to this IO document, there is only one official Z80 port used, at 0xFE. Actually, if you look at the schematics, only ADDRESS[0] is decoded...so, any even address would trigger it!

    Output

    On an OUT instruction to to 0xFE, only BIT[3] is really used; the MIC. This, assumably, would send high/low signals to the tape recorder to save files.

    Input

    The input of port 0xFE is a little more interesting:

    BIT[5] is the current level of the incoming tape signal for loading from the tape.

    BIT[4:0] is a sampling of the keyboard matrix. The current row is selected using the Z80 ADDRESS[15:8] during the IN instruction read. 

    There is plenty of spare bits in the port for future use. For example, the ZX Spectrum uses BIT[4] on the output for speaker control, and BIT[2:0] for border colour...

    Speaker

    So, what about the speaker? The Jupiter ACE had a speaker, and it needs to be controlled. Shouldn't there be a BIT in the 0xFE port assigned to it?

    Well, looking at the schematics, there is a flip-flop used in the speaker. Why? Well, think about how a speaker diaphram works, to make sound: in, out, in, out, in, out...

    Looking at the schematics, any write to 0xFE pushes the speaker's diaphragm out, and any read from 0xFE pulls the diaphragm in. IN, OUT, IN, OUT....

    The Jupiter ACE ROM listing seems to confirm it, with the BEEP word.

  • Initial thoughts...

    Justin Skists09/12/2021 at 16:49 0 comments

    Overall

    In this project, I am not going for full Jupiter ACE compatibility. I'd like to work toward it, but there is no way I'l be able to complete it by the end of October. My challenge is to run the official ROM, not every single demo! :)

    My initial thoughts are to take a standard RC2014 with the following items, and add my new "challenge" modules:

    • Enhanced backplane
    • Z80 Module
    • Dual clock module (currently at 7.3MHz, with option to support external clock for the Jupiter ACE frequency)
    • Pageable ROM module (flashed with Jupiter ACE ROM image)
    • Pageable RAM module

    The Interface Module

    The Jupiter ACE had very simple I/O. Basically, everything on-board was accessed by a single Z80 I/O port: 0xFE. (To be honest, looking at the schematics, it's actually accessable by every odd-numbered port address! But, officially, it's 0xFE)

    This port gave access to:

    • the keyboard
    • the speaker
    • the EAR socket (for tape loading)
    • the MIC socket (for tape saving)

    This is what I'm thinking of:

    I originally choose the PIC18F4520, as it has the "parallel slave port", for microprocessor interfacing. Handy!

    However, I didn't beleive the microcontroller could read the "keyboard", to place the value into the port, quick enough before the Z80 finished its read-cycle. So, I would need to choose another microcontroller; probably PIC16F1509 or PIC18F26K22 (as I know that I have at least one of them lying around), and use a couple of 74LS' chips to implement the read and write port.

    I'll probably not get tape saving/loading completed in this challenge, but I'm leaving it open for future development. The reason for using the PIC, is that it should be possible to process the tape audio and save it to EEPROM, output hex-file through a UART, or similar. Also, the PIC will provide the 50Hz frame interrupt.

    Video  module

    The Jupiter ACE has a nice memory-mapped character-based monochrome video. It was implemented using TTL logic!

    There is no way I'm going to be able to replicate it in the time given.

    So, I'll be taking my inspiration from the BusRaider module. In this case, a PIC microcontroller will read the area of RAM, that hold the the Jupiter ACE's screen and character map, which will then be passed to the Raspberry PI (via SPI) to decode and display to screen.

    My choice of the PIC18F46K22 is because it is a 40pin IC, plenty of pins to access the Z80 bus, and has enough RAM to read and store the area of memory read for Jupiter ACE, before passing it to the Raspberry PI.

    Yes, using the Raspberry PI as a vdeo card is cheating, but there is precedent for its use in the RC2014 world. I'm not a hardware engineer, and I don't have the tools to create and debug a full-on TTL VGA/HDMI video interface, so I intend to push that problem into a domain I'm much more familiar with: software.

    I'm fully aware that I won't get 50Hz frame rate using this setup. I'm just hoping for enough frames-per-second (FPS) to make it usable -- to be honest, I'd probably be happy with just 1 FPS, as long as I can see something!

View all 6 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates